【LLM进阶-Agent】3.ReAct Agent 进阶--如何解决幻觉输出工具调用结果

进阶篇:如何根治弱模型在 ReAct 中的"幻觉 Observation"越界问题?

在基于开源小模型(如 Llama-3-8B、Qwen-7B)或经过重度量化的模型构建 ReAct Agent 时,我们经常会遇到一个令人抓狂的现象:模型在执行完第一轮动作后,开始自顾自地往下生成"Observation(观察结果,属于幻觉)",不仅不等待环境的真实返回,连我们在 Prompt 中再三警告"严禁输出 Observation"的指令也完全无视。

一、 底层原理解析:为什么小模型会"越俎代庖"?

要解决这个问题,我们需要脱离 Prompt 工程的表层,进入大模型的生成原理层面。

1. 自回归生成的"惯性"与条件概率

大语言模型的本质是自回归(Auto-regressive)的 Token 预测机。在生成第 ttt 个 Token 时,其目标是最大化条件概率: P(xt∣x1,x2,...,xt−1)P(x_t | x_1, x_2, \dots, x_{t-1})P(xt∣x1,x2,...,xt−1) 在传统的 ReAct 模板中(例如早期 LangChain 的设计),上下文通常是一个扁平的纯文本(Flat Text):

text 复制代码
Thought: 我需要搜索北京的天气
Action: Search[北京天气]
Observation: 北京今天晴,25度。
Thought: 我已经知道答案了。
Action: Finish[北京今天晴]

当你在第一轮把环境真实的 Observation 拼接到上下文中并交给模型进行第二轮生成时,上下文的结尾是: ... Action: Search[北京天气]\nObservation:[环境返回的结果]\n

由于小模型的逻辑泛化能力较弱,它在自回归生成时,高度依赖模式匹配(Pattern Matching) 。它发现上文的规律是 Thought -> Action -> Observation 交替出现。因此,在它输出完第二轮的 Action 后,其隐空间(Latent Space)中 Observation 这个词的下一个 Token 预测概率 P("Observation:"∣Context)P(\text{"Observation:"} | \text{Context})P("Observation:"∣Context) 会急剧升高。它不是在"思考",它只是在"顺口溜"式地补全接下来的文本序列。

2. 负向指令(Negative Constraints)的注意力稀释

"即使在 prompt 中再三强调不要输出",模型也不听。 这是因为 LLM 的注意力机制(Self-Attention)在处理负向指令 (如 "Do NOT output...")时存在天然缺陷。 在公式表达中,Attention Score 的计算为: Attention(Q,K,V)=softmax(QKTdk)V\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)VAttention(Q,K,V)=softmax(dk QKT)V 模型在计算时,"Observation" 这个词汇本身作为 Key,获得了极高的 Attention 权重,而 "Not" 或 "严禁" 这些逻辑否定词的权重在长上下文中被严重稀释。能力越弱、量化程度越高的模型,对这种复杂逻辑组合的 Attention 捕获能力就越差。

3. "扁平文本"导致的角色混淆

早期的 ReAct 直接把"模型生成的动作"和"环境返回的观察"以纯文本的形式拼接在一起,这打破了指令微调(SFT)阶段建立的角色隔离(Role Separation)。模型分不清哪些是"我"说的话,哪些是"外部环境"喂给我的信息,最终导致角色越界。


二、 工业级解决方案:从工程到推理框架的组合拳

在实际业务中,我们绝不会仅仅依赖"修改提示词"来解决这个问题。面对弱模型,我们需要在推理端和数据端施加硬性约束。

解法一:👍 推理层的硬截断(Stop Tokens / Early Stopping)------ 最简单直接的工业标配

核心逻辑 :不指望小模型听话,而是通过推理引擎(如 vLLM, TGI)的参数直接从物理层面打断它。 实现方式 : 在调用大模型 API 或推理引擎时,设置 stop 参数。

python 复制代码
response = llm.generate(
    prompt=react_prompt,
    stop=["Observation:", "Observation", "<|observation|>"] 
)

原理:无论模型内部有多想预测出 "Observation:" 这个词,只要推理框架的 Logits 处理层监测到生成的 Token 命中了 Stop Words 列表,就会立刻强制停止生成(Early Stopping),并将控制权交还给业务代码(外部环境)。这是目前几乎所有 Agent 框架(包括 LangChain)处理 ReAct 的兜底方案。

解法二:👍 重构上下文模板(ChatML Role-playing)------ 从源头隔离角色

技术的来龙去脉 : 如前文所述,扁平文本是万恶之源。因此,近一两年的 Agent 改造中,大厂普遍废弃了 Thought/Action/Observation 的纯文本拼接,转向使用结构化的 Chat Template(如 ChatML 格式)。

实现方式 : 我们将 ReAct 映射到 system, user, assistanttool 四个原生角色中:

  • Assistant :专职负责输出 ThoughtAction
  • User/Tool :环境的返回结果 Observation 不再作为文本拼在 Assistant 的回复后面,而是作为新的一轮 User 轮次(或专用的 Tool 轮次)输入给模型。

上下文结构变迁: [System]→[User: 任务]→[Assistant: 思考+工具调用]→[Tool: 观察结果]→[Assistant: 继续思考]\text{[System]} \rightarrow \text{[User: 任务]} \rightarrow \text{[Assistant: 思考+工具调用]} \rightarrow \text{[Tool: 观察结果]} \rightarrow \text{[Assistant: 继续思考]}[System]→[User: 任务]→[Assistant: 思考+工具调用]→[Tool: 观察结果]→[Assistant: 继续思考] 通过强制穿插 <|im_start|>tool<|im_start|>assistant 这样的特殊 Control Tokens,模型在自回归时,就不会顺势把环境的台词给抢了。

解法三:👍 引导式解码(Guided Decoding / Logits Processor)------ 剥夺模型的自由发挥权

核心逻辑 :如果我们希望模型 100% 按照我们的格式输出,最暴力的手段是在解码阶段(Decoding Phase)对概率分布 P(xt∣ct)P(x_t | c_t)P(xt∣ct) 进行掩码干预。 实现方式 : 利用 OutlinesvLLM 支持的 JSON Schema 引导式解码,或者编写自定义的 Logits Processor。 强制约束模型当前轮次只能按照如下正则化语法(Grammar)或 JSON 结构生成:

json 复制代码
{
  "thought": "字符串",
  "action": "工具名",
  "action_input": "参数"
}

原理 :如果强行规定了生成格式是 JSON 且只有上述三个 Key,模型在生成完 "action_input" 后,推理引擎会自动将闭合括号 } 的概率调整为 1(或极高),从而直接结束生成,物理上阻断了它继续生成"Observation"的可能性。

解法四:对齐微调期的掩码惩罚(Loss Masking in SFT)------ 算法本源的纠偏

如果你有权限微调这个小模型,那么从数据层面解决是最彻底的。 在构建 ReAct 轨迹(Trajectory)进行 SFT 时,一个极为常见的算法错误是:让模型去拟合整段包含 Observation 的对话。

正确的做法是采用 Masked Language Modeling Loss : 设一条训练轨迹为 X=[System,User,Thought,Action,Observation,Thought,Finish]X =[System, User, Thought, Action, Observation, Thought, Finish]X=[System,User,Thought,Action,Observation,Thought,Finish]。 在计算 Cross-Entropy Loss 时: L=−∑tmtlog⁡P(xt∣x<t)\mathcal{L} = - \sum_{t} m_t \log P(x_t | x_{<t})L=−∑tmtlogP(xt∣x<t) 这里的 mt∈{0,1}m_t \in \{0, 1\}mt∈{0,1} 是 Mask 掩码。

  • 对于 System,User,ObservationSystem, User, ObservationSystem,User,Observation 的 Token,mt=0m_t = 0mt=0(不计算 Loss,即不要求模型学习预测这些内容)。
  • 只有 对于 Thought,Action,FinishThought, Action, FinishThought,Action,Finish 的 Token,mt=1m_t = 1mt=1。 通过这种微调,模型在潜意识(权重)里就会知道:"Observation 不是我该生成的东西",从而在根本上消灭幻觉越界。

总结

将一个看似玄学的"Prompt 不听话"问题,拆解为自回归机制、解码层干预、角色模板以及 SFT 损失函数的系统性问题。

相关推荐
skywalk81632 小时前
看到有人提到:有网站使用分解质因数来区分人和机器,一种新兴的“反向CAPTCHA”策略
人工智能
陈天伟教授2 小时前
人工智能应用- 机器做梦:03.回顾卷积神经网络
人工智能·神经网络·cnn
Lw中2 小时前
模型忽略关键实体怎么办?
人工智能·大模型应用基础
致Great2 小时前
AI Harness 工程:Agent 能跑起来的那一层到底是什么?
人工智能
木枷2 小时前
Immersion in the GitHub Universe: Scaling Coding Aents to Mastery
人工智能·软件工程·swe
互联网江湖2 小时前
鹿客科技IPO,陈彬不想“站在门外”
大数据·人工智能·物联网
Lw中2 小时前
大模型生成内容出错
人工智能·rag·大模型应用开发
星爷AG I2 小时前
14-9 够取与抓握(AGI基础理论)
人工智能·计算机视觉·agi