第 02 期 | 核心三剑客:Models, Prompts 与 Parsers
🎯 本期学习目标
- 深入理解 Agent 的核心工作机制:掌握 LLM 如何从“问答机器”蜕变为“智能决策者”的原理。
- 掌握 LangChain 中 Agent 的构建方法:学会定义工具 (Tools)、选择合适的 Agent 类型,并组装成一个能自主思考和行动的智能体。
- 实战应用 Agent 提升客服小助手能力:为我们的智能客服项目增添调用外部 API、执行复杂逻辑的“大脑”。
- 识别 Agent 开发中的常见陷阱:了解 Agent 可能遇到的问题,并学习如何有效规避,确保其稳定可靠运行。
📖 原理解析
各位未来的 AI 架构师们,欢迎来到《LangChain 全栈大师课》的第三期!前两期我们打下了基础,学会了如何让 LLM 理解并生成文本,以及如何通过 RAG 赋予它“记忆”和“知识”。但你有没有觉得,它依然像个“问答机”?你问什么,它答什么,缺乏主动性和决策力。
想象一下,你的智能客服小助手,用户问“我的订单号是多少?”,它可能能从知识库里找到订单查询的入口;但如果用户问“我的订单号是 XYZ,请帮我查一下物流状态,如果发货了,预计多久能到?”,这时候,仅仅是知识检索就不够了!它需要:
- 识别这是一个“查物流”的需求。
- 知道要调用一个“物流查询 API”。
- 从用户输入中提取“订单号”。
- 调用 API,获取结果。
- 将结果组织成用户友好的回答。
这,就是 Agent 的用武之地!Agent 的核心思想是:让 LLM 不仅仅是回答问题,而是作为一个推理引擎 (Reasoning Engine),去思考“我需要做什么”,然后“采取行动” (Action),并根据行动结果继续“思考”和“行动”,直到达到目标。 简单来说,Agent 让 LLM 有了“手”和“脚”,不再只是个“嘴炮王者”。
Agent 的核心组件
一个 LangChain Agent 主要由以下几部分构成:
- LLM (Large Language Model):Agent 的“大脑”。它负责理解用户意图、规划行动步骤、选择合适的工具,并根据工具的输出进行下一步推理。它的智能水平直接决定了 Agent 的上限。
- Tools (工具):Agent 的“手脚”。这些是 Agent 可以调用的外部功能或能力。可以是查询知识库的函数、调用外部 API 的接口、执行数学计算的工具,甚至是发送邮件、创建日程等。每个工具都有一个清晰的名称和描述,方便 LLM 理解其用途。
- Agent Executor (执行器):Agent 的“中枢神经系统”。它负责接收 LLM 的决策(即“我打算用哪个工具,参数是什么”),实际调用该工具,然后将工具的输出(Observation)反馈给 LLM,形成一个思考-行动-观察的循环,直到 LLM 决定给出最终答案。
- Prompt (指令):Agent 的“行动纲领”。它告诉 LLM 你是一个 Agent,你有什么能力(工具),你的目标是什么,以及你应该如何思考和输出。一个好的 Prompt 是 Agent 成功的关键。
Agent 的工作流程 (ReAct 模式为例)
LangChain 中最经典、也最强大的 Agent 模式之一是 ReAct (Reasoning and Acting)。它模仿人类解决问题的过程:思考 (Thought) -> 行动 (Action) -> 观察 (Observation) -> 再次思考...
下面是 Agent 的一个典型工作流:
graph TD
A[用户查询] --> B{Agent Executor};
B -- 将用户查询和历史信息传入 --> C(LLM);
C -- 根据Prompt和工具描述进行推理 --> D{LLM 输出: Thought};
D -- LLM 输出: Action (工具名称, 参数) --> E{Agent Executor};
E -- 调用指定的 Tool --> F(Tool 执行);
F -- Tool 执行结果 (Observation) --> G{Agent Executor};
G -- 将 Observation 反馈给 LLM --> C;
C -- 继续推理, 直到得到最终答案 --> H{LLM 输出: Final Answer};
H -- 返回给用户 --> A;工作流解析:
- 用户查询:用户向客服小助手提出一个问题。
- Agent Executor 接收:执行器接收到查询,并将其连同 Agent 的“行动纲领”(Prompt)以及可用工具的列表和描述,一并发送给 LLM。
- LLM 推理 (Thought):LLM 根据 Prompt 和工具描述,分析用户意图,思考解决问题所需的步骤。例如:“用户想查订单,我需要调用订单查询工具。”
- LLM 决策 (Action):LLM 决定采取哪个行动,并以特定格式输出:
Action: tool_name和Action Input: parameters。 - Agent Executor 执行 Tool:执行器解析 LLM 的输出,调用对应的工具,并传入参数。
- Tool 返回 Observation:工具执行完毕,返回结果。例如,订单查询 API 返回了订单状态、物流信息等。
- Agent Executor 反馈 Observation:执行器将工具的执行结果(Observation)再次反馈给 LLM。
- LLM 再次推理:LLM 收到 Observation 后,会根据新的信息继续思考:问题解决了吗?还需要其他工具吗?例如,如果查到了物流信息,它可能会直接组织答案;如果发现订单不存在,它可能会引导用户检查订单号。
- 循环:这个“思考-行动-观察”的循环会一直持续,直到 LLM 认为问题已解决,并输出
Final Answer。 - 返回最终答案:执行器将
Final Answer返回给用户。
这种循环式的推理和行动,让 Agent 具备了处理复杂、多步骤任务的能力,这正是我们智能客服小助手从“傻瓜式问答机”进化为“智能决策引擎”的关键!
Agent 类型选择
LangChain 提供了多种 Agent 类型,每种都有其适用场景:
zero-shot-react-description: 最通用、最灵活的 Agent,基于 ReAct 模式,适用于大多数需要通用推理和工具调用的场景。它完全依赖 LLM 的零样本推理能力。openai-functions: 专为 OpenAI 模型(如 GPT-3.5-turbo, GPT-4)设计,利用其 Function Calling 能力。这种模式下,LLM 会以结构化的 JSON 格式直接“建议”调用哪个函数及参数,效率和准确性通常更高。强烈推荐在 OpenAI 模型下使用此类型。conversational-react-description: 同样基于 ReAct 模式,但内置了记忆功能,使其更适合多轮对话场景,能记住之前的对话内容。structured-chat-zero-shot-react-description: 适用于需要更严格、结构化输入/输出的场景。
在我们的客服小助手项目中,如果使用 OpenAI 模型,openai-functions 是首选;如果使用其他模型,zero-shot-react-description 或 conversational-react-description 会是很好的选择。
💻 实战代码演练 (客服项目中的具体应用)
好了,原理讲明白了,接下来我们来点真格的。我们如何将 Agent 融入到智能客服小助手中,让它不再是只会检索知识的“书呆子”,而是能真正解决问题的“多面手”?
客服小助手的新能力需求:
- 查询订单状态:用户提供订单号,小助手能调用外部 API 查询并返回物流信息。
- 计算退款金额:用户提供商品价格和折扣信息,小助手能进行简单的数学计算。
- 通用知识问答:如果以上工具无法解决,依然能从知识库中检索信息。
我们将模拟一个拥有这三种能力的 Agent。
1. 定义工具 (Tools)
首先,我们需要为 Agent 准备“手脚”——Tools。这里我们模拟三个工具:order_status_checker (查询订单)、calculator (计算器)、knowledge_base_search (知识库搜索)。
import os
from typing import List, Union
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI
from langchain.agents import AgentExecutor, create_openai_functions_agent
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.messages import BaseMessage, HumanMessage, AIMessage
# 设置你的 OpenAI API Key
# os.environ["OPENAI_API_KEY"] = "YOUR_OPENAI_API_KEY"
# --- 1. 定义工具 (Tools) ---
@tool
def order_status_checker(order_id: str) -> str:
"""
根据订单ID查询订单的当前状态和物流信息。
输入参数:order_id (string) - 用户的订单ID。
"""
print(f"\n--- 调用 order_status_checker 工具查询订单ID: {order_id} ---")
# 模拟外部API调用
if order_id == "2023081512345":
return "订单号 2023081512345 已发货,快递单号 SF123456789,预计3天内送达。"
elif order_id == "2023081567890":
return "订单号 2023081567890 正在备货中,预计明天发货。"
else:
return f"未找到订单号 {order_id} 的信息,请检查订单号是否正确。"
@tool
def calculator(expression: str) -> str:
"""
一个简单的计算器,可以执行数学表达式。
输入参数:expression (string) - 需要计算的数学表达式,例如 "100 * 0.8"。
"""
print(f"\n--- 调用 calculator 工具计算表达式: {expression} ---")
try:
# 使用 eval() 函数进行计算,注意生产环境中 eval() 的安全风险
# 实际生产中应使用更安全的数学表达式解析库
result = eval(expression)
return f"计算结果是: {result}"
except Exception as e:
return f"计算失败: {e}"
@tool
def knowledge_base_search(query: str) -> str:
"""
在客服知识库中搜索相关信息以回答用户问题。
输入参数:query (string) - 用户的问题或关键词。
"""
print(f"\n--- 调用 knowledge_base_search 工具搜索知识库: {query} ---")
# 模拟知识库检索,这里可以集成RAG模块
if "退货政策" in query:
return "我们的退货政策是:商品在签收后7天内支持无理由退货,请确保商品完好,不影响二次销售。具体流程请联系客服。"
elif "联系客服" in query:
return "您可以通过拨打客服热线 400-123-4567,或在官网在线聊天窗口联系我们的客服团队。"
elif "工作时间" in query:
return "客服工作时间为周一至周五,上午9点至下午6点。"
else:
return f"抱歉,知识库中未能找到关于 '{query}' 的直接答案。您可以尝试换个关键词或联系人工客服。"
# 将所有工具组合成一个列表
tools = [order_status_checker, calculator, knowledge_base_search]
### 2. 初始化 LLM
我们使用 `ChatOpenAI` 作为 Agent 的“大脑”。
```python
# --- 2. 初始化 LLM ---
llm = ChatOpenAI(model="gpt-4o", temperature=0) # 或者 gpt-3.5-turbo
3. 构建 Agent Prompt
Prompt 是 Agent 的行动纲领。对于 openai-functions 类型的 Agent,LangChain 已经为我们封装好了大部分逻辑,我们只需要提供一个系统消息和消息历史的占位符。
# --- 3. 构建 Agent Prompt ---
prompt = ChatPromptTemplate.from_messages(
[
("system", "你是一个智能客服小助手,拥有查询订单、进行计算和搜索知识库的能力。请根据用户的问题,利用你的工具来提供准确的帮助。如果工具无法解决,请引导用户联系人工客服。"),
MessagesPlaceholder("chat_history"), # 用于存储对话历史,实现多轮对话
("human", "{input}"),
MessagesPlaceholder("agent_scratchpad"), # 用于Agent内部的思考和行动记录
]
)
4. 创建 Agent
使用 create_openai_functions_agent 函数来创建 Agent。
# --- 4. 创建 Agent ---
agent = create_openai_functions_agent(llm, tools, prompt)
5. 创建 Agent Executor
最后,将 Agent 和 Tools 组合成 AgentExecutor,它将负责驱动整个“思考-行动-观察”循环。
# --- 5. 创建 Agent Executor ---
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
verbose=True, # 设置为True可以看到Agent的思考过程
handle_parsing_errors=True, # 自动处理Agent输出解析错误
max_iterations=10, # 最大迭代次数,防止无限循环
early_stopping_method="generate", # 达到max_iterations后生成一个答案
)
6. 运行 Agent 进行交互
现在,我们的智能客服小助手已经具备了自主决策的能力!我们来和它聊聊。
# --- 6. 运行 Agent 进行交互 ---
async def run_agent_interaction(user_input: str, chat_history: List[BaseMessage]) -> List[BaseMessage]:
print(f"\n--- 用户提问: {user_input} ---")
response = await agent_executor.ainvoke({"input": user_input, "chat_history": chat_history})
ai_response = response["output"]
print(f"\n--- 小助手回复: {ai_response} ---")
# 更新对话历史
chat_history.append(HumanMessage(content=user_input))
chat_history.append(AIMessage(content=ai_response))
return chat_history
# 模拟对话历史
chat_history: List[BaseMessage] = []
# 场景1: 查询订单状态
chat_history = await run_agent_interaction("我的订单号 2023081512345 什么时候能送到?", chat_history)
# 场景2: 进行数学计算
chat_history = await run_agent_interaction("我买了120块钱的东西,打了个8折,再减去10块钱优惠券,实际支付了多少?", chat_history)
# 场景3: 知识库查询
chat_history = await run_agent_interaction("你们的退货政策是什么?", chat_history)
# 场景4: 无法解决的问题 (Agent会尝试知识库,然后引导)
chat_history = await run_agent_interaction("你们公司有多少员工?", chat_history)
# 场景5: 错误订单号
chat_history = await run_agent_interaction("我的订单号 99999999999 是什么状态?", chat_history)
# 场景6: 多轮对话 (Agent记住上下文) - 注意openai-functions agent的chat_history处理
# 这个例子中,我们每次都传入完整的chat_history,Agent可以利用它。
chat_history = await run_agent_interaction("那如果我要联系客服呢?", chat_history)
运行上述代码,你会看到verbose=True的输出,清晰地展示了 Agent 的思考过程:
- 它会先
Thinking...(思考)。 - 然后决定
Calling Tool: order_status_checker(调用工具)。 - 获得
Observation(观察结果)。 - 最后给出
Final Answer(最终答案)。
这就像你的客服小助手在后台默默地进行着复杂的推理和操作,最终为你呈现出精准的答案!是不是感觉一下子就“活”起来了?
TypeScript 提示
在 TypeScript 中实现 Agent 的思路是完全一致的。你需要:
- 定义 Tools:使用
@langchain/core/tools中的StructuredTool或DynamicTool。 - 初始化 LLM:使用
@langchain/openai中的ChatOpenAI。 - 构建 Prompt:使用
@langchain/core/prompts中的ChatPromptTemplate和MessagesPlaceholder。 - 创建 Agent:使用
@langchain/openai中的createOpenAIFunctionsAgent。 - 创建 Agent Executor:使用
@langchain/agents中的AgentExecutor。
核心 API 和参数命名都非常相似,可以无缝迁移。
坑与避坑指南
Agent 固然强大,但它也并非万能药,开发过程中会遇到不少“坑”。作为一位经验丰富的老司机,我来帮你提前踩踩雷,指明方向。
1. 幻觉与推理误差 (Hallucinations & Reasoning Errors)
- 坑点:Agent 的决策完全依赖 LLM 的推理能力。如果 LLM 对工具的理解有偏差,或者推理能力不足,它可能会选择错误的工具、提供错误的参数,甚至编造工具不存在的输出。这就导致 Agent 行为异常,甚至“胡说八道”。
- 避坑指南:
- 清晰、精确的工具描述:这是重中之重!工具的
name要直观,description要详细描述其功能、用途、输入参数及其类型、输出结果。想象你是在给一个聪明但没见过这个工具的人解释。 - 高质量的 LLM:使用能力更强的 LLM (如 GPT-4o/GPT-4) 可以显著减少推理错误。
- Prompt 工程:在 Agent 的系统 Prompt 中明确其职责、行为规范和错误处理原则。例如,如果工具无法解决,要引导用户联系人工客服。
- 清晰、精确的工具描述:这是重中之重!工具的
2. 工具选择与描述的艺术
- 坑点:工具描述模糊、命名不当,会导致 LLM 难以准确选择。例如,有两个工具一个叫
search_db,一个叫query_data,LLM 可能搞不清哪个才是知识库搜索。 - 避坑指南:
- 唯一且具象的命名:工具名称应简洁、有意义且唯一,如
order_status_checker而非check。 - 详细且无歧义的描述:描述中包含关键词,说明工具解决什么问题,需要什么信息,返回什么结果。思考用户会如何提问,并把这些关键词融入描述。
- 避免工具功能重叠:如果两个工具功能过于相似,LLM 可能会混淆。
- 唯一且具象的命名:工具名称应简洁、有意义且唯一,如
3. 无限循环与死锁 (Infinite Loops & Deadlocks)
- 坑点:Agent 可能会陷入“思考-行动-观察”的死循环。例如,LLM 每次都选择一个无法解决问题的工具,然后收到一个错误观察,又再次尝试同一个或另一个无效工具。
- 避坑指南:
max_iterations:在AgentExecutor中设置最大迭代次数。这是最直接的保护措施。early_stopping_method:当达到最大迭代次数时,如何处理?"generate"会尝试生成一个最终答案,"force"会直接抛出错误。通常generate更友好。- 工具的健壮性:确保你的工具能处理各种输入,并返回有意义的错误信息,而不是直接崩溃。LLM 收到清晰的错误信息,更有可能调整策略。
- Prompt 引导:在 Prompt 中明确,如果多次尝试无果,应主动停止并告知用户。
4. 安全性与权限控制 (Security & Permissions)
- 坑点:Agent 可以调用任意工具,如果这些工具涉及敏感操作(如删除数据、转账),恶意用户或不当的 LLM 决策可能导致严重后果。
- 避坑指南:
- 最小权限原则:Agent 调用的工具,其背后 API 的权限应严格控制,只赋予其完成任务所需的最小权限。
- 沙箱环境:对于高风险操作,考虑在沙箱环境中执行,或者需要人工确认。
- 输入验证:工具在执行前,必须对 LLM 提供的参数进行严格的输入验证,防止注入攻击或不合法的数据。
- 敏感数据处理:避免 Agent 处理或暴露敏感的用户数据。
5. 性能考量与成本控制 (Performance & Cost)
- 坑点:Agent 的每次“思考-行动”循环都可能是一次 LLM API 调用。复杂任务会导致多次调用,从而增加延迟和成本。
- 避坑指南:
- 缓存 (Caching):对重复的 LLM 调用或工具结果进行缓存。
- 异步处理 (Asynchronous Execution):对于耗时的工具,考虑异步调用,提高并发性。
- LLM 模型选择:在保证效果的前提下,选择成本效益更高的模型(如 GPT-3.5-turbo 往往比 GPT-4 便宜)。
- 优化 Prompt:精简 Prompt,减少不必要的上下文,降低 token 消耗。
- LangSmith 等可观测性工具:使用 LangSmith 可以清晰地看到 Agent 的每一步操作,帮助你优化流程,发现冗余调用。
6. 可观测性与调试 (Observability & Debugging)
- 坑点:Agent 的内部决策过程是黑盒,当它行为异常时,很难知道它“为什么”会那样做。
- 避坑指南:
verbose=True:在开发阶段,始终将AgentExecutor的verbose设置为True,这将打印出 Agent 的详细思考过程,是调试的利器。- LangSmith:LangChain 官方推荐的 LangSmith 是一个强大的可观测性平台,它可以记录并可视化 Agent 的每一次运行,包括 LLM 输入/输出、工具调用、链的执行轨迹等,对于生产环境的监控和调试至关重要。
- 日志记录:在工具内部加入详细的日志,记录工具被调用的参数和返回结果。
开发 Agent 就像训练一个学徒,你得给它清晰的指令 (Prompt),给它好用的工具 (Tools),并且在它犯错时及时纠正和引导。多实践,多观察,你就能训练出真正强大的智能体!
📝 本期小结
恭喜你!在本期课程中,我们一起揭开了 LangChain Agents 的神秘面纱,让你的智能客服小助手从一个简单的“问答机器人”华丽转身,成为一个拥有自主决策能力的“智能多面手”!
我们深入理解了 Agent 的核心原理——LLM 如何作为推理引擎,通过“思考-行动-观察”的循环,利用外部工具解决复杂问题。我们还亲手为客服小助手构建了查询订单、执行计算和搜索知识库的工具,并通过实战代码演示了如何将这些能力集成到 Agent 中。
更重要的是,我们一起预判并学习了 Agent 开发中的各种“坑点”和“避坑指南”,包括如何优化工具描述、防止循环、确保安全以及提升调试效率。这些高阶的经验,将是你未来构建生产级 AI 应用的宝贵财富。
Agent 是 LangChain 中最具想象力也最复杂的模块之一,掌握它,你就掌握了构建真正智能、自主的 AI 应用的核心钥匙。
下一期,我们将继续探索 LangChain 的另一个强大特性:记忆 (Memory)。你将学会如何让你的客服小助手记住用户的喜好、历史对话,从而提供更加个性化、连贯的服务。敬请期待!