From eb4c3253ff2a6b064e4dd1d9837af3fb265fdffc Mon Sep 17 00:00:00 2001 From: redbotu Date: Mon, 25 May 2026 21:46:18 +0800 Subject: [PATCH] feat: add ReAct agent with custom API support --- README.md | 3 - langgraph-tutorial/README.md | 72 +++++----- langgraph-tutorial/react_agent.py | 227 ++++++++++++++++++++++++++++++ 3 files changed, 259 insertions(+), 43 deletions(-) delete mode 100644 README.md create mode 100644 langgraph-tutorial/react_agent.py diff --git a/README.md b/README.md deleted file mode 100644 index cdd61af..0000000 --- a/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# langgraph_learing - -langgraph_learing \ No newline at end of file diff --git a/langgraph-tutorial/README.md b/langgraph-tutorial/README.md index 3943742..6906e49 100644 --- a/langgraph-tutorial/README.md +++ b/langgraph-tutorial/README.md @@ -2,23 +2,15 @@ ## 课程大纲 -### ✅ 第 1 步:核心理念 -- LangGraph 是基于图的 AI 应用框架 -- 支持有状态、循环、分支的智能体构建 +| 步骤 | 内容 | 文件 | 状态 | +|------|------|------|------| +| 第 1 步 | 核心理念 | - | ✅ | +| 第 2 步 | Hello World | `hello.py` | ✅ | +| 第 3 步 | 条件边 | `conditional.py` | ✅ | +| 第 4 步 | 循环 + 智能体 | `loop.py`, `agent.py` | ✅ | +| 第 5 步 | ReAct 智能体 | `react_agent.py` | ✅ | -### ✅ 第 2 步:Hello World -- 文件: `hello.py` -- 学习了:State、Node、Edge 三大核心概念 - -### ✅ 第 3 步:条件边 -- 文件: `conditional.py` -- 学习了:让图学会做决定,根据输入走不同路径 - -### ✅ 第 4 步:循环 + 智能体 -- 文件: `loop.py` - 循环演示(尝试-评估-重试) -- 文件: `agent.py` - AI 智能体(思考-行动-回答) - -## 核心概念总结 +## 核心概念 ``` LangGraph 图 = State(状态) + Node(节点) + Edge(边) @@ -30,38 +22,38 @@ Edge: 连接节点,定义流程走向 - 条件边: add_conditional_edges() ``` -## 下一步学习 - -### 第 5 步:真实 LLM 智能体 -- 接入 OpenAI API -- 构建 ReAct 模式智能体 -- 添加工具调用能力 - -### 第 6 步:高级特性 -- 检查点机制(暂停/恢复) -- 子图(嵌套图) -- 并行执行 -- 流式输出 - ## 运行示例 -```bash +```powershell # 设置编码 $env:PYTHONIOENCODING="utf-8" # 运行各个示例 -python hello.py # Hello World -python conditional.py # 条件边 -python loop.py # 循环 -python agent.py # AI 智能体 +python hello.py # Hello World +python conditional.py # 条件边 +python loop.py # 循环 +python agent.py # AI 智能体 (模拟模式) +python react_agent.py # ReAct 智能体 (需要 API) ``` -## 接入真实 LLM +## ReAct 智能体配置 ```powershell -# 设置 API Key -$env:OPENAI_API_KEY = "sk-..." +# 必需 +$env:OPENAI_API_KEY = "your-api-key" -# 运行智能体示例 -python agent.py -``` \ No newline at end of file +# 可选 +$env:OPENAI_BASE_URL = "https://your-api.com/v1" # 默认 OpenAI +$env:MODEL_NAME = "gpt-4o-mini" # 默认 gpt-4o-mini + +# 运行 +python react_agent.py # 内置测试 +python react_agent.py "你的问题" # 自定义问题 +``` + +## 下一步学习 + +- [ ] 检查点机制(暂停/恢复) +- [ ] 子图(嵌套图) +- [ ] 并行执行 +- [ ] 流式输出 \ No newline at end of file diff --git a/langgraph-tutorial/react_agent.py b/langgraph-tutorial/react_agent.py new file mode 100644 index 0000000..b7333e9 --- /dev/null +++ b/langgraph-tutorial/react_agent.py @@ -0,0 +1,227 @@ +""" +LangGraph 第 5 步:真实 LLM 智能体 +ReAct 模式:思考(Reason) - 行动(Act) - 观察(Observe) +支持自定义 OpenAI 兼容 API +""" +import os +import sys +from langgraph.graph import StateGraph, START, END +from typing import TypedDict + +# 1️⃣ 配置 API +API_KEY = os.environ.get("OPENAI_API_KEY", "") +BASE_URL = os.environ.get("OPENAI_BASE_URL", "https://api.openai.com/v1") +MODEL = os.environ.get("MODEL_NAME", "gpt-4o-mini") + +if not API_KEY: + print("请先设置环境变量:") + print(" $env:OPENAI_API_KEY = 'your-api-key'") + print(" $env:OPENAI_BASE_URL = 'your-api-base-url' # 可选,默认 OpenAI") + print(" $env:MODEL_NAME = 'gpt-4o-mini' # 可选") + exit(1) + +from openai import OpenAI +client = OpenAI(api_key=API_KEY, base_url=BASE_URL) + +# 2️⃣ 定义状态 +class AgentState(TypedDict): + question: str + thoughts: list + current_thought: str + action: str + action_param: str + observation: str + final_answer: str + iteration: int + max_iterations: int + +# 3️⃣ 定义工具 +def calculator(expression: str) -> str: + """计算器工具""" + try: + result = eval(expression, {"__builtins__": {}}, {}) + return f"计算结果: {expression} = {result}" + except Exception as e: + return f"计算错误: {e}" + +def search_knowledge(query: str) -> str: + """知识库搜索工具""" + knowledge = { + "langgraph": "LangGraph 是 LangChain 团队开发的框架,用于构建有状态、基于图的 AI 应用。", + "python": "Python 是一种高级编程语言,广泛用于 AI、Web 开发、数据分析等领域。", + "langchain": "LangChain 是构建 LLM 应用的框架,提供 Prompt 管理、Chain、Agent 等组件。", + "ai": "人工智能 (AI) 是计算机科学的一个分支,致力于创建能执行智能任务的系统。", + } + for key, value in knowledge.items(): + if key in query.lower(): + return f"搜索到: {value}" + return f"未找到关于 '{query}' 的精确信息" + +tools = { + "calculator": calculator, + "search": search_knowledge, +} + +# 4️⃣ 定义节点 +def think_node(state: AgentState): + """思考节点 - 让 LLM 决定下一步""" + state = state.copy() + state['iteration'] += 1 + + system_prompt = f"""你是一个智能助手,可以使用以下工具: +1. calculator - 数学计算,参数是数学表达式如 "2+3*4" +2. search - 搜索知识,参数是搜索关键词 + +当前是第 {state['iteration']}/{state['max_iterations']} 轮。 + +请严格按照以下格式回复: +[思考] 你的思考过程 +[行动] 工具名称|参数 +例如: +[思考] 我需要计算这个数学题 +[行动] calculator|2+3*4 + +如果可以直接回答,请这样回复: +[思考] 我已经知道答案了 +[回答] 你的最终答案""" + + messages = [{"role": "system", "content": system_prompt}] + messages.append({"role": "user", "content": state['question']}) + + if state.get('observation'): + messages.append({"role": "assistant", "content": f"[观察] {state['observation']}"}) + + response = client.chat.completions.create( + model=MODEL, + messages=messages, + max_tokens=300, + temperature=0.3, + ) + + thought_text = response.choices[0].message.content + state['current_thought'] = thought_text + state['thoughts'] = state.get('thoughts', []) + [thought_text] + + print(f"\n{'='*50}") + print(f"[思考] 第 {state['iteration']} 轮:") + print(thought_text) + + # 解析行动 + for line in thought_text.split('\n'): + if '[行动]' in line: + parts = line.replace('[行动]', '').strip().split('|') + if len(parts) == 2: + state['action'] = parts[0].strip() + state['action_param'] = parts[1].strip() + return state + if '[回答]' in line: + state['final_answer'] = line.replace('[回答]', '').strip() + return state + + state['action'] = "" + return state + +def act_node(state: AgentState): + """行动节点 - 执行工具""" + state = state.copy() + action = state.get('action', '') + param = state.get('action_param', '') + + print(f"\n[行动] 执行 {action}({param})") + + if action in tools: + result = tools[action](param) + state['observation'] = result + print(f"[观察] {result}") + else: + state['observation'] = f"未知工具: {action}" + print(f"[观察] 未知工具: {action}") + + return state + +def answer_node(state: AgentState): + """回答节点 - 生成最终答案""" + state = state.copy() + + if state.get('final_answer'): + print(f"\n[回答] {state['final_answer']}") + return state + + messages = [ + {"role": "system", "content": "请根据以下信息给出简洁的最终答案"}, + {"role": "user", "content": f"问题: {state['question']}\n\n思考过程:\n" + "\n".join(state.get('thoughts', []))} + ] + + response = client.chat.completions.create( + model=MODEL, + messages=messages, + max_tokens=200, + ) + + state['final_answer'] = response.choices[0].message.content + print(f"\n[回答] {state['final_answer']}") + return state + +# 5️⃣ 路由函数 +def route(state: AgentState): + """决定下一步""" + if state.get('final_answer'): + return "answer" + if state['iteration'] >= state['max_iterations']: + return "answer" + if state.get('action'): + return "act" + return "answer" + +# 6️⃣ 构建图 +graph = StateGraph(AgentState) +graph.add_node("think", think_node) +graph.add_node("act", act_node) +graph.add_node("answer", answer_node) + +graph.add_edge(START, "think") +graph.add_conditional_edges("think", route, {"act": "act", "answer": "answer"}) +graph.add_edge("act", "think") +graph.add_edge("answer", END) + +app = graph.compile() + +# 7️⃣ 运行 +print("=" * 50) +print("LangGraph ReAct 智能体") +print("=" * 50) +print(f"API: {BASE_URL}") +print(f"模型: {MODEL}") +print("\n图结构:") +print(" START -> think -> [有行动?] -> act -> think (循环)") +print(" |") +print(" +-> [无行动/达到限制] -> answer -> END") + +# 如果有命令行参数,用命令行参数作为问题 +if len(sys.argv) > 1: + questions = [" ".join(sys.argv[1:])] +else: + questions = [ + "计算一下 123 * 456", + "什么是 LangGraph?", + ] + +for q in questions: + print(f"\n{'#'*50}") + print(f"问题: {q}") + print(f"{'#'*50}") + + result = app.invoke({ + "question": q, + "thoughts": [], + "iteration": 0, + "max_iterations": 4, + "action": "", + "observation": "", + "final_answer": "", + "action_param": "", + }) + + print(f"\n{'='*50}") + print(f"最终答案: {result['final_answer']}") + print(f"{'='*50}") \ No newline at end of file