三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

Agent 输出带 Markdown 代码块?Prompt 约束 + 解析兜底解决 JSON 解析失败

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_schema=None解决了,工具“死磕”用结构化返回+容错规则解决了。

我以为世界清净了。

结果跑起来之后,又看到了一个熟悉的身影:

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 默认格式化成 Markdown

LangChain 的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 代码块,工具都能正常解析:

情况1:Agent 遵守约束(纯净 JSON)

Action Input: {"line_name": "交互屏组装A线", ...} ✅ 直接解析成功

情况2:Agent 仍带 Markdown

Action Input: ```json {"line_name": "交互屏组装A线", ...} ✅ 剥离后解析成功

方案对比

方案优点缺点推荐度
仅 Prompt 约束实现简单无法保证 100% 生效⭐⭐
仅解析兜底100% 容错治标不治本,增加代码复杂度⭐⭐⭐
Prompt 约束 + 解析兜底源头减少 + 兜底保障,双重保险需要同时维护两处代码⭐⭐⭐⭐⭐

通用工程规范扩展

基于本文经验,可进一步扩展统一的工具返回规范,定义通用状态码:

状态码含义使用场景
success操作成功正常返回数据
not_found数据不存在查询无结果
param_error参数错误输入参数不合法
system_error系统异常内部错误

所有工具遵循同一套结构,后续 Prompt 只需统一识别status字段,即可实现全链路容错,适配更多工具扩展。

经验总结

怕你忘了,我再啰嗦一遍😀😀😀Agent 给 Action Input 套 Markdown 代码块是“本能”,Prompt 管不住是正常的。只有“源头约束 + 兜底处理”才能彻底解决。

落到具体操作上就是三条:

  1. Prompt 中要有“正确示例 vs 错误示例”:不要只写“禁止”,还要展示“正确的应该长什么样”。模型的模仿能力比理解指令更强,用示例约束比用规则约束更有效。

  2. 解析逻辑必须包含 Markdown 剥离json.loads()不认识 Markdown,但你可以先剥离再解析。这一行代码可以解决 90% 的格式问题。

  3. 不要相信 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代码块 #工具调用排坑

← 返回列表