1. 为什么企业级 Agent 不能直接“套模板”
先聊个现象。现在一搜“AI Agent 架构”,满屏都是 LangGraph 单节点 ReAct、AutoGPT 式自主循环,看着很热闹。但真要往企业生产环境里放,十有八九会撞上几个绕不开的问题:Agent 跑着跑着就“脱缰”了,不是调错工具就是陷入死循环;业务方要求流程必须可控、每一步可回溯可审计;数据虽然接进了知识库,但回答的时效性和准确性总差口气;再加上企业内部一堆老系统、新系统,每个都有自己的一套接口写法,Agent 要接入就得做一堆脏活。
我自己的经验是,在企业里做 Agent,核心矛盾永远是“可控性”和“能力复杂度”之间的拉扯。你想要 Agent 足够聪明,就得给它自主决策的空间;但你要它对结果负责,就得把决策边界画清楚。纯 LangGraph 的 StateGraph 能解决编排和状态流转,但解决不了工具接入的标准化;纯 RAG 能解决知识检索,但解决不了需要多步推理和调工具的场景;而如果直接上一套完全自主的 Agent 循环,又容易在关键业务上犯低级错误。
所以“混合架构”这件事,不是赶时髦,是被现实逼出来的。我现在的做法是:用 LangGraph 统一编排,按任务复杂度划分两条路径——一条是经过打磨的确定性工作流,另一条是给 Agent 的有限度自主空间;同时用 RAG 做静态知识底座,用 MCP 把工具接入标准化。这套架构跑了几个月,最直观的感受是:该稳的时候稳得住,该灵活的时候放得开,而且出问题时定位效率比以前高了一个量级。
这篇文章不聊概念,只聊落地。我会把我实际踩过的坑、调过的参数、反复改过的设计方案都摆出来,尽量让看完的人能直接照着搭一套。
2. LangGraph 编排层:4 个核心机制决定方案能否落地
2.1 StateGraph:把“状态”当成一等公民来设计
LangGraph 和市面上其他编排框架最大的区别,是它把状态管理放进框架核心。你定义的不是“流程”,而是一个“状态机”:每个节点接收当前状态,处理完返回新的状态,框架负责把状态在节点之间传递并持久化。
这一点在企业场景里太关键了。传统工作流引擎处理的是“数据流”,状态散落在各个模块里;LangGraph 把整个对话过程、任务上下文、中间结果全部收敛到一个 State 对象里。我第一个项目就吃过亏:当时对 State 设计得很随意,用什么字段、字段的粒度是啥,全没想清楚,结果图一复杂,状态字段互相覆盖、上下文丢失的问题接踵而至。
我的建议是:State 必须区分“不变上下文”和“可变中间量”两类。所谓不变上下文,例如用户身份、会话 ID、原始输入、经过清洗的结构化意图,这些从头到尾不能被节点改写;可变中间量则包括检索结果、中间推理、候选工具列表、最终输出等,每个节点可以自由修改。在代码里,你可以用 TypedDict 或 Pydantic 模型来定义,特别注意 Pydantic 模型能做嵌套校验,对企业级场景更稳妥。
from typing_extensions import TypedDict from typing import List, Optional, Any class AgentState(TypedDict): # 不变上下文 session_id: str user_query: str intent: str # 可变中间量 rag_results: List[dict] mcp_tool_results: List[dict] reasoning_trace: List[str] final_answer: Optional[str] error_flag: Optional[str]这样设计的直接好处是:节点之间完全解耦,每个节点只用关心自己负责的那部分状态。排查问题的时候,把 State 打出来看一遍,谁改了什么、改没改对,一目了然。
2.2 节点和边:区分“固定流程”和“自主决策”
LangGraph 的节点(Node)是执行单元,边(Edge)是流转规则。但真正要领会的一点在于:节点内部用不用 LLM、用几次 LLM,完全由你定;边可以是固定跳转,也可以是条件跳转、LLM 判断跳转。
在企业架构里,我一般把节点分成三类:
- 纯函数节点:比如意图识别、输入清洗、格式规范化。这类节点不走 LLM 或者只走一次小模型,延迟低、逻辑确定,出了问题也好修。
- RAG 节点:负责检索知识库,拼接上下文。这个节点是确定性逻辑,不涉及“思考”。
- Agent 决策节点:这是唯一给 LLM“自主权”的地方,它决定下一步调哪个工具、怎么调、参数是什么。
边的设计直接决定了可控性。我最常用的是“条件边”加“路由函数”,让 LLM 只输出一个结构化的“下一步指令”,然后由路由函数解析这个指令、决定跳转。而不是让 LLM 直接决定跳转逻辑本身——你要在代码里兜底,LLM 输出不合法就走进 fallback 分支。
def route_after_agent(state: AgentState): next_step = state.get("next_step", "fallback") if next_step == "use_mcp_tool": return "mcp_tool_node" elif next_step == "retrieve_docs": return "rag_node" elif next_step == "answer": return "respond_node" else: return "fallback_node"这里有个很关键的经验:路由逻辑宁可写得“笨”,不要写得“聪明”。企业场景里最怕 LLM 自己发明路径,一旦路径不可控,审计就没法做。我曾经试过让 LLM 直接输出跳转目标节点名,结果它在一次对话里输出了一个根本不存在于图中的节点名,整个链路直接崩掉。从那以后,所有跳转都经过一层路由函数做白名单校验。
2.3 Checkpointer:断点续跑和人机协同的基石
LangGraph 的 Checkpointer 机制,可以理解为给整个状态机加了“存档功能”——每个节点执行完,当前状态都可以被快照保存。这个机制在企业场景下价值巨大,我之前一直没重视,直到被业务方逼着做了人机协同审批,才发现这是整个架构里最值钱的能力。
具体来说,你可以在一个节点执行前设置 interrupt,让整个图暂停,把这个暂停点用函数直接暴露出来,把它随便挂到 API 里做二次确认,确认完之后再传回图里继续往下跑。我做过一个报销流程 Agent:它先自动审核发票和单据,如果金额超过阈值或者命中风控规则,就停在“等待人工审批”节点,邮件把单据摘要推给业务主管;主管点了同意之后,Agent 继续执行后续操作。整个过程状态不丢失,恢复执行时上下文完好。
另一个实际应用是“失败重试”。过去一条链路挂掉,需要从零开始;现在有了 Checkpointer,你可以从失败节点恢复。这在多轮长任务场景里尤其重要,因为重跑整个 Agent 的成本可比单节点恢复高太多了。
配置也很简单,存储可以用内存、SQLite 或 Postgres,生产环境我建议直接用 Postgres,出了问题还能查快照表定位。
from langgraph.checkpoint.sqlite import SqliteSaver memory = SqliteSaver.from_conn_string("postgresql://user:pass@host/db") graph = workflow.compile(checkpointer=memory)2.4 持久化状态的容量控制
Checkpointer 虽好,但如果不控制状态体量,早晚会出问题。我见过一个团队把整个 RAG 检索结果全文塞进 State,Token 量直接爆炸,最后上下文窗口撑爆、成本飙升。
我的实践是:State 里只放检索结果的元数据和引用位置,正文内容按需二次检索;推理过程的中间文本要截断,超过长度只保留摘要;工具调用结果尽量结构化,不要存大段原始输出;定期把过期的会话状态归档清理。这些细节看着琐碎,但企业 Agent 一跑起来,状态膨胀就是最常见的内存杀手。
3. 企业 RAG 不是“塞给模型就行”:分块、混合检索与重排的落地细节
3.1 分块策略:决定检索质量的上限
RAG 的检索质量,先说一个反直觉的结论:分块策略的影响往往比选什么向量模型更重要。因为向量检索的粒度是块(chunk),块切得不好,后面的模型再好也白搭。
我在企业文档上试过好几种分块方案:固定长度切分,大概率会把一个完整知识点从中间劈开;按标题切分,不同文档的标题层级和粒度差异很大,效果不稳定;最终我采用的方案是“结构感知分块”。简单说有两条线:一是按 Markdown/HTML 的结构标签切大块,再在每个大块内按语义段落和句子边界切小块;二是块与块之间保留上下文关联,父块和子块同时入库,检索时命中子块就带上父块信息,命中父块也能下探到子块。
这里涉及到一个很重要的概念——父子块检索。只存子块,检索到的上下文太碎、信息不足;只存父块,检索到的粒度太粗、匹配精度差。两者结合才能在“上下文完整”和“检索精准”之间找到平衡。
语言上也要区别对待:中文文档按句号和段落切分更合理,英文文档按句子和语义单元切分好一些。代码类文档按代码块和函数边界切。混合语言的场景,建议你宁可多切一刀,也不要太贪心。
from langchain_text_splitters import RecursiveCharacterTextSplitter text_splitter = RecursiveCharacterTextSplitter( chunk_size=800, chunk_overlap=150, separators=["\n\n", "\n", "。", "!", "?", ".", "!", "?", " ", ""], length_function=len, )3.2 混合检索:向量检索和关键词检索不是选择题
企业知识库里有很多东西,向量模型处理起来并不舒服:产品编号、合同号、报错代码、专有名词缩写。这些东西恰恰是业务查询的高频词。我刚开始只用向量检索,结果用户问“订单 SHT-2024-0917 的退款进度”,检索结果一塌糊涂,因为系统把具体编号语义化得面目全非。
所以我现在一律采用混合检索:向量检索负责召回相似语义,关键词检索负责精确匹配专有名词。具体做法是抽象一个检索接口,内部同时执行两种检索方式,把两路结果按分数加权后合并。
权重通常需要调,我一般先把向量结果和关键词结果各归一化到 0-1 区间,再按 0.7 和 0.3 加权,某些精确查询场景会调成 0.5/0.5。这里特别提醒:不要搞纯函数式的固定权重,应该把权重参数做成配置项,不同业务域可以各自调节。
3.3 重排:别让 Embedding 的相似度当最终裁判
Embedding 的相似度计算速度快,但和“语义相关性”并不是一回事。我印象特别深的是一个案例:用户问“笔记本电脑内存不够用怎么办”,向量检索把“笔记本内存条购买指南”排在了前面,但真正有用的是“如何排查内存占用高的进程”。这就是典型的相关性判断偏差。
解决办法是加一层重排(Rerank)。最常用的做法是用交叉编码器模型对召回结果重新打分,把最相关的排到最前,只保留 Top-K。交叉编码器模型虽然慢,但通常只对前 20~50 条结果重排,性能上完全能扛住。经验值是:先向量召回 Top-50,再重排取 Top-5,这样效果和性能都兼顾。
3.4 引用溯源:企业 RAG 的保命设计
企业级 RAG 和 ChatPDF 之类玩具级应用有一个本质区别:每一个回答都需要能够解释“这句结论是从哪份文档里出来的”。业务方往往会直接拿着 Agent 的结论去做决策,没有溯源,出了问题就是事故。
我的做法是:检索结果里记录文档名、章节路径、页码/段落 ID;最终回答生成时,我要求模型按“内联引用”格式输出,引用编号对应到具体检索片段;前端展示时把引用链接到原文位置。
{ "answer": "根据《员工报销制度》第三条,单笔报销金额超过5000元需要提交额外审批材料[1]。", "citations": [ { "chunk_id": "expense-policy-004", "doc_name": "员工报销制度_v3.2", "section": "3. 报销审批流程", "page": 4 } ] }这里我觉得最值得提醒的是:RAG 的成败不只靠模型,更要靠整个工程链路。知识库文档版本过期、权限隔离失效、增量更新不及时,这些都是比模型效果更致命的问题。关于知识库的更新机制,我建议先用定时批量更新保证数据新鲜度,后续再考虑增量向量化,不要一上来就搞太复杂。
4. MCP 工具集成:把 Agent 的能力边界变成标准协议
4.1 为什么企业一定要用 MCP 而不是“自己封装工具函数”
在 MCP 出现之前,Agent 接入工具的方式很朴素,就是在代码里定义一堆函数,然后把函数的 JSON Schema 塞给模型当工具描述。小范围内还够用,但一旦涉及跨部门、跨系统协作,问题就来了:每个系统都有自己的认证方式,每个人的工具封装风格都不一样,同一个“查库存”功能,业务系统 A 给的是 REST 接口、业务系统 B 给的是内部 RPC、业务系统 C 干脆只有数据库直连权限。Agent 接一次要写一堆胶水代码。
MCP(Model Context Protocol,模型上下文协议)做的事情,是把工具接入变成一种标准化的“服务器-客户端”模式。工具提供方写一个 MCP Server,通过标准协议暴露工具和资源,Agent 作为 MCP Client 去发现和调用。整个过程中,模型不需要关心工具背后的实现细节,只需要理解 MCP Server 暴露出来的工具描述。
我现在的企业架构里,所有工具统一通过 MCP 接入。这也带来了一个额外的好处:MCP Server 本身成为“工具层”的天然微服务边界,不同部门维护各自的 Server,发布权限互相隔离,Agent 的可用工具集合变得可控。上线新工具只需要注册到 Server 里,不需要改 Agent 主流程。
4.2 MCP 的工具调用控制:白名单、审计、超时一个都不能少
MCP 接入降低的是集成成本,真正决定系统安全性的反而是“调用控制”这件事。我的经验是:MCP Client 侧必须做三层控制。
第一层是工具白名单。MCP Server 可以暴露几十个工具,但当前 Agent 实例只能调用被授权的那些。白名单按业务场景配置,不同 Agent 只能调自己职责范围内的工具。第二层是参数校验。在把工具调用请求发给 Server 之前,Client 侧先用 JSON Schema 校验一遍参数,非法参数直接拦截。第三层是执行审计。每次工具调用的入参、出参、耗时、调用者、调用链 ID 全量记录,方便事后追查。
超时和重试也要提前设计好。LLM 生成工具调用参数本身就有延迟,再加上真实服务的响应时间,单次工具调用我建议超时设置为 10 秒以上,重试最多 2 次。如果服务不稳定,与其让 Agent 反复等,不如直接返回“工具暂时不可用”的降级结果。
4.3 实战示例:一个查询订单的 MCP Server 封装
写个最小示例帮助理解 MCP Server 长什么样。假设我们要封装一个查询订单状态的工具,用 Python 的 FastMCP 库来做,代码量很少,但生产环境你大概率需要自己完善鉴权逻辑。
from fastmcp import FastMCP from typing import Optional mcp = FastMCP("order-service") @mcp.tool() def query_order_status(order_id: str) -> str: """查询订单当前状态。""" # 实际实现:调用内部订单系统接口 # 这里简化为直接返回 return f"订单 {order_id} 的状态:已发货,物流单号 SF1234567890" @mcp.resource("order://{order_id}") def order_resource(order_id: str) -> str: return f"Order {order_id} detail: ..." if __name__ == "__main__": mcp.run()对应的 LangGraph 节点里,MCP 调用可以被封装成一个通用节点,从 State 里读取 LLM 生成的工具调用意图,再通过 MCP Client 执行。
from mcp import ClientSession, StdioServerParameters from mcp.client.stdio import stdio_client server_params = StdioServerParameters(command="python", args=["order_mcp_server.py"]) async with stdio_client(server_params) as (read, write): async with ClientSession(read, write) as session: tools = await session.list_tools() result = await session.call_tool("query_order_status", {"order_id": "SHT-2024-0917"})上面这种 Stdio 方式适合开发联调,生产环境建议把 MCP Server 部署成 SSE 或 Streamable HTTP 服务,这样 Agent 进程可以在独立的机器上,工具 Server 也可以独立扩缩容。
5. 整体实现路线图:从需求到部署的完整落地步骤
5.1 第一步:识别企业场景中的“确定性”和“自主性”边界(混合架构第一步)
这是整个方案里最难、也最容易被跳过的一步。很多团队一上来就开始画图、写代码,结果做着做着发现,业务方根本分不清哪些流程要确定性执行、哪些环节需要 AI 自主判断。
我的经验是做一张“任务拆解表”,把核心业务场景逐项列出来,判断依据有三条:
- 决策风险:如果 Agent 判断错误,造成的损失有多大?金额、合规、用户体验都属于风险维度。
- 异常频率:这个场景里长尾异常多不多?比如客服场景里,用户诉求千奇百怪,那就需要更大的自主性。
- 流程稳定度:流程是否经常调整?流程如果两周变一次,那确定性工作流要跟着改动,不如给 Agent 一定自由空间。
拿我做过的一个智能运维工单系统举例:日志初步分类、工单自动分派、常规操作建议,这些属于“确定性强”的,直接走工作流子图;但遇到新型故障、需要组合多种命令排查的场景,就交给 Agent 自主决策。两类任务复用一个图骨架,靠一个路由节点分叉。
5.2 第二步:搭建 LangGraph 核心骨架并配置持久化
这一块回到上文的 StateGraph 设计。搭骨架时我有两个习惯:一是先把图结构脑内过一遍,画清楚节点和边的依赖关系,再落代码;二是所有节点的输入输出都定义类型别名,提高可读性和可测试性。
from langgraph.graph import StateGraph, START, END workflow = StateGraph(AgentState) workflow.add_node("intent_node", intent_recognition) workflow.add_node("rag_node", rag_retrieval) workflow.add_node("agent_node", agent_decision) workflow.add_node("mcp_node", mcp_tool_call) workflow.add_node("respond_node", generate_answer) workflow.add_node("fallback_node", handle_fallback) workflow.add_edge(START, "intent_node") workflow.add_conditional_edges( "intent_node", route_after_intent, { "rag": "rag_node", "agent": "agent_node", "direct": "respond_node", }, ) workflow.add_edge("rag_node", "agent_node") workflow.add_conditional_edges("agent_node", route_after_agent, {"mcp": "mcp_node", "answer": "respond_node", "fallback": "fallback_node"}) workflow.add_edge("mcp_node", "agent_node") workflow.add_edge("respond_node", END) workflow.add_edge("fallback_node", END) app = workflow.compile(checkpointer=memory)这里要注意一个循环结构:mcp_node 调用完工具后回到 agent_node,这给了 Agent“多轮调用工具”的能力。企业场景里这一步必须有最大轮次限制,否则 Agent 钻牛角尖会一直在工具调用循环里打转。我一般设 5 轮上限,超过就强制 fallback。
5.3 第三步:搭建知识库与 RAG 检索链路
先明确一个原则:知识库不等于“向量数据库”。企业知识库通常由多来源构成:内部 Wiki、操作手册、FAQ、结构化工单数据。我的建议是分层处理:
- 结构化数据(订单、库存、排班等)优先查数据库或业务 API,不要塞向量库。
- 半结构化数据(工单、FAQ)用混合检索。
- 非结构化文档(PDF、Word、手册)先做解析清洗,再走父子分块入库。
具体流程可以这样:
- 建立文档清单,标记文档类型、归属部门、更新周期。
- 解析清洗各类文档,剔除页眉页脚、图表噪声。
- 做结构感知分块,生成父块和子块。
- 建立向量索引、关键词倒排索引。
- 定期全量更新,增量更新视内容活跃度决定。
还可以做一个“知识库健康度检查”脚本:随机抽检一批已入库文档,确认分块没有粘连、元数据没有丢失、标题层级没有错乱。这一步成本不高,但能显著提升后续检索质量。
5.4 第四步:通过 MCP 接入标准化工具层
工具接入的优先级可以从高频、低风险的工具开始,比如查询类、检索类工具,逐步扩展到写入、变更类工具。写入类工具必须加人工审批节点,这也是上文提到的 interrupt 机制最具价值的地方。
我当时做的一个系统里,把内部的所有查询工具先接进了 MCP Server,一两天就把主要系统打通了。反而是那些需要写数据的工具,审批流程和权限模型花了很长时间。所以建议你给“工具的接入难度”和“业务价值”排个优先级矩阵,先接价值高、难度低的,快速跑通。
5.5 第五步:评测、监控、反馈闭环
企业 Agent 最怕“上了线、没人管”。我见过太多 POC 项目演示时效果好得不行,上线一周后被业务方吐槽成“人工智障”,原因就是没有评测和监控闭环。
我的做法分三层:
- 离线评测集:从历史工单和常见问题里人工标注 200~500 条“问题-期望答案-期望工具调用”三元组,每次改模型或改提示词后,先跑一遍离线评测,防止回归。
- 在线日志监控:记录每次请求的图路径、节点耗时、工具调用次数、RAG 命中上下文、最终回答,发现有异常轨迹(比如某条边频繁走 fallback)就主动告警。
- 用户反馈通道:给网页端界面加“点赞/点踩”按钮,点踩样本定期回流到标注团队,变成新的评测数据。
这套闭环听起来不炫酷,但企业里真正决定 Agent 能不能持续好用,靠的就是这个东西。
5.6 第六步:部署形态和性能优化
最后聊聊部署。NumPy 和 LangGraph 这类框架,生产部署建议用独立的 Agent Service,提供 HTTP/gRPC 接口供上层业务调用。一个请求进来后内部可能跑多个节点、调多个外部服务,如何保证合理的端到端延迟?
我的建议是:Agent 主链路用异步执行。LangGraph 里的节点可以定义为 async 函数,RAG 检索和 MCP 工具调用都是 IO 密集操作,并行度可以做得比较高。另外加上 LLM 缓存:相同的上游查询可以走语义缓存,直接命中缓存就不需要重新生成,这对高频客服问答场景收益非常明显。
另外,把重试、熔断、限流这些基础能力补上。LLM 服务提供商偶尔会有波动,一旦超时或限流,最好做本地 fallback,而不是让整个 Agent 直接报错。降级策略在系统设计阶段就要考虑:查询类工具挂了可以直接回复“该功能暂时不可用”,但要保证对话本身不中断。
6. 常见问题与排查技巧实录
6.1 Agent 陷入工具调用死循环
症状:一个查询订单的工具被反复调用,每次参数几乎一样;对话无法收敛到最终回答。
排查过程:一开始我以为是模型问题,换了更贵的模型反而更严重。后来打日志才发现,工具返回值里有一句话“如需了解更多请查询……”被 Agent 理解成“还需要再查一次”,于是它又调了一次。问题出在工具返回内容和提示词的配合上,而不是模型能力。
解决的组合拳:
- 在系统提示词里明确写“工具返回的是最终精确结果,不要重复调用同一工具确认”。
- 给每个工具调用记录加一个“重复检测”,参数完全一致的调用连续出现 3 次就强制跳出。
- 设置全局工具调用轮次上限,超出后走“基于当前已有信息直接作答”的分支。
6.2 RAG 检索结果虽然多,但答案的关键句消失了
症状:Top-5 结果每一块看起来都相关,但回答的时候缺乏关键证据,靠猜。
原因大概率出在分块环节——某个核心结论被切分到了两块的边界中间,语义残破。解决方案就是前文提到的父子块策略:检索命中子块后,把对应的父块一起带上,模型就有足够的上下文空间去理解完整含义。
另一种常见情况是召回数量不足。如果 Top-5 里压根没有包含答案的块,重排做得再准也没用。建议把召回数量调大一些,我一般向量召回 Top-50,关键词召回 Top-20,合并后重排取 Top-5,确保“真正有用的块先被捞回来”。
6.3 MCP 工具调用总是超时
MCP Server 通过 Stdio 方式跑在 Agent 进程里时,一旦某次调用卡住,整个 Agent 进程都会受影响。我在上线初期就遇到过:某个内部系统接口平均响应 3 秒,但偶尔会飙到 60 秒,MCP 调用超时设的是 10 秒,结果一天触发几十次告警。
排查后做了三个改动:
- 把所有 MCP Server 改为独立的 HTTP 服务部署,和 Agent 进程彻底隔离。
- 针对慢接口,单独设置更长的超时时间,但增加缓存策略,相同请求直接复用历史结果。
- 给慢接口加异步任务模式:先返回“查询处理中”,Agent 挂起,等结果就绪后再恢复执行。这个模式用 LangGraph 的 interrupt 机制很容易实现。
6.4 LLM 路由误判,把简单问题导进了重流程
LLM 做意图分类,偶尔会把“今天天气怎么样”这样的闲聊判成“需要查询企业知识库”,或者把简单问题判定为重流程。这种做法本身没错,但有个细节值得优化:在路由节点前加一个“轻量规则层”,用正则和关键词先拦截超短问题、超常见问题,能直接匹配的就不进 LLM 判断。企业里很多高频问题其实是重复的,语义缓存和规则路由能把 30%~40% 的请求挡在图的最前面,延迟和成本都会明显下降。
6.5 不同部门的知识库权限混淆
企业 RAG 如果把所有文档都放在一个大向量库里,检索时不做权限过滤,用户问什么都能搜到,这在合规上是过不去的。特别是涉及薪酬、客户隐私、内部战略的资料,越权查询就是大事故。
我现在的做法是:每个文档入库时打上权限标签,比如“全员可见”“部门可见”“仅管理员”;检索向量库时,把用户身份作为强制过滤条件,先过滤掉没权限的文档再走相似度搜索,而不是搜完再删。后者在召回阶段就可能带着无权限内容进入上下文,存在泄密风险,这个顺序问题各位务必重视。
最后分享几个我反复验证过的“土办法”
这篇文章写到这儿,核心架构的内容基本都到位了。最后分享几个不常写进文档、但实际保命的东西。
第一,所有复杂链路都要有“人工兜底”按钮。不管 Agent 设计得多完善,总会有预料之外的场景。我的做法是:在终端界面放一个“转人工”的显眼入口,一旦用户点了转人工,会话状态自动打包同步给人工客服。这个功能是对业务方的定心丸,它决定了他们敢不敢让 Agent 独立负责业务。
第二,日志和状态快照要多留一段时间。我经历过一次线上事故,当时 Agent 在执行中报了错,但由于我没有把状态快照保留下来,只能在现场复现中逐步排查,非常被动。后来改成灰度环境加生产快照双备份的方案,再出问题直接看状态机演化过程,定位效率高得多。
第三,评测指标不要只看准确率。企业 Agent 上线后,我最看重的三个指标其实是:需要人工干预的比例、单次任务的端到端耗时、以及用户主动给负面反馈的比例。准确率只是离线参考,线上体验才是真正值得盯着的东西。这三个指标能直观反映系统在真实环境里到底“帮了多少忙”还是“添了多少乱”。
第四,关于后续扩展,我自己的经验是把 Agent 的决策记录和流程轨迹做成可回放的审计日志,既满足合规要求,也方便训练阶段的数据回流。更长远一点,你可以考虑把“高置信度正确”的历史决策路径沉淀成新模板,逐步从 Agent 模式向工作流模式收敛。这样一来,系统会越用越稳定,而不是越用越“野”。
这套架构放在不同企业里,具体细节一定会有差别,但底层的核心思路是一致的:把确定性的交给工作流,把不确定性的交给 Agent,把知识问答交给 RAG,把工具接入交给 MCP,最后用 LangGraph 把所有东西串成一个可监控、可干预、可恢复的状态机。按这个思路搭出来的系统,不敢说效果上限有多高,但至少下限是兜得住的。