做AI Agent落地这一年多我最大的感受是模型能力大家都能拿到真正决定一个Agent能不能从Demo走向生产环境的反而是一些看起来没那么“炫”的基础能力。Hermes Agent这个项目就是在这种背景下推进的它面向企业内部业务流程要处理真实用户的日常请求解决的是“AI助手只会聊天、没法真正办事”的痛点。今天想把这些基础能力拆开聊聊会话、工具、MCP和记忆。这四个词单独看都不算新鲜但把它们组合在一个稳定的运行时里坑远比想象中多。这篇就当是给自己踩坑做个记录也给正在搭建Agent的朋友一句实在话先把地基打牢再去追求复杂的推理链路。1. 先给Hermes Agent画个像它到底要解决什么问题1.1 为什么是“Agent”而不是“Chatbot”很多团队做智能助手做着做着就做成了“高级版问答机器人”用户提问模型回答上下文靠历史消息硬拼。Chatbot和Agent最大的区别在哪我理解是Chatbot的终点是“说”Agent的终点是“做”。Hermes从一开始就定了调子——它必须能把用户的话转成具体的动作查某个系统的数据、调用某个接口、修改某个状态、记住某个偏好。这也决定了它的架构不能是“一条提示词走天下”而是需要一个能编排模型、工具、外部协议和存储的运行时。这个定位直接影响了技术选型。比如语言模型这块我们选择了支持函数调用/工具调用Function Calling的模型而不是只靠“让模型输出JSON然后自己解析”的老办法。为什么因为函数调用是模型原生训练的指令遵循能力输出结构更稳定容错率更高。老办法也能用但凡是参数多一点的工具模型很容易把JSON字段记错、写错调试起来非常痛苦。1.2 核心能力地图Hermes Agent的能力拆开来看可以画成四个相互咬合的模块会话Conversation负责多轮对话的状态维护、上下文管理、中断恢复。工具Tools负责对外部系统操作能力的封装、注册、校验和执行。MCPModel Context Protocol负责用标准协议接入外部工具生态解决“接一个系统写一套适配”的重复劳动。记忆Memory负责短期上下文和长期用户偏好的存储、检索和遗忘。这四个模块不是独立存在的。会话是骨架工具是手脚MCP是标准化插座记忆是大脑的持久层。后面每一个我都会展开讲但在展开之前得先说清楚一个问题为什么它们缺一不可。2. 会话机制Agent的对话底盘2.1 会话不是“把消息拼起来”很多人以为会话管理就是把用户消息和AI回复按顺序丢给模型上下文一长就截断截断完接着聊。这么做在Demo阶段没问题但生产环境很快会翻车。Hermes第一版就是这种天真实现结果用户连续聊了一个星期之后会话历史已经长到模型输入窗口装不下系统只能粗暴截断导致用户几轮之前提过的重要约束被丢掉——比如用户两周前说过“之后所有订单都走紧急通道”结果模型忘了差点出了大问题。所以会话管理的第一原则是上下文不是越多越好而是“该留的留、该丢的丢、该压缩的压缩”。Hermes现在的做法是基于线程Thread管理会话每个会话有一个独立的thread_id所有消息落库。和模型交互时不会把全量历史都塞进去而是构造一个“有效上下文窗口”。2.2 多轮状态的管理多轮会话真正的难点在于状态。举个例子用户在会话里说“帮我查一下上个月的成本报表”然后又问“和上上个月比呢”这里“和上上个月比”的指代对象必须落到上一轮提到的“成本报表”上。模型本身有指代消解能力前提是你把上一轮的关键信息保留在上下文里。Hermes在处理这种场景时会在每条消息上附带额外的状态元数据比如当前会话的意图标签、最近一次工具调用结果、待确认的下游动作。这些元数据不一定全量发给模型但会参与构建Prompt。你可以理解成会话模块维护一份“情景记忆”模型看到的不只是原始聊天记录还有系统帮它整理好的当前任务进度。这里有个容易忽略的点工具调用的中间结果到底要不要进会话历史我的建议是只保留“精简后的结果摘要”而不是把接口返回的几千行JSON原封不动塞进历史。否则第二轮对话上下文立刻爆炸。2.3 会话中断、恢复与超时设计生产环境里用户不可能每句话都在几秒内连续发。有人早上问了一句下午才又回来。这时候如果还把所有历史都堆给模型既浪费Token响应也可能变慢。Hermes的会话模块引入了几种机制空闲超过5分钟触发上下文压缩将早期对话做一次摘要生成摘要以特殊标记替代原始消息保留最近几轮完整内容。支持长期暂停与恢复thread_id存的是数据库记录模型侧只保留当轮请求的上下文。用户几天后回来加载最近摘要和最近几轮即可。并发保护同一个thread_id同时只能有一个运行中的请求避免用户短时间内重复点击导致状态错乱。提示上下文压缩一定要有触发器不要频繁执行。摘要本身要消耗一次模型调用如果每轮对话都压成本反而升高。Hermes的触发条件是历史消息超过窗口上限70%或者空闲时间超过阈值。3. 工具调用给Agent装上手脚3.1 工具的本质把LLM从“嘴”变成“手”我一直跟团队里的新同学强调模型本身是不会执行任何操作的它只会“建议下一步该调用什么”。所谓工具能力就是你给模型提供了哪些可调用的函数模型根据用户意图从中选择、填好参数然后由Agent运行时去真正执行再把执行结果返回给模型继续推理。这个“模型决策-运行时执行-结果回填-再决策”的循环就是Agent干活的基本模式。Hermes把工具分成三类第一类是内部函数比如查询订单、创建审批第二类是HTTP API封装比如对接内部业务系统第三类是通过MCP接入的外部工具。三类工具在Agent内部统一抽象成同一种结构这样模型侧看到的工具列表是一致的不需要区分来源。3.2 工具注册与Schema定义工具注册是工具模块的地基。Hermes里每个工具都是一个描述清晰的结构核心是一个JSON Schema。Schema写得好不好直接决定模型能不能正确调用。我总结了几条经验参数描述要写“这个参数在什么场景下用”不要把文档里的字段说明原样抄过来。枚举值必须列全而且每个枚举值最好补充简短含义。必填参数和可选参数一定要分清楚模型选错必填参数的概率比你想象的高。参数名尽量用与业务一致的名字不要用内部缩写。下面是一个简化版工具注册示例伪代码hermes.tool( namequery_order_status, description查询订单当前状态适用于用户询问订单进度、物流、是否发货等场景, params_schema{ type: object, properties: { order_id: { type: string, description: 订单编号通常以PO开头例如 PO20240315001 }, customer_only: { type: boolean, description: 是否仅返回客户可见的状态信息默认True } }, required: [order_id] } ) def query_order_status(order_id: str, customer_only: bool True): # 执行内部逻辑返回结构化结果 return {status: shipped, tracking_no: SF123456}实际执行时Agent运行时拿到模型输出的工具调用请求先做参数校验再做权限校验然后执行工具函数。执行结果会统一转成文本摘要写回对话上下文让模型能“看到”工具干了什么。3.3 工具执行的安全与回滚工具一旦能改数据安全问题就绕不开。Hermes对工具执行做了几层防护权限分级只读工具任何用户会话都能调写操作类工具需要用户二次确认或者只能由具备权限的角色触发。参数白名单比如订单查询工具只允许订单号匹配当前用户权限范围内的客户防止横向越权。执行超时与熔断单个工具调用超过15秒就放弃并返回超时错误。连续失败5次会触发工具级熔断避免Agent反复调一个不稳定的接口。操作日志所有工具调用都会记录入参、出参、耗时、调用者会话方便事后审计。回滚这块我吃过亏。最开始时我们允许“创建订单”这类工具直接执行用户后悔了只能去业务系统手工取消体验很糟糕。后来改为高风险写操作先返回“预执行结果”让模型向用户确认确认后才真正提交。这就相当于给工具调用加了一个事务边界虽然没有数据库回滚那么严格但至少给了用户反悔的机会。4. MCP工具生态的标准化协议4.1 MCP解决什么问题做Agent接入外部系统的过程中最烦的就是每个系统都要单独适配这边系统暴露的是REST接口那边系统是gRPC还有一个是内部SDK每个的鉴权方式还不一样。每接一个数据源就要写一遍胶水代码。Hermes在接了大概第五个业务系统的时候我们决定认真引入MCPModel Context Protocol。MCP本质上是一个标准协议解决的是“Agent运行时如何发现并调用外部工具”的问题。你可以把它理解成USB接口以前每接一个外设都要专门焊接线现在设备只要做成USB标准接口插上就能用。MCP把工具、资源、提示词都标准化Agent用一个统一客户端就能对接所有实现了MCP协议的服务器。4.2 服务器、客户端和工具路由在Hermes里每个外部系统对应一个MCP Server通过配置文件描述如何连接。最常用的是stdio模式Agent运行时MCP Client直接启动子进程通过标准输入输出和MCP Server通信。好处是不用开端口适合内部部署缺点是Server进程生命周期要管理好不能有内存泄漏。以下是Hermes中一个MCP Server配置文件示例{ mcp_servers: [ { name: crm_tools, command: python, args: [-m, hermes_mcp_servers.crm], auth_token_env: CRM_TOKEN, tools_namespace: crm }, { name: hr_tools, command: node, args: [server.js], auth_token_env: HR_TOKEN, tools_namespace: hr } ] }启动时Hermes的MCP Client会逐一连接这些Server调用tools/list拉取工具清单然后为每个工具生成统一的工具Schema注册到Agent的工具列表里。为了区分来源每个来自MCP的工具名会加上命名空间前缀例如crm_get_contact、hr_get_leave_balance。这样做的好处是模型看到工具名就知道该调哪个系统冲突也不会发生。4.3 从MCP到内部工具的无缝整合集成MCP不是把工具列出来就完事。真正麻烦的是工具参数和业务语义的统一。不同MCP Server返回的数据格式可能差异很大有的返回蛇形命名有的返回驼峰命名有的时间是时间戳有的是字符串。Hermes内部增加了一个轻量转换层每个MCP Server可以配置一个响应转换器把返回结果统一转成内部标准格式。还有鉴权问题。MCP协议本身不规定鉴权方式实际落地时我们用的是“客户端侧注入”内部MCP Server不直接保存用户身份而是在每次调用时从请求上下文拿到用户标识和会话凭证由Hermes运行时统一注入。这样做的好处是MCP Server不需要理解Agent的业务权限逻辑只管执行权限控制在Agent层集中管理审计也集中在同一处。注意MCP Server的日志一定要和Agent主进程日志分开。MCP Server里出问题查日志时如果混在一起特别难定位。我们后来统一走结构化日志并给每条MCP调用加上带有trace_id的关联ID排查效率才上来。5. 记忆从“每次都重新认识”到“老朋友”5.1 记忆分几层记忆这块最怕的是“什么都要记”。如果Agent把每轮对话都永久存储过不了多久检索出来的记忆全是噪音反而干扰判断。Hermes把记忆分成三个层次会话级短期记忆当前会话里的关键信息由上下文窗口管理。用户级长期记忆用户跨会话的稳定偏好、事实性信息比如“偏好简体中文”“喜欢简洁汇报”“常用收货地址是上海某园区”。系统级参考记忆企业内部知识、业务规则属于只读参考不随用户对话变化。日常开发中很多人只关注了短期记忆忽视了长期记忆和参考记忆的区分导致用户偏好和业务规则混在一个向量库里检索结果非常不可控。5.2 短期记忆会话窗口管理短期记忆其实就是前文说的会话上下文。它要做到的是“在不超出模型窗口的前提下保留最关键的信息”。Hermes的窗口管理策略是这样的计算当前消息总Token数超过模型窗口上限的70%触发压缩。压缩时将最早的一批完整消息发给摘要模型生成一段“会话摘要”用这段摘要替换原始消息。压缩后的上下文保证最近3轮完整对话永远不丢。摘要生成时额外提取“待办事项”和“明确承诺”两个字段这样即使细节丢了模型也知道还有哪些事没办完。这套策略实际运行下来效果不错但有个副作用摘要生成本身会增加一次模型调用延迟。所以我们在压缩触发上加了一个“冷却时间”同一会话10分钟内最多压缩一次避免对话频繁压缩导致响应变慢。5.3 长期记忆向量检索与摘要归档长期记忆的存储我们用了向量数据库核心流程是写和读两条链路。写入侧每轮会话结束后后台异步任务分析对话内容抽取出人称明确、具有长期价值的事实陈述。抽取时会做一次置信度打分分数低于阈值的直接丢弃。为什么要打分因为模型抽出来的东西不一定可靠比如用户随口说“我下周可能去北京”这种模糊信息没有长期存储价值。读取侧每当开启新的会话系统会基于用户ID检索相关记忆片段把最相关的若干条注入系统Prompt。检索相似度的计算要结合向量相似度和时间衰减因子一条三个月前的记忆即使语义很像权重也要往下降不然旧信息可能误导当前对话。Hermes的长期记忆项结构大致如下{ memory_id: mem_20250710_001, user_id: user_10086, memory_type: preference, content: 用户希望日报类查询结果附带环比说明, importance: 0.82, last_accessed: 2025-07-10T09:30:00Z, created_at: 2025-05-02T11:00:00Z, source_session: thread_88231 }5.4 记忆的写入与遗忘策略有写入就必须有遗忘。Hermes的记忆模块设计了遗忘机制主要分三种方式被动遗忘通过TTL过期比如一次性的临时事实例如“本周三用户请假”一周后自动降权、归档、删除。主动覆盖用户明确说“我之前说错了改成……”系统找到对应的旧记忆先置为失效再写入新记忆。定期清理每周对记忆做一次重要度重排低于阈值的直接删除长期未被访问的记忆逐步降低热度权重。这里我想强调一点记忆的“删除”最好是软删除也就是标记失效而不是物理删除。一方面方便审计另一方面AI抽取出错时能回溯。真正物理删除只会在用户明确要求“忘掉我所有数据”时执行。6. 四者如何协同一个完整请求的生命周期6.1 一条消息进来后发生了什么前面把四个能力分开讲但实际运行它们是一个整体。我拿一个带记忆多轮对话的具体场景来演示用户说“帮我查一下A客户最近的订单顺便按我上次说的那样格式给我”。请求进入Hermes后的处理顺序大致是会话模块先加载该thread_id的历史上下文判断是否有压缩摘要需要加载。记忆模块基于用户ID检索长期记忆从中发现“上次说的那样”指代的是“按客户、金额、交付日期三列简表展示”。Agent编排器组装Prompt注入系统工具查询订单、查询客户、格式化报表和长期记忆片段。模型第一次输出调用query_order_list的请求传入客户标识和时间范围。运行时执行工具拿到订单列表把结果简化成摘要回填上下文。模型第二次输出基于所有上下文生成最终回复并按用户偏好使用表格格式。会话模块保存本轮完整消息后台异步抽取新的长期记忆。如果上下文超过阈值触发摘要压缩。整个过程看起来简单但每一环都可能出问题记忆检索差了指代就落空工具执行慢了模型就干等上下文太长模型就答非所问。所以协同的稳定性取决于每一环节的容错设计。6.2 协同的关键点我总结协同设计的三个关键点。第一所有外部信息工具结果、记忆片段、MCP返回必须在进入模型前统一为“少量、结构化、可读”的文本不能让模型处理原始JSON堆。第二工具调用的中间结果要轻量化但关键数字不可丢需要在简化时留出保留字段。第三记忆注入要克制一般不超过总Prompt的10%。塞太多记忆会让模型分不清哪句是当前用户的请求。这套协同逻辑运行了一段时间之后我们发现“上下文压缩和长期记忆提取同时写库”会产生竞争条件压缩器在改写历史时后台记忆提取还在读旧消息导致提取内容不完整。后来我们加了一个简单的锁记忆提取只处理已经落库且状态为“完成”的消息压缩只处理“已提取过特征”的消息。顺序理顺了问题就消失了。7. 落地排查我在实际接入中踩过的坑7.1 会话与记忆串台一开始我们把thread_id当成user_id的维度去检索记忆结果一个用户开了多个会话记忆串来串去A会话里用户的偏好被B会话读走引发了很不好的体验。后来改成记忆检索维度以user_id为主、thread_id为辅且只允许读取该用户允许的记忆类型才算稳定下来。这个坑提醒我会话和记忆在概念上虽然是两类模块但落地时必须有清晰的归属关系。7.2 工具调用循环卡死有一段时间Hermes会陷入“工具调用-报错-重试-再报错”的死循环原因是一个查询接口偶发返回空数据模型看到空结果以为调用失败于是反复调用同一个工具。解决方式是加了两道防线一是运行时对同一个工具设置单轮最多调用3次超过次数直接从工具列表里临时移除二是对工具返回结果增加显式的“空结果”标记让模型明确知道“没有数据不是错误不用重试”。7.3 MCP连接不稳MCP的stdio模式在我们接入初期经常出问题子进程启动后偶发僵死工具调用超时。后来排查出两个原因一是子进程未设置工作目录导致找不到配置文件二是MCP Server内部有未捕获的异常异常发生时进程没有优雅退出而是挂起。修复方案是启动前显式设置env和工作目录同时在MCP Client侧增加了心跳检测和自动重启机制。7.4 记忆污染最大的一次事故是模型把“用户在会话中提到的某个人名”识别成了长期偏好写进了记忆库导致后续所有对话都被这个错误记忆干扰。后来我们在记忆抽取流程里加了“事实性校验获取用户确认”机制高置信度的偏好直接写库低置信度的候选记忆先以“待确认”状态保存如果后续对话中用户再次提到同一偏好才升级为正式记忆。这条机制大大减少了记忆污染带来的连带错误。8. 常见问题速查Hermes能力篇的排障清单为了让有类似场景的同行能快速定位问题我把高频问题整理成了一张速查表按模块划分问题表现可能原因排查思路模型总是忘记几轮前说过的话上下文窗口被压缩或者摘要丢失了关键信息检查压缩策略的触发阈值查看摘要是否包含任务待办字段工具参数频繁被模型填错工具Schema描述含糊枚举和例子缺失重建Schema为每个参数补充使用场景和典型示例值某个工具始终调用超时工具执行时间过长或者依赖的下游接口不稳定检查执行超时配置为下游接口加缓存和降级逻辑MCP工具列表时有时无MCP Server进程异常退出或启动失败查看Server日志检查工作目录和启动配置确认鉴权Token是否过期用户跨会话时记忆不生效记忆写入是异步的上一轮刚结束就开始新会话记忆还没落库缩短记忆落库延迟新会话时增加记忆就绪等待或重试检索出来的记忆与当前话题无关向量相似度匹配太宽时间衰减权重不足增加记忆类型过滤调低语义相似度阈值设置更积极的时间衰减模型回复中混入了别的用户的偏好记忆检索维度错误用了thread_id而不是user_id核对记忆读取链路确认检索键是user_id上下文一长响应速度明显变慢工具结果和消息未精简Token占用过高打开Token统计对工具返回内容做摘要化压缩排查这类问题我的经验是先看日志里的trace_id链路从会话入口一路追到工具调用和记忆检索不要凭感觉猜。Hermes在我们内部稳定跑了近半年上面这些问题大多是在前三个月集中爆发的解决之后整个系统的可靠性上了一个台阶。最后说句心里话Agent能力的核心壁垒并不在于模型选得多强而在于会话、工具、MCP和记忆这些“基础能力”是否打磨得足够细致。每次看到别人家Agent Demo炫酷无比我反而会去问一句它在生产环境扛得住多少真实用户它的记忆会不会出错它的工具调用是不是会被一个异常接口卡死这些问题才是Agent能不能用起来的关键。Hermes这条路我们还在继续走后续如果踩到新的坑我再接着分享。