用 LangChain 表达式语言构建 Agent (一)
https://python.langchain.com.cn/docs/expression_language/cookbook/agent
讲解:用 LangChain 表达式语言构建 Agent(基于文档内容)
同学们,今天我们聚焦文档里的核心内容——如何用 LangChain 的“可运行对象(Runnable)”构建一个能自主调用工具解决问题的 Agent(代理人)。整个过程我们完全跟着文档的代码和逻辑走,不额外扩展,重点理解“Agent 为什么能自主做事”以及“每一步代码的作用”。
一、先明确:这节课的核心目标
文档里的 Agent 最终要实现一个具体功能——回答“纽约天气如何”。但背后的核心是教会我们:如何让 Agent 具备“判断是否需要工具→调用工具→用工具结果生成答案”的能力。
简单说,Agent 不是直接“知道”天气,而是“知道要查天气→调用搜索工具查→用工具返回的结果组织答案”,这就是它和普通“提示+模型”链的区别。
二、课前准备:Agent 必须的 5 个核心组件(文档明确提到)
在写代码前,我们得先认识文档里的 5 个“零件”,少一个都不行,且它们必须“格式匹配”(比如提示格式和解析器要对应):
- 工具(Tools):Agent 的“手脚”——能帮 Agent 做具体事(比如查天气、算数据)。文档里用
@tool装饰器定义,必须写清楚“工具名+描述”(让 Agent 知道什么时候该用它)。 - 提示(Prompt):Agent 的“说明书”——告诉 Agent“该怎么思考”(比如“不知道答案就调用搜索工具”“调用工具要按 XML 格式写”)。文档里从 LangChain Hub 拉了一个现成的 XML 格式提示(
hwchase17/xml-agent-convo),也可以自己改。 - 模型(LLM):Agent 的“大脑”——负责判断“要不要调用工具”“调用哪个工具”“怎么生成最终答案”。文档里用的是 Anthropic 的
claude-2模型。 - 输出解析器(OutputParser):Agent 的“翻译官”——把模型输出的“混乱文本”转成“明确指令”(比如从模型输出里提取“调用 search 工具”的指令,或“最终答案”)。文档里用
XMLAgentOutputParser,因为提示是 XML 格式,解析器要对应。 - 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 个核心考点
- 组件匹配:提示格式 ↔ 输出解析器 ↔ 中间步骤转换函数,必须格式一致(文档里都是 XML),不然 Agent 会“混乱”。
- 工具描述很重要:Agent 靠工具描述判断“什么时候用哪个工具”,描述不准会导致 Agent 用错工具。
- AgentExecutor 的作用:它是“总指挥”,负责管流程、调工具、记步骤,没有它,Agent 只是一条“死链”,没法自主循环。
课后作业:跟着文档改一改
试着把文档里的 search 工具改一改——比如让它返回“25 degrees, sunny”(模拟晴天),再调用 Agent,看输出会不会变成“The weather in New York is 25 degrees, sunny”。这样能帮你确认自己是不是真的懂了每一步的作用。
有哪里没看懂的,我们再回到文档的对应代码,一起再拆一遍~
更多推荐

所有评论(0)