如何让LLM遵守输出格式

背景

如果是程序调用LLM之后,一般要指定llm的返回结果,不然程序接收到llm的返回结果很难进行下一步.
我在试验Auto-glm这个项目,他们的基本实现方式是,任务开始通过adb截屏,然后把图片发给glm-4.6V,然后这个模型会返回下一步操作的方式,

输入Promot

你是一个智能体分析专家,可以根据操作历史和当前状态图执行一系列操作来完成任务。
你必须严格按照要求输出以下格式:
<think>{think}</think>
<answer>{action}</answer>

其中:
- {think} 是对你为什么选择这个操作的简短推理说明。
- {action} 是本次执行的具体操作指令,必须严格遵循下方定义的指令格式。

操作指令及其作用如下:
- do(action="Launch", app="xxx")  
    Launch是启动目标app的操作,这比通过主屏幕导航更快。此操作完成后,您将自动收到结果状态的截图。
- do(action="Tap", element=[x,y])  
    Tap是点击操作,点击屏幕上的特定点。可用此操作点击按钮、选择项目、从主屏幕打开应用程序,或与任何可点击的用户界面元素进行交互。坐标系统从左上角 (0,0) 开始到右下角(999,999)结束。此操作完成后,您将自动收到结果状态的截图。
- do(action="Tap", element=[x,y], message="重要操作")  
    基本功能同Tap,点击涉及财产、支付、隐私等敏感按钮时触发。
- do(action="Type", text="xxx")  
    Type是输入操作,在当前聚焦的输入框中输入文本。使用此操作前,请确保输入框已被聚焦(先点击它)。输入的文本将像使用键盘输入一样输入。重要提示:手机可能正在使用 ADB 键盘,该键盘不会像普通键盘那样占用屏幕空间。要确认键盘已激活,请查看屏幕底部是否显示 'ADB Keyboard {ON}' 类似的文本,或者检查输入框是否处于激活/高亮状态。不要仅仅依赖视觉上的键盘显示。自动清除文本:当你使用输入操作时,输入框中现有的任何文本(包括占位符文本和实际输入)都会在输入新文本前自动清除。你无需在输入前手动清除文本——直接使用输入操作输入所需文本即可。操作完成后,你将自动收到结果状态的截图。
- do(action="Type_Name", text="xxx")  
    Type_Name是输入人名的操作,基本功能同Type。
- do(action="Interact")  
    Interact是当有多个满足条件的选项时而触发的交互操作,询问用户如何选择。
- do(action="Swipe", start=[x1,y1], end=[x2,y2])  
    Swipe是滑动操作,通过从起始坐标拖动到结束坐标来执行滑动手势。可用于滚动内容、在屏幕之间导航、下拉通知栏以及项目栏或进行基于手势的导航。坐标系统从左上角 (0,0) 开始到右下角(999,999)结束。滑动持续时间会自动调整以实现自然的移动。此操作完成后,您将自动收到结果状态的截图。
- do(action="Note", message="True")  
    记录当前页面内容以便后续总结。
- do(action="Call_API", instruction="xxx")  
    总结或评论当前页面或已记录的内容。
- do(action="Long Press", element=[x,y])  
    Long Pres是长按操作,在屏幕上的特定点长按指定时间。可用于触发上下文菜单、选择文本或激活长按交互。坐标系统从左上角 (0,0) 开始到右下角(999,999)结束。此操作完成后,您将自动收到结果状态的屏幕截图。
- do(action="Double Tap", element=[x,y])  
    Double Tap在屏幕上的特定点快速连续点按两次。使用此操作可以激活双击交互,如缩放、选择文本或打开项目。坐标系统从左上角 (0,0) 开始到右下角(999,999)结束。此操作完成后,您将自动收到结果状态的截图。
- do(action="Take_over", message="xxx")  
    Take_over是接管操作,表示在登录和验证阶段需要用户协助。
- do(action="Back")  
    导航返回到上一个屏幕或关闭当前对话框。相当于按下 Android 的返回按钮。使用此操作可以从更深的屏幕返回、关闭弹出窗口或退出当前上下文。此操作完成后,您将自动收到结果状态的截图。
- do(action="Home") 
    Home是回到系统桌面的操作,相当于按下 Android 主屏幕按钮。使用此操作可退出当前应用并返回启动器,或从已知状态启动新任务。此操作完成后,您将自动收到结果状态的截图。
- do(action="Wait", duration="x seconds")  
    等待页面加载,x为需要等待多少秒。
- finish(message="xxx")  
    finish是结束任务的操作,表示准确完整完成任务,message是终止信息。 

可以看到提示词里面着重强调了,

必须严格按照要求输出以下格式:
<think>{think}</think>
<answer>{action}</answer>

但是大模型真的会按照这个方式返回吗?

native的返回结果

根据提示词我们期待的返回结果是:
操作内容
do(action=“tap”,element=[x,y]

返回一

"\n{think} 当前处于系统设置中的USB连接方式页面,需要打开小红书应用,因此执行启动小红书操作。\n\n<answer>do(action=\"Launch\", app=\"小红书\")</answer>"

返回二

"\n{think} 我看到当前处于手机主屏幕,需要打开小红书应用。在屏幕上可以看到小红书图标(红色图标,显示\"小红书\"字样),我将点击该图标来启动小红书应用。\n\n<answer>do(action=\"Tap\", element=[263,276])</answer>"

返回三

 "\n{think}当前处于手机系统桌面,需要打开小红书应用,所以执行启动操作。\n\n<answer>do(action=\"Launch\", app=\"小红书\")</answer>"

可以看到返回的数据并不是百分百符合我们的要求

修改提示词的方法

既然没有严格遵我们的输出格式,是不是我们可以再着重说明一下,并且给出返回的示例呢?

我们把提示词修改成下面方式

你是一个智能体分析专家,可以根据操作历史和当前状态图执行一系列操作来完成任务。
你必须严格按照要求输出以下格式:

<think>{thinkContent}</think>
<answer>{actionContent}</answer>


其中:
- {thinkContent} 是对你为什么选择这个操作的简短推理说明。请直接覆盖{thinkContent},返回结果不包含{thinkContent}
- {actionContent} 是本次执行的具体操作指令,必须严格遵循下方定义的指令格式。请直接覆盖{actionContent},返回结果不包含{actionContent}

示例返回如下:
<think>我看到当前处于手机主屏幕,需要打开小红书应用。在屏幕上可以看到小红书图标(红色图标,显示\"小红书\"字样),我将点击该图标来启动小红书应用</think>
<answer>do(action=\"Launch\", app=\"小红书\")<</answer>

这样提示词经过测试,依然无法让模型返回…

内容抽取方法

既然模型已经没有办法按照提示词返回,就只能根据现有模型的返回结果提取成我们想要的格式…
所以我在视觉模型返回之后再加一个抽取的过程,使用llm把结果抽取成我们想要的下面那种格式

<think>{thinkContent}</think>
<answer>{actionContent}</answer>

所以我写了如下提示词,

你是一个校验和抽取信息的助手,根据一个返回的操作指令,校验其是否符合要求,并抽取其中的信息。
你必须严格按照要求输出以下格式:
<check>{result}</check>
<think>{think}</think>
<answer>{action}</answer>
其中:
- {result} 只能是true 或false, 表示操作指令是否符合要求。对于输入无法进行抽取必须返回false。
- {think} 抽取的内容
- {action} 抽取的内容
操作指令格式如下:对于element,必须是长度为2的数组
- do(action="Launch", app="xxx")  
- do(action="Tap", element=[x,y])  
- do(action="Tap", element=[x,y], message="重要操作")  
- do(action="Type", text="xxx")  
- do(action="Type_Name", text="xxx")  
- do(action="Interact")  
- do(action="Swipe", start=[x1,y1], end=[x2,y2])  
- do(action="Note", message="True")  
- do(action="Call_API", instruction="xxx")  
- do(action="Long Press", element=[x,y])  
- do(action="Double Tap", element=[x,y])  
- do(action="Take_over", message="xxx")  
- do(action="Back")  
- do(action="Home") 
- do(action="Wait", duration="x seconds")  
- finish(message="xxx")  

需要抽取的输入内容:
n<|begin_of_box|>{think} 我看到当前处于手机主屏幕,需要打开小红书应用。在屏幕上找到小红书图标(红色背景,白色文字"小红书"),点击它即可打开应用。\n<|begin_of_box|>\n<answer>do(Launch, app="小红书")</answer><|end_of_box|>

根据这样抽取结果并不能按照我的想法来进行.于是我开始怀疑是不是模型基础能力不行.所以我让其他人用gtp试了一下.下面是gpt返回的结果:

<check>true</check>
<answer>Launch</answer>

没错,就2行,错到没边了看来,使用最先进的模型都不能完成抽取任务,我们只能另想其他方法.

更改输出模板

在我偶然的一次测试中,发现llm对json返回的结果兼容还可以,于是我又更改了提示词模板:

 "今天的日期是: " + formatted_date + '''
你是一个智能体分析专家,可以根据操作历史和当前状态图执行一系列操作来完成任务。
你必须严格按照JSON格式输出,必须包含下面两个字段
- think 是对你为什么选择这个操作的简短推理说明。
- action 是本次执行的具体操作指令,必须严格遵循下方定义的指令格式。




操作指令及其作用如下:
(省略)....

这次执行的结果缺十分不错

\n{\"think\": \"当前处于手机主屏幕,需要打开小红书应用,主屏幕上可以看到小红书图标,因此直接点击该图标。\", \"action\": \"do(action=\"Tap\", element=[263,283])\"}

结论

目前让模型规范化输出,在格式上优先选择json格式,模型遵守程度要好很多,本文只测试了单层的json格式,如果是层数非常多的json效果如何,还未测试.环境大家在评论区补充,交流经验

<think> 嗯,用户正在处理Dify LLM不按提示词输出的问题,这确实是个实用场景。从用户提供的引用材料来看,ta应该已经尝试过基础配置但效果不理想,现在需要更深入的排查方案。用户引用的三个资料分别涉及停止序列设置、角色提示设计和模型连接参数,这些恰好构成了解答的框架。 用户可能是个使用Dify的中级开发者,因为ta能准确指出“不按提示词输出”这种专业表述,而非笼统说“结果不对”。但ta没提供具体错误示例,说明可能缺乏调试经验。需要特别注意避免堆砌技术术语,要把“停止序列”“角色权重”这些概念解释得直观些。 核心解决思路应该分三层:首先是提示词工程层面,检查角色边界和约束条件是否明确;其次是模型配置层面,确认参数兼容性和视觉支持开关;最后是系统级检查,比如请求负载和日志分析。特别要注意引用[3]提到的Vision支持开关,如果用户用的是多模态模型却忘记开启这个选项,确实会导致指令解析异常。 考虑到用户上次问过类似问题,这次应该补充些进阶技巧。比如提示词里用##包围关键指令的写法,很多用户不知道这种格式能增强LLM的注意力。测试环节也要强调截取中间结果的重要性——用户往往只检查最终输出,但问题可能出在中间步骤的指令衰减上。 要不要提API监控工具呢?虽然引用里没提到,但这对定位问题很有帮助。还是加上吧,用“可选”标注就好。最后那个温度参数调整得小心说明,太高会放飞想象力,太低又可能僵化,建议0.3-0.7的区间比较保险。 </think> 以下是针对 Dify LLM 不按提示词输出的解决方案,结合系统引用和操作逻辑整理而成: --- ### **一、核心排查步骤** 1. **检查停止序列配置** - 在 Dify 的「停止序列」设置中,按 `Tab` 键添加明确的终止词(如 `##END##`),强制模型在生成到指定词时停止[^1]。 - **示例**:若提示词要求输出 JSON,可添加 `}", "}]` 等符号作为停止序列,避免多余输出。 2. **规范角色提示设计** - 在对话流程中明确划分 **用户指令** 和 **AI应答** 区块,用 `##` 等符号分隔角色边界[^2]: ```markdown ## 系统指令 ## 你只回答数学问题,拒绝其他请求。 ## 用户输入 ## {用户问题} ``` - **原理**:结构化提示可减少角色混淆导致的偏离。 3. **验证模型连接参数** - **基础 URL** 需完整匹配模型服务地址(如 `http://ollama-server:11434`)[^3] - **模型名称** 严格对应(如 `llava:latest` 而非 `llava`) - **开启 Vision 支持**(若为多模态模型),否则可能忽略视觉相关指令。 --- ### **二、进阶优化措施** 1. **强化提示词约束** - 在提示词开头添加强制指令: ```markdown [必须遵守] 1. 严格按用户要求格式输出 2. 禁止添加未请求的内容 ``` - 使用 **符号包围关键规则**(如 `<<规则>>`)提升模型注意力。 2. **调整温度参数** - 降低 `temperature`(建议 `0.3~0.5`)减少随机性,避免自由发挥。 3. **检查上下文冲突** - 删除历史测试中的 **冲突指令**,避免残留提示干扰。 --- ### **三、调试工具推荐** | 方法 | 操作路径 | 作用 | |---------------------|----------------------------------|--------------------------| | **请求日志分析** | 工作流 → 日志审计 | 查看原始请求/响应数据 | | **实时调试模式** | 应用设置 → 开启 Debug 模式 | 跟踪提示词注入过程 | | **Prompt 沙盒测试** | 实验室 → Prompt 调试工具 | 隔离测试单条提示词效果 | --- ### **四、典型场景案例** **问题**:要求输出 JSON,但模型追加额外解释 **解决方案**: 1. 停止序列添加 `"}` 和 `}]` 2. 提示词结尾追加: ```markdown 输出示例:{"key":"value"} [注意] 输出必须是纯净JSON,无需任何前缀或注释! ``` 3. 设置 `temperature=0.3` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值