https://python.langchain.com.cn/docs/expression_language/cookbook/agent

讲解:用 LangChain 表达式语言构建 Agent(基于文档内容)

同学们,今天我们聚焦文档里的核心内容——如何用 LangChain 的“可运行对象(Runnable)”构建一个能自主调用工具解决问题的 Agent(代理人)。整个过程我们完全跟着文档的代码和逻辑走,不额外扩展,重点理解“Agent 为什么能自主做事”以及“每一步代码的作用”。

一、先明确:这节课的核心目标

文档里的 Agent 最终要实现一个具体功能——回答“纽约天气如何”。但背后的核心是教会我们:如何让 Agent 具备“判断是否需要工具→调用工具→用工具结果生成答案”的能力。
简单说,Agent 不是直接“知道”天气,而是“知道要查天气→调用搜索工具查→用工具返回的结果组织答案”,这就是它和普通“提示+模型”链的区别。

二、课前准备:Agent 必须的 5 个核心组件(文档明确提到)

在写代码前,我们得先认识文档里的 5 个“零件”,少一个都不行,且它们必须“格式匹配”(比如提示格式和解析器要对应):

  1. 工具(Tools):Agent 的“手脚”——能帮 Agent 做具体事(比如查天气、算数据)。文档里用 @tool 装饰器定义,必须写清楚“工具名+描述”(让 Agent 知道什么时候该用它)。
  2. 提示(Prompt):Agent 的“说明书”——告诉 Agent“该怎么思考”(比如“不知道答案就调用搜索工具”“调用工具要按 XML 格式写”)。文档里从 LangChain Hub 拉了一个现成的 XML 格式提示(hwchase17/xml-agent-convo),也可以自己改。
  3. 模型(LLM):Agent 的“大脑”——负责判断“要不要调用工具”“调用哪个工具”“怎么生成最终答案”。文档里用的是 Anthropic 的 claude-2 模型。
  4. 输出解析器(OutputParser):Agent 的“翻译官”——把模型输出的“混乱文本”转成“明确指令”(比如从模型输出里提取“调用 search 工具”的指令,或“最终答案”)。文档里用 XMLAgentOutputParser,因为提示是 XML 格式,解析器要对应。
  5. AgentExecutor:Agent 的“运行容器”——负责管整个流程:把输入传给 Agent、执行工具调用、记录中间步骤、判断是否要继续调用工具(还是直接出答案)。

三、分步实操:跟着文档写 Agent(每步讲透作用)

我们完全按文档的代码顺序来,每写一段就解释“为什么这么写”,对应哪个核心组件。

第一步:导入文档里的所有依赖(先搭好环境)

文档开头先导入需要的库,这些是构建 Agent 的“基础工具包”,缺一不可:

from langchain import hub  # 用来从Hub拉取提示
from langchain.agents import AgentExecutor, tool  # Agent执行器+工具装饰器
from langchain.agents.output_parsers import XMLAgentOutputParser  # XML格式解析器
from langchain_community.chat_models import ChatAnthropic  # Anthropic模型

第二步:定义“工具”——给 Agent 装“手脚”

文档里定义了一个 search 工具,作用是“查当前事件”(这里模拟查天气)。重点看 @tool 装饰器和工具描述:

# @tool装饰器:告诉LangChain“这是一个Agent可用的工具”
@tool
def search(query: str) -> str:
    """搜索有关当前事件的内容。"""  # 工具描述:关键!Agent会靠这个判断“什么时候用它”
    # 文档里用固定值模拟工具返回(实际项目中这里要对接真实搜索API,比如百度搜索)
    return "32 degrees"

# 把工具放进列表:后续传给AgentExecutor,让Agent知道有哪些工具可用
tool_list = [search]

提问:为什么要写工具描述?——如果描述写“查历史数据”,Agent 问实时天气时就不会调用它;写“查当前事件”,Agent 才知道“查实时天气该用这个工具”。

第三步:处理“提示和格式”——给 Agent 写“说明书”

文档里这一步做了两件关键的事:拉取提示 + 写“格式转换函数”。因为模型只能读“字符串”,但 Agent 需要“工具列表”和“历史步骤记录”,得把它们转成字符串才能传给模型。

1. 从 Hub 拉取提示
# 拉取XML格式的提示:这个提示里已经写好了“Agent该怎么思考”的规则
# 比如“不知道答案就调用工具,调用工具要写<tool>标签,最终答案要写<final_answer>标签”
prompt = hub.pull("hwchase17/xml-agent-convo")
2. 写两个“格式转换函数”(文档里的核心逻辑)

这两个函数是“桥梁”,把 Agent 的“中间数据”转成模型能懂的字符串:

# 函数1:把“中间步骤”转成XML字符串(让Agent记住“之前干了什么”)
# intermediate_steps:存的是“之前调用的工具+工具返回的结果”
def convert_intermediate_steps(intermediate_steps):
    log = ""
    for action, observation in intermediate_steps:
        # 把“调用search工具+输入weather in New York+返回32 degrees”转成XML格式字符串
        log += f"<tool>{action.tool}</tool><tool_input>{action.tool_input}</tool_input><observation>{observation}</observation>"
    return log

# 函数2:把“工具列表”转成字符串(让Agent知道“自己有什么工具”)
def convert_tools(tools):
    # 把tool_list转成“search:搜索有关当前事件的内容”这样的字符串
    return "\n".join([f"{tool.name}: {tool.description}" for tool in tools])

重点:这两个函数的输出格式,必须和我们拉取的“XML 提示”格式一致——提示要 XML,我们就转成 XML 字符串,不然模型看不懂。

第四步:构建“Agent 链”——给 Agent 装“大脑+翻译官”

这是文档的核心代码!用“| 运算符”把“输入处理→提示填充→模型调用→输出解析”串起来,形成 Agent 的“思考-行动”逻辑:

# 1. 先初始化“大脑”:Anthropic的claude-2模型
model = ChatAnthropic(model="claude-2")

# 2. 构建Agent链:一步一步拆解
agent = (
    # 第一步:处理输入——把“用户问题”和“中间步骤记录”整理成模型需要的格式
    {
        "input": lambda x: x["input"],  # 传递用户输入(比如“whats the weather in New york?”)
        "agent_scratchpad": lambda x: convert_intermediate_steps(x["intermediate_steps"])  # 传递“历史操作记录”
    }
    # 第二步:填充提示——把“工具列表”提前塞进提示里(不用每次调用都传)
    | prompt.partial(tools=convert_tools(tool_list))
    # 第三步:调用模型——给模型绑“停止标记”,防止输出超出XML格式
    # 模型输出到“</tool_input>”或“</final_answer>”就停,避免多输出无关内容
    | model.bind(stop=["</tool_input>", "</final_answer>"])
    # 第四步:解析输出——用XML解析器把模型输出转成“明确指令”(是调用工具,还是返回最终答案)
    | XMLAgentOutputParser()
)

拆解:这条链就像 Agent 的“流水线”——先整理原材料(输入和历史记录),再填进说明书(提示),然后让大脑思考(模型),最后翻译大脑的想法(解析器)。

第五步:用“AgentExecutor”运行 Agent——让 Agent 动起来

文档里最后一步,用 AgentExecutor 把“Agent 链”和“工具列表”装起来,然后调用:

# 1. 初始化运行容器:verbose=True表示“显示详细过程”(方便我们看Agent怎么思考的)
agent_executor = AgentExecutor(agent=agent, tools=tool_list, verbose=True)

# 2. 调用Agent:输入用户问题
result = agent_executor.invoke({"input": "whats the weather in New york?"})

# 输出结果:{'input': 'whats the weather in New york?', 'output': 'The weather in New York is 32 degrees'}

四、关键:看懂 Agent 的“思考过程”(文档里的执行日志)

因为我们设了 verbose=True,文档里给出了 Agent 的完整“思考日志”,这是理解 Agent 核心的关键!我们逐行分析:

> 进入新的AgentExecutor链...  # 开始处理问题
 <tool>search</tool><tool_input>weather in New York  # Agent判断:需要调用search工具,输入是“纽约天气”
32 degrees  # 工具返回结果:32度
 <tool>search</tool><tool_input>weather in New York  # Agent再确认:结果对吗?再调用一次工具(文档里模拟重复调用,实际可优化)
32 degrees  # 工具再次返回32度
 <final_answer>The weather in New York is 32 degrees  # Agent判断:信息足够,生成最终答案
> 完成链。  # 流程结束

总结:Agent 不是“一次就好”,而是会“循环判断”——调用工具→看结果→不够就再调用→够了就出答案,这个循环是 AgentExecutor 帮我们管理的。

五、课堂小结:文档里的 3 个核心考点

  1. 组件匹配:提示格式 ↔ 输出解析器 ↔ 中间步骤转换函数,必须格式一致(文档里都是 XML),不然 Agent 会“混乱”。
  2. 工具描述很重要:Agent 靠工具描述判断“什么时候用哪个工具”,描述不准会导致 Agent 用错工具。
  3. AgentExecutor 的作用:它是“总指挥”,负责管流程、调工具、记步骤,没有它,Agent 只是一条“死链”,没法自主循环。

课后作业:跟着文档改一改

试着把文档里的 search 工具改一改——比如让它返回“25 degrees, sunny”(模拟晴天),再调用 Agent,看输出会不会变成“The weather in New York is 25 degrees, sunny”。这样能帮你确认自己是不是真的懂了每一步的作用。

有哪里没看懂的,我们再回到文档的对应代码,一起再拆一遍~

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐