Q5: 解释 LangChain 中的 ZeroShotAgent 和 ReActAgent 的区别。
这是一个很经典的 LangChain 面试题。很多初学者会混淆这两个概念,因为它们在实现上确实有重叠。
简单直接的结论是:在 LangChain 的早期版本中,ZeroShotAgent 就是 ReActAgent 的一个具体实现。 两者都基于 ReAct(Reasoning + Acting) 框架。如果你面试的是较新版本(LangChain >= 0.1.0),ZeroShotAgent 已被标记为弃用,官方推荐直接使用 ReActAgent 或 create_react_agent。
不过,既然面试官专门问“区别”,他考察的是你对设计理念和提示词模板结构的理解。下面是核心区别:
1. 核心定义:名字背后的含义
- ReAct Agent:强调推理循环。它强制要求模型在每个步骤中交替输出 Thought(思考)、Action(行动)、Observation(观察)。这是一个通用的架构模式。
- ZeroShot Agent:强调零样本能力。意思是,它没有给模型提供当前任务的具体示例(Few-shot Examples)。模型必须仅凭工具的描述文字,就推断出该调用哪个工具。
2. 关键区别:提示词模板(最重要)
这是两者最实质的技术差异。
-
ReActAgent 的提示词:非常简洁,只告诉模型遵循
Thought/Action/Action Input/Observation的格式,并输出Final Answer结束。不包含任何“如何使用工具”的示例。// 伪代码“你可以使用这些工具:[工具列表]。请严格按照以下格式回复:Thought: 思考...Action: 要调用的工具名Action Input: 工具的输入Observation: 工具返回的结果...(重复)...Thought: 我知道答案了Final Answer: 最终回复” -
ZeroShotAgent 的提示词:它继承自
ReAct的单步推理模式,但默认包含了动态生成的工具描述。更重要的是,它允许你通过agent_kwargs传入prefix和suffix来定制指令。它的核心假设是:“模型只要看懂工具的描述(docstring),就知道怎么用,不需要我给它例子。”
面试话术总结:
“在实现上,ZeroShotAgent 使用了标准的 ReAct 格式,但它没有内置的 few-shot 示例。它完全依赖工具自身的描述文本(
func.__doc__)来指导模型行为。”
3. 实际使用场景的区别
| 维度 | ZeroShotAgent (旧版) | ReActAgent (新版) |
|---|---|---|
| 任务复杂度 | 适合单一、明确的简单任务 | 适合需要多步推理、回溯的复杂任务 |
| 工具数量 | 工具较多时(5+),表现可能下降 | 配合 handle_parsing_errors 更鲁棒 |
| 是否需要示例 | 不需要(零样本) | 可以通过 few_shot_examples 手动提供 |
| 当前状态 | Deprecated (弃用) | Active (推荐) |
4. 一个容易踩坑的“假区别”
很多面试者会回答:“ZeroShotAgent 只能调用一次工具,ReActAgent 可以循环多次调用工具。” 这是错误的。 两者都支持多步循环(比如先搜索,再计算,再搜索)。区别不在于循环次数,而在于是否依赖上下文示例。
面试最佳答案(参考话术)
你可以这样组织语言:
“在 LangChain 中,两者的核心区别在于设计定位。
- ReActAgent 是严格遵循 ReAct 论文思想的通用代理,它强制模型执行 ‘思考-行动-观察’ 的循环,并且支持通过
few_shot_examples进行上下文学习。- ZeroShotAgent 本质上是一个特化的 ReActAgent。‘Zero Shot’ 意味着它在提示词中不包含任何具体的任务示例,完全依靠工具本身的描述(docstring)来让模型推理如何行动。
在实际代码演进中,
ZeroShotAgent已经被标记为弃用,因为create_react_agent可以更清晰地实现相同功能。如果面试官追问何时用谁,我会说:新版代码一律使用ReActAgent或create_react_agent;只有在阅读老项目代码时,才需要知道ZeroShotAgent相当于一个没有提供examples参数的ReActAgent。”