使用工具调用、人工审批和执行限制构建 LangGraph 客服代理
实用的 LangGraph 客服分流模式:路由请求、调用查询工具、暂停退款以等待人工审批,并限制图执行步数。
文章目录
设计决策:让代理负责查找,但不负责最终决定是否退款
客服代理可以收集购买详情并查找匹配记录,但不应自行发起退款。在 LangGraph 中,可以通过工作流明确区分这两类职责:先路由请求,按需调用只读查询工具,然后在退款操作前暂停,由人工批准或拒绝。
本教程围绕客服分流构建这一模式,使用 LangGraph 进行编排,使用支持工具调用的聊天模型执行查询,并在有重要影响的节点前设置人工检查点,同时通过递归限制约束图执行。这段代码是一种供你调整的模式,并非经过测试的 OneMux 集成。
本文引用的 LangSmith 教程构建了一个数字音乐商店客服机器人,分别使用查询子图和退款子图。教程介绍了提取客户和购买详情、询问缺失信息,以及评估最终回复、工具调用轨迹和单个步骤。其退款示例通过数据库删除记录,并明确跳过身份验证及其他生产安全措施。切勿将该演示中的破坏性行为照搬到线上客服系统。阅读 LangSmith 复杂代理评估教程。
前置条件与边界
安装 LangGraph,以及所选模型提供商对应的 LangChain 集成。原教程使用 langgraph 和 langchain[openai],但以下示例刻意不指定模型初始化方式。你还需要一个数据库或服务,能够提供只读购买查询,并由应用控制退款操作。
在连接模型之前,先明确每个工具可以执行哪些操作:
- **查询:**仅返回经过身份验证的客户有权查看的记录。
- **准备退款:**确定具体交易和金额,但不修改记录。
- **执行退款:**在应用代码中执行已批准的操作,并记录授权信息和审计日志。
将客户身份验证、授权、幂等性和退款政策留在模型的决策范围之外。模型生成的客户姓名或发票 ID 不能证明身份。
构建带人工检查点的图
以下示例使用支持工具调用的模型、查询工具和独立的退款节点。请将标记的应用函数替换为你自己的授权服务调用。模型和工具架构必须受到你所配置端点的支持;是否兼容取决于端点和参数是否有文档说明并获得支持。这段代码并不能证明某个特定模型或 OneMux 配置已经过测试。
from typing import Annotated, Literal
from langchain.tools import tool
from langchain_core.messages import AnyMessage
from langgraph.graph import END, START, StateGraph
from langgraph.graph.message import add_messages
from langgraph.prebuilt import ToolNode, tools_condition
from langgraph.checkpoint.memory import MemorySaver
from langgraph.types import interrupt
from typing_extensions import TypedDict
# Replace with your provider's documented LangChain model setup.
# Confirm that the chosen endpoint supports tool calling and the parameters used.
model = make_your_chat_model().bind_tools([]) # bind the tools below in your application
class State(TypedDict):
messages: Annotated[list[AnyMessage], add_messages]
refund_request: dict | None
approval: str | None
@tool
def find_purchases(customer_reference: str) -> list[dict]:
"""Find purchases for an already authenticated customer."""
return authorized_purchase_lookup(customer_reference)
tools = [find_purchases]
model = make_your_chat_model().bind_tools(tools)
def assistant(state: State):
return {"messages": [model.invoke(state["messages"])]}
def prepare_refund(state: State):
# Build this from validated application data, not model-invented values.
request = build_validated_refund_request(state["messages"])
decision = interrupt({
"type": "refund_approval",
"request": request,
"instruction": "Approve or reject this exact refund request.",
})
if decision != "approve":
return {"approval": "rejected", "refund_request": request}
return {"approval": "approved", "refund_request": request}
def execute_refund(state: State):
if state.get("approval") != "approved":
return {"messages": [{"role": "assistant", "content": "The refund was not approved."}]}
result = issue_refund_safely(state["refund_request"])
return {"messages": [{"role": "assistant", "content": format_refund_result(result)}]}
builder = StateGraph(State)
builder.add_node("assistant", assistant)
builder.add_node("tools", ToolNode(tools))
builder.add_node("prepare_refund", prepare_refund)
builder.add_node("execute_refund", execute_refund)
builder.add_edge(START, "assistant")
builder.add_conditional_edges("assistant", tools_condition)
builder.add_edge("tools", "assistant")
builder.add_edge("prepare_refund", "execute_refund")
builder.add_edge("execute_refund", END)
graph = builder.compile(checkpointer=MemorySaver())
这只是结构示例,并非完整的路由图:你的应用必须将已验证的退款意图路由到 prepare_refund,并将普通问题路由到助手/查询路径。interrupt() 调用会暂停图,并将审批负载返回给调用方。人工审核界面应展示客户、交易、金额和政策背景,然后再提交决定。使用 LangGraph 的 Command(resume=...) 机制,并提供相同的线程标识符,以恢复同一线程。请妥善持久化检查点;内存检查点适用于开发,不适用于需要持久性的生产工作流。
有一个关键的控制流细节需要注意:interrupt 只是暂停机制,不是权限系统。退款执行节点中要再次验证审批值,并确保退款服务本身会检查授权、交易是否符合条件,以及请求是否重复。
限制执行并明确呈现失败
调用图时传入递归限制。它限制图的超步数,但不限制模型 token 数量、工具运行时间、数据库查询成本或用户请求数量。应为模型调用和工具分别设置超时与资源限制。
from langgraph.errors import GraphRecursionError
config = {
"configurable": {"thread_id": "support-case-123"},
"recursion_limit": 12,
}
try:
result = graph.invoke(
{"messages": [{"role": "user", "content": "I need help with a purchase."}]},
config=config,
)
except GraphRecursionError:
# Return a safe handoff or ask for clarification; log the trace for review.
result = {"status": "needs_human_review"}
根据预期工作流选择限制值,并同时测试正常完成和容易陷入循环的输入。不要在图执行次数耗尽后默默重试:如果工具不具备幂等性,重试可能导致重复调用。与产生副作用的操作相比,只读查询工具重试起来更安全。
评估执行路径,而不只是答案
所引用的 LangSmith 教程指出了三个值得评估的目标:最终回复(给客户的答复是否恰当?)、执行轨迹(代理是否走了预期的工具调用路径?)和单步决策(在某个具体决策中,它是否选对了工具?)。针对退款分流,应创建涵盖身份信息缺失、存在多笔匹配购买记录、查询无结果、审批被拒以及退款获批的测试案例。
检查代理是否会询问必需信息,而不是自行编造;确保查询不会触发退款;并验证未经批准就无法执行退款。使用模拟或测试服务进行评估,避免测试更改真实客户记录。原教程在其数据库操作中使用了模拟模式,但这仅说明其演示设置,并不代表获得了生产安全保证。
使用 OneMux 访问模型
OneMux 通过一个 OpenAI 兼容 API 提供对主流 AI 模型的访问。如果你希望通过统一的 API 接口访问模型,请查看 OneMux 快速入门、API 文档和可用模型。此工作流是否兼容,取决于所选模型端点的工具调用支持情况及其接受的参数。部署前请确认相关详情;本文不声称已针对 OneMux 测试此 LangGraph 示例。
限制与故障排查
- **模型没有调用工具:**检查所选端点是否支持工具调用,并确认工具架构是否按文档要求的格式传入。
- **图陷入循环:**检查助手与工具之间的转移逻辑,限制图的执行步数,并确保工具执行完成后,结果会影响下一步决策。
- **审批恢复了错误的案例:**使用与客服案例关联的稳定线程标识符,并在恢复前验证检查点。
- **退款执行了两次:**在服务边界实现幂等性,并记录获批请求及其结果。
- **代理泄露了其他客户的记录:**在查询服务中修复授权逻辑;提示词指令不是访问控制。
结论
LangGraph 可以将客服分流表示为明确的工作流步骤,而不是让单次模型回复同时决定并执行操作。让查询保持只读,在退款前暂停,在执行时验证审批,并限制图的执行步数。然后评估答复内容以及代理采用的执行路径。这些控制措施能让工作流更易于检查,但生产环境中的安全和政策执行仍由应用负责。
来源
- LangSmith:评估复杂代理——数字音乐商店客服示例、查询/退款结构、模拟模式以及三类评估目标的来源。
常见问题
如何要求 LangGraph 代理在退款前获得人工审批?
在退款执行节点前使用 LangGraph interrupt 暂停图,将经过验证的请求提交给审核人员,并仅在收到明确决定后恢复同一检查点。此外,还要在执行节点中再次检查审批,并在退款服务中执行政策验证。
LangGraph 递归限制会限制工具运行时间或模型 token 数量吗?
不会。它限制图的超步数,不限制模型 token 用量、工具运行时间、数据库成本或传入请求量。请另外设置超时和资源限制。
能让模型判断客户是否有权申请退款吗?
不能。应将模型输出视为不可信输入。请在应用和服务代码中验证客户身份、交易访问权限和退款资格。
客服代理应该评估哪些方面?
评估最终回复、工具调用顺序以及单个工具选择步骤。测试用例应包括信息缺失、购买记录不明确、审批被拒和退款获批场景,并使用测试服务处理副作用。
相关文章
指南
构建带人工审批的 n8n 客户支持分流工作流
创建一个 n8n 支持分流工作流,使用兼容 OpenAI 的 API 起草处理建议,然后暂停,等待人工决定是回复还是升级处理。
指南
构建有界 LangGraph Agent,实现经 Schema 校验的提取
构建一个 LangGraph 工作流:在需要时调用工具,在规定的执行预算内停止,并返回经过 Pydantic schema 校验的结构化数据。
指南
LangGraph Agentic RAG:工具调用、来源归属与有界执行
构建一个 LangGraph 文档问答代理,让它决定何时检索、评估上下文、在搜索效果不佳时重试,并返回带来源归属且不会无限执行的答案。
指南
构建带人工审批与测试验证的 n8n 代码变更工作流
设计一个 n8n 工作流,提出代码仓库变更、请求人工批准写入,并通过测试验证生成的提交。