1. 从零构建ReAct Agent:解决LLM"一本正经胡说八道"的利器
第一次听说ReAct时,我也误以为是什么新潮的前端框架。直到真正动手实现后才发现,这简直是解决大语言模型(LLM)"幻觉"问题的神器。作为一名在LLM应用开发领域摸爬滚打多年的工程师,我可以负责任地说:ReAct是目前最实用的Agent设计模式之一。
传统LLM调用就像让一个闭门造车的学者回答问题——它只能基于训练数据给出答案,无法获取最新信息,也无法在多步推理中自我修正。而ReAct让LLM具备了"思考-行动-观察"的能力循环,就像人类解决问题时的自然过程:先分析问题,再采取行动,根据反馈调整策略,最终得出可靠结论。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. ReAct核心原理与适用场景
2.1 为什么需要ReAct?
LLM最令人头疼的问题就是"一本正经地胡说八道"。当被问到"苹果公司现任CEO是谁?他今年多大?"时:
- 传统LLM可能给出过时答案(如错误地认为乔布斯仍在世)
- 或者直接编造一个看似合理实则错误的回答
这是因为传统LLM:
- 无法主动获取实时信息(如最新人事变动)
- 缺乏多步推理中的自我验证机制
- 对知识截止日期后的信息无能为力
2.2 ReAct工作原理图解
ReAct = Reasoning(推理) + Acting(行动),其工作流程如下:
code复制用户提问 → LLM思考所需信息 → 调用工具获取数据 →
分析工具返回结果 → 决定下一步行动 → ... → 最终回答
与直接生成答案相比,ReAct的优势在于:
- 每个推理步骤都可验证
- 能主动获取最新信息
- 通过工具调用扩展能力边界
2.3 最适合使用ReAct的四大场景
2.3.1 需要实时信息的任务
- 股票行情查询
- 天气预报
- 新闻事件追踪
2.3.2 复杂多步推理问题
示例问题:"找出2024年诺贝尔奖得主中年龄最大者的主要成就"
- 获取2024年诺奖得主名单
- 查询每位得主年龄
- 比较确定最年长者
- 检索该得主的主要成就
2.3.3 需要调用外部工具的场景
- 数学计算(如复杂公式求解)
- 代码执行与调试
- 数据库查询操作
2.3.4 高准确性要求的专业领域
- 医疗诊断辅助
- 法律咨询
- 金融分析
在这些领域,一个错误答案可能造成严重后果,ReAct的自我验证机制能显著降低错误率。
3. 完整ReAct Agent实现解析
3.1 基础架构设计
我们的ReAct Agent核心类结构如下:
python复制class ReActAgent:
def __init__(self, llm_client, tools: Dict[str, Callable], max_iterations: int = 10):
self.llm = llm_client # LLM客户端
self.tools = tools # 可用工具字典
self.max_iterations = max_iterations # 最大循环次数
def run(self, query: str) -> str:
# 主运行逻辑
pass
def _build_system_prompt(self) -> str:
# 构建系统提示词
pass
def _parse_response(self, response: str) -> tuple:
# 解析LLM响应
pass
3.2 核心运行逻辑详解
run()方法是ReAct循环的核心实现:
python复制def run(self, query: str) -> str:
trajectory = [] # 记录完整执行轨迹
system_prompt = self._build_system_prompt()
messages = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": f"Question: {query}\n\nLet's solve this step by step."}
]
for i in range(self.max_iterations):
# 1. 获取LLM的思考与行动决策
response = self.llm.generate(messages)
thought, action, action_input = self._parse_response(response)
# 2. 记录当前步骤
trajectory.append({
"thought": thought,
"action": action,
"action_input": action_input
})
# 3. 检查是否完成
if action.lower() == "finish":
return action_input
# 4. 执行工具调用
if action in self.tools:
try:
observation = self.tools[action](action_input)
except Exception as e:
observation = f"Error: {str(e)}"
else:
observation = f"Error: Unknown action '{action}'"
# 5. 更新对话上下文
messages.append({"role": "assistant", "content": response})
messages.append({
"role": "user",
"content": f"Observation: {observation}\n\nContinue with the next thought and action."
})
return f"Reached maximum iterations. Last thought: {thought}"
3.3 关键设计决策解析
3.3.1 严格的输出格式控制
系统提示词强制要求LLM按照固定格式响应:
code复制Thought: [推理过程]
Action: [工具名称或"finish"]
Action Input: [工具输入参数或最终答案]
这是ReAct正常工作的基础。实际项目中建议:
- 提供few-shot示例
- 对格式错误的响应自动重试
- 使用GPT-4等更强大的模型提高格式遵从性
3.3.2 执行轨迹记录
trajectory列表记录了完整的思考-行动链条,可用于:
- 调试和优化Agent行为
- 作为上下文喂给LLM保持连贯性
- 向用户展示推理过程增加透明度
3.3.3 工具系统设计
工具以字典形式注册,完全解耦:
python复制tools = {
"search_web": search_web,
"calculate": calculate,
"get_current_time": get_current_time,
}
添加新工具只需定义函数并注册:
python复制def get_stock_price(symbol: str) -> str:
"""查询股票最新价格"""
# 实现代码...
return price
tools["get_stock_price"] = get_stock_price
4. 工具系统实现细节
4.1 模拟网络搜索工具
python复制def search_web(query: str) -> str:
"""模拟网络搜索工具"""
knowledge_base = {
"tim cook age": "Tim Cook was born on November 1, 1960...",
"apple ceo": "Tim Cook has been the CEO of Apple Inc. since August 2011.",
# 更多知识条目...
}
query_lower = query.lower()
for key, value in knowledge_base.items():
if key in query_lower:
return value
return f"No information found for: {query}"
实际应用中应替换为:
- 真实搜索引擎API(如Google Search)
- 内部知识库查询
- 数据库检索
4.2 安全计算工具实现
python复制def calculate(expression: str) -> str:
"""安全执行数学计算"""
allowed_chars = set('0123456789+-*/.() ')
if not all(c in allowed_chars for c in expression):
return "Error: Invalid characters"
try:
return str(eval(expression))
except Exception as e:
return f"Error: {str(e)}"
安全注意事项:
- 严格限制允许的字符集
- 使用try-catch处理异常
- 生产环境建议使用更安全的计算库
4.3 时间查询工具
python复制def get_current_time() -> str:
"""获取当前时间"""
return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
5. 连接真实LLM服务
5.1 OpenAI API适配器
python复制from openai import OpenAI
class OpenAIAdapter:
def __init__(self, api_key: str, model: str = "gpt-4"):
self.client = OpenAI(api_key=api_key)
self.model = model
def generate(self, messages: List[Dict]) -> str:
response = self.client.chat.completions.create(
model=self.model,
messages=messages,
temperature=0.7,
max_tokens=500
)
return response.choices[0].message.content
5.2 使用示例
python复制llm = OpenAIAdapter(api_key="your-api-key")
agent = ReActAgent(llm, tools)
answer = agent.run("苹果现任CEO是谁?他今年多大?")
6. 生产环境优化建议
6.1 健壮性增强
- 错误重试机制:工具调用失败时自动重试
- 超时处理:为每个工具调用设置超时限制
- 备选策略:当主工具失败时尝试替代方案
6.2 性能优化
- 工具并行化:当多个工具可并行调用时
- 缓存机制:缓存常用工具调用结果
- 上下文压缩:对长对话历史进行摘要
6.3 可观测性
- 详细日志:记录完整的思考-行动链条
- 性能指标:跟踪工具调用耗时、成功率
- 用户反馈:收集答案准确性反馈用于改进
7. 实际应用案例演示
7.1 多步信息查询示例
问题:"苹果公司现任CEO是谁?他今年多大?"
执行流程:
-
Thought: 需要查询苹果现任CEO信息
Action: search_web
Action Input: Apple CEO current -
Observation: Tim Cook has been the CEO since August 2011
Thought: 现在需要查询Tim Cook的年龄
Action: search_web
Action Input: Tim Cook age -
Observation: Tim Cook was born on November 1, 1960
Thought: 已获得全部必要信息
Action: finish
Action Input: Tim Cook是苹果现任CEO,出生于1960年11月1日,现年63岁。
7.2 数学计算示例
问题:"计算123乘以456"
执行流程:
-
Thought: 需要计算123和456的乘积
Action: calculate
Action Input: 123 * 456 -
Observation: 56088
Thought: 已获得计算结果
Action: finish
Action Input: 123乘以456等于56088
8. 开发中的常见问题与解决方案
8.1 LLM不遵守输出格式
现象:LLM回复不按Thought/Action格式
解决方案:
- 在系统提示中加入更明确的格式示例
- 使用更强大的模型(如GPT-4)
- 实现自动格式修正逻辑
8.2 工具调用失败
现象:工具返回错误或超时
解决方案:
- 实现自动重试机制
- 提供备选工具方案
- 记录错误信息供LLM调整策略
8.3 循环无法终止
现象:Agent陷入无限循环
解决方案:
- 设置最大迭代次数(如代码中的max_iterations)
- 检测重复操作模式
- 超时强制终止机制
9. 进阶扩展方向
9.1 多Agent协作系统
- 让多个Agent各司其职(搜索Agent、计算Agent等)
- 设计Agent间的通信协议
- 实现任务分解与结果聚合
9.2 动态工具注册
- 运行时动态添加/移除工具
- 工具的热加载机制
- 工具版本管理
9.3 长期记忆系统
- 保存重要信息供后续使用
- 实现类似人类的记忆机制
- 记忆的检索与更新策略
在实现ReAct Agent的过程中,最大的收获是认识到:让LLM"思考"比让它"回答"更重要。通过结构化的思考-行动循环,我们不仅解决了幻觉问题,还大幅扩展了LLM的能力边界。这个200行的基础实现,已经可以处理许多真实场景的问题。当你需要进一步扩展时,记住三个关键:清晰的思考轨迹、可靠的工具系统、以及严格的执行控制。
