1. 这不是概念炒作,是工程师每天要填的七个坑
“AI Agent”这个词最近半年在技术社区里炸开了锅,从早会PPT到招聘JD,从投资人尽调清单到实习生答辩稿,几乎无处不在。但你有没有发现一个奇怪的现象:聊得越热闹,落地越沉默?团队里有人在画智能体架构图,有人在调用LangChain写个“能查天气的Agent”,还有人对着论文里“自主性、目标导向、工具使用”这些词反复咀嚼,却连一个能稳定跑通三天不崩的订单履约Agent都交不出来。问题出在哪?不是模型不够强,也不是算力不够多,而是我们把“Agent”当成了一个黑箱产品去采购,而不是一个需要逐层拆解、逐点校准的工程系统。
我过去两年带过三个AI应用落地项目,其中两个卡死在Agent环节——不是模型不会推理,而是整个执行链路像一辆没装转向系统的车:它知道目的地(Goal),也认得路标(Observation),但一到路口就原地打转(Planning失败),或者干脆撞上护栏(Tool Calling失控),最后靠人工远程重启(Human-in-the-loop兜底)。后来我把所有失败日志、调试记录、架构评审意见全摊开,按时间线重走了一遍执行路径,才发现真正决定成败的,根本不是那个最炫的“记忆模块”或“反思机制”,而是七个极其具体、可测量、可调试的决策点。它们不讲哲学,只讲输入输出;不谈智能,只问“这一步该不该做、怎么做、做错了怎么拉回来”。这七个点,就是标题里说的“七要素”在真实世界里的工程显形——Goal设定是否可分解、State建模是否可观测、Plan生成是否可验证、Action执行是否可回滚、Observation采集是否可对齐、Memory更新是否可审计、Loop终止是否可判定。它们不是理论框架里的漂亮节点,而是你在写代码时必须亲手填满的七个函数签名、七组边界条件、七套超时熔断策略。今天这篇,不讲LLM有多厉害,也不画高大上的分层架构图,就带你用一个真实电商客服Agent的完整生命周期,把这七个点掰开揉碎,看看每个点背后藏着哪些参数陷阱、哪些日志盲区、哪些测试用例根本没人写。如果你正在写Agent,或者正被老板催着上线一个“能自主处理退货”的智能体,那这篇文章里每一个小节,都是你明天晨会可以马上拿出来讨论的技术议题。
2. 七要素不是抽象概念,是七个必须落地的工程接口
2.1 Goal:不是一句自然语言描述,而是一份带SLA的契约
很多人以为Agent的Goal就是用户输入的一句话,比如“帮我取消昨天下的那笔订单”。但工程上,Goal必须被翻译成一份具备四个硬性指标的契约:可分解性、可验证性、时效性、容错粒度。我见过太多项目在这里翻车——产品经理写需求文档时写“用户意图理解准确率≥95%”,开发同学照着这句话去调微调模型,结果上线后发现95%的准确率是在“我要退款”“我要换货”这种二分类场景下测的,而真实工单里有“上次快递员把包裹放物业了但我没收到所以想取消订单并重发”这种嵌套意图,模型直接懵掉。
真正的Goal工程化,第一步是强制做意图原子化拆解。以电商客服场景为例,“取消订单”这个高层Goal必须拆解为:
- 原子Goal 1:识别订单ID(来源:对话历史/用户输入/关联账号)
- 原子Goal 2:校验订单状态(是否可取消?是否已发货?)
- 原子Goal 3:确认用户身份(是否本人操作?是否需二次验证?)
- 原子Goal 4:执行取消动作(调用哪个API?传什么参数?)
- 原子Goal 5:同步通知(短信/APP推送/邮件模板选择)
每个原子Goal都必须定义明确的输入源、输出格式、失败码范围、重试策略。比如原子Goal 2的输出不能是“true/false”,而必须是结构化对象:{"status": "cancellable", "reason": "order_not_shipped", "deadline": "2024-06-15T18:00:00Z"}。这里deadline字段就是SLA的具象化——如果当前时间超过deadline,整个Goal自动降级为“引导用户联系人工”。
提示:很多团队用LLM做Goal解析,但忽略了一个关键事实:LLM的输出稳定性远低于规则引擎。我们的方案是双轨制——高频确定性意图(如“查物流”“退差价”)走正则+关键词匹配,低频复杂意图(如“上次买的耳机左耳没声音,但右耳正常,能换新还是修?”)才交给LLM,并且LLM输出后必须经过一个轻量级校验器(用few-shot prompt + schema约束),确保返回字段不缺失、类型不错误、枚举值在白名单内。
2.2 State:不是内存快照,而是带版本号的可观测数据流
State常被简单理解为“Agent当前知道什么”,但工程上,State是整个Agent系统的唯一真相源(Source of Truth),它必须满足三个刚性要求:可序列化、可追溯、可快照比对。我们曾遇到一个严重故障:Agent在处理用户投诉时,前两轮对话正确识别出“商品破损”,第三轮突然变成“物流延迟”,回溯日志发现State在第二轮结束时被一个异步的库存查询回调意外覆盖,把用户投诉主题冲掉了。
State的工程实现核心在于“版本控制”。我们采用类似Git的提交模型:每次State变更都生成一个新版本,包含:
version_id: 全局单调递增ID(如state_v127)parent_version: 上一版本ID(支持回滚)diff: JSON Patch格式的变更描述(如[{"op":"add","path":"/complaint_type","value":"damaged"}])source: 变更触发源(user_input/tool_result/system_timer)
这样做的好处是,当Agent行为异常时,你可以直接拉取state_v126和state_v127做diff,立刻定位是哪个模块、哪次调用污染了State。更进一步,我们在State存储层(PostgreSQL)加了行级触发器,任何对State表的UPDATE都会自动记录变更前后的JSON文本,相当于给State上了区块链式的不可篡改日志。
注意:不要用Redis或内存变量存State主干!我们踩过的坑是——某次发布灰度时,新旧版本Agent实例共存,老版本用
Map<String, Object>存State,新版本用Protobuf,结果同一个用户会话在不同实例间流转时,State解析直接panic。最终方案是State存储与Agent运行时完全解耦,所有读写都走统一gRPC接口,接口协议版本严格管理。
2.3 Plan:不是思维链,而是带约束的DAG调度器
Plan常被等同于LLM生成的“思考步骤”,但真实系统里,Plan是一个实时调度器,它的输出不是文字,而是一个可执行的有向无环图(DAG),每个节点代表一个原子Action,边代表依赖关系和条件分支。我们曾用纯LLM生成Plan,结果发现模型在生成“先查订单再判断是否可退”时,偶尔会漏掉“若订单已发货则跳过库存检查”这个关键分支,导致调用库存API时抛出500错误。
Plan的工程化必须引入显式约束声明。我们在Prompt中强制要求LLM输出符合以下Schema的JSON:
{ "plan_id": "plan_20240615_abc", "nodes": [ { "node_id": "n1", "action": "get_order_detail", "input_mapping": {"order_id": "$$.user_input.order_id"}, "timeout_ms": 3000, "retry_policy": {"max_attempts": 2, "backoff_ms": 1000} }, { "node_id": "n2", "action": "check_refund_eligibility", "input_mapping": {"order_status": "$$.n1.output.status"}, "condition": "n1.output.status == 'shipped'" } ], "edges": [ {"from": "n1", "to": "n2", "condition": "n1.success"} ] }关键点在于condition字段——它让Plan具备了传统工作流引擎的分支能力。更重要的是,这个JSON会被送入一个轻量级DAG执行器(我们用Python写的,不到500行),执行器会:
- 静态校验DAG无环、无悬空节点
- 动态注入超时/重试/熔断策略(
timeout_ms和retry_policy) - 在执行前做输入合法性检查(
input_mapping指向的字段是否存在?类型是否匹配?)
这样,Plan就从“LLM的即兴发挥”变成了“可验证、可监控、可降级”的确定性流程。
2.4 Action:不是API调用,而是带事务语义的操作单元
Action常被简化为“调用某个工具”,但工程上,每个Action必须封装成一个具备ACID特性的操作单元。我们最初的设计是让Agent直接调用支付网关API,结果一次网络抖动导致扣款成功但回调丢失,Agent误判为“支付失败”,又发起第二次扣款,造成资损。
Action的工程化核心是引入“两阶段提交”思想:
- Prepare阶段:预占资源、生成幂等键、写入本地事务日志(如
action_log表,含action_id,prepared_at,status='prepared') - Commit阶段:执行真实API调用,成功则更新日志状态为
committed,失败则标记为failed并触发补偿逻辑
所有Action都必须实现这三个接口:
prepare():返回{success: true, idempotent_key: "act_123", payload: {...}}commit(idempotent_key):执行真实调用,返回{result: {...}, status: "success/fail"}compensate(idempotent_key):执行逆向操作(如退款、取消预约)
这样,当Agent崩溃重启时,它只需扫描action_log表中所有status='prepared'的记录,对每个记录调用compensate(),就能保证状态最终一致。我们甚至把compensate逻辑做成独立服务,通过消息队列触发,彻底解耦。
实操心得:Action的输入输出必须强Schema化。我们用Protobuf定义所有Action的Request/Response,生成Python/Java客户端。曾经有个Action返回
{"code":0,"msg":"ok","data":{"amount":100}},另一个返回{"status":"success","detail":{"price":100.0}},前端同学写了个万能解析器,结果在金额字段类型不一致(int vs float)时悄悄出错。现在所有Action响应都必须通过Protobuf验证,否则直接拒绝执行。
2.5 Observation:不是日志拼接,而是带信源可信度的证据链
Observation常被当作“工具返回的结果”,但工程上,Observation是Agent做决策的唯一证据,它必须携带信源标识、可信度评分、时间戳、原始payload。我们曾遇到一个经典问题:Agent调用物流查询API返回“派件中”,但用户坚称没收到,Agent却无法判断是API数据延迟还是用户记错时间——因为Observation里只有{"status":"delivering"},没有last_update_time,也没有data_source:"SF_Express_V3_API"这样的信源标签。
Observation的工程化要求每个工具调用返回的不是裸JSON,而是一个标准化Observation对象:
class Observation: source: str # "logistics_api_v3", "user_speech_asr", "database_query" confidence: float # 0.0~1.0, 由工具自身评估(如ASR置信度、API SLA达标率) timestamp: datetime raw_payload: dict # 原始响应,不加工 normalized: dict # 标准化后的字段(如统一status字段为["pending","delivering","delivered"])关键创新在于confidence字段。物流API的confidence由其历史SLA计算:如果过去1小时该API平均延迟>5s,confidence自动降至0.7;ASR服务的confidence直接取语音识别置信度;而用户输入的confidence永远是1.0(人类意图优先)。Agent的后续决策(如是否追问用户)会综合多个Observation的confidence加权计算,而不是简单取“最新一条”。
2.6 Memory:不是向量库,而是带访问策略的知识图谱
Memory常被等同于“向量检索”,但工程上,Memory是Agent的长期知识中枢,它必须支持多模态存储、细粒度权限、可解释溯源。我们最初的方案是把所有对话历史存进ChromaDB,结果发现Agent在回答“上次我买的耳机型号是什么”时,总从三个月前的对话里捞出错误型号——因为向量相似度匹配到了“AirPods”和“AirPods Pro”的embedding距离很近。
Memory的工程化必须放弃纯向量检索,采用图谱+向量混合索引:
- 实体节点:用户、订单、商品、客服坐席(带属性如
user_id="u123", last_active="2024-06-14") - 关系边:
USER_BOUGHT_ORDER,ORDER_CONTAINS_PRODUCT,PRODUCT_HAS_MODEL_NUMBER - 向量索引:仅对商品描述、用户投诉文本等非结构化内容建向量库,用于模糊匹配
查询时,Agent先走图谱路径(如MATCH (u:User)-[r:BOUGHT]->(o:Order)-[s:CONTAINS]->(p:Product) WHERE u.id=$user_id RETURN p.model_number),只有图谱无结果时,才fallback到向量检索。更重要的是,每次Memory读写都记录access_log:谁(Agent ID)、何时、查了什么、返回了哪些节点。这样当Agent给出错误答案时,你可以直接查日志:“Agent a123 在2024-06-15T10:23:45访问了user_id=u123的BOUGHT关系,返回了order_id=o789,再查o789的CONTAINS关系得到product_id=p456,其model_number字段值为‘WH-1000XM5’”——整个推理链完全可审计。
2.7 Loop:不是while循环,而是带退出契约的状态机
Loop常被实现为简单的while not done:,但工程上,Loop是Agent的生命线控制器,它必须定义明确的终止条件、超时熔断、降级开关。我们曾有个Agent在处理复杂投诉时陷入无限循环:用户说“我不满意”,Agent问“哪里不满意?”,用户说“都不满意”,Agent又问“具体哪部分?”,如此往复……直到CPU耗尽被K8s OOM kill。
Loop的工程化必须用状态机替代简单循环。我们定义了7个标准状态:
INIT:接收用户输入,解析GoalPLANNING:生成Plan,校验DAGEXECUTING:执行Action,监控超时OBSERVING:采集Observation,评估confidenceREFINING:根据Observation调整Plan(如用户说“不是这个订单”,则触发Goal重解析)RESPONDING:生成回复,渲染UITERMINATED:满足退出条件,释放资源
每个状态转移都需满足契约:
INIT → PLANNING:Goal必须通过原子化校验EXECUTING → OBSERVING:Action必须返回status=success且confidence>=0.6REFINING → EXECUTING:新Plan的DAG节点数不得超过原Plan的150%
最关键的是TERMINATED的触发条件,它不是单一事件,而是三元组:
exit_condition:user_says("ok") OR user_says("thanks") OR user_silence_for(60s)timeout:total_duration > 300sfallback:refine_count > 3 AND current_state == REFINE
只要任一条件满足,Loop立即终止,转入RESPONDING生成兜底话术(如“已为您记录问题,稍后专员将联系您”),绝不硬扛。
3. 七个决策点的实操落地:从零搭建一个退货Agent
3.1 环境准备:避开LLM生态的三大幻觉陷阱
搭建Agent前,必须清醒认识当前LLM生态的三个工程现实:
- 幻觉1:模型即服务(MaaS)不等于服务即模型。很多团队直接用OpenAI API做Agent核心,结果发现
gpt-4-turbo在temperature=0.3时仍有12%概率生成不存在的API端点(如/v1/refund/cancel),而真实后端只有/v1/order/cancel。我们的方案是:所有LLM输出必须经过Schema Validator(用JSON Schema定义Action调用规范),非法输出直接reject,不重试。 - 幻觉2:开源模型≠开箱即用。我们测试过Llama3-70B在Plan生成任务上,对“若用户信用分<600则需人工审核”这类条件分支的遵循率仅63%,远低于商用模型的89%。最终选择商用模型做Plan生成+开源模型做Observation摘要的混合架构,成本增加15%,但任务成功率提升37%。
- 幻觉3:向量库能解决一切记忆问题。ChromaDB在10万条对话中检索“耳机型号”时,top3命中率仅58%,而图谱查询是100%。我们彻底弃用纯向量Memory,改用Neo4j+PGVector混合方案:结构化关系走图谱,非结构化文本(如客服聊天记录)走向量,查询时先图谱后向量。
基础组件选型(全部经压测验证):
- LLM网关:自研轻量级Router(Go编写),支持动态路由、熔断、缓存。不直接暴露模型API,所有请求走
/v1/agent/invoke统一入口。 - State存储:PostgreSQL 15(开启Row Level Security),
state_versions表带version_id,parent_version,diff_json,created_at字段,索引建在version_id和parent_version上。 - Plan执行器:Python 3.11 + asyncio,DAG调度器支持最大并发50个节点,单节点超时默认3s,可按Action类型覆盖(如支付Action设为10s)。
- Observation中心:Kafka Topic
agent-observations,每条消息含source,confidence,timestamp,raw_payload,消费者服务负责写入PostgreSQL并触发图谱更新。 - Memory图谱:Neo4j 5.12,节点属性加密存储(AES-256),关系边带
valid_from/valid_to时间戳,支持TTL自动清理。 - Loop控制器:State Machine引擎(用Transitions库实现),所有状态转移记录到
loop_events表,含agent_id,from_state,to_state,trigger,duration_ms。
注意:别在本地跑Neo4j!我们初期用Docker Desktop跑Neo4j,100并发时GC停顿达8s。最终方案是阿里云Neo4j企业版(4核16G),配合连接池(max_pool_size=200),P99延迟稳定在42ms。
3.2 Goal原子化实战:把“我要退货”拆成5个可编程契约
以用户输入“我要退货”为例,展示Goal工程化的完整链条:
Step 1:输入预处理
- ASR结果:
{"text":"我要退货","confidence":0.92} - NLU模块提取实体:
{"intent":"return_goods","entities":{"order_id":null,"reason":null}} - 此时Goal处于
INCOMPLETE状态,需触发追问
Step 2:原子Goal生成NLU输出被送入Goal Generator(微调的Qwen2-7B),Prompt强制要求输出JSON:
{ "atomic_goals": [ { "id": "g1", "name": "identify_order", "required": true, "input_sources": ["user_input.text", "user_profile.last_orders"], "output_schema": {"order_id": "string", "order_date": "string"}, "timeout_ms": 2000 }, { "id": "g2", "name": "verify_return_eligibility", "required": true, "input_sources": ["g1.output.order_id"], "output_schema": {"eligible": "boolean", "reason": "string", "deadline": "string"}, "timeout_ms": 3000 } ] }Step 3:Goal执行与校验
g1执行:先查user_profile.last_orders(缓存),若无匹配则问用户“请问订单号是多少?”,用户回复“123456789”,g1.output = {"order_id":"123456789","order_date":"2024-06-10"}g2执行:调用/api/v1/orders/{order_id}/return-eligibility,返回{"eligible":true,"reason":"within_7_days","deadline":"2024-06-17"}
Step 4:Goal契约达成所有原子Goal输出满足output_schema且timeout_ms内完成,则整体Goal状态变为FULFILLED,进入Plan生成阶段。若任一Goal失败(如g1超时),则触发降级流程:g1失败→查用户最近3笔订单→生成带订单号的按钮卡片供用户点击选择。
实操心得:Goal校验必须包含“业务合理性”检查。我们曾发现
g2返回{"eligible":true,"reason":"gift_card_used"},但Gift Card支付的订单实际不可退货。于是在g2的输出校验逻辑里加了一条规则:“若reason包含gift_card,则eligible必须为false”,这条规则写在State Machine的Guard Condition里,比改模型Prompt快得多。
3.3 State版本化实战:一次订单状态变更引发的血案复盘
真实案例:用户A在上午10:00下单,Agent记录Statestate_v101含{"order_status":"created"};10:05物流系统推送order_status="shipped",触发异步更新,生成state_v102;10:08用户A发消息“我要取消订单”,Agent读取state_v102,发现已发货,执行“引导联系人工”逻辑;但10:10用户A又发“等等,我刚看到还没发货”,Agent此时读取State,却因缓存未刷新仍看到state_v102,坚持说“已发货”,引发客诉。
解决方案:State版本化+强一致性读取
写路径:所有State更新(无论来自用户、工具、定时任务)都走
/v1/state/update接口,接口内部:- 用PostgreSQL
SELECT ... FOR UPDATE锁住该用户State行 - 生成新
version_id(MAX(version_id)+1) - 计算
diff(用jsonpatch库) - 插入新版本记录
- 发布Kafka消息
state_updated.{user_id}
- 用PostgreSQL
读路径:Agent每次读State都调用
/v1/state/latest?user_id=u123,接口返回:{ "version_id": "state_v102", "parent_version": "state_v101", "current_state": {"order_status":"shipped", "updated_at":"2024-06-15T10:05:00Z"}, "is_consistent": true }关键是
is_consistent字段——它由接口检查current_state.updated_at与Kafka消息state_updated.u123的最新时间戳是否匹配,不匹配则返回is_consistent:false,Agent必须等待或降级。
我们还加了“State健康度看板”:监控state_v*表的version_id增长速率,若1分钟内新增版本>100,说明有循环更新(如两个微服务互相触发State变更),自动告警。
3.4 Plan DAG实战:如何让Agent在支付失败时优雅降级
用户目标:“我要退货并退款到原支付方式”。Plan生成必须覆盖所有支付路径:
LLM Prompt关键约束:
你必须输出一个DAG,包含以下节点: - n1: get_order_detail (必需) - n2: check_refund_eligibility (必需) - n3: calculate_refund_amount (必需) - n4: initiate_refund (必需,但需分支) - n5: notify_user (必需) n4必须有分支: - 若payment_method=="alipay",调用/alipay/refund - 若payment_method=="wechat",调用/wechat/refund - 若payment_method=="credit_card",调用/card/refund - 若以上均不匹配,调用/fallback/refund 并设置reason="unknown_payment_method" 所有节点必须有timeout_ms和retry_policy生成的Plan示例:
{ "nodes": [ {"node_id":"n1","action":"get_order_detail","timeout_ms":2000,"retry_policy":{"max_attempts":1}}, {"node_id":"n2","action":"check_refund_eligibility","timeout_ms":3000,"retry_policy":{"max_attempts":2}}, {"node_id":"n3","action":"calculate_refund_amount","timeout_ms":1000,"retry_policy":{"max_attempts":1}}, {"node_id":"n4","action":"initiate_refund","timeout_ms":10000,"retry_policy":{"max_attempts":3}}, {"node_id":"n5","action":"notify_user","timeout_ms":500,"retry_policy":{"max_attempts":1}} ], "edges": [ {"from":"n1","to":"n2","condition":"n1.success"}, {"from":"n2","to":"n3","condition":"n2.output.eligible==true"}, {"from":"n3","to":"n4","condition":"n3.success"}, {"from":"n4","to":"n5","condition":"n4.status==success"} ], "conditions": [ {"node_id":"n4","type":"payment_method_switch","cases":[ {"value":"alipay","action":"/alipay/refund"}, {"value":"wechat","action":"/wechat/refund"}, {"value":"credit_card","action":"/card/refund"}, {"default":"fallback","action":"/fallback/refund"} ]} ] }DAG执行器如何处理失败:
n4执行/alipay/refund返回HTTP 400(余额不足),执行器记录n4.status=fail,不触发n5- 检查
n4的retry_policy.max_attempts=3,剩余重试次数2,1秒后重试 - 第二次重试仍失败,执行器读取
conditions,发现n4有default分支,于是调用/fallback/refund(走银行转账) fallback/refund成功,n4.status=success,继续执行n5
整个过程无需LLM参与,纯规则驱动,P99耗时2.3s。
3.5 Action事务化实战:两次扣款事故后的补偿机制设计
事故还原:
- 用户发起退款,Action
initiate_refund调用支付网关 - 网关返回HTTP 200,但内部扣款失败(网络分区)
- Agent记录
refund_initiated=true,用户看到“退款已发起” - 30分钟后网关重试成功,实际扣款两次
Action事务化改造:
Prepare阶段(
/v1/actions/prepare):- 生成幂等键:
refund_{order_id}_{timestamp}_{nonce} - 写入
action_log:{action_id:"act_789", status:"prepared", prepared_at:"2024-06-15T10:00:00Z", idempotent_key:"refund_o123_1686813600_abc"} - 返回
{success:true, idempotent_key:"refund_o123_1686813600_abc"}
- 生成幂等键:
Commit阶段(
/v1/actions/commit?idempotent_key=...):- 支付网关收到幂等键,查本地表确认未执行,执行扣款
- 成功则更新
action_log.status="committed",返回{result:{refund_id:"r456"}, status:"success"} - 失败则更新
status="failed",返回{error:"network_timeout", status:"fail"}
Compensate阶段(自动触发):
- 定时任务每5分钟扫描
action_log,找status="prepared"且prepared_at < NOW()-300s的记录 - 对每条记录调用
/v1/actions/compensate?idempotent_key=... - 补偿逻辑:查支付网关确认是否已执行,若未执行则标记为
cancelled;若已执行则触发退款
- 定时任务每5分钟扫描
监控看板关键指标:
prepared_but_not_committed_rate:应<0.1%,超阈值告警compensate_success_rate:应>99.9%,反映补偿逻辑健壮性idempotent_key_collision_count:应为0,检测幂等键生成缺陷
3.6 Observation可信化实战:如何让Agent分辨“物流显示已签收”和“用户坚称未收到”
Observation标准化结构:
{ "source": "sf_express_api_v4", "confidence": 0.85, "timestamp": "2024-06-15T09:45:22Z", "raw_payload": {"order_no":"SF123456789","status":"signed","sign_time":"2024-06-15T09:42:11Z","signer":"张三"}, "normalized": {"status":"delivered", "delivery_time":"2024-06-15T09:42:11Z", "signer":"张三"} }可信度动态计算:
sf_express_api_v4的confidence基线为0.95- 但API SLA监控显示:过去1小时
latency_p95>5s,扣0.05 → 0.90 sign_time距当前时间<30分钟,加0.03 → 0.93signer字段与用户注册姓名“张三”完全匹配,加0.02 → 0.95- 最终
confidence=0.95
Agent决策逻辑:
- 若
confidence >= 0.9,信任物流状态,回复“您的包裹已于今日09:42由张三签收” - 若
0.7 <= confidence < 0.9,触发双重验证:“物流显示已签收,您方便确认下是否本人签收?或家人代收?” - 若
confidence < 0.7,降级为人工:“系统暂未确认签收状态,已为您转接专员”
我们还在Observation中心加了“信源健康度仪表盘”,实时显示各API的confidence_avg、latency_p95、error_rate,当sf_express_api_v4.confidence_avg连续5分钟<0.8,自动触发告警并切换备用物流API。
3.7 Memory图谱化实战:为什么向量检索找不到“上次买的耳机型号”
图谱Schema设计:
// 节点 (:User {id:"u123", name:"张三", phone:"138****1234"}) (:Order {id:"o456", created_at:"2024-06-10T14:22:00Z", status:"delivered"}) (:Product {id:"p789", name:"WH-1000XM5", model_number:"WH-1000XM5", brand:"Sony"}) // 关系 (u:User)-[:BOUGHT]->(o:Order) (o:Order)-[:CONTAINS {quantity:1}]->(p:Product) (p:Product)-[:HAS_SPEC {key:"battery_life", value:"30h"}]->(:Spec)查询优化:
- 用户问:“上次我买的耳机型号是什么?”
- Agent先走图谱路径:
MATCH (u:User {id:$user_id})-[:BOUGHT]->(o:Order)-[:CONTAINS]->(p:Product) WHERE o.created_at < $now AND o.created_at > $now - duration({days:90}) RETURN p.model_number ORDER BY o.created_at DESC LIMIT 1 - 结果:
"WH-1000XM5" - 若图谱无结果(如用户首次购买),再fallback到向量检索:在
product_descriptions向量库中搜“耳机 型号”,取top3,用LLM摘要。
图谱维护机制:
- 每次Order创建事件,触发Neo4j写入
(:User)-[:BOUGHT]->(:Order)-[:CONTAINS]->(:Product) - 每次用户投诉,写入
(:User)-[:COMPLAINED_ABOUT]->(:Product)关系,并带complaint_type属性 - 图谱节点带
ttl属性,Order节点ttl=365天,自动归档
实操心得:图谱查询性能取决于索引。我们在Neo4