第 17 期 | Agent Executor 与调试:拯救跑偏的 Agent
(申请发送: agentupdate)
本期副标题:让你的智能客服拥有自主思考和行动的能力,不再是只会聊天的复读机!
嘿,同学们!我是你们的老朋友,AI 技术导师,今天我们又要搞点大事情了!
前面几期,我们已经把 LangChain 的地基打得非常牢固了。LLM、PromptTemplate、OutputParser、Chains,这些都是构建智能应用的基石。但有没有觉得,我们的智能客服小助手虽然能理解问题、生成回复,但总感觉少了点“灵魂”?它能聊天,能基于已有的文本知识回答问题,但如果用户问:“我的订单状态是什么?”或者“请帮我查一下最新的产品手册里关于退货政策的条款”,它是不是就傻眼了?
没错,它还缺乏“手脚”——也就是执行外部动作的能力,更缺乏“大脑”——根据问题动态决策,选择合适的“手脚”去解决问题的能力。
今天,我们就来给我们的智能客服小助手装上真正的大脑和手脚,让它从一个“复读机”进化成一个能自主思考、能调用外部工具解决复杂问题的“智能代理人”——Agent!
🎯 本期学习目标
- 透彻理解 LangChain Agents 的核心原理和工作机制:搞清楚 Agent 到底是如何让 LLM 变得“聪明”起来,不再仅仅是文本生成器。
- 掌握如何为 Agent 配置和使用不同的工具 (Tools):学会如何设计和封装外部功能,让 Agent 能够调用它们来获取信息或执行操作。
- 学会构建一个能够自主决策、调用工具解决复杂问题的智能客服 Agent:亲手搭建一个能查订单、搜知识库的客服 Agent。
- 提升智能客服的实用性,使其能够处理超出自身知识范围的问题:让我们的客服小助手真正具备解决实际生产问题的能力。
📖 原理解析
什么是 LangChain Agent?
简单来说,LangChain Agent 就是一个由大型语言模型 (LLM) 驱动的“智能决策者”,它能够根据用户的输入,自主地规划一系列行动。这些行动可能包括:思考、调用外部工具、观察工具的执行结果,然后循环往复,直到得出最终答案。
你把 LLM 想象成一个超级聪明但缺乏行动能力的大脑。Agent 的出现,就是给这个大脑配备了各种“手脚”(Tools),并教它如何根据思考(Thought)来选择并使用这些手脚(Action),然后从手脚的反馈(Observation)中学习,最终达到目标。
Agent 的核心价值在于:让 LLM 具备了超越其训练数据和上下文窗口的实时信息获取和行动能力。 我们的智能客服,不再是只能在预设知识库里打转的“信息复读机”,而是能真正接入企业后台、调用各种 API 的“问题解决专家”。
Agent vs. Chain:动态决策与预设路径
我们之前学过 Chain,它就像一条预先铺设好的流水线,数据按照既定的步骤一步步流转。比如,一个LLMChain后面接一个OutputParser。这条路径是固定的。
但 Agent 不同,它没有固定的执行路径。它的每一步都是动态决策的。当 Agent 接收到输入后,它会:
- 思考 (Thought):根据当前输入和历史对话,LLM 会思考下一步该做什么。是直接回答?还是需要调用工具?如果调用工具,应该调用哪个工具?参数是什么?
- 行动 (Action):根据思考的结果,Agent 会选择一个合适的工具,并提供相应的输入。
- 观察 (Observation):工具被执行后,Agent 会获取工具的输出结果。
- 循环 (Loop):将观察结果反馈给 LLM,LLM 再次思考,决定下一步行动,直到问题解决或达到预设的停止条件。
这个循环过程,就是 Agent 能够处理复杂、多步骤任务的秘密。
Agent 的核心组件
- LLM (Language Model): Agent 的“大脑”,负责思考、推理和生成行动计划。它的智能水平直接决定了 Agent 的上限。
- Tools (工具): Agent 的“手脚”,封装了外部功能,可以是 API 调用、数据库查询、文件读写、甚至调用其他模型。每个工具都有一个描述 (description),Agent 会根据这个描述来决定何时以及如何使用它。
- Agent Executor (代理执行器): Agent 的“协调者”或“调度器”。它接收 LLM 的思考和行动指令,执行相应的工具,并将工具的输出返回给 LLM。它负责管理整个思考-行动-观察的循环。
ReAct 范式:Agent 思考的艺术
在 LangChain 中,最常见也最强大的 Agent 范式之一就是 ReAct (Reasoning and Acting)。ReAct 的核心思想是让 LLM 不仅能“行动 (Act)”,还能“推理 (Reason)”。它的工作流非常直观:
- Thought: 我应该怎么做?我需要什么信息?我应该用哪个工具?
- Action: 好的,我决定使用这个工具。
- Action Input: 这是工具需要的参数。
- Observation: 工具执行后,我得到了这个结果。
- ... (循环)
- Final Answer: 根据所有信息,这是最终的答案。
通过这种显式的推理过程,LLM 能够更好地理解任务、规划步骤、纠正错误,从而提高解决问题的成功率。
Mermaid 图解 Agent 工作流
好了,嘴上说得再多,不如一张图来得清晰!我们来看看 Agent 的核心工作流程:
graph TD
A[用户输入/问题] --> B{Agent Executor};
B -- 将输入和历史对话传递给 LLM --> C[LLM (大脑)];
C -- 根据输入和可用工具的描述进行思考 --> D[Thought (思考)];
D -- 决定使用哪个工具及参数 --> E[Action (行动)];
E -- 提供工具所需参数 --> F[Action Input];
F --> G{工具库 (Tools)};
G -- 执行选定的工具 --> H[Observation (观察结果)];
H -- 将工具输出反馈给 LLM --> C;
C -- 持续思考,直到得出最终答案 --> I[Final Answer (最终答案)];
I --> J[返回给用户];
H -- 如果问题未解决,继续循环 --> D;
subgraph Agent的工作循环
D --> E --> F --> G --> H
end图解说明:
- 用户输入:用户提出问题,比如“我的订单号 XYZ 的状态是什么?”
- Agent Executor 接收:执行器拿到问题。
- LLM 思考:执行器将问题和当前可用的工具列表(以及它们的描述)交给 LLM。LLM 会基于 ReAct 范式开始“思考”:根据问题,我需要调用一个查询订单的工具。
- Thought/Action/Action Input:LLM 决定调用
OrderLookupTool,输入是XYZ。 - 工具库执行:Agent Executor 找到
OrderLookupTool,并传入XYZ。 - Observation:
OrderLookupTool返回“订单 XYZ 已发货,预计明天送达”。 - LLM 再次思考:LLM 拿到这个结果,发现问题已经解决,可以直接给出答案。
- Final Answer:LLM 生成最终的回复:“您的订单 XYZ 已发货,预计明天送达。”
- 返回给用户:Agent Executor 将最终答案返回给用户。
如果中间发现还需要更多信息,比如订单号不对,LLM 会再次思考,可能会建议用户提供正确的订单号,或者调用一个搜索用户历史订单的工具。这就是 Agent 的强大之处!
💻 实战代码演练 (客服项目中的具体应用)
好了,原理都懂了,是时候撸起袖子,把这些概念落地到我们的智能客服小助手上了!
场景设定: 我们的智能客服现在需要处理两种更高级的请求:
- 查询用户订单状态:用户提供订单号,客服需要查询内部订单系统。
- 搜索外部知识库:用户提出一些具体的产品问题,客服需要在一个模拟的外部知识库中搜索答案。
为了实现这两个功能,我们需要创建两个“工具 (Tools)”,然后用它们来武装我们的 Agent。
我们将使用 Python 进行演示,因为它在 LangChain 社区中更为流行和成熟。
1. 准备环境与 LLM
首先,确保你安装了必要的库,并配置好你的 OpenAI API Key(或者其他 LLM 提供商的 Key)。
pip install langchain openai faiss-cpu tiktoken
import os
from langchain_openai import ChatOpenAI
from langchain.agents import AgentExecutor, create_react_agent
from langchain.tools import Tool
from langchain import hub
from langchain_core.prompts import PromptTemplate
from typing import List, Dict, Any
# 设置你的 OpenAI API Key
# 建议通过环境变量设置,这里为了演示方便直接写出
os.environ["OPENAI_API_KEY"] = "YOUR_OPENAI_API_KEY"
# 初始化 LLM
# 我们使用 ChatOpenAI,因为它在代理决策方面表现出色
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.7)
print("LLM 初始化完成!")
2. 定义我们的工具 (Tools)
这是 Agent 的“手脚”部分。每个工具都需要一个 name(工具的唯一标识)、description(Agent 决定何时使用工具的关键)和一个 func(实际执行的函数)。
# 模拟一个订单查询系统
def get_order_status(order_id: str) -> str:
"""
查询指定订单号的当前状态。
输入参数为订单号 (order_id)。
"""
print(f"\n--- 正在查询订单 {order_id} ---")
mock_orders = {
"ORDER_12345": "您的订单 ORDER_12345 已发货,预计明天送达。",
"ORDER_67890": "您的订单 ORDER_67890 正在打包中,预计3天内发货。",
"ORDER_ABCDE": "您的订单 ORDER_ABCDE 已完成退款,退款将在3-5个工作日内到账。",
"ORDER_TEST1": "订单 ORDER_TEST1 异常,请联系客服处理。",
}
status = mock_orders.get(order_id.upper(), f"未找到订单号 {order_id} 的信息。请检查订单号是否正确。")
print(f"--- 订单查询结果: {status} ---")
return status
# 模拟一个外部知识库搜索功能
def search_knowledge_base(query: str) -> str:
"""
在智能客服知识库中搜索相关信息。
输入参数为搜索查询 (query)。
适用于查找产品特性、使用指南、常见问题等。
"""
print(f"\n--- 正在搜索知识库:'{query}' ---")
mock_kb_articles = {
"退货政策": "我们的退货政策允许在购买后30天内无理由退货,商品需保持完好。",
"产品保修": "所有电子产品均提供一年免费保修服务,人为损坏不在保修范围内。",
"发货时间": "订单通常在24小时内处理并发货,国内快递预计3-5个工作日送达。",
"账户安全": "请定期更换密码,不要与他人分享您的账户信息,我们不会通过邮件索要密码。",
"支付方式": "我们支持支付宝、微信支付、银联和Visa信用卡支付。",
"联系客服": "您可以通过在线聊天、拨打客服热线400-123-4567或发送邮件至[email protected]联系我们。",
}
# 简单的关键词匹配搜索
results = [
article for topic, article in mock_kb_articles.items()
if query.lower() in topic.lower() or query.lower() in article.lower()
]
if results:
# 为了演示,只返回第一个匹配的结果
print(f"--- 知识库搜索结果: {results[0]} ---")
return results[0]
else:
print("--- 知识库搜索结果: 未找到相关信息。---")
return "未找到与您查询相关的知识库信息,请尝试其他关键词或联系人工客服。"
# 将函数封装成 LangChain Tool 对象
tools = [
Tool(
name="GetOrderStatus",
func=get_order_status,
description="""
当你需要查询用户订单的当前状态时使用此工具。
输入必须是订单号,例如 'ORDER_12345'。
示例:用户问“我的订单状态如何?订单号是 ORDER_12345”时,你应该调用此工具并传入 'ORDER_12345'。
"""
),
Tool(
name="SearchKnowledgeBase",
func=search_knowledge_base,
description="""
当你需要在一个智能客服知识库中搜索关于产品特性、使用指南、常见问题、政策条款等信息时使用此工具。
输入必须是搜索查询的关键词,例如 '退货政策' 或 '产品保修期'。
示例:用户问“你们的退货政策是什么?”时,你应该调用此工具并传入 '退货政策'。
"""
)
]
print("工具定义完成!")
划重点:工具的 description 是 Agent 成功与否的关键!
LLM 会根据这个描述来判断哪个工具最适合当前的问题。所以,描述要清晰、准确,包含工具的用途、输入要求和适用场景。我这里特意加了详细的描述和使用示例,就是为了让 Agent 更好地理解。
3. 构建 Agent
我们使用 create_react_agent 函数来创建一个基于 ReAct 范式的 Agent。这个函数需要三个核心参数:
llm: 我们的大脑。tools: 我们定义好的手脚。prompt: Agent 的指令,告诉 LLM 如何进行思考和行动。LangChain 提供了一个默认的 ReAct 提示模板,我们可以直接从langchain.hub拉取。
# 从 LangChain Hub 获取 ReAct 提示模板
# 这个模板包含了 ReAct 范式的核心指令,指导 LLM 如何思考和行动
prompt = hub.pull("hwchase17/react")
# 打印提示模板,看看它长什么样
print("\n--- ReAct Agent 提示模板 ---")
# print(prompt.template) # 打印完整的模板内容,可以自行取消注释查看
# 创建 ReAct Agent
# create_react_agent 会结合 LLM、Tools 和 Prompt 来生成一个 Agent 可调用的 Runnable
agent = create_react_agent(llm, tools, prompt)
# 创建 Agent Executor
# Agent Executor 负责驱动 Agent 的执行循环
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True, handle_parsing_errors=True)
print("Agent 构建完成!")
verbose=True 非常重要!它会让 Agent 在执行过程中打印出每一步的 Thought、Action、Action Input 和 Observation,这对于我们理解 Agent 的决策过程和调试非常有帮助。handle_parsing_errors=True 则会在 Agent 的输出解析失败时,将其作为 Observation 返回给 Agent,让 Agent 有机会自我修复。
4. 运行 Agent:智能客服的实战演练
现在,我们的智能客服小助手已经武装完毕,是时候看看它的表现了!
print("\n--- 智能客服小助手启动!开始提问吧! ---")
# 场景 1: 查询订单状态
question1 = "我的订单号是 ORDER_12345,请问现在是什么状态?"
print(f"\n用户: {question1}")
response1 = agent_executor.invoke({"input": question1})
print(f"客服小助手: {response1['output']}")
# 预期:Agent 会调用 GetOrderStatus 工具,并返回订单状态
print("\n" + "="*50 + "\n")
# 场景 2: 搜索知识库 - 退货政策
question2 = "你们的退货政策是怎样的?"
print(f"\n用户: {question2}")
response2 = agent_executor.invoke({"input": question2})
print(f"客服小助手: {response2['output']}")
# 预期:Agent 会调用 SearchKnowledgeBase 工具,并返回退货政策
print("\n" + "="*50 + "\n")
# 场景 3: 搜索知识库 - 不存在的查询
question3 = "关于你们的AI芯片最新进展有什么资料吗?"
print(f"\n用户: {question3}")
response3 = agent_executor.invoke({"input": question3})
print(f"客服小助手: {response3['output']}")
# 预期:Agent 会调用 SearchKnowledgeBase 工具,但返回未找到相关信息
print("\n" + "="*50 + "\n")
# 场景 4: 混合问题 - 既需要工具,又需要简单回答
question4 = "我的订单号 ORDER_67890 状态如何?另外,你们的客服电话是多少?"
print(f"\n用户: {question4}")
response4 = agent_executor.invoke({"input": question4})
print(f"客服小助手: {response4['output']}")
# 预期:Agent 会先查询订单,然后可能再次调用 SearchKnowledgeBase 查询客服电话
print("\n" + "="*50 + "\n")
# 场景 5: 无法使用工具的问题
question5 = "你好,最近天气怎么样?"
print(f"\n用户: {question5}")
response5 = agent_executor.invoke({"input": question5})
print(f"客服小助手: {response5['output']}")
# 预期:Agent 不会使用任何工具,直接回答
运行上面的代码,你会看到 Agent 详细的思考过程 (Thought)、选择的工具 (Action)、工具的输入 (Action Input) 和工具的输出 (Observation),最终生成一个自然的回复。这就像你亲眼看到 LLM 在“思考”和“行动”!
通过这个实战,我们的智能客服小助手已经不再是只会“聊天”的摆设了,它真正拥有了解决实际业务问题的能力!这才是生产级 AI 应用的基石。
坑与避坑指南
Agent 虽强,但也不是银弹。在使用过程中,你会遇到一些“坑”,这里我作为老司机,提前给你打个预防针。
工具描述的“黑盒”与“魔术”:
- 坑点:Agent 决定使用哪个工具,完全依赖于工具的
description。如果描述不清晰、不准确,或者有歧义,Agent 可能会选择错误的工具,或者无法选择正确的工具。 - 避坑指南:把工具的
description当作是给 LLM 的 mini-prompt。它应该:- 清晰明确:直接说明工具的用途。
- 包含输入示例:告诉 LLM 工具需要什么样的输入。
- 说明适用场景:帮助 LLM 在多个工具中做出最佳选择。
- 迭代优化:观察
verbose=True的输出,如果 Agent 决策错误,第一时间检查并优化工具描述。
- 坑点:Agent 决定使用哪个工具,完全依赖于工具的
幻觉 (Hallucination) 与工具输出:
- 坑点:LLM 可能会对工具的输出结果进行“润色”甚至“编造”,或者在工具返回“未找到”时,自己“发明”一个答案。
- 避坑指南:
- 信任工具输出:在 Agent 的提示中强调,必须严格基于工具的
Observation来生成最终答案。 - 明确错误处理:让工具在找不到结果时返回明确的“未找到”信息,而不是空字符串或模糊的提示。
- 后处理 (Post-processing):如果对 LLM 的最终回复不放心,可以在 Agent 输出后进行简单的关键词检查或格式校验。
- 信任工具输出:在 Agent 的提示中强调,必须严格基于工具的
Agent 的成本与效率:
- 坑点:Agent 的每次
Thought、Action、Observation循环,都意味着一次或多次 LLM 调用。复杂的问题可能导致多轮循环,从而显著增加 API 调用成本和响应时间。 - 避坑指南:
- 精简工具:只提供 Agent 真正需要的工具,避免不必要的工具干扰决策。
- 优化工具效率:确保你的工具函数本身执行效率高,避免长时间等待。
- 设置最大迭代次数:通过
AgentExecutor的max_iterations参数限制 Agent 的思考轮数,防止无限循环或成本过高。 - 缓存策略:对于频繁查询的工具结果,考虑引入缓存。
- 坑点:Agent 的每次
安全性与权限管理:
- 坑点:Agent 能够调用外部工具,这意味着它拥有了执行外部操作的能力。如果工具设计不当,或者 Agent 决策失误,可能导致敏感数据泄露、系统被误操作等安全问题。
- 避坑指南:
- 最小权限原则:工具访问外部系统时,只赋予其完成任务所需的最小权限。
- 输入验证:工具函数内部必须对所有输入参数进行严格的验证和清理,防止注入攻击。
- 审计与监控:对 Agent 的工具调用行为进行日志记录和监控,及时发现异常。
- 限制敏感操作:对于涉及资金、用户数据修改等敏感操作,考虑加入人工审批环节或更严格的验证机制。
Agent 类型选择:
- 坑点:LangChain 有多种 Agent 类型(ReAct、OpenAI Functions、Structured Tool Agent 等),选择不当可能影响效果。
- 避坑指南:
- ReAct:通用性强,适用于需要多步推理和复杂决策的场景。缺点是提示词较长,可能增加 token 消耗。
- OpenAI Functions Agent:如果你使用 OpenAI 模型,强烈推荐使用。它利用 OpenAI 函数调用的能力,将工具的选择和参数提取直接集成到模型层面,效率更高,幻觉更少,更易于构建结构化输入。这是目前最推荐的 Agent 之一。
- Structured Tool Agent:当你的工具需要结构化输入(比如 JSON 对象)时非常有用。
- 持续关注 LangChain 更新:Agent 模块发展迅速,新类型和优化层出不穷。
📝 本期小结
恭喜你!通过本期学习,你已经掌握了 LangChain Agent 的核心秘密,并成功为我们的智能客服小助手装上了“大脑”和“手脚”!
我们深入理解了 Agent 的工作原理,它不再是简单的文本生成,而是通过 LLM 的推理能力,结合外部工具,实现动态决策和多步任务解决。我们还动手实践了如何定义工具、构建 Agent,并让智能客服能够查询订单、搜索知识库。
Agent 的引入,将我们的智能客服从一个“信息复读机”提升到了一个能够“自主思考、解决问题”的生产级应用。这是构建真正智能、有用的 AI 应用的关键一步。
但也要记住,能力越大,责任越大。Agent 的设计、工具的描述、安全性的考量,都是我们需要精雕细琢的地方。
下一期,我们将继续深入,探索如何让 Agent 处理更复杂的对话和记忆,敬请期待!