W12 · 知识点09:Agent之ReAct模式实战
学习目标:从零实现ReAct Agent,让AI像维修工程师一样"想→查→再想→给结论"。
一、ReAct vs Function Calling
| 维度 | Function Calling | ReAct |
|---|---|---|
| 工具选择 | 模型内部决定 | Prompt显式定义 |
| 推理可见性 | 黑箱 | 每步可见 |
| 框架依赖 | 需API支持 | 纯Prompt |
| 调试 | 较难 | 容易(看trace) |
二、ReAct核心循环
Thought: 我需要查这台设备的维修历史
Action: maintenance_history
Action Input: {"device_id": "DEV-002"}
Observation: 维修4次,总费用3250元
Thought: 4次维修较多,再查当前状态
Action: query_device
Action Input: {"device_id": "DEV-002"}
Observation: 状态=停机, 运行4200小时
Thought: 设备已停机且维修频繁
Final Answer: 建议评估是否更换设备...
三、动手练习
bash
pip install openai
python scripts/09_react_agent.py
四、本知识点检验标准
- 理解Thought→Action→Observation循环
- 从零实现ReAct Agent
- Agent能串联多工具完成复杂诊断
code
python
"""
知识点09: ReAct Agent 实战
===========================
从零实现ReAct设备维修诊断Agent(不依赖LangChain)
pip install openai
"""
import os, json, re
from openai import OpenAI
client = OpenAI(
api_key=os.environ.get("OPENAI_API_KEY", "sk-your-key"),
base_url=os.environ.get("OPENAI_BASE_URL", "https://api.deepseek.com/v1"),
)
MOCK = {
"devices": {"DEV-001": {"name": "1号空压机", "status": "运行中", "hours": 2350}, "DEV-002": {"name": "2号空压机", "status": "停机", "hours": 4200}, "DEV-003": {"name": "3号注塑机", "status": "运行中", "hours": 6800}},
"history": {"DEV-001": [{"date": "2025-01-15", "fault": "进气阀漏气", "cost": 90}, {"date": "2025-03-20", "fault": "排气温度高", "cost": 500}], "DEV-002": [{"date": "2024-08-10", "fault": "电机过热", "cost": 800}, {"date": "2024-12-05", "fault": "E-07报警", "cost": 1200}, {"date": "2025-03-18", "fault": "漏油", "cost": 350}, {"date": "2025-05-22", "fault": "异响", "cost": 900}]},
"inventory": {"空压机": [{"name": "进气阀密封件", "qty": 15}], "注塑机": [{"name": "加热圈", "qty": 6}], "通用": [{"name": "轴承6205-2RS", "qty": 20}]},
}
def tool_query_device(device_id): return json.dumps(MOCK["devices"].get(device_id, {"error": "不存在"}), ensure_ascii=False)
def tool_history(device_id):
h = MOCK["history"].get(device_id, [])
return json.dumps({"records": h, "count": len(h), "total_cost": sum(r["cost"] for r in h)}, ensure_ascii=False)
def tool_inventory(device_type): return json.dumps(MOCK["inventory"].get(device_type, MOCK["inventory"]["通用"]), ensure_ascii=False)
def tool_work_order(device_id, fault, priority):
import random
return json.dumps({"order_id": f"WO-{random.randint(1000,9999)}", "status": "已创建"}, ensure_ascii=False)
TOOLS = {"query_device": tool_query_device, "maintenance_history": tool_history, "spare_inventory": tool_inventory, "create_work_order": tool_work_order}
SYSTEM = """你是设备维修诊断助手。按ReAct格式思考和行动。
可用工具:
- query_device(device_id): 查设备状态(如DEV-001)
- maintenance_history(device_id): 查维修历史
- spare_inventory(device_type): 查备件库存(如"空压机")
- create_work_order(device_id, fault, priority): 创建工单
格式:
Thought: [你的分析]
Action: [工具名]
Action Input: [JSON参数]
或:
Thought: [最终分析]
Final Answer: [完整回答]"""
class ReActAgent:
def __init__(self, max_steps=8):
self.max_steps = max_steps
def run(self, question):
messages = [{"role": "system", "content": SYSTEM}, {"role": "user", "content": question}]
print(f"\n👤 {question}")
print("=" * 55)
for step in range(self.max_steps):
resp = client.chat.completions.create(model="deepseek-chat", messages=messages, temperature=0.3)
text = resp.choices[0].message.content
messages.append({"role": "assistant", "content": text})
if "Final Answer:" in text:
answer = text.split("Final Answer:")[-1].strip()
print(f"\n✅ {answer}")
return answer
am = re.search(r'Action:\s*(\w+)', text)
im = re.search(r'Action Input:\s*(\{.*?\})', text, re.DOTALL)
if am:
action = am.group(1)
args = json.loads(im.group(1)) if im else {}
thought = text.split("Action:")[0].replace("Thought:", "").strip()
print(f"\n🧠 {thought}")
print(f"🔧 {action}({json.dumps(args, ensure_ascii=False)})")
try:
obs = TOOLS[action](**args) if action in TOOLS else '{"error":"未知工具"}'
except Exception as e:
obs = json.dumps({"error": str(e)})
print(f"📋 {obs}")
messages.append({"role": "user", "content": f"Observation: {obs}"})
else:
messages.append({"role": "user", "content": "格式有误,请按Thought/Action/Action Input或Final Answer格式。"})
return "达到步骤限制"
def main():
print("=" * 60)
print(" 设备维修 ReAct Agent")
print("=" * 60)
agent = ReActAgent()
for q in ["DEV-002维修了几次?还能继续用吗?", "3号注塑机需要什么备件?库存够吗?", "DEV-001排气温度高,帮我诊断并生成工单"]:
agent.run(q)
print("\n" + "=" * 60)
if __name__ == "__main__":
main()