ReACT Agent 开发知识点总结

📚 ReACT Agent 开发知识点总结

python langchain QW 文生图

一、核心概念

1. Agent 的两种创建方式

方法 create_react_agent create_tool_calling_agent
原理 文本解析(Thought/Action/Observation) 原生工具调用(Function Calling)
提示词 严格格式要求 简单通用
可靠性 ⭐⭐⭐(易解析失败) ⭐⭐⭐⭐⭐(结构化调用)
模型要求 任意 LLM 支持 Function Calling 的模型
推荐度 旧项目兼容 强烈推荐

2. 工作流程对比

markdown 复制代码
ReAct Agent:
用户输入 → LLM 生成文本 → 正则解析 → 提取 Action → 执行工具 → 返回 Observation
          ↑                                                        ↓
          └─────────────────── 循环多次 ───────────────────────────┘

Tool Calling Agent:
用户输入 → LLM 结构化输出 → 自动匹配工具 → 执行 → 返回结果
          (JSON 格式)                              ↓
                                            自动生成回答

二、关键代码模式

正确的 Tool Calling Agent 实现

python 复制代码
from langchain.agents import create_tool_calling_agent, AgentExecutor
from langchain_core.tools import Tool
from langchain_openai import ChatOpenAI

# 1. 定义工具
def search_func(query: str) -> str:
    return f"搜索结果:{query}"

search_tool = Tool(
    name="search",
    description="搜索信息",
    func=search_func,
)

tools = [search_tool]

# 2. 初始化 LLM
llm = ChatOpenAI(model="qwen3-max", api_key="...", base_url="...")

# 3. 🔑 关键步骤:绑定工具
llm_with_tools = llm.bind_tools(tools)

# 4. 创建 Agent
prompt = ChatPromptTemplate.from_messages([
    ("system", "你是助手,可用工具:{tools}"),
    ("human", "{input}"),
    ("placeholder", "{agent_scratchpad}"),
])

agent = create_tool_calling_agent(
    llm=llm_with_tools,  # ✅ 使用绑定工具的 LLM
    tools=tools,          # ✅ 工具列表
    prompt=prompt
)

# 5. 创建执行器
executor = AgentExecutor(
    agent=agent,
    tools=tools,
    verbose=True,
    handle_parsing_errors=True,
    max_iterations=5
)

# 6. ✅ 正确调用方式
result = executor.invoke({"input": "用户问题"})
print(result["output"])

三、常见错误与修复

错误 1:直接传字符串给 invoke

python 复制代码
# ❌ 错误
executor.invoke("用户问题")

# ✅ 正确
executor.invoke({"input": "用户问题"})

错误 2:未绑定工具

python 复制代码
# ❌ 错误
agent = create_tool_calling_agent(llm=llm, tools=tools, ...)

# ✅ 正确
llm_with_tools = llm.bind_tools(tools)
agent = create_tool_calling_agent(llm=llm_with_tools, tools=tools, ...)

错误 3:使用非标准 Tool 类

python 复制代码
# ❌ 容易出错
google_serper = GoogleSerperRun(...)

# ✅ 推荐
google_serper = Tool(
    name="google_serper",
    func=google_serper_api.run,
    description="..."
)

错误 4:Placeholder 格式错误

python 复制代码
# ❌ 错误
("placeholder", "chat_history")

# ✅ 正确
MessagesPlaceholder(variable_name="chat_history", optional=True)
# 或
("placeholder", "{chat_history}")  # 注意花括号

四、工具定义最佳实践

1. 简单工具 - 使用 Tool 包装

python 复制代码
tool = Tool(
    name="tool_name",
    func=function,
    description="清晰的用途说明",
)

2. 复杂参数 - 添加 args_schema

python 复制代码
from pydantic.v1 import BaseModel, Field

class ArgsSchema(BaseModel):
    query: str = Field(..., description="参数描述")

tool = Tool(
    name="tool_name",
    func=function,
    args_schema=ArgsSchema,
    description="..."
)

3. 自定义 API - 封装函数

python 复制代码
def generate_image(prompt: str) -> str:
    """使用阿里云生成图片"""
    try:
        response = MultiModalConversation.call(...)
        
        # ✅ 正确解析嵌套结构
        if response.output and response.output.get('choices'):
            content = response.output['choices'][0]['message']['content']
            for item in content:
                if 'image' in item:
                    return f"图片链接:{item['image']}"
        
        return "生成失败"
    except Exception as e:
        return f"出错:{str(e)}"

image_tool = Tool(
    name="image_generator",
    func=generate_image,
    description="根据描述生成图片",
    args_schema=DallEArgsSchema
)

五、API 响应解析技巧

阿里云通义万相响应结构

json 复制代码
{
  "output": {
    "choices": [
      {
        "message": {
          "content": [
            {"image": "https://..."}  // 或 {"text": "..."}
          ]
        }
      }
    ]
  }
}

安全解析代码

python 复制代码
def parse_response(response):
    try:
        # 多层防御式解析
        if response.output:
            choices = response.output.get('choices', [])
            if choices:
                content = choices[0].get('message', {}).get('content', [])
                for item in content:
                    if isinstance(item, dict) and 'image' in item:
                        return item['image']
        
        # 备用方案
        if response.output and response.output.get('text'):
            return response.output['text']
            
        return "无法解析响应"
    except Exception as e:
        return f"解析出错:{str(e)}"

六、提示词设计要点

优秀提示词模板

python 复制代码
prompt = ChatPromptTemplate.from_messages([
    ("system", 
     "你是智能助手,可使用工具帮助用户。\n"
     "\n"
     "可用工具:\n"
     "{tools}\n"
     "\n"
     "使用格式:\n"
     "- Thought: 分析问题\n"
     "- Action: 选择工具\n"
     "- Action Input: 参数\n"
     "- Observation: 结果\n"
     "- Final Answer: 最终回答\n"
     "\n"
     "注意:\n"
     "- 生成图片用 openai_dalle\n"
     "- 搜索信息用 google_serper\n"
     "- 英文描述图片效果更好"
    ),
    MessagesPlaceholder("chat_history", optional=True),
    ("human", "{input}"),
    ("placeholder", "{agent_scratchpad}"),
])

七、调试技巧

1. 启用详细日志

python 复制代码
executor = AgentExecutor(verbose=True, ...)

2. 限制迭代次数

python 复制代码
executor = AgentExecutor(max_iterations=5, ...)  # 防止无限循环

3. 错误处理

python 复制代码
executor = AgentExecutor(handle_parsing_errors=True, ...)  # 自动修复

4. 打印中间结果

python 复制代码
def generate_image(prompt):
    print(f"收到请求:{prompt}")
    response = ...
    print(f"API 响应:{response}")
    return result

八、完整流程图

css 复制代码
用户提问
   ↓
┌─────────────────────────────────────┐
│  AgentExecutor.invoke({"input": ...}) │
└─────────────────────────────────────┘
   ↓
┌─────────────────────────────────────┐
│  create_tool_calling_agent           │
│  - 分析是否需要工具                  │
│  - 输出结构化 JSON                   │
└─────────────────────────────────────┘
   ↓
┌─────────────────────────────────────┐
│  判断 Action                          │
│  - None → 直接回答                   │
│  - tool_name → 执行工具              │
└─────────────────────────────────────┘
   ↓
┌─────────────────────────────────────┐
│  Tool.run()                          │
│  - 调用实际函数                      │
│  - 返回 Observation                  │
└─────────────────────────────────────┘
   ↓
┌─────────────────────────────────────┐
│  循环或生成最终回答                  │
└─────────────────────────────────────┘

九、核心要点速记

必须做的:

  1. 使用 bind_tools() 绑定工具
  2. invoke() 传入字典 {"input": "..."}
  3. 使用 Tool 包装自定义函数
  4. 添加 handle_parsing_errors=True
  5. 设置 max_iterations 防止死循环

避免做的:

  1. 不要直接传字符串给 invoke()
  2. 不要使用非标准 Tool 类(如 GoogleSerperRun
  3. 不要忘记绑定工具就创建 Agent
  4. 不要使用错误的 Placeholder 格式
  5. 不要假设 API 响应结构固定

十、扩展学习

下一步可以学习:

  • 🔄 添加对话历史记忆(Memory)
  • 📊 多工具协同工作
  • 🎯 自定义 Agent 路由逻辑
  • 📝 更复杂的提示词工程
  • 🔐 生产环境的错误处理和重试机制

十、核心代码

ini 复制代码
import os
from dashscope import MultiModalConversation

import dotenv
from langchain_classic.agents import AgentExecutor, create_react_agent, create_tool_calling_agent
from langchain_community.tools import GoogleSerperRun
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.tools import render_text_description_and_args, Tool
from langchain_openai import ChatOpenAI
from pydantic.v1 import Field, BaseModel
from langchain_community.utilities import GoogleSerperAPIWrapper

dotenv.load_dotenv()


class GoogleSerperArgsSchema(BaseModel):
    query: str = Field(..., description="执行谷歌搜索的查询语句")


class DallEArgsSchema(BaseModel):
    query: str = Field(..., description="输入是生成图像的文本提示(prompt)")




def generate_image(prompt: str) -> str:
    """使用阿里云通义万相生成图片"""
    messages = [
        {
            "role": "user",
            "content": [
                {"text": prompt}
            ]
        }
    ]

    try:
        response = MultiModalConversation.call(
            api_key=os.getenv("OPENAI_API_KEY"),
            model="qwen-image-2.0-pro",
            messages=messages,
            result_format='message',
            stream=False,
            watermark=False,
            prompt_extend=True,
            negative_prompt="低分辨率,低画质,肢体畸形,手指畸形,画面过饱和,蜡像感,人脸无细节,过度光滑,画面具有 AI 感。构图混乱。文字模糊,扭曲。",
            size='2048*2048'
        )

        print("API 响应:", response)

        # 从 output.choices 中获取图片
        # {"status_code": 200, "request_id": "6ab051b0-6c8f-4675-974f-919143e91d98", "code": "", "message": "", "output": {"text": null, "finish_reason": null, "choices": [{"finish_reason": "stop", "message": {"role": "assistant", "content": [{"image": "https://dashscope-7c2c.oss-accelerate.aliyuncs.com/7d/c3/20260326/e74037e8/2d748881-31a4-4e0a-b2c3-1508ee17a3f1.png?Expires=1775104234&OSSAccessKeyId=LTAI5tPxpiCM2hjmWrFXrym1&Signature=4t53r7bOV3%2Fg01L%2FUPvouMgqKeQ%3D"}]}}], "audio": null}, "usage": {"input_tokens": 0, "output_tokens": 0, "characters": 0, "height": 2048, "image_count": 1, "width": 2048}}
        if response.output and response.output.get('choices'):
            choices = response.output['choices']
            if len(choices) > 0:
                message = choices[0].get('message', {})
                content = message.get('content', [])

                if isinstance(content, list) and len(content) > 0:
                    # 查找包含 image 的内容
                    for item in content:
                        if isinstance(item, dict) and 'image' in item:
                            image_url = item['image']
                            return f"图片已生成成功!\n图片链接:{image_url}"

                    # 如果没有找到 image,返回文本内容
                    text_content = content[0].get('text', '')
                    if text_content:
                        return f"生成结果:{text_content}"

        # 备用方案:尝试从 output.text 获取
        if response.output and response.output.get('text'):
            return f"生成结果:{response.output['text']}"

        return "图片生成失败,无法解析响应数据"

    except Exception as e:
        print(f"异常详情:{str(e)}")
        return f"图片生成出错:{str(e)}"





google_serper = GoogleSerperRun(
    name="google_serper",
    description=(
        "根据传入的搜索内容,返回搜索结果"
        "谷歌搜索工具"
    ),
    args_schema=GoogleSerperArgsSchema,
    api_wrapper=GoogleSerperAPIWrapper(),
)

dalle = Tool(
    name="openai_dalle",
    description="根据传入的描述生成图片。当用户要求生成图像、创建图片、画图时使用此工具。输入应该是详细的图像描述。",
    func=generate_image,
    args_schema=DallEArgsSchema,
)
tools = [
    google_serper,
    dalle
]

prompt = ChatPromptTemplate.from_messages([
    ("system", "Answer the following questions as best you can. You have access to the following tools:"),
    ("placeholder", "{chat_history}"),
    ("human", "{input}"),
    ("placeholder", "{agent_scratchpad}"),
])

llm = ChatOpenAI(
    model="qwen3-max-2026-01-23",
    api_key=os.getenv("OPENAI_API_KEY"),
    base_url=os.getenv("OPENAI_API_BASE_URL")
)
# 绑定工具到 LLM
agent = create_tool_calling_agent(
    llm=llm,
    tools=tools,
    prompt=prompt
)

agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)

print(agent_executor.invoke({"input": "帮我生成一张老爷爷爬山的图片, 要求是 亚洲脸"}))
相关推荐
晚霞的不甘2 小时前
HarmonyOS ArkTS 进阶实战:深入理解边距、边框与嵌套布局
前端·计算机视觉·华为·智能手机·harmonyos
牛奶2 小时前
你发送的消息,微信到底怎么送到的?
前端·websocket·http
酉鬼女又兒2 小时前
零基础快速入门前端DOM 元素获取方法详解:从代码到实践(可用于备赛蓝桥杯Web应用开发)
前端·javascript·职场和发展·蓝桥杯·js
牛奶2 小时前
为什么关掉浏览器再打开,你还是登录状态?
前端·网络协议·https
bjxiaxueliang2 小时前
一文掌握Python aiohttp:异步Web开发从入门到部署
开发语言·前端·python
Liudef062 小时前
从0到1开发ReAct智能体:原理、实现与最佳实践
前端·react.js·前端框架
金豆呀2 小时前
WPS自定义公式,相似度匹配
前端·javascript·wps
jiayong232 小时前
0基础学习VUE3 第 1 课:项目启动流程
前端·vue.js·学习
今天又在摸鱼2 小时前
学习vue前必要的js语法
前端·vue.js·学习