指南OneMux

使用工具调用、人工审批和执行限制构建 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 可以将客服分流表示为明确的工作流步骤,而不是让单次模型回复同时决定并执行操作。让查询保持只读,在退款前暂停,在执行时验证审批,并限制图的执行步数。然后评估答复内容以及代理采用的执行路径。这些控制措施能让工作流更易于检查,但生产环境中的安全和政策执行仍由应用负责。

来源

常见问题

如何要求 LangGraph 代理在退款前获得人工审批?

在退款执行节点前使用 LangGraph interrupt 暂停图,将经过验证的请求提交给审核人员,并仅在收到明确决定后恢复同一检查点。此外,还要在执行节点中再次检查审批,并在退款服务中执行政策验证。

LangGraph 递归限制会限制工具运行时间或模型 token 数量吗?

不会。它限制图的超步数,不限制模型 token 用量、工具运行时间、数据库成本或传入请求量。请另外设置超时和资源限制。

能让模型判断客户是否有权申请退款吗?

不能。应将模型输出视为不可信输入。请在应用和服务代码中验证客户身份、交易访问权限和退款资格。

客服代理应该评估哪些方面?

评估最终回复、工具调用顺序以及单个工具选择步骤。测试用例应包括信息缺失、购买记录不明确、审批被拒和退款获批场景,并使用测试服务处理副作用。

相关文章