logo

LangChain Agents 代理

Agent 是 LangChain 里最强大也最容易被滥用的功能。

先说清楚它和普通 Chain 的根本区别:Chain 的执行路径是你预先定义的,Agent 的执行路径是 LLM 在运行时自己决定的。你给它一堆工具(搜索、计算、查数据库),它自己判断现在该用哪个、用几次、用什么参数。

这带来了极大的灵活性,但也带来了三个代价:(每次工具调用都是一次 LLM 请求)、(多次 API 调用)、不可预测(LLM 有时会走奇怪的路径)。

Agent 不是万能的。如果你的流程可以提前定义好步骤,用 Chain 或 LangGraph 比 Agent 更稳定、更便宜、更容易调试。我的经验是,很多人在不需要 Agent 的地方用了 Agent,然后抱怨"为什么这么慢、这么贵、结果不稳定"——其实换成普通 Chain 就解决了。


Agent 的工作流程

用户输入
   │
   ▼
LLM 分析:我需要用什么工具?
   │
   ▼
调用工具 1(如搜索天气)
   │
   ▼
LLM 分析:结果够了吗?还需要其他信息?
   │
   ▼
调用工具 2(如计算)
   │
   ▼
LLM 生成最终答案
   │
   ▼
返回用户

每个"调用工具 → 分析结果"都是一次 LLM API 请求。一次用户请求可能触发 3-5 次甚至更多的 LLM 调用。


快速开始

from langchain_openai import ChatOpenAI
from langchain.agents import create_tool_calling_agent, AgentExecutor
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.tools import tool

# 1. 定义工具
@tool
def get_weather(city: str) -> str:
    """获取指定城市当前天气。

    当用户问天气相关问题时使用。
    返回天气描述和温度信息。
    """
    weather_data = {
        "北京": "晴,25°C,空气质量良好",
        "上海": "多云,28°C,微风",
        "广州": "小雨,32°C,湿度高",
    }
    return weather_data.get(city, f"暂无 {city} 天气数据")

@tool
def calculate(expression: str) -> str:
    """执行数学计算。

    支持加减乘除等基本运算。expression 是合法的数学表达式,如 '123 * 456'。
    """
    try:
        result = eval(expression)
        return str(result)
    except Exception as e:
        return f"计算出错:{e}"

# 2. 创建 Prompt(必须有 agent_scratchpad 占位符)
prompt = ChatPromptTemplate.from_messages([
    ("system", "你是一个有帮助的助手,可以使用工具来回答问题。"),
    ("placeholder", "{chat_history}"),
    ("human", "{input}"),
    ("placeholder", "{agent_scratchpad}"),  # Agent 的中间思考过程放这里
])

# 3. 组装
llm = ChatOpenAI(model="gpt-4o")  # Agent 用 gpt-4o,不要用 mini,工具调用准确率差很多
tools = [get_weather, calculate]

agent = create_tool_calling_agent(llm, tools, prompt)
agent_executor = AgentExecutor(
    agent=agent,
    tools=tools,
    verbose=True,       # 开发时开着,能看到每次工具调用的细节
    max_iterations=5,   # 必须设上限,防止无限循环
    max_execution_time=30,  # 超时保险
)

# 4. 运行
result = agent_executor.invoke({
    "input": "北京天气怎么样?另外帮我算一下 123 × 456"
})
print(result["output"])

工具定义:这里最容易搞砸

工具的 docstring 不是给你看的,是给 LLM 看的。LLM 读 docstring 来决定要不要用这个工具、怎么用。这是 Agent 开发里最被低估的技能——同样的工具,写好 docstring 和写烂 docstring,Agent 的行为差异非常大。

❌ 写法差(含糊,LLM 不知道何时用)

@tool
def search(query: str) -> str:
    """搜索信息"""
    pass

✅ 写法好(明确说明适用场景和参数格式)

@tool
def search_web(query: str) -> str:
    """搜索网络上的实时信息,用于获取最新新闻、近期事件或需要实时数据的问题。

    适用场景:
    - 最新的新闻、事件
    - 需要实时数据的问题(股价、天气、比赛结果)
    - 验证某个事实是否正确

    不适用于:
    - 需要深度分析的问题(直接用你的知识回答更好)
    - 历史事件(训练数据里有)

    Args:
        query: 搜索关键词,尽量精确,英文效果更好

    Returns:
        搜索结果摘要
    """
    pass

使用 StructuredTool(多参数工具)

当工具需要多个参数,用 Pydantic 定义输入结构更清晰:

from langchain_core.tools import StructuredTool
from pydantic import BaseModel, Field

class EmailInput(BaseModel):
    to: str = Field(description="收件人邮箱地址")
    subject: str = Field(description="邮件主题")
    body: str = Field(description="邮件正文,纯文本格式")

def send_email_func(to: str, subject: str, body: str) -> str:
    # 实际发送逻辑
    return f"邮件已发送给 {to}"

send_email = StructuredTool.from_function(
    func=send_email_func,
    name="send_email",
    description="发送电子邮件给指定收件人。确认用户明确要求发送邮件时才调用。",
    args_schema=EmailInput,
)

用哪种 Agent 类型

LangChain 提供了好几种 Agent,推荐很明确:

类型创建函数推荐度说明
Tool Calling Agentcreate_tool_calling_agent⭐⭐⭐⭐⭐首选,模型原生支持 function calling
ReAct Agentcreate_react_agent⭐⭐⭐兼容性好,但比 Tool Calling 慢
OpenAI Functionscreate_openai_functions_agent⭐⭐已被 Tool Calling 取代

create_tool_calling_agent,其他的不需要研究了。


AgentExecutor 的关键参数

agent_executor = AgentExecutor(
    agent=agent,
    tools=tools,
    verbose=True,                    # 开发时开,生产环境关掉
    max_iterations=5,                # 最大工具调用轮次(必须设!)
    max_execution_time=60,           # 整体超时秒数
    handle_parsing_errors=True,      # 工具输出解析失败时不崩溃
    return_intermediate_steps=True,  # 返回每步工具调用记录(调试用)
)

max_iterations 是必须设的。我见过没设这个值的 Agent 在某次 LLM 返回异常时进入无限调用循环,Token 消耗一直跑到 API 额度耗尽才停。5-10 次通常够用,超过的话往往是 Agent 陷入了循环,应该结束并报错。


带记忆的 Agent

给 Agent 加上多轮对话记忆:

from langchain_community.chat_message_histories import ChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory

store = {}

def get_session_history(session_id: str):
    if session_id not in store:
        store[session_id] = ChatMessageHistory()
    return store[session_id]

agent_with_history = RunnableWithMessageHistory(
    agent_executor,
    get_session_history,
    input_messages_key="input",
    history_messages_key="chat_history",
)

config = {"configurable": {"session_id": "user-123"}}
agent_with_history.invoke({"input": "我叫小明"}, config=config)
agent_with_history.invoke({"input": "我叫什么名字?"}, config=config)  # 记得你叫小明

实用示例:研究助手

一个能搜索网络并保存笔记的研究 Agent:

from langchain_community.tools import DuckDuckGoSearchRun

search = DuckDuckGoSearchRun()

@tool
def take_notes(content: str) -> str:
    """保存研究发现到笔记文件。

    当发现重要信息需要记录时调用。content 是笔记内容。
    """
    with open("research_notes.txt", "a", encoding="utf-8") as f:
        f.write(content + "\n\n---\n\n")
    return "笔记已保存"

@tool
def read_notes() -> str:
    """读取之前保存的研究笔记。

    当需要回顾已记录的信息时调用。
    """
    try:
        with open("research_notes.txt", "r", encoding="utf-8") as f:
            return f.read() or "暂无笔记"
    except FileNotFoundError:
        return "暂无笔记"

prompt = ChatPromptTemplate.from_messages([
    ("system", """你是一个研究助手。系统性地进行研究:
1. 搜索相关信息
2. 记录重要发现(不要重复记录已有内容)
3. 整理成清晰的总结

做事要有条理,不要重复同样的搜索。"""),
    ("placeholder", "{chat_history}"),
    ("human", "{input}"),
    ("placeholder", "{agent_scratchpad}"),
])

agent_executor = AgentExecutor(
    agent=create_tool_calling_agent(
        ChatOpenAI(model="gpt-4o"),
        [search, take_notes, read_notes],
        prompt
    ),
    tools=[search, take_notes, read_notes],
    max_iterations=8,
    verbose=True,
)

流式输出(生产环境)

在 Web 应用里实时展示 Agent 的思考过程:

async for event in agent_executor.astream_events(
    {"input": "分析一下 2024 年 AI 行业发展趋势"},
    version="v1",
):
    if event["event"] == "on_chat_model_stream":
        content = event["data"]["chunk"].content
        if content:
            print(content, end="", flush=True)
    elif event["event"] == "on_tool_start":
        print(f"\n[调用工具: {event['name']}]")

常见问题

Agent 反复调用同一个工具:大概率是工具返回结果没有给 LLM 足够的信息去推进,或者 System Prompt 没有说清楚何时停止。在 System Prompt 里加"已经有足够信息时直接给出答案,不要继续搜索"。

工具调用参数格式错误:通常是 docstring 写得不清楚,LLM 对参数格式有误解。把参数的格式要求写进 docstring 里(如"日期格式 YYYY-MM-DD")。

Agent 比预期慢很多:检查 max_iterations 实际触发了多少次。如果经常跑到 4-5 次才完成,可能是任务适合用 Chain 而不是 Agent,或者 System Prompt 需要引导 LLM 更直接地解决问题。

安全隐患:涉及文件操作、代码执行、发送邮件等不可逆操作的工具,考虑在工具里加确认逻辑,或者用 LangGraph 的 Human-in-the-Loop 让人工确认后再执行。


小结

  1. Agent 让 LLM 自主选择工具,比 Chain 灵活,但也更慢、更贵、更难预测——不要把 Agent 当默认方案。
  2. 工具的 docstring 是写给 LLM 看的,说清楚何时用、不适合什么场景、参数格式——这是 Agent 开发的核心技能。
  3. 始终设置 max_iterations,没有上限的 Agent 在异常情况下会无限循环消耗 API 额度。
  4. 2024 年起用 create_tool_calling_agent,比 ReAct 准确,比旧的 OpenAI Functions 接口更通用。
  5. 生产环境的复杂 Agent 逻辑(循环、条件分支、人工审核)考虑迁移到 LangGraph,控制和可观察性更好。

下一步LangGraph 进阶 — 用 Graph 结构管理 Agent 的状态和工作流,支持断点恢复和人工审核

相关参考Tool use 官方文档 | AgentExecutor

LangChain 框架指南
AI Engineer

LangChain 框架指南

LangChain 是构建 LLM 应用的流行框架,提供了链式调用、Agent、RAG 等丰富的功能模块。

LangChain 框架指南Agents 代理

LangChain Agents 代理

Agent 是 LangChain 里最强大也最容易被滥用的功能。

先说清楚它和普通 Chain 的根本区别:Chain 的执行路径是你预先定义的,Agent 的执行路径是 LLM 在运行时自己决定的。你给它一堆工具(搜索、计算、查数据库),它自己判断现在该用哪个、用几次、用什么参数。

这带来了极大的灵活性,但也带来了三个代价:(每次工具调用都是一次 LLM 请求)、(多次 API 调用)、不可预测(LLM 有时会走奇怪的路径)。

Agent 不是万能的。如果你的流程可以提前定义好步骤,用 Chain 或 LangGraph 比 Agent 更稳定、更便宜、更容易调试。我的经验是,很多人在不需要 Agent 的地方用了 Agent,然后抱怨"为什么这么慢、这么贵、结果不稳定"——其实换成普通 Chain 就解决了。


#Agent 的工作流程

用户输入
   │
   ▼
LLM 分析:我需要用什么工具?
   │
   ▼
调用工具 1(如搜索天气)
   │
   ▼
LLM 分析:结果够了吗?还需要其他信息?
   │
   ▼
调用工具 2(如计算)
   │
   ▼
LLM 生成最终答案
   │
   ▼
返回用户

每个"调用工具 → 分析结果"都是一次 LLM API 请求。一次用户请求可能触发 3-5 次甚至更多的 LLM 调用。


#快速开始

python
from langchain_openai import ChatOpenAI from langchain.agents import create_tool_calling_agent, AgentExecutor from langchain_core.prompts import ChatPromptTemplate from langchain_core.tools import tool # 1. 定义工具 @tool def get_weather(city: str) -> str: """获取指定城市当前天气。 当用户问天气相关问题时使用。 返回天气描述和温度信息。 """ weather_data = { "北京": "晴,25°C,空气质量良好", "上海": "多云,28°C,微风", "广州": "小雨,32°C,湿度高", } return weather_data.get(city, f"暂无 {city} 天气数据") @tool def calculate(expression: str) -> str: """执行数学计算。 支持加减乘除等基本运算。expression 是合法的数学表达式,如 '123 * 456'。 """ try: result = eval(expression) return str(result) except Exception as e: return f"计算出错:{e}" # 2. 创建 Prompt(必须有 agent_scratchpad 占位符) prompt = ChatPromptTemplate.from_messages([ ("system", "你是一个有帮助的助手,可以使用工具来回答问题。"), ("placeholder", "{chat_history}"), ("human", "{input}"), ("placeholder", "{agent_scratchpad}"), # Agent 的中间思考过程放这里 ]) # 3. 组装 llm = ChatOpenAI(model="gpt-4o") # Agent 用 gpt-4o,不要用 mini,工具调用准确率差很多 tools = [get_weather, calculate] agent = create_tool_calling_agent(llm, tools, prompt) agent_executor = AgentExecutor( agent=agent, tools=tools, verbose=True, # 开发时开着,能看到每次工具调用的细节 max_iterations=5, # 必须设上限,防止无限循环 max_execution_time=30, # 超时保险 ) # 4. 运行 result = agent_executor.invoke({ "input": "北京天气怎么样?另外帮我算一下 123 × 456" }) print(result["output"])

#工具定义:这里最容易搞砸

工具的 docstring 不是给你看的,是给 LLM 看的。LLM 读 docstring 来决定要不要用这个工具、怎么用。这是 Agent 开发里最被低估的技能——同样的工具,写好 docstring 和写烂 docstring,Agent 的行为差异非常大。

❌ 写法差(含糊,LLM 不知道何时用)

python
@tool def search(query: str) -> str: """搜索信息""" pass

✅ 写法好(明确说明适用场景和参数格式)

python
@tool def search_web(query: str) -> str: """搜索网络上的实时信息,用于获取最新新闻、近期事件或需要实时数据的问题。 适用场景: - 最新的新闻、事件 - 需要实时数据的问题(股价、天气、比赛结果) - 验证某个事实是否正确 不适用于: - 需要深度分析的问题(直接用你的知识回答更好) - 历史事件(训练数据里有) Args: query: 搜索关键词,尽量精确,英文效果更好 Returns: 搜索结果摘要 """ pass

#使用 StructuredTool(多参数工具)

当工具需要多个参数,用 Pydantic 定义输入结构更清晰:

python
from langchain_core.tools import StructuredTool from pydantic import BaseModel, Field class EmailInput(BaseModel): to: str = Field(description="收件人邮箱地址") subject: str = Field(description="邮件主题") body: str = Field(description="邮件正文,纯文本格式") def send_email_func(to: str, subject: str, body: str) -> str: # 实际发送逻辑 return f"邮件已发送给 {to}" send_email = StructuredTool.from_function( func=send_email_func, name="send_email", description="发送电子邮件给指定收件人。确认用户明确要求发送邮件时才调用。", args_schema=EmailInput, )

#用哪种 Agent 类型

LangChain 提供了好几种 Agent,推荐很明确:

类型创建函数推荐度说明
Tool Calling Agentcreate_tool_calling_agent⭐⭐⭐⭐⭐首选,模型原生支持 function calling
ReAct Agentcreate_react_agent⭐⭐⭐兼容性好,但比 Tool Calling 慢
OpenAI Functionscreate_openai_functions_agent⭐⭐已被 Tool Calling 取代

create_tool_calling_agent,其他的不需要研究了。


#AgentExecutor 的关键参数

python
agent_executor = AgentExecutor( agent=agent, tools=tools, verbose=True, # 开发时开,生产环境关掉 max_iterations=5, # 最大工具调用轮次(必须设!) max_execution_time=60, # 整体超时秒数 handle_parsing_errors=True, # 工具输出解析失败时不崩溃 return_intermediate_steps=True, # 返回每步工具调用记录(调试用) )

max_iterations 是必须设的。我见过没设这个值的 Agent 在某次 LLM 返回异常时进入无限调用循环,Token 消耗一直跑到 API 额度耗尽才停。5-10 次通常够用,超过的话往往是 Agent 陷入了循环,应该结束并报错。


#带记忆的 Agent

给 Agent 加上多轮对话记忆:

python
from langchain_community.chat_message_histories import ChatMessageHistory from langchain_core.runnables.history import RunnableWithMessageHistory store = {} def get_session_history(session_id: str): if session_id not in store: store[session_id] = ChatMessageHistory() return store[session_id] agent_with_history = RunnableWithMessageHistory( agent_executor, get_session_history, input_messages_key="input", history_messages_key="chat_history", ) config = {"configurable": {"session_id": "user-123"}} agent_with_history.invoke({"input": "我叫小明"}, config=config) agent_with_history.invoke({"input": "我叫什么名字?"}, config=config) # 记得你叫小明

#实用示例:研究助手

一个能搜索网络并保存笔记的研究 Agent:

python
from langchain_community.tools import DuckDuckGoSearchRun search = DuckDuckGoSearchRun() @tool def take_notes(content: str) -> str: """保存研究发现到笔记文件。 当发现重要信息需要记录时调用。content 是笔记内容。 """ with open("research_notes.txt", "a", encoding="utf-8") as f: f.write(content + "\n\n---\n\n") return "笔记已保存" @tool def read_notes() -> str: """读取之前保存的研究笔记。 当需要回顾已记录的信息时调用。 """ try: with open("research_notes.txt", "r", encoding="utf-8") as f: return f.read() or "暂无笔记" except FileNotFoundError: return "暂无笔记" prompt = ChatPromptTemplate.from_messages([ ("system", """你是一个研究助手。系统性地进行研究: 1. 搜索相关信息 2. 记录重要发现(不要重复记录已有内容) 3. 整理成清晰的总结 做事要有条理,不要重复同样的搜索。"""), ("placeholder", "{chat_history}"), ("human", "{input}"), ("placeholder", "{agent_scratchpad}"), ]) agent_executor = AgentExecutor( agent=create_tool_calling_agent( ChatOpenAI(model="gpt-4o"), [search, take_notes, read_notes], prompt ), tools=[search, take_notes, read_notes], max_iterations=8, verbose=True, )

#流式输出(生产环境)

在 Web 应用里实时展示 Agent 的思考过程:

python
async for event in agent_executor.astream_events( {"input": "分析一下 2024 年 AI 行业发展趋势"}, version="v1", ): if event["event"] == "on_chat_model_stream": content = event["data"]["chunk"].content if content: print(content, end="", flush=True) elif event["event"] == "on_tool_start": print(f"\n[调用工具: {event['name']}]")

#常见问题

Agent 反复调用同一个工具:大概率是工具返回结果没有给 LLM 足够的信息去推进,或者 System Prompt 没有说清楚何时停止。在 System Prompt 里加"已经有足够信息时直接给出答案,不要继续搜索"。

工具调用参数格式错误:通常是 docstring 写得不清楚,LLM 对参数格式有误解。把参数的格式要求写进 docstring 里(如"日期格式 YYYY-MM-DD")。

Agent 比预期慢很多:检查 max_iterations 实际触发了多少次。如果经常跑到 4-5 次才完成,可能是任务适合用 Chain 而不是 Agent,或者 System Prompt 需要引导 LLM 更直接地解决问题。

安全隐患:涉及文件操作、代码执行、发送邮件等不可逆操作的工具,考虑在工具里加确认逻辑,或者用 LangGraph 的 Human-in-the-Loop 让人工确认后再执行。


#小结

  1. Agent 让 LLM 自主选择工具,比 Chain 灵活,但也更慢、更贵、更难预测——不要把 Agent 当默认方案。
  2. 工具的 docstring 是写给 LLM 看的,说清楚何时用、不适合什么场景、参数格式——这是 Agent 开发的核心技能。
  3. 始终设置 max_iterations,没有上限的 Agent 在异常情况下会无限循环消耗 API 额度。
  4. 2024 年起用 create_tool_calling_agent,比 ReAct 准确,比旧的 OpenAI Functions 接口更通用。
  5. 生产环境的复杂 Agent 逻辑(循环、条件分支、人工审核)考虑迁移到 LangGraph,控制和可观察性更好。

下一步LangGraph 进阶 — 用 Graph 结构管理 Agent 的状态和工作流,支持断点恢复和人工审核

相关参考Tool use 官方文档 | AgentExecutor

System Design

系统设计必备:核心概念 + 经典案例

快速掌握取舍与设计套路,备战系统设计面试。

进入 System Design →

相关路线图