Agent 输出带 Markdown 代码块?Prompt 约束 + 解析兜底解决 JSON 解析失败
【导航台账】制造业数据与AI践行者老蒋的技术博客全系列文章汇总持续更新 文章摘要LangChain ReAct Agent 调用工具时Action Input 常自带 Markdown 代码块导致 JSON 解析抛出 Expecting value 报错。本文拆解根因模型训练数据形成的格式偏好单靠 Prompt 无法根治提供「Prompt 强约束 解析层兜底剥离」双层解决方案可复用到所有工具定义中。目录问题现象根因分析第一层ReAct Agent 的 output_parser 默认格式化成 Markdown第二层Markdown 代码块对工具解析是“灾难”第三层这个问题单靠 Prompt 很难根治解决方案第一步Prompt 强约束源头阻止第二步解析层兜底工具层容错验证结果方案对比通用工程规范扩展经验总结系列导航互动与交流关于作者问题现象兄弟们《智联工坊实战多工具协同Agent让AI像人类一样规划与执行复杂任务》的前两个坑我们填平了嵌套 JSON 用args_schemaNone解决了工具“死磕”用结构化返回容错规则解决了。我以为世界清净了。结果跑起来之后又看到了一个熟悉的身影Action: generate_work_order Action Input: json { line_name: 交互屏组装A线, fault_code: E401, solution: 重启工控机并检查USB连接线, spare_parts_status: 充足, assigned_shift: 白班 } 然后工具报错 ❌ 错误: Expecting value: line 1 column 1 (char 0)我当时的第一反应是这不科学啊……JSON 本身是合法的但前面多了个json后面多了个。Python 的json.loads()根本不认识 Markdown 语法直接报错。Agent 为什么要给 Action Input 套上 Markdown 代码块这不是画蛇添足吗根因分析说实话这个问题我一开始以为是 Agent“抽风”了。多轮查询验证 LangChain 的源码才发现——这是 ReAct Agent 的出厂设置不是 Bug是 Feature。第一层ReAct Agent 的 output_parser 默认格式化成 MarkdownLangChain 的create_react_agent使用的ReActSingleInputOutputParser在解析 Agent 的输出时默认期望的格式就是带 Markdown 代码块的。虽然create_react_agent本身不会强制 Agent 输出 Markdown但Agent 在训练数据中见过大量“工具调用用 Markdown 代码块包裹”的示例于是它自然地“学会”了这种格式。尤其是在使用 Qwen2.5 这类本地模型时这种倾向更加明显。很多开发者遇到这个问题第一反应是“Prompt 写得不够清楚”于是反复加规则、加强调甚至强行限制调用次数但效果甚微。本质问题不在 Prompt 的措辞而在返回值的信号形式——模糊的自然语言天然不如结构化字段可靠。第二层Markdown 代码块对工具解析是“灾难”工具端的_run方法收到的是json {line_name: 交互屏组装A线, ...}而json.loads()期望的是纯净的 JSON 字符串。多一个反引号、多一个空格都会导致解析失败。Agent 以为它在“美化”输出实际上它在“破坏”输出。第三层这个问题单靠 Prompt 很难根治我在 Prompt 里写了**严禁**使用 Markdown 代码块例如 json ... 。但 Agent 依然会时不时地输出 Markdown。因为模型在生成文本时格式习惯是“潜意识”层面的就像一个人打字时习惯用两个空格而不是一个空格——你告诉他“不要用两个空格”他下一句可能还是会按习惯敲两个空格。本质上这是一个“训练数据偏见”问题模型在训练时见惯了 Markdown 格式的工具调用它认为这就是“标准写法”。解决方案别慌既然单靠 Prompt 管不住那就“源头约束 兜底处理”双管齐下。第一步Prompt 强约束源头阻止在builder.py的_get_prompt_template中用“正确示例 vs 错误示例”的方式强化约束**调用工具的格式要求必须严格遵守** - Action Input 必须是**纯净的 JSON 对象**不包含任何 Markdown 标记。 - **严禁**使用 json ... 代码块包裹 Action Input。 - **严禁**在 JSON 前后添加任何说明文字。 ✅ 正确格式 Action: generate_work_order Action Input: {line_name: 交互屏组装A线, fault_code: E401} ❌ 错误格式严禁使用 Action: generate_work_order Action Input: json {line_name: 交互屏组装A线}第二步解析层兜底工具层容错在generate_work_order.py的_parse_agent_input方法中增加“剥离 Markdown 代码块”的逻辑def _parse_agent_input(self, raw_input: Any) - Dict[str, Any]: if isinstance(raw_input, dict): return raw_input if not isinstance(raw_input, str): return {} text raw_input.strip() # ---- 核心剥离 Markdown 代码块 ---- if text.startswith(json): text re.sub(r^json\s*, , text) text re.sub(r\s*$, , text) elif text.startswith(): text re.sub(r^\s*, , text) text re.sub(r\s*$, , text) # 然后继续尝试 JSON 解析 try: return json.loads(text) except json.JSONDecodeError: # 继续用正则兜底提取... pass验证结果修改后重新运行无论 Agent 是否输出 Markdown 代码块工具都能正常解析情况1Agent 遵守约束纯净 JSONAction Input: {line_name: 交互屏组装A线, ...} ✅ 直接解析成功情况2Agent 仍带 MarkdownAction Input: json {line_name: 交互屏组装A线, ...} ✅ 剥离后解析成功方案对比方案优点缺点推荐度仅 Prompt 约束实现简单无法保证 100% 生效⭐⭐仅解析兜底100% 容错治标不治本增加代码复杂度⭐⭐⭐Prompt 约束 解析兜底源头减少 兜底保障双重保险需要同时维护两处代码⭐⭐⭐⭐⭐通用工程规范扩展基于本文经验可进一步扩展统一的工具返回规范定义通用状态码状态码含义使用场景success操作成功正常返回数据not_found数据不存在查询无结果param_error参数错误输入参数不合法system_error系统异常内部错误所有工具遵循同一套结构后续 Prompt 只需统一识别status字段即可实现全链路容错适配更多工具扩展。经验总结怕你忘了我再啰嗦一遍Agent 给 Action Input 套 Markdown 代码块是“本能”Prompt 管不住是正常的。只有“源头约束 兜底处理”才能彻底解决。落到具体操作上就是三条Prompt 中要有“正确示例 vs 错误示例”不要只写“禁止”还要展示“正确的应该长什么样”。模型的模仿能力比理解指令更强用示例约束比用规则约束更有效。解析逻辑必须包含 Markdown 剥离json.loads()不认识 Markdown但你可以先剥离再解析。这一行代码可以解决 90% 的格式问题。不要相信 Agent 会 100% 遵守格式约定Agent 是概率模型不是规则引擎。任何时候都要在工具层做好容错而不是期望 Agent 永远正确。适用范围本文方案适用于所有使用 ReAct Agent 调用 JSON 格式参数的工具尤其适用于本地小模型Qwen2.5-7B 等这类模型对格式的“惯性”比大模型更强更需要双层兜底。系列导航本文属于《数据与AI工程排坑笔记》系列(点击跳转查看)上一篇LangChain Agent 反复调用工具死循环结构化返回 Prompt 规则让它学会跳过下一篇《数据质量智能巡检Agent》Case04即将发布点击查看往期实战分享本文问题源自《智联工坊实战多工具协同Agent》实战过程完整源码及深度教程见该文链接建议收藏开发多工具协同 Agent 时Markdown 代码块是最容易被忽略的格式陷阱。本文的双层方案Prompt 约束 解析兜底可直接复用到所有工具定义中遇到 JSON 解析失败时可直接对照排查。互动与交流你在使用 LangChain ReAct Agent 时有没有遇到过 Agent“自作主张”给参数加格式的情况除了 Markdown 代码块还见过哪些“画蛇添足”的格式欢迎评论区吐槽咱们互相交流一下——说实话让 Agent 输出纯净 JSON 这件事比教会它调用工具难多了。关于作者制造业数据与 AI 践行者老蒋23 年 IT 老兵。聚焦制造业数据架构与 AI 融合落地。全流程实战全源码开源。标签#排坑笔记 #LangChain #Agent #ReAct Agent #多工具协同Agent #JSON解析失败 #Markdown代码块 #工具调用排坑