ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Agent工程化核心:Harness引擎与MCP审计方案实战解析

Agent工程化核心:Harness引擎与MCP审计方案实战解析 云栖那几天我本来只打算看模型和算力的展台结果在一场分享里被一个叫 Kymo 团队讲的东西拽住了。他们聊的不是又一个新模型而是 Agent 工程化的底座Harness 引擎以及配套的 MCP 审计方案。当时台下很多人提问问得最多的几乎都是同一个问题——“你这些记录到底怎么才算审计而不是普通日志”这场分享让我回去之后把这两个方向都重新研究了一遍包括 Harness 工程到底是什么、MCP 审计到底要审哪些东西、以及我实际搭一个审计中间件的完整过程。这篇文章就是整理后的结果。如果你正在做 Agent 工程化、给现有系统接入 MCP 工具或者已经被合规追着要“调用记录和权限审批”那这篇内容大概率对你有用。1. 先搞清楚Harness 引擎到底解决什么问题1.1 Harness 不是新瓶装旧酒它是 Agent 的“笼头”Harness 这个词搞过测试的人应该不陌生test harness 就是测试脚手架。但在 AI Agent 领域它指的是另一层东西套在模型外面、连接外部世界的那层工程框架。为什么需要这层框架因为大模型本质上是一个“不可控的输入-输出函数”。给它一个任务它可能会规划也可能会瞎编更可能会在调用工具时传出一堆危险的参数。如果不加约束AI 就只是一匹没有笼头的马跑得越快撞坏的墙越多。Harness 就是那个笼头它负责把模型的行为圈在可控范围内能看哪些上下文、能调哪些工具、参数怎么校验、失败怎么回退、全过程怎么留痕。这也是为什么“harness engineering”这个词最近在社区里越来越热。大家逐渐意识到Agent 的性能上限看模型但下限看工程。Claude Code 能流行不只是因为模型强它外面那层 harness 做得极其克制和可用才是关键。同样社区里像 DeepSeek harness 这样的项目本质上是把类似的工作流搬到其他模型上包括 skill技能包管理、权限拦截、上下文压缩。它说明一个事实harness 是可以抽出来、跨模型复用的它本身就是一个独立的工程研究对象。Kymo 分享里有一句话我印象很深“不要把 Agent 想成一个人把 Harness 想成他的公司制度。人会有发挥失常制度保证混乱不至于失控。” 这个类比很糙但很准确。1.2 Harness 和 Agent 到底有什么区别在做笔记的时候我给自己列了一张对比表用来理清这两个概念的关系。很多文章把这两个词混着用实际研究起来会发现它们关注的问题完全不同。对比维度AgentHarness核心问题怎么做出正确的决策怎么安全可靠地执行决策关注点规划、推理、工具选择权限、审计、上下文、重试、回退表现形式一套 Prompt 加上推理循环一套运行时框架包含拦截器和执行管道可以没有固定工作流无法脱离 Agent 独立产生智能崩溃后果决策错误答错一道题权限失控外部系统被连坐破坏简单说Agent 是一个概念Harness 是实现这个概念的那套代码和机制。同一个 Agent 策略跑在裸 Prompt 上和跑在一个完善的 Harness 里结果天差地别。裸 Prompt 环境下模型可以直接输出任意内容工具调用的成败全看运气而 Harness 环境下模型每一步决策都被闸门过滤该拦截的拦截该记录的记录该回退的回退。还有一点值得注意Harness 也可以单独用来做 Agent 评测。把一批测试任务喂进去统计成功率、平均耗时、工具调用次数、回退率这些指标能反过来帮你优化上层 Agent 策略。热词里有人搜“deepseek harness 代码回退”我理解的就是在 harness 层做的失败重试和状态恢复机制。这个话题放到后面实操部分细说。2. 从 Harness 引擎的三个设计关键看它为什么值得研究2.1 上下文窗口管理让模型永远知道“现在该干嘛”大模型的上下文窗口是有限的哪怕模型支持 200K token你也不能把整个业务系统的信息全塞进去。Harness 的首要任务就是对输入上下文做编排。它决定哪些内容固定留在窗口里哪些内容滚动淘汰。这有点像你给同事发需求文档重要的约定放在最前面历史聊天记录折叠起来附件太大就只给摘要。我研究过的几个成熟 harness 实现普遍会做三件事第一把系统提示和业务规则做成“锚点”固定在窗口头部不被滚动策略冲掉。第二对工具返回结果做裁剪超过一定长度后只把摘要放进上下文原始结果存到外部存储模型需要时再按 ID 拉取。第三对多轮历史做滑动窗口折叠早期对话会被压缩成一段 brief。这个设计直接影响成本和效果。裁剪做得好同样的任务可以省下大量 token而且模型反而更专注。裁剪做得差上下文被工具结果塞满模型很快进入“注意力分散”状态回答质量肉眼可见地下降。另外“skill 部署”这个词也会在这种场景下出现。DeepSeek harness 里经常提到附带 skill 怎么部署到内网服务器本质就是把它当成一种可加载的上下文资源把 skill 文档放到指定目录在配置文件里声明为本地路径harness 在任务开始时按需读入。内网环境没有外网可以拉取远程依赖所以全部要改成离线包方式和本地路径引用。2.2 工具调用的闸门与回退机制给外部世界加隔离层为什么不让模型直接调函数因为模型没有“谨慎”这个属性。它可能在你允许调用 SQL 工具的权限下顺手执行一条DELETE FROM users WHERE 11。这听起来夸张但在真实测试里模型在复杂任务的压力下确实会做出超出授权的操作。Harness 在模型和外部工具之间加了一道闸门所有工具调用必须过这层白名单检查、参数格式校验、敏感字段脱敏、次数限制、异常回退。这里提到的“代码回退”我会在实操环节被反复确认一旦工具调用结果异常自动回滚到上一个稳定状态而不是继续让模型“将错就错”。我还发现一个跟“RPA 落地实现”非常契合的用法固定的流程步骤可以放在 harness 里跑模型只在关键决策节点被调用。这本质上是一种“带外流程编排”好处是稳定流程不会被大模型的随机性带偏审计点也清晰可控——哪些步骤是固定代码执行的哪些是模型决策的边界一目了然。审计方案最怕的就是“分不清责任”这种混编排方式反而把责任线划得清清楚楚。2.3 可观测性Harness 天然就是审计的最佳落点这是我这次研究里收获最大的一点。很多人以为审计是给 MCP 加日志其实不对。工具调用日志要想可靠前提是所有调用都经过同一个通道。而 harness 正是这个统一通道模型不会直接去调外部 API它必须通过 harness 注册的工具接口。这就意味着只要在 harness 层做埋点你不需要改动任何业务代码就能拿到“模型到底调用了什么工具、传了什么参数、返回了什么结果、用了多久、消耗了多少上下文”这些数据。这也是 Kymo 的 MCP 审计方案能成立的前提审计不是事后从日志里捞而是在调用链上主动采集。有了这层统一出口后面要做的所有事情——权限复核、用量统计、异常告警、成本归因——都变得顺理成章。所以如果你打算给 Agent 接入系统第一件事不是写 Prompt而是先确定 harness 层有没有没有就先补上。3. MCP 协议和审计方案的关系要审的是“模型到工具”的通道3.1 先把 MCP 讲清楚它是一份软件接口协议我注意到热词里有人问“MCP 是软件协议还是硬件协议”。这里明确一下MCPModel Context Protocol模型上下文协议是一个应用层的软件协议由 Anthropic 提出并开源。它的定位相当于给所有 AI 应用和外部工具之间定义一个统一的插口规范。打个比方USB-C 是硬件接口标准解决的是“物理上怎么插得上”MCP 更像设备驱动的软件规范解决的是“拔插之后数据怎么互相理解”。在 MCP 之前每个 AI 应用接一个数据源都要单独开发一套集成。现在数据源只要实现一个 MCP Server所有支持 MCP 的客户端Claude Code、Codex、通义灵码等都能用同一套方式访问。这也是为什么“ruoyi-vue-pro 合并 MCP 功能”这种传统 Java 业务系统改造的需求会冒出来——大家都想把现有系统的能力快速暴露给 Agent 用。但这里有个容易被忽略的点MCP 只负责“连通”不负责“授权”和“审计”。它就像一根 USB 线只管传输数据不管传输的内容是否合规。所以接入 MCP 之后你反而更需要对它做审计。因为通道一旦打通权限边界从“人到人”变成了“模型到工具”如果没有人盯着模型替人类做出的每个决定都可能在真实系统里生效。3.2 审计要覆盖的五个层次我在研究 Kymo 的审计方案时把他们建议的审计粒度拆成了五个层次我觉得这个框架非常实用拿来即用审计层次需要记录的内容典型事件示例连接层MCP Server 地址、认证方式、握手结果、心跳状态server 上下线、鉴权失败、重连恢复调用层工具名称、入参/出参摘要、耗时、调用结果create_branch、run_sql、read_file参数层脱敏后的参数内容、参数哈希、是否包含敏感字段SQL 的 db_name、where 条件、文件路径数据层返回数据条数、体积、是否命中敏感数据查询结果超限、身份证字段被返回权限层模型身份、会话角色、授权检查结果未授权调用被拦截、角色升级这五个层次合在一起才叫“审计方案”。如果只记了工具名和参数那是流水账真正的审计要能回答四个问题谁在什么时间通过什么会话调用了哪个工具、传了什么参数、结果如何、是否符合当时的权限范围。这里还牵出一个概念MCP 的“resource”和“tool”是有区别的。resource 是只读的数据资源比如文档片段、数据库视图tool 是可执行的操作。审计策略上resource 的访问可以相对宽松tool 的调用必须严格记录和审批。很多团队把这两类混着用出了事故去溯源时才发现关键的操作记录没存下来。3.3 Kymo 方案里我最有共鸣的一点审计做成中间层事件流当时分享里我最认可的设计是他们把审计从“旁路日志”升级成了“中间层事件流”——所有工具调用在到达 MCP Server 之前必须先经过一个审计网关。这个网关既负责采集事件也负责执行控制策略。这不是简单的记录而是一个双向闸门可以放行也可以拦截。这么设计的好处很明显。第一不需要侵入每个 MCP Server 的实现无论你接的是 PostgreSQL 工具、Figma 工具还是内部 RPA 平台都统一走同一个网关。第二审计能力可以独立升级策略调整不影响业务逻辑。第三这也为“控制面”留了后路将来要做危险操作拦截、限流甚至计费都在同一个位置实现。4. 实操从零搭一个 MCP 审计中间件4.1 设计选型优先采用网关模式而不是插件模式我自己在搭审计中间件的时候一开始想的是给每个 MCP Server 装插件。结果发现这条路很难走不同 MCP Server 的实现语言不同插件机制也不统一而且有些工具是商业 SaaS根本不允许你装插件。后来我切到了网关模式。架构上就是一条链Agent 端 → 审计网关 → MCP Server。Agent 配置 MCP Server 地址时指向网关提供的地址网关拿到请求后先记录事件再转发给真实的后端。这个模式最大的优点是审计能力集中、可统一升级且对上层 Agent 完全透明。缺点是多一跳网络开销但实测下来在局域网内延迟增加可以忽略不计比起它带来的控制力这点成本完全值得。4.2 关键代码与字段设计一个极简可跑的版本这里给出一个最小版本的审计事件结构用 Python 的 dataclass 实现。完整代码我放在项目里了下面这段是核心思路import hashlib import json import time import uuid from dataclasses import asdict, dataclass, field dataclass class AuditEvent: event_id: str field(default_factorylambda: uuid.uuid4().hex) ts: str field(default_factorylambda: time.strftime(%Y-%m-%dT%H:%M:%S%z)) trace_id: str session_id: str agent_name: str mcp_server: str tool_name: str args_hash: str args_preview: str result_preview: str latency_ms: int 0 status: str ok error: str def to_dict(self): return asdict(self)注意几个字段设计上的考虑。args_hash是入参的 SHA256 摘要目的是在不存完整参数的前提下保证某次调用确实发生过且参数未被篡改。args_preview和result_preview存储的是经过脱敏和截断后的预览文本默认只保留前 500 个字符。trace_id是串联整条调用链的关键一次用户会话中的多次工具调用都应该共享同一个 trace_id。拦截器的核心逻辑如下def wrap_tool_call(tool_name, args, mcp_server, call_fn, sink, trace_id, session_id, agent_namedefault): started time.time() evt AuditEvent( trace_idtrace_id, session_idsession_id, agent_nameagent_name, mcp_servermcp_server, tool_nametool_name, ) evt.args_hash hashlib.sha256( json.dumps(args, sort_keysTrue, ensure_asciiFalse).encode(utf-8) ).hexdigest() evt.args_preview json.dumps(args, ensure_asciiFalse)[:500] try: result call_fn(args) evt.result_preview json.dumps(result, ensure_asciiFalse)[:500] return result except Exception as e: evt.status error evt.error f{type(e).__name__}: {e} raise finally: evt.latency_ms int((time.time() - started) * 1000) sink(evt.to_dict())sink是一个写入函数可以是往 Kafka、PostgreSQL 或对象存储写一条记录。这样每次工具调用无论成功失败都会生成一条结构化的审计事件。我实际跑下来这套最小实现已经能覆盖调用层和参数层的大部分需求。生产环境还需要加上脱敏处理和更精细的拆分这里先不展开。4.3 存储与保留策略审计日志不是越满越好很多人觉得审计就是什么都存存得越多越安全。实际踩过坑之后你会发现存储策略设计不好审计系统比业务系统先崩溃。我的经验是把审计记录和业务日志分开存用独立的 topic 或数据表且做冷热分层。热数据保留 7 天用于线上排查和实时告警温数据保留 90 天用于安全事件回溯超过 90 天的归档到对象存储或廉价存储保留一年以上以备合规检查。这是“审计事件表”常见的字段结构可以直接抄event_id唯一事件编号trace_id调用链追踪 IDts事件时间注意带时区session_id、agent_name主体标识mcp_server、tool_name客体标识args_hash、args_preview入参摘要与哈希result_preview出参预览latency_ms、status、error执行结果信息token_estimate估算的上下文消耗关于 token 估算我用的粗方法是出参文本长度除以 4因为英文平均约 4 个字符一个 token中文更复杂一些。这个值不需要精确到个位能用来做成本归因和异常检测就够了。还有一个重点审计记录应该是只追加、不可篡改的。数据库层面做 INSERT ONLY 权限应用层禁止 UPDATE 和 DELETE这是最基本的要求。否则审计就失去意义了。5. 落地排查实录热词背后都是真坑5.1 Harness failed to load plugins / Web Boot 无法激活我接触过一些社区 harness 工具在内网部署时经常遇到“harness failed to load plugins”和“web boot: 1 entry did not activate”这类报错。这几个报错其实指向同一个问题插件加载失败后新能力没注册进去但进程启动了所以只在启动日志里留一行警告功能到了运行时才发现不对。排查思路我建议按顺序来第一步检查插件目录的权限和属主。很多内网环境用 root 跑进程插件目录却是普通用户创建的权限不对直接加载失败。第二步检查离线依赖。如果网络环境受限而插件需要从远端拉取某个依赖就会静默失败。第三步看配置文件里的 enable 标识和架构匹配。有些插件只支持特定 CPU 架构在 x86 和 ARM 混布的集群里容易翻车。处理手法也讲究别急着一次性开全部插件先在配置里逐个 disable确认单个插件能正常加载后再批量开启。每改一个配置观察一次日志不要贪多。我自己在这上面吃过亏以为多开几个插件能让系统更强结果问题交织在一起排查成本翻了好几倍。5.2 MCP 连接问题Codex 找不到 MCP、SSE 超时热词里有“codex 接入 figma mcp 怎么授权”和“codex 无法找到 mcp”这些都是我在接入过程中真实踩过的坑。MCP 接入最常见的问题是Agent 端明明配置了 MCP Server但工具列表里就是刷不出来。我的排查顺序是这样的第一确认 MCP Server 进程本身是活的直接访问它的健康检查接口或启动地址看返回是否正常。第二检查 Agent 端的 MCP 清单看配置的 server 名称和路径是否匹配。第三判断是不是认证问题比如 OAuth token 过期、redirect 回调地址不匹配。有一个小技巧很管用用 curl 直接调 MCP Server 的 endpoint绕开 Agent 框架看原始响应。如果 curl 通了而 Agent 里不通问题大概率在认证或传输配置而不是 Server 本身。这里就能看出来审计中间件的价值了。如果审计网关的记录里连接层事件显示握手失败发生在某个精确时间点同时伴随具体错误码排查效率会高很多。没有这层记录只能靠人肉复现场景非常痛苦。5.3 审计日志“参数过长”和循环调用我建的审计中间件上线没多久就遇到了日志系统拒收记录的问题。原因很简单有些工具调用把整段 SQL 和完整文件内容放进了参数我的args_preview截断到 500 字符压根没生效因为参数本身是嵌套的 JSONjson 序列化之后再截断前面的整个结构体占了太多字符后面的有效信息全被切没了。这个问题被迫让我重新设计了字段逻辑先做结构化的参数提取给出摘要字段再截断成预览。比如一个run_sql的调用审计事件里单独存db_name、table_name、condition这些提取字段原始 SQL 全文放到对象存储事件里只存对象存储的引用 ID。这样既保证可复核又不撑爆日志系统。另一个高发问题是循环调用。模型会在某些场景下反复调用同一个工具比如不断查当前时间、反复读同一个文件。这在审计数据里是非常明显的异常信号。我给网关加了一个简单规则同一个 trace_id 下同一个 tool_name 的调用次数超过 5 次就触发告警。实际跑下来这个规则帮我抓住了几个 Agent 因为任务描述不清而原地打转的案例。这类问题不动审计方案很难发现因为单看每一步调用都觉得“没毛病”。5.4 敏感信息脱敏的“漏网之鱼”脱敏是审计方案里最容易被低估的部分。一开始我写了个 Masker只按 key 名字脱敏比如password、token、api_key这些字段直接变***。结果很快发现真正的风险在业务参数里明明 key 叫query但里面带着身份证号或者 key 叫content里面是一段含手机号的文本。因这个原因我把脱敏策略改成了三类叠加第一按 key 名称脱敏覆盖显式敏感字段第二按正则匹配识别手机号、邮箱、身份证号这类固定格式的数据第三自定义字典匹配把企业内部标识符、项目代号也加进去。每一条审计记录落库之前都先过这三道筛子同时在预览文本里只保留对调查有用的必要字段。这里我特别想强调审计和隐私不是对立的。合理的脱敏不会破坏审计能力反而能让审计数据更安全地长期留存。不脱敏的审计日志本身就是一个巨大的数据安全隐患。等到日志被拖库了再去追责那已经晚了。6. 最后沉淀下来的几条判断6.1 构建 Harness 的最小可行路径研究完整轮之后我有一个很实际的观点如果你是中小团队不一定要从零造 harness。可以先从成熟的社区工程读代码入手比如把 Claude Code 的 harness 工作流拆开看一遍或者在 DeepSeek harness 这类项目上做二次开发理解它的 skill 机制、权限模型和回退逻辑之后再开始设计自研。如果你确实要自研记住三件事必须当一等公民放进架构而不是后补功能权限模型、审计记录、失败回退。这三个能力如果写在纸上不可能穿透到所有工具调用里只有把它们做成 harness 层的强制逻辑才有意义。补一句话harness 的代码评审应该比业务代码更严格因为它是所有 Agent 行为的总闸门。6.2 审计方案可以继续扩展的方向审计中间件一旦跑通很多能力都可以在这个底座上自然生长。比如把“只记录”升级成“控制面”对危险工具调用直接阻断把 token 成本和工具调用次数归因到具体会话和项目解决内部费用分摊的争议再往下走可以跟企业现有的 RBAC 权限体系打通每个 MCP Server 接入前先登记、再授权、后审计整个链路闭环。我还在探索的方向是“MCP 逆向”。调试外部 MCP Server 时用一个代理层把所有请求响应流量抓下来看协议交互的细节这在协议理解上非常高效同时也给审计网关的设计提供了很多真实素材。MCP 还有大量值得研究的细节但先把审计这件基本功打牢后面接再多的工具都不慌。这次把 Kymo 的分享和 MCP 审计方案重新研究一遍我自己最大的体感是Agent 工程化的分水岭不在模型选型而在你肯不肯给模型外面加一套可靠的笼头。审计不是业务上线之后补的“合规作业”或“附加题”而是前提。先把这条链路想清楚、跑通再让模型去碰真实工具你晚上才能睡得着。
返回列表