|
@@ -0,0 +1,113 @@
|
|
|
|
|
+"""
|
|
|
|
|
+ReAct vs Plan-and-Execute
|
|
|
|
|
+
|
|
|
|
|
+ ReAct(Reasoning + Acting): 边想边做 —— 每一步都「推理 → 行动 → 观察 → 再推理」
|
|
|
|
|
+ 适合: 简单任务,不需要提前规划
|
|
|
|
|
+ 缺点:由于缺乏全局规划,它像个"近视眼",极易在步骤繁多的复杂任务中迷失方向,
|
|
|
|
|
+ 甚至在出错时陷入工具调用的无限死循环。
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ Plan-and-Execute: 先计划再执行 —— 先制定完整计划,再逐步执行
|
|
|
|
|
+ 适合: 复杂任务,需要全局规划
|
|
|
|
|
+ 缺点:由于强依赖前期的静态规划,它显得过于笨重且延迟较高,一旦中间步骤执行出错
|
|
|
|
|
+ 或现实情况发生突变,就必须频繁重新规划,缺乏像 ReAct 那样快速、灵活的即时反应能力。
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+本文件演示 ReAct 模式的 Agent:
|
|
|
|
|
+ 每轮循环:LLM 推理当前状况 → 决定调用哪个工具 → 工具返回结果 → LLM 观察结果继续推理
|
|
|
|
|
+ 直到 LLM 认为问题已解决,输出最终答案。
|
|
|
|
|
+"""
|
|
|
|
|
+
|
|
|
|
|
+import sys
|
|
|
|
|
+from pathlib import Path
|
|
|
|
|
+# 将项目根目录加入 sys.path,确保能 import llm_client 和 agent_demo 等模块
|
|
|
|
|
+path = Path(__file__).resolve().parent.parent
|
|
|
|
|
+sys.path.insert(0, str(path))
|
|
|
|
|
+
|
|
|
|
|
+"""企业需求:智能客服售后分析 Agent(ReAct 模式)"""
|
|
|
|
|
+
|
|
|
|
|
+# ================================================================
|
|
|
|
|
+# 1. 初始化大模型(LLM)
|
|
|
|
|
+# get_llm() 返回 ChatOpenAI 单例,自动读取 .env 中的 API Key / base_url / model
|
|
|
|
|
+# LLM 是 ReAct 循环中的「推理引擎」,负责理解问题 + 决定调用哪个工具
|
|
|
|
|
+# ================================================================
|
|
|
|
|
+from llm_client import get_llm
|
|
|
|
|
+llm = get_llm()
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+# ================================================================
|
|
|
|
|
+# 2. 加载工具(Tools)
|
|
|
|
|
+# ReAct 中的 "Act" 环节 —— Agent 通过调用这些工具与外部系统交互
|
|
|
|
|
+# - query_order: 查询订单系统(订单状态、物流等)
|
|
|
|
|
+# - query_product: 查询商品知识库(商品信息、价格等)
|
|
|
|
|
+# tools 列表会在创建 Agent 时注入,LLM 可以自主决定调用哪个
|
|
|
|
|
+# ================================================================
|
|
|
|
|
+from langchain.agents import create_agent
|
|
|
|
|
+from agent_demo.React_tool import query_order, query_product
|
|
|
|
|
+
|
|
|
|
|
+tools = [query_order, query_product]
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+# ================================================================
|
|
|
|
|
+# 3. 系统提示词(System Prompt)
|
|
|
|
|
+# 定义 Agent 的角色、行为规范和输出要求
|
|
|
|
|
+# 这段 prompt 会作为 SystemMessage 拼接在每轮 LLM 调用的最前面
|
|
|
|
|
+# ReAct 的 "Reasoning" 质量很大程度上取决于 prompt 写得是否清晰
|
|
|
|
|
+# ================================================================
|
|
|
|
|
+prompt = """你是企业售后智能客服Agent。
|
|
|
|
|
+
|
|
|
|
|
+你的任务:
|
|
|
|
|
+
|
|
|
|
|
+1. 理解用户问题
|
|
|
|
|
+2. 判断是否需要调用工具
|
|
|
|
|
+3. 获取业务数据
|
|
|
|
|
+4. 根据结果分析
|
|
|
|
|
+5. 给出准确解决方案
|
|
|
|
|
+
|
|
|
|
|
+你不能编造订单信息。
|
|
|
|
|
+如果缺少信息,需要主动询问。
|
|
|
|
|
+"""
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+# ================================================================
|
|
|
|
|
+# 4. 创建 ReAct Agent
|
|
|
|
|
+# create_agent 内部封装了完整的 ReAct 循环:
|
|
|
|
|
+#
|
|
|
|
|
+# ┌──────────┐ 有工具调用 ┌──────────┐
|
|
|
|
|
+# │ LLM │ ───────────────→ │ 工具执行 │
|
|
|
|
|
+# │ (推理) │ ←─────────────── │ (行动) │
|
|
|
|
|
+# └──────────┘ 返回观察结果 └──────────┘
|
|
|
|
|
+# │
|
|
|
|
|
+# │ 无工具调用,直接输出
|
|
|
|
|
+# ↓
|
|
|
|
|
+# 最终答案
|
|
|
|
|
+#
|
|
|
|
|
+# 参数说明:
|
|
|
|
|
+# - llm: 负责推理的 LLM
|
|
|
|
|
+# - tools: 可调用的工具列表(ReAct 中的 Action 来源)
|
|
|
|
|
+# - system_prompt: 系统提示词,定义 Agent 的角色和行为规范
|
|
|
|
|
+# ================================================================
|
|
|
|
|
+agent = create_agent(
|
|
|
|
|
+ llm,
|
|
|
|
|
+ tools,
|
|
|
|
|
+ system_prompt=prompt,
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+# ================================================================
|
|
|
|
|
+# 5. 运行 Agent
|
|
|
|
|
+# agent.invoke() 启动 ReAct 循环,输入 messages 列表
|
|
|
|
|
+# 返回的 result['messages'] 包含完整的对话历史:
|
|
|
|
|
+# HumanMessage → 用户输入
|
|
|
|
|
+# AIMessage → LLM 推理(可能包含 tool_calls)
|
|
|
|
|
+# ToolMessage → 工具执行结果
|
|
|
|
|
+# ...(重复上述过程直到 LLM 输出最终答案)
|
|
|
|
|
+# AIMessage → 最终回答(不含 tool_calls)
|
|
|
|
|
+# ================================================================
|
|
|
|
|
+if __name__ == '__main__':
|
|
|
|
|
+ # 用户问题:查询订单 10001 的状态
|
|
|
|
|
+ result = agent.invoke(
|
|
|
|
|
+ {"messages": [("user", "我的订单10001为什么还没有收到?")]}
|
|
|
|
|
+ )
|
|
|
|
|
+ # 取最后一条消息的 content 作为最终回答
|
|
|
|
|
+ print(f"\n最终回答:{result['messages'][-1].content}")
|