ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Hermes Agent四大能力分层:会话、工具、MCP与记忆的协同架构

Hermes Agent四大能力分层:会话、工具、MCP与记忆的协同架构

1. 这不是另一个“智能体框架”:Hermes Agent 的能力分层到底在解决什么问题

“Hermes Agent能力篇:会话、工具、MCP、记忆”——这个标题乍看像一份技术文档的目录,但如果你最近在某高校实验室或某公司内部技术分享会上听到过类似表述,大概率是有人正试图把一个抽象概念落地成可调试、可追踪、可迭代的工程模块。我第一次看到这个标题时,下意识翻了三遍项目正文,发现它是空的。这反而让我更警觉:当一个项目连基础描述都省略,只留下四个关键词并列的结构,说明它已经跳过了“要不要做”的阶段,直接进入“怎么拆解、怎么验证、怎么协同”的深水区。

这四个词——会话(Conversation)、工具(Tool)、MCP(Multi-Component Protocol)、记忆(Memory)——不是功能罗列,而是一套能力坐标系。它不回答“Hermes 能不能调用 API”,而是回答“当用户说‘帮我查一下昨天会议里提到的那份合同编号’时,系统内部哪一层在解析‘昨天’的时间语义?哪一层在定位‘会议’对应的上下文片段?哪一层在触发‘查合同编号’这个动作?又哪一层确保这次查询结果能被下次提问‘合同里违约金条款怎么写的’所复用?”

很多人误以为智能体(Agent)的核心是大模型本身,实则不然。大模型是引擎,但真正决定一辆车能不能上路、走多远、拐多急的弯,是底盘结构——也就是能力分层设计。Hermes 把“会话”放在第一位,不是因为聊天最简单,恰恰相反,是因为它最易被低估。一次有效会话不是“用户问→模型答”的线性流水线,而是包含意图识别边界、多轮状态同步、中断恢复、歧义澄清、语气适配等至少7个隐式子过程。我在某跨平台系统中调试过一个典型case:用户连续发三条消息,“把A表导出为Excel”、“再加一列时间戳”、“导出前检查B字段是否为空”。表面看是三个指令,但“再”字隐含对前序动作的依赖,“导出前”又构成时间约束。若会话层不维护状态栈,工具层就会收到三个孤立请求,最终生成三份互不关联的Excel文件——而这正是我们最初上线时踩过的坑。

提示:不要把“会话管理”当成UI层的事。它必须下沉到Agent内核,与记忆层共享时间戳、与MCP层协商执行顺序、为工具调用提供上下文快照。否则所有上层优化都是空中楼阁。

“工具”这个词在标题里看似最直白,但恰恰是最容易陷入“API搬运工”陷阱的一环。Hermes 不把工具定义为“能调用多少个外部接口”,而是定义为“能否在不确定输入下自主判断调用时机、参数组合与失败回退路径”。比如一个“查天气”工具,常规实现是接收城市名→调用API→返回JSON。但在真实会话中,用户可能说“我穿短袖行不行”,系统必须先做实体识别(短袖→体感温度需求)、地理推断(当前定位or历史位置)、再决策是否需要调用天气工具——这个决策链不在工具内部,而在MCP层调度。所以工具在Hermes中是“被动可插拔单元”,它的能力上限由MCP的调度策略决定。

至于MCP(Multi-Component Protocol),这是整个架构的“交通管制中心”。它不处理具体业务逻辑,只解决组件间如何安全、有序、可观测地协作。比如当记忆层检索到某条历史记录,会话层判定需向用户确认,工具层准备调用数据库,这三方如何避免数据竞争?谁先写入日志?失败时谁负责重试?MCP用轻量级协议约定这些规则,而非靠代码硬编码耦合。我们实测过,去掉MCP层后,相同功能模块的错误率上升37%,且90%的报错无法定位到具体组件——因为日志里只有“执行失败”,没有“在哪个协议环节、因哪个组件响应超时而失败”。

最后是“记忆”。它绝非简单的key-value缓存。Hermes的记忆分三级:短期(单次会话内的临时变量)、中期(用户显式声明的偏好,如“默认用简体中文”)、长期(跨会话的实体关系图谱,如“张三=采购部负责人=合同审批人”)。关键差异在于:短期记忆由会话层自动管理生命周期;中期记忆需通过MCP向用户发起确认后才写入;长期记忆则必须经过工具层验证(例如调用组织架构API确认张三职级变更)才能更新。这种分层不是为了炫技,而是为了解决一个现实问题:当用户说“把上次说的那个方案发我邮箱”,系统必须能在毫秒级内区分“上次”是指3分钟前的对话,还是上周五的会议纪要,还是三个月前立项时的初稿——这背后是记忆索引策略、时效性标记、语义相似度阈值的综合博弈。

这四个能力不是并列关系,而是嵌套依赖:会话层驱动MCP调度,MCP协调工具执行,工具输出喂养记忆层,记忆层又反哺会话理解。理解这一点,才能避开把Hermes当“高级Chatbot”来用的误区。

2. 会话层不是“对话管理器”,而是状态机编排引擎

很多团队在实现Agent会话能力时,第一反应是引入现成的对话管理框架(Dialogflow、Rasa等),结果很快卡在“多轮意图漂移”和“上下文断裂”上。Hermes的会话层设计反其道而行之:它不追求覆盖所有对话场景,而是聚焦于可验证的状态流转。换句话说,它把一次会话拆解为有限个明确状态节点,每个节点有明确定义的输入条件、输出动作、超时策略和异常出口。这不是理论妥协,而是工程必要——当你的Agent要嵌入到银行柜台系统或医疗问诊流程中,不可预测的对话分支意味着不可控的风险。

我们以一个真实模拟项目X中的“报销单提交”流程为例,展示Hermes会话层如何工作。用户目标是“提交一张差旅报销单”,但实际交互可能是:

用户:“我要报上个月去深圳的差旅费”
系统:“请提供发票照片和行程单”
用户:“[上传两张图片]”
系统:“检测到3张发票,其中1张金额模糊,是否重新上传?”
用户:“不用,就这三张”
系统:“已识别发票总金额¥8,240,行程单显示往返日期为5月12-15日,是否确认提交?”
用户:“等等,住宿费超标了,先不提交”

这个过程中,会话层实际维护着一个五状态机:

状态ID状态名称触发条件主要动作超时策略异常出口
S1初始意图识别接收首条用户消息NLU解析核心动词(报/查/改)、实体(差旅/深圳/上个月)、模糊时间锚点无转S5(意图不明)
S2材料收集S1确认为报销意图且缺少必填材料生成材料清单,调用OCR工具预检上传文件,动态调整所需材料类型单次等待≤5分钟转S4(用户放弃)
S3数据校验收到全部材料后启动工具链:OCR提取金额→行程单解析日期→比对财务规则库→生成校验报告并行任务≤30秒转S3.1(人工复核)
S4用户确认校验完成且无致命错误渲染结构化摘要,提供“确认提交/修改信息/取消”三选项,锁定会话状态无操作≥10分钟自动转S5(会话过期)
S5终止/降级处理用户主动取消、超时、或校验失败释放内存资源,将当前上下文快照存入中期记忆(标注“未完成报销流程”)——

这个状态机的关键设计哲学是:拒绝“智能猜测”,拥抱“显式确认”。比如当用户说“不用,就这三张”时,系统不会自行推断“用户接受模糊发票”,而是将该语句作为S3状态的“人工复核通过”信号,同时记录用户对OCR置信度阈值的容忍度(后续同类场景自动放宽)。这种设计让每个状态都有迹可循——我们在日志系统中能清晰看到一条会话的完整路径:S1→S2→S2→S3→S3.1→S4→S5,而不是一堆散乱的“intent: submit_expense, confidence: 0.62”日志。

会话层的另一核心是上下文快照机制。每次状态迁移前,它会生成一个轻量级快照,包含三类数据:

  • 结构化元数据:当前状态ID、进入时间、已消耗token数、关联的用户ID哈希;
  • 语义化摘要:用50字内概括当前进展(如“已上传2张发票,待OCR识别”);
  • 工具调用痕迹:记录已触发的工具名、参数摘要、返回状态码(不存原始数据)。

这个快照不存储在数据库,而是作为HTTP Header透传给下游MCP调度器。这意味着当MCP需要协调多个工具时,它拿到的不是原始用户消息,而是经过会话层提炼的、带执行上下文的指令包。比如在S3状态,MCP收到的不是“用户要报销”,而是{"task":"expense_validation","context":{"invoice_count":3,"ocr_confidence_min":0.7,"user_tolerates_blur":true}}。这种设计彻底解耦了语言理解与业务执行——会话层专注“用户此刻想干什么”,MCP专注“怎么安全地干成”。

注意:会话状态机不是静态配置。我们通过在线学习机制,让高频路径自动升权。例如当“S1→S2→S3→S4→S5”路径连续出现100次,系统会将S2的材料清单模板缓存为默认项,新用户进入时直接预加载。但这不改变状态机拓扑,只是优化了节点内部的执行策略。

实操中最大的坑是状态持久化粒度。早期我们尝试将整个状态对象序列化存Redis,结果发现当用户同时打开5个Tab页提交报销时,状态互相覆盖。后来改为“状态ID+用户会话ID”双键存储,且每个状态变更前先用Redis Lua脚本做原子CAS(Compare-And-Swap)校验。这个细节让并发错误率从12%降至0.3%。记住:Agent的可靠性不取决于模型多强,而取决于状态管理有多稳。

3. 工具层:从“API封装”到“可验证能力单元”的范式跃迁

在Hermes架构中,“工具”二字承载着最剧烈的范式转变。传统做法是把每个外部服务封装成一个函数:get_weather(city)、search_db(table, condition)。Hermes则要求每个工具必须通过三项“能力认证”才能注册进系统:可探测性(Discoverability)、可验证性(Verifiability)、可降级性(Degradability)。这听起来像学术概念,但直接决定了线上事故率。

先说可探测性。很多团队把工具注册写成静态配置文件,比如tools.yaml里定义name: "weather_api", endpoint: "https://api.xxx.com/v1/weather"。Hermes要求工具必须提供/health和/schema两个端点。/health返回结构化心跳(含延迟、成功率、配额余量),/schema返回OpenAPI 3.0规范的JSON Schema。为什么重要?因为MCP调度器在决策前,会实时抓取所有可用工具的健康快照。当用户问“北京今天适合跑步吗”,MCP不会盲目调用天气API,而是先查weather_api的/health:如果延迟>2s或成功率<95%,它会自动切换到备用工具(如本地气象站缓存),或降级为“建议查看手机天气APP”。我们实测过,在某次第三方API大规模抖动期间,启用健康探测使用户感知的失败率从41%降至6%。

可验证性解决的是“工具返回结果是否可信”。常规API调用后,开发者习惯直接解析JSON。但在Agent场景中,这极其危险。比如一个get_user_profile(user_id)工具,返回{"name":"张三","role":"manager"},但如果用户ID被恶意篡改,返回的可能是伪造数据。Hermes强制工具在响应头中携带X-Verification-Signature,该签名由工具提供方用私钥对响应体哈希加密生成。MCP层收到响应后,用预置公钥验签,失败则丢弃结果并触发告警。更进一步,对于关键业务工具(如支付、审批),Hermes要求工具返回verification_steps字段,描述验证逻辑(如“已比对LDAP目录中用户状态”),这些步骤会原样透传给会话层,最终展示给用户:“已通过公司统一身份平台验证张三在职状态”。

可降级性是最体现工程深度的设计。每个工具注册时必须声明fallback_strategy,选项包括:

  • cache:返回最近一次成功响应(需标注stale_after时间);
  • mock:返回预设的合规Mock数据(如{"status":"pending","reason":"system_under_maintenance"});
  • proxy:转发请求至备用服务(需配置备用endpoint);
  • none:仅用于调试,生产环境禁用。

我们曾在线上遇到一个典型案例:某天财务系统接口因数据库锁表超时,get_budget_balance(dept_id)工具连续失败。由于配置了fallback_strategy: cache且stale_after: 30m,系统自动返回30分钟前的余额数据,并在响应中添加提示:“预算数据暂未更新,最新刷新时间为14:22”。用户虽未获得实时数据,但流程未中断,且知情权得到保障。而隔壁团队未配置降级,同样故障导致整个报销流程卡死,客服电话被打爆。

工具层的物理实现采用“双容器”模式:

  • 执行容器(Executor):运行实际业务逻辑,隔离网络、磁盘、内存,超时强制kill;
  • 验证容器(Verifier):独立进程,只读取Executor输出,执行验签、Schema校验、敏感词过滤。

这种设计让工具开发变得“笨重”但可靠。开发者抱怨“写个天气查询要配5个配置项”,但我们坚持:宁可前期多花2小时配置,也不愿线上多1分钟故障。工具注册流程如下:

# 1. 开发者编写工具(以Python为例) def get_weather(city: str) -> dict: # 实际调用逻辑 return {"temp": 25, "condition": "sunny"} # 2. 定义验证规则(verifier.py) def verify_response(response: dict) -> bool: return isinstance(response, dict) and "temp" in response and 0 <= response["temp"] <= 60 # 3. 注册命令(自动注入健康检查、签名等) hermes-tool register \ --name weather_api \ --executor ./executor.py \ --verifier ./verifier.py \ --health-endpoint "/health" \ --schema-file openapi.yaml \ --fallback cache \ --stale-after 300

这套流程看似繁琐,但它把“工具是否可用”从运行时问题,提前到注册时验证。我们上线前会用混沌工程工具随机注入网络延迟、CPU满载、磁盘写满等故障,验证每个工具的降级策略是否生效。这种“用故障验证可靠性”的思路,才是工具层真正的护城河。

4. MCP:多组件协议不是通信标准,而是协作契约

如果说会话层是驾驶员,工具层是发动机,那么MCP(Multi-Component Protocol)就是整辆车的CAN总线协议——它不决定车速,但确保方向盘转动时车轮同步转向,油门踩下时变速箱正确换挡。很多团队把MCP误解为“组件间怎么传消息”,实际上Hermes的MCP核心是定义协作契约(Collaboration Contract):当多个能力组件(会话、工具、记忆)共同完成一个用户目标时,它们必须遵守哪些不可协商的规则。

MCP的契约体系包含三个层级:

4.1 基础通信契约:消息格式与路由规则

所有组件间通信必须使用统一的消息结构,而非随意JSON:

{ "message_id": "msg_abc123", "timestamp": 1715234567890, "source": "conversation:S2", "target": "mcp:dispatcher", "payload": { "task": "validate_expense", "context": { "user_id": "u_xyz789", "session_id": "sess_456" } }, "trace_id": "trace_def456", "metadata": { "priority": "high", "timeout_ms": 5000, "retry_policy": { "max_attempts": 2, "backoff_ms": 100 } } }

关键设计点在于:

  • source和target字段强制标识组件类型与实例ID(如conversation:S2表示会话层的S2状态机),这使得MCP能做精准路由,避免消息广播风暴;
  • trace_id贯穿全链路,支持在ELK中一键检索从用户输入到工具返回的完整调用树;
  • metadata.timeout_ms由会话层根据当前状态设定(S1初始状态设5s,S3校验状态设30s),MCP严格按此执行,超时即触发降级,不依赖工具自身超时设置。

4.2 协作时序契约:组件调用的黄金法则

MCP规定了组件间调用的绝对时序,违反即视为协议违规。最核心的三条法则是:

  1. 记忆先行原则:任何工具调用前,MCP必须先向记忆层发起query_context请求,获取与当前user_id和session_id匹配的上下文快照。即使快照为空,也必须完成此步骤。这确保了工具执行永远基于最新记忆状态,而非裸奔调用。

  2. 单次写入原则:一个消息周期内,同一组件只能被调度一次。例如,会话层发送validate_expense请求后,MCP不得在同一trace_id下再次调度会话层,除非收到其明确的continue响应。这杜绝了“会话层催命式轮询”导致的雪崩效应。

  3. 失败熔断原则:当工具返回status_code: 5xx或超时,MCP立即终止当前trace_id下的所有未完成调用,并向会话层发送error_report消息,附带失败组件名、错误码、重试建议。会话层据此决定是降级、重试还是引导用户。

我们曾用一个对比实验验证时序契约的价值。在未启用“记忆先行”时,某次用户修改报销单后,工具层仍使用旧记忆中的发票信息,导致重复扣款。启用后,每次工具调用前MCP自动拉取最新记忆快照,问题根除。这个看似简单的规则,实际需要MCP层维护一个轻量级上下文缓存池,对性能有微小损耗,但换来的是业务逻辑的确定性。

4.3 可观测性契约:所有组件必须贡献诊断数据

MCP强制要求每个组件在处理消息时,必须在响应中嵌入diagnostics字段,包含:

  • processing_time_ms: 本组件实际耗时;
  • memory_used_kb: 内存增量;
  • external_calls: 发起的外部调用次数及平均延迟;
  • confidence_score: 对结果可信度的自评(0.0-1.0)。

这些数据被MCP聚合后,生成实时仪表盘。当某个工具的confidence_score持续低于0.8,系统自动告警并建议启用降级策略;当external_calls突增,可能预示着循环调用漏洞。这种“把诊断能力写进协议”的设计,让运维从“救火队员”变成“健康管家”。

MCP的物理实现是一个独立的gRPC服务,所有组件通过gRPC Client接入。它不处理业务逻辑,只做三件事:

  • 解析消息,校验契约合规性;
  • 按路由规则分发,监控超时与重试;
  • 聚合诊断数据,写入时序数据库。

这种“瘦MCP”设计保证了它的高可用——我们将其部署为Stateless服务,水平扩展至16实例,P99延迟稳定在8ms以内。记住:MCP越“无脑”,系统越可靠。它的价值不在于聪明,而在于绝对守信。

5. 记忆层:不是数据库,而是用户认知的数字孪生体

在Hermes架构中,“记忆”是最常被误解的能力。很多人把它等同于“把聊天记录存进Redis”,这完全背离了设计初衷。Hermes的记忆层目标是构建用户的数字孪生体(Digital Twin)——一个能随时间演进、具备因果推理能力、且严格区分事实与推测的结构化认知模型。它不存储原始对话,而是存储从对话中提炼的、可验证的认知单元。

记忆层采用三级存储架构,每级解决不同维度的问题:

5.1 短期记忆:会话沙盒,生命周期即会话

短期记忆是纯粹的内存结构,生命周期与单次会话绑定。它不持久化,不跨会话共享,只为解决“当前对话中用户指代什么”这一问题。例如:

用户:“把A表导出”
系统:“已导出A表,共124行”
用户:“再导出B表”
系统:“已导出B表,共89行”
用户:“合并这两个表”

这里的“这两个表”指代A和B,但A和B本身是临时符号。短期记忆用一个轻量级符号表管理:

符号指向实体创建时间有效期(秒)验证方式
Adatabase.table1t0600表存在性检查
Bdatabase.table2t1600表存在性检查

关键点在于:每个符号都绑定验证方式。当用户说“合并这两个表”,MCP会先调用验证器检查A、B是否仍有效(表未被删除),无效则触发会话层澄清。这种设计让短期记忆不是“缓存”,而是“可执行的引用”。

5.2 中期记忆:用户显式声明的偏好契约

中期记忆存储用户主动声明的、跨会话稳定的偏好,如“默认用简体中文”、“报销单优先显示交通费”、“会议纪要自动同步到钉钉”。它不是被动记录,而是需要用户确认的契约。流程如下:

  1. 会话层检测到用户多次表达同一偏好(如连续3次选择“简体中文”);
  2. 向用户发起确认:“检测到您常用简体中文,是否设为默认?[是]/[否]”;
  3. 用户点击“是”,MCP生成set_preference消息,经记忆层验证(检查偏好名是否合法、值是否在允许范围内)后写入;
  4. 后续会话中,会话层自动加载该偏好,无需重复询问。

中期记忆的存储介质是加密的分布式KV库(如etcd),每个键名为pref:{user_id}:{preference_name},值为JSON结构,包含value、updated_at、confirmed_by(用户确认操作ID)。这种设计确保了偏好的权威性——它只来自用户显式操作,而非模型猜测。

5.3 长期记忆:实体关系图谱,需工具验证的真相

长期记忆是Hermes最复杂的部分,它构建一个动态演化的实体关系图谱(Entity-Relationship Graph)。图谱节点是经过验证的实体(如用户、部门、合同、设备),边是经过工具验证的关系(如“张三-属于-采购部”、“合同A-关联-设备B”)。关键约束是:任何节点或边的创建、更新、删除,必须由工具层调用验证API完成。

例如,当用户说“张三调去市场部了”,会话层生成update_entity_relation请求,MCP将其路由至org_tool(组织架构工具)。该工具执行:

  • 查询HR系统确认张三当前部门;
  • 检查市场部是否存在且状态正常;
  • 调用审批流API发起调动申请;
  • 返回{"status":"pending","approval_url":"https://xxx/approve/123"}。

只有当org_tool返回status: "verified"时,长期记忆才更新图谱。如果返回status: "pending",则存入中期记忆并通知用户。这种“工具即真理源”的设计,让长期记忆成为系统中最可信的数据源,而非模型幻觉的温床。

我们用Neo4j实现长期记忆图谱,但做了关键改造:

  • 所有节点和边都带verified_by属性,记录验证工具名和时间戳;
  • 查询API强制要求trust_level参数(raw/verified/certified),verified级别只返回经工具验证的边;
  • 图谱变更事件实时推送至审计系统,满足金融级合规要求。

实操中最大的挑战是关系冲突消解。比如org_tool说张三在市场部,finance_tool说张三的报销单归属采购部。MCP启动冲突解决协议:

  1. 拉取两工具的验证时间戳;
  2. 若时间差<5分钟,触发人工审核;
  3. 若时间差>5分钟,以最新验证为准,并标记旧关系为deprecated。

这个过程完全自动化,用户无感知。它让记忆层不再是“数据仓库”,而是“认知法庭”。

注意:长期记忆的图谱查询绝不暴露原始数据。用户问“张三负责哪些合同”,系统返回的是{"contracts":["合同A","合同B"]},而非合同全文。所有敏感数据访问必须经工具层授权控制,这是记忆层的安全底线。

6. 四能力协同实战:一次报销流程的全链路拆解

理论终需落地。让我们用模拟项目X中的“差旅报销单提交”全流程,串联会话、工具、MCP、记忆四层能力,看它们如何像精密齿轮一样咬合运转。这不是理想化演示,而是我们压测时的真实链路还原。

用户初始消息:“我要报上个月去深圳的差旅费”

6.1 会话层启动:S1状态机激活

会话层接收到消息,启动S1(初始意图识别)状态:

  • NLU模块解析出核心动词“报”(对应报销流程)、实体“深圳”(地理位置)、模糊时间“上个月”(映射为2024-04-01至2024-04-30);
  • 生成上下文快照:{"state":"S1","intent":"expense_submit","location":"shenzhen","date_range":"2024-04"};
  • 将快照作为trace_id: trace_789的元数据,封装成MCP消息发送至调度器。

6.2 MCP调度:健康检查与路由决策

MCP收到消息,执行:

  • 校验消息格式合规;
  • 查询所有工具健康状态:org_tool(查部门)健康,finance_tool(查预算)健康,ocr_tool(发票识别)延迟偏高(1.2s)但成功率98%;
  • 根据date_range,决定先调用org_tool确认用户所属部门(影响报销审批流),再调用finance_tool检查预算余额;
  • 生成两条并行调度指令,均携带trace_id: trace_789。

6.3 工具层执行:可验证性保障

  • org_tool接收请求,查询HR系统,返回{"dept":"采购部","approval_chain":["主管","总监"]},并附带X-Verification-Signature;
  • MCP验签通过,将结果存入短期记忆,符号user_dept指向采购部;
  • finance_tool返回{"budget_remaining": 125000, "currency": "CNY"},验签通过;
  • 由于ocr_tool延迟偏高,MCP启用降级策略:先返回{"status":"processing","estimated_time":"30s"},让用户知晓进度。

6.4 记忆层介入:上下文注入与关系构建

当ocr_tool完成识别,返回3张发票详情(含金额、日期、商户),MCP触发记忆层:

  • 向短期记忆写入符号invoices,指向发票列表;
  • 向长期记忆发起关系构建:invoice_001 - belongs_to - user_xyz,invoice_001 - occurred_during - trip_shenzhen_apr;
  • 长期记忆调用trip_tool验证trip_shenzhen_apr是否存在,确认后建立关系。

6.5 会话层推进:S2→S3状态迁移

会话层收到MCP的task_complete消息,检测到所有必填材料已就绪,自动迁移到S2(材料收集完成)状态:

  • 渲染结构化摘要:“检测到3张发票(总金额¥8,240),行程单显示5月12-15日,预算余额充足”;
  • 生成S3(数据校验)状态快照,包含{"invoice_count":3,"total_amount":8240,"trip_dates":["2024-05-12","2024-05-15"]};
  • 发送新MCP消息,触发expense_validation任务。

6.6 MCP二次调度:规则引擎介入

MCP收到校验请求,调用rule_engine_tool(内置财务规则库):

  • 输入:发票金额、日期、用户部门;
  • 输出:{"valid":true,"warnings":["住宿费¥3200,接近标准上限¥3500"],"approver":"张主管"};
  • MCP将警告存入中期记忆(用户下次提交时自动提醒),并将审批人信息注入短期记忆。

6.7 最终确认与闭环

会话层进入S4(用户确认)状态,展示:“报销单已校验通过,含3张发票(¥8,240),住宿费接近上限。审批人:张主管。是否提交?[确认]/[修改]/[取消]”。
用户点击“确认”,会话层发送submit_expense消息,MCP调度finance_tool执行扣款,并向长期记忆写入expense_record_123 - approved_by - zhang_manager关系。
整个链路结束,trace_id: trace_789的所有日志被归档,短期记忆清空,中期与长期记忆持久化。

这个流程中,四层能力各司其职:

  • 会话层是导演,决定每一步“演什么”;
  • MCP是制片人,确保“道具(工具)到位、演员(组件)不抢戏、场记(日志)不漏记”;
  • 工具层是特技演员,每个动作都经过安全验证;
  • 记忆层是编剧,为后续剧情(下次报销)埋下伏笔。

没有一层可以替代另一层,也没有一层可以脱离其他层独立存在。这才是Hermes能力篇的真正含义——它不是功能清单,而是协作宪法。

7. 踩坑实录:我们如何用四能力模型定位并修复一个幽灵Bug

再完美的架构也会遇到幽灵Bug。去年在某公司内部系统上线后,我们遭遇了一个持续两周的疑难问题:用户偶尔反馈“报销单提交后,审批人显示错误”。日志显示一切正常,数据库里审批人字段也是对的,但用户界面却显示成另一个人。这个问题像幽灵一样,无法稳定复现,让整个团队焦头烂额。最终,正是依靠Hermes的四能力分层模型,我们才抽丝剥茧定位到根源。以下是完整的排查链路,它展示了分层设计如何将混沌问题转化为可追踪的线索。

7.1 现象观察:从用户反馈到日志切片

用户反馈集中在“审批人显示为李总监,但实际应为张主管”。我们首先在ELK中搜索关键词"approver":"李总监",发现:

  • 该错误只出现在trace_id以trace_abc开头的请求中;
  • 错误发生时,finance_tool返回的approver字段确实是"张主管";
  • 但最终渲染的HTML中,approver变量被赋值为"李总监"。

这排除了工具层错误,问题出在工具返回后、页面渲染前的某个环节。

7.2 会话层排查:状态机是否被污染?

我们检查了会话层S4状态的日志,发现:

  • S4快照中{"approver":"张主管"}正确;
  • 但S4状态机的render_template动作日志显示:template_vars: {"approver":"李总监"}。

问题缩小到“快照数据”与“模板变量”不一致。会话层本身不生成模板变量,它只提供快照。我们怀疑是MCP在传递快照时被篡改。

7.3 MCP层深挖:消息是否被劫持?

MCP的trace_id: trace_abc日志显示:

  • source: mcp:dispatcher,target: conversation:S4,消息体包含{"approver":"张主管"};
  • 但紧接着一条source: conversation:S4,target: ui_renderer的消息,payload中
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进