在当今技术领域,能够自主思考并执行复杂任务的智能体系统正成为行业热点。作为一名长期深耕AI应用开发的工程师,我发现ReAct框架因其简洁高效的特点,成为入门智能体开发的最佳选择。本文将带您从零开始,手把手构建一个完整的ReAct智能体系统,并通过实际案例演示其完整工作流程。
ReAct(Reasoning + Acting)框架的核心在于模拟人类解决问题的思维过程。当面对复杂任务时,我们通常会经历以下步骤:
这种"思考-行动-观察-再思考"的循环机制,正是ReAct框架的精髓所在。相比传统的一次性问答模式,ReAct框架使大语言模型具备了多步推理和工具调用的能力,能够解决更复杂的实际问题。
一个完整的ReAct智能体包含四大核心组件:
这四者协同工作,共同构成了一个能够自主完成复杂任务的智能系统。下面我们将深入每个组件的实现细节。
我们推荐使用conda创建独立的Python环境,避免依赖冲突。以下是具体步骤:
bash复制conda create -n langgraph python=3.12 -y
conda activate langgraph
pip install openai
提示:使用Python 3.12能确保获得最佳的性能和最新的语言特性支持。如果遇到网络问题,可以考虑配置国内镜像源加速包下载。
本示例使用阿里云百炼平台提供的API服务,您需要先获取有效的API Key。配置代码如下:
python复制from openai import OpenAI
aliyun_api_key = 'your_api_key_here' # 替换为实际API Key
client = OpenAI(
api_key=aliyun_api_key,
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"
)
# 测试API连通性
response = client.chat.completions.create(
model="qwen-max",
messages=[{'role': 'user', 'content': "你是谁?"}]
)
print(response.choices[0].message.content)
注意:在实际项目中,API Key应通过环境变量或配置管理系统获取,避免硬编码在源代码中。测试阶段确保返回正常响应后再继续开发。
我们首先构建一个基础的Agent类,作为整个系统的核心交互接口:
python复制class Agent:
def __init__(self, system=""):
self.system = system # 系统提示词
self.messages = [] # 对话历史记录
if self.system:
self.messages.append({"role": "system", "content": system})
def __call__(self, message):
self.messages.append({"role": "user", "content": message})
result = self.execute()
self.messages.append({"role": "assistant", "content": result})
return result
def execute(self):
response = client.chat.completions.create(
model="qwen-max",
messages=self.messages
)
return response.choices[0].message.content
这个类实现了三个关键功能:
__call__方法实现类实例的函数式调用开发心得:将消息历史维护在类内部是实现多轮对话的关键。这种设计模式既保持了接口简洁,又确保了对话上下文的完整性。
工具是扩展智能体能力的关键。我们首先实现两个基础工具:
python复制def calculate(expression):
"""数学计算工具"""
try:
return str(eval(expression))
except Exception as e:
return f"计算错误: {str(e)}"
def average_dog_weight(breed):
"""狗狗平均体重查询工具"""
weight_map = {
"Scottish Terrier": "20 lbs",
"Border Collie": "37 lbs",
"Toy Poodle": "7 lbs"
}
return weight_map.get(breed, "50 lbs (默认平均体重)")
工具注册系统将可用工具组织起来:
python复制known_actions = {
"calculate": calculate,
"average_dog_weight": average_dog_weight
}
注意事项:生产环境中应对eval的使用进行严格安全检查,或替换为更安全的表达式求值库。此处为演示简化了安全处理。
提示词是指导模型行为的关键。以下是经过精心设计的ReAct提示模板:
python复制react_prompt = """
你运行在Thought(思考)、Action(行动)、PAUSE(暂停)、Observation(观察)的循环中。
最终你需要输出Answer(答案)。
思考(Thought): 描述你对问题的思考过程
行动(Action): 执行一个可用动作后返回PAUSE
观察(Observation): 将获得动作执行结果
可用动作:
calculate: 执行数学计算,如calculate: 3 + 5
average_dog_weight: 查询犬种平均体重,如average_dog_weight: Collie
示例会话:
问题: Bulldog的平均体重是多少?
思考: 我需要使用average_dog_weight查询
行动: average_dog_weight: Bulldog
PAUSE
观察: Bulldog平均体重51磅
答案: Bulldog的平均体重是51磅
""".strip()
这个提示词通过:
设计要点:示例会话(few-shot learning)能显著提升模型遵循指定格式的能力。建议至少包含一个完整的工作流程示例。
我们使用正则表达式来识别和提取模型输出中的动作指令:
python复制import re
action_re = re.compile(r'^Action: (\w+): (.*)$')
这个正则表达式会匹配以"Action:"开头,后接动作名称和参数的字符串,例如:
code复制Action: calculate: 3 + 7
下面是实现ReAct循环的核心代码:
python复制def query(question, max_turns=5):
bot = Agent(react_prompt)
next_prompt = question
history = []
for turn in range(max_turns):
# 执行模型推理
result = bot(next_prompt)
history.append(f"Turn {turn+1}:\n{result}")
print(f"\nTurn {turn+1}:\n{result}")
# 解析动作指令
actions = [
action_re.match(a)
for a in result.split('\n')
if action_re.match(a)
]
if not actions:
return result, history
# 执行动作
action, action_input = actions[0].groups()
if action not in known_actions:
raise ValueError(f"未知动作: {action}: {action_input}")
print(f"执行: {action} {action_input}")
observation = known_actions[action](action_input.strip())
print(f"观察: {observation}")
# 准备下一轮输入
next_prompt = f"Observation: {observation}"
raise RuntimeError(f"达到最大轮数({max_turns})仍未得到答案")
这个函数实现了完整的ReAct工作流程:
性能优化:在实际应用中,可以考虑添加异步IO支持来优化工具调用的等待时间,特别是当工具涉及网络请求时。
我们用一个具体问题来测试这个ReAct智能体:
"我有两只狗,一只是Border Collie,一只是Scottish Terrier,它们的总重是多少?"
python复制question = """I have 2 dogs, a border collie and a scottish terrier.
What is their combined weight?"""
result, history = query(question)
让我们逐步分析智能体的思考过程:
第一轮:
code复制思考:我需要先查询Border Collie和Scottish Terrier的平均体重,然后相加
行动:average_dog_weight: Border Collie
PAUSE
系统执行动作并返回观察结果:"37 lbs"
第二轮:
code复制思考:已获得Border Collie体重,现在查询Scottish Terrier
行动:average_dog_weight: Scottish Terrier
PAUSE
系统返回观察结果:"20 lbs"
第三轮:
code复制思考:现在有了两只狗的体重,需要计算总和
行动:calculate: 37 + 20
PAUSE
系统计算并返回结果:"57"
第四轮:
code复制答案:两只狗的总重量是57磅
| 轮次 | 思考过程 | 执行动作 | 观察结果 |
|---|---|---|---|
| 1 | 需要查询两种狗的体重 | 查询Border Collie | 37 lbs |
| 2 | 查询第二只狗体重 | 查询Scottish Terrier | 20 lbs |
| 3 | 计算总重量 | 计算37+20 | 57 |
| 4 | 生成最终答案 | - | 57 lbs |
这个案例清晰展示了ReAct框架如何通过多步推理和工具调用解决复杂问题。智能体不仅能够分解问题,还能自主选择适当的工具获取必要信息,最终合成完整答案。
python复制import asyncio
async def execute_tools(actions):
tasks = []
for action in actions:
func = known_actions[action['name']]
tasks.append(asyncio.create_task(func(action['input'])))
return await asyncio.gather(*tasks)
缓存机制:对频繁查询的工具结果进行缓存,减少重复计算和API调用
早期终止:当模型输出答案时立即返回,不必等待最大轮次
python复制def safe_execute_tool(action, input_str, max_retries=3):
for attempt in range(max_retries):
try:
return known_actions[action](input_str)
except Exception as e:
if attempt == max_retries - 1:
return f"动作执行失败: {str(e)}"
time.sleep(1)
输入验证:对工具输入进行严格的验证和清理,防止注入攻击
超时控制:为每个工具调用设置超时限制,避免长时间阻塞
python复制def register_tool(name, func, description=""):
known_actions[name] = func
update_prompt_with_tool(name, description)
状态持久化:将会话状态保存到数据库,支持长时间运行的智能体
插件架构:通过插件机制扩展智能体能力,保持核心系统稳定
API密钥管理:
敏感数据处理:
沙箱环境:
python复制# 示例:安全的计算函数替代方案
from ast import literal_eval
def safe_calculate(expr):
try:
return str(literal_eval(expr))
except:
return "无法计算该表达式"
完整审计日志:
性能指标:
python复制# 示例监控装饰器
def monitor_tool(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
try:
result = func(*args, **kwargs)
log_success(func.__name__, time.time()-start)
return result
except Exception as e:
log_error(func.__name__, str(e))
raise
return wrapper
异常警报:设置异常检测和通知机制
批处理请求:对多个相关查询进行批处理优化
缓存策略:
python复制from functools import lru_cache
@lru_cache(maxsize=1000)
def cached_dog_weight(breed):
# 原始实现
模型量化:使用量化技术减少大模型的内存占用和计算需求
| 症状 | 可能原因 | 解决方案 |
|---|---|---|
| 模型不调用工具 | 提示词格式不正确 | 检查示例会话格式,确保包含完整的Action示例 |
| 工具调用错误 | 参数格式不匹配 | 在提示词中明确参数格式要求,添加输入验证 |
| 循环无法终止 | 最大轮次设置过高/逻辑错误 | 添加中间答案检测,合理设置最大轮次(5-10) |
| 结果不准确 | 工具实现有误 | 为每个工具添加单元测试,验证边界条件 |
| API调用失败 | 网络/认证问题 | 检查API端点、密钥和网络连接 |
python复制def verbose_call(self, message):
print(f"用户输入: {message}")
result = self.execute()
print(f"模型原始输出:\n{result}")
return result
交互式调试:在关键决策点添加断点检查状态
简化测试:从最小可工作示例开始,逐步增加复杂度
工具调用分析:识别并优化耗时最长的工具
提示词精简:移除不必要的说明,保持提示词简洁
批处理优化:合并多个工具调用,减少往返次数
python复制class SpecialistAgent:
def __init__(self, role, expertise):
self.role = role
self.expertise = expertise
# 初始化专业化的工具和提示词
python复制class LongTermMemory:
def store(self, key, value):
# 持久化存储实现
def retrieve(self, key):
# 检索实现
开源框架:
在线课程:
实践项目:
通过本指南,您已经掌握了ReAct智能体的核心原理和实现方法。在实际项目中,建议从小规模开始,逐步验证核心功能,再扩展到更复杂的应用场景。记住,一个好的智能体系统需要持续的迭代和优化,不断从用户反馈和实际使用中学习和改进。