坐在云栖大会的专场里,Kymo 分享到后半段时,我原本只是在记一些 Agent 编排的要点,结果屏幕切到一张调用链路图,图上明确标出了“Harness 引擎”和“MCP 审计”两个模块,现场提问环节立刻有人举手问这两个东西是不是配套的。我当时也有同样的疑问。回来之后,我花了大半个周末把这两个方向串起来研究了一遍,顺手在内部系统里复现了一个最小可跑的版本。这篇文章就是那段时间的完整记录。
如果你正在做 AI Agent 应用,或者你们团队已经接了 MCP 工具但发现“调用黑盒化、权限收不住、出了问题查不到”,那这篇文章会比较有用。我会从 Kymo 分享里听到的设计思路出发,把 Harness 引擎的定位、MCP 审计方案的核心设计、以及我落地时踩过的坑全部展开讲,不绕弯子。
1. 云栖现场的启发:Kymo 到底在解决什么问题
1.1 MCP 生态越繁荣,Agent 失控越容易
先聊一个现象。MCP(Model Context Protocol,模型上下文协议)这一年多的扩散速度,比我预想中快得多。以前接一个工具能力要写插件、写适配层、对齐参数,现在只要对方实现了 MCP Server,客户端这边几乎是拿到即用。我随手列几个身边团队正在接的:设计稿对接用 Figma MCP、蓝湖 MCP,浏览器自动化有浏览器 MCP,低代码平台有 Dify 的 MCP 接入,数据库场景里连 IDE 插件都在做 Oracle 的 MCP 连接。往下走一点,调试器、逆向工具链里也出现了 MCP 桥接插件,甚至游戏引擎 Unreal 5.8 的工具链都在往 MCP 靠。
这带来的直接问题是:模型能碰的东西越来越多,但你真正“管得住”的东西越来越少。以前你给模型开放一个函数调用,至少要在代码里写清楚这个函数的入参出参、权限范围,出了问题能顺着代码往上查。MCP 把这件事简化成了一个“发现工具、声明参数、发起调用”的标准流程,服务端和客户端之间只通过 JSON-RPC 通信。好处是生态打通了,坏处是所有工具调用长一个样,你在调用层想做权限控制、参数校验、链路追踪,反而没了抓手。
Kymo 分享里那张链路图,就是在回答一个问题:当 Agent 可以调用几十个甚至上百个 MCP 工具时,怎么让每一次调用都可控、可审、可追溯。他把答案拆成了两层,一层负责执行管控,叫 Harness 引擎;一层负责事后留痕与分析,就是 MCP 审计方案。这两个东西不是二选一,而是一条链路的两端。
1.2 Harness 引擎和 MCP 审计方案的分工
Harness 这个英文词,本义是“马具、挽具”,引申过来就是“给 Agent 装上缰绳”。Kymo 团队说的 Harness 引擎,我理解下来并不是又一个 Agent 编排框架,而是一个插在“模型发起工具调用”和“实际执行工具调用”之间的控制层。它不负责 Agent 怎么思考、怎么规划,只负责工具这扇门怎么开、开给谁、开了之后留什么证据。
MCP 审计方案则完全不同。审计关注的是链路和证据,也就是每一次调用到底由哪一轮对话触发、发给了哪个 MCP Server、传了什么参数、返回了什么结果、耗时多少、最终成功还是失败。这些数据不是为了实时控制,而是为了事后回答“刚才是谁让模型去执行了那个操作”以及“如果出问题了,问题出在哪个环节”。
把这两层放在一起看,逻辑就很顺:Harness 引擎管“能做什么”,MCP 审计方案管“做了什么”。一个偏实时,一个偏离线;一个负责改行为,一个负责留证据。我在复现的时候是把他们当成一整套方案来设计的,下面逐步拆解。
提示:Kymo 分享里涉及的实现级细节其实不算多,很多设计思路我是在现场边听边记录的。这篇文章里的工程级补充,一半来自分享内容,一半来自我回来后在内部 Agent 平台上的复现实践。
2. Harness 引擎的技术拆解:它到底拦截了什么
2.1 Harness 不是 Agent 框架,而是一个“工具网关”
很多团队一听“引擎”两个字,下意识会问:能不能替换 LangChain、替换我们自己写的 Agent 编排?我在研究完之后得出的结论是:Harness 引擎的定位不是替换编排层,而是替编排层看门。
打个比方,Agent 编排层是快递分拨中心,负责根据面单决定包裹往哪走;Harness 引擎则是分拨中心出口处的安检通道。它不参与分拣逻辑,但每一件包裹离开发货区之前,必须从它这里过一遍。这个位置很关键,因为它天然卡住了所有工具调用的必经路径。只要 Agent 最终要发起 MCP 调用,请求就一定经过 Harness,不需要在每个 MCP Server 里单独植入逻辑。
把这个设计落地到工程上,一个典型的 Harness 引擎通常会包含这么几个能力:
- MCP 客户端管理:维护一组 MCP Server 的连接,负责工具发现和调用转发;
- 统一工具目录:把多个 Server 暴露的 tools 汇总成一个可检索、可筛选的目录;
- 策略引擎:在调用前评估“这次调用是否被允许”,策略可以是权限类、频率类、参数类;
- 审计事件生产者:把调用前、调用后、异常时的关键信息落成结构化事件。
我在内部系统里复现时,并没有把 Harness 设计成一个独立服务,而是做成了一个库加一个 sidecar 网关的组合形态。Agent 服务通过 SDK 接入 Harness,SDK 内部维护 MCP Client;同时审计事件异步上报给轻量网关,由网关负责落库。这样做的好处是,Agent 侧不用关心 HTTP 服务细节,而审计数据不占用 Agent 进程的资源。
2.2 MCP 客户端、工具发现与统一工具目录
要理解 Harness 为什么能统一拦截,得先搞清楚 MCP 的通信模型。MCP 协议里有三个核心角色:MCP Server 负责暴露工具,MCP Client 负责发起调用,人类或上层应用作为调用方。工具本身是用 JSON-Schema 描述参数的,按约定声明 name、description、inputSchema 等字段。传输层常见有三种:标准输入输出(stdio)、HTTP 长连接、SSE 流式传输。stdio 适合本地子进程场景,比如跑一个本地文件工具;HTTP 和 SSE 适合跨服务场景,比如接一个远程设计稿服务。
Harness 引擎在这里扮演的角色,是一个“多路 MCP Client”。它会同时连接若干个 MCP Server,启动时或周期性执行一次tools/list调用,把所有 Server 暴露的工具全部拉回来,合并进统一工具目录。目录里不只有工具名和参数模型,我还会额外维护三个元信息字段:来源 Server、调用协议、安全等级。
举个例子,这是我简化后的工具目录配置结构:
{ "version": "2025.06", "servers": [ { "name": "internal-filesystem", "transport": "stdio", "command": ["node", "fs-server.js"], "toolsPrefix": "fs", "securityLevel": "high" }, { "name": "vector-db", "transport": "http", "baseUrl": "http://localhost:8300/mcp", "toolsPrefix": "vector", "securityLevel": "medium" }, { "name": "external-search", "transport": "http", "baseUrl": "http://search-gateway:8320/mcp", "toolsPrefix": "web", "securityLevel": "low" } ] }为什么要在工具名前加前缀?因为多个 MCP Server 很可能暴露同名工具,比如search这个工具名在搜索服务和内部知识库里都可能存在。合并目录时不加前缀,Agent 看到的就是一个冲突且不含来源信息的工具集合,调用时容易发错目标。加上来源前缀之后,LLM 看到的工具名是vector_search、web_search,工具目录自带命名空间,调用路径天然清晰。这个细节我强烈建议大家在设计工具目录时提前考虑,否则线上会频繁出现“看起来调用了 search,结果是从错误的数据源返回的结果”。
2.3 调用链路上的钩子与策略点
工具目录建好之后,真正的核心是调用拦截。Harness 引擎在一个 MCP 调用请求被转发出去之前,会先经过一系列钩子。我把这些钩子按执行顺序分成三类:前置检查、调用转发、后置处理。
前置检查包括会话鉴权、工具级白名单、参数级校验、频控判断。这一步做的是“让不让过”的决定,如果决定是“不让过”,Harness 会直接向 Agent 返回一个拒绝原因,并生成一条拒绝审计事件。这里的要点是:拒绝要显式返回给 Agent,不能静默丢弃。否则模型会一直认为工具调用已成功,继而基于幻觉继续往下规划。
调用转发则相对直接。Harness 把原始请求通过 MCP Client 转发给对应 Server,等 Server 返回结果或异常。这一步真正容易出问题的地方在参数序列化。MCP 的输入参数本质上是一段自由 JSON,Agent 经常会生成多层嵌套结构,如果只是简单透传,有些 Server 会解析失败。我在转发前会做一步“参数清洗”,把明显不符合目标工具 JSON-Schema 的字段剥掉,并且把字符串形式的数组恢复成真正的数组,这能显著降低调用失败率。
后置处理是三个环节里最重要但最容易被忽略的。调用返回后,Harness 需要把结果做摘要化处理,再写审计事件。模型拿到的完整输出可能很大,但审计并不需要存全部内容,存摘要和关键字段就足够。同时,敏感字段必须在这里做脱敏,而不是等落库了再处理。这个顺序问题我放到后面单独讲。
下面是我在实际代码里使用的一段简化示例,用于展示一次 MCP 工具调用的拦截骨架:
async function handleToolCall(toolCall: ToolCall, session: Session) { const policy = await policyEngine.evaluate(session, toolCall); if (!policy.allowed) { await auditChannel.emit('tool_call_denied', { traceId: session.traceId, toolName: toolCall.name, reason: policy.reason, source: session.source, }); throw new ToolCallDeniedError(policy.reason); } const redactedParams = redactSensitiveFields(toolCall.arguments); const startedAt = Date.now(); await auditChannel.emit('tool_call_start', { traceId: session.traceId, toolName: toolCall.name, args: redactedParams, server: toolCall.server, }); try { const result = await mcpClient.callTool(toolCall.server, toolCall.name, toolCall.arguments); const redactedResult = redactSensitiveResponse(result); await auditChannel.emit('tool_call_end', { traceId: session.traceId, toolName: toolCall.name, resultSummary: summarize(redactedResult), durationMs: Date.now() - startedAt, status: 'success', }); return result; } catch (err) { await auditChannel.emit('tool_call_end', { traceId: session.traceId, toolName: toolCall.name, error: err.message, durationMs: Date.now() - startedAt, status: 'error', }); throw err; } }有朋友可能会问,既然已经有 MCP 协议标准,为什么还要自己写一个引擎层?直接在每个 Agent 进程里维护一个 MCP Client 不是更简单吗?如果团队只有一两个 Agent,确实没必要引入 Harness。但一旦 Agent 数量超过五六个,或者不同业务线各自维护一套 MCP 接入代码,你会发现审计格式不统一、权限策略各写各的、新接入一个 MCP Server 要改好几个服务。Harness 的价值就在这个规模节点上开始显现:它把“接 MCP”这件事收敛成了一处配置,把“管调用”收敛成了一处策略。
3. MCP 审计方案怎么设计才能落地
3.1 审计到底要审什么:三个维度
讲到审计方案,很多人第一反应是“记日志”。审计和日志确实是近亲,但需求维度完全不同。日志关注的是系统运行状态,审计关注的是“谁在什么时间通过什么方式做了什么操作”。落到 MCP 调用场景,我梳理成三个维度。
合规审计要回答的问题是:是否有权限依据。企业内部尤其是涉及数据权限的业务,每一次工具调用都要能对应到具体会话、具体用户、具体授权范围。比如一个内部账单查询工具,模型在用户对话中调用了它,合规审计就需要能追溯到触发这次调用的用户是谁、这个用户是否在该工具的白名单里。
安全审计要回答的问题是:是否有异常行为。典型场景包括高频调用、越权尝试、敏感参数外发。比如某个 Agent 在短时间内连续调用了大量外部搜索工具,或者工具参数里出现了类密钥字段,这些行为应该能被审计规则捕捉并触发告警。
链路追踪要回答的问题是:出问题时怎么排障。Agent 程序是非确定性的,同一个用户的同一个问题,两次运行的路径可能完全不同。如果某一个工具返回了错误结果,导致模型最终生成了错误答案,你需要在审计事件里把“哪一轮对话→哪个子任务→哪个工具调用→什么结果”整条链路拼出来。
这三个维度对应到审计事件字段上,我总结了下面这张表:
| 审计维度 | 核心关注点 | 典型字段示例 |
|---|---|---|
| 合规审计 | 谁、何时、依据什么权限调用 | userId、sessionId、policyId、permissionScope |
| 安全审计 | 越权、高频、敏感数据外发 | clientIp、toolName、argsHash、riskScore、blocked |
| 链路追踪 | 调用来源、父子关系、耗时 | traceId、spanId、parentSpanId、durationMs、status |
三个维度最好不要分开做三套方案,否则复杂度会成倍增加。我的做法是统一生成一种“宽表型”审计事件,字段里同时带上身份、策略、链路和结果四个组的信息。这样查询时可以按任意维度过滤,不需要维护多套数据结构。
3.2 审计事件模型与存储选型
审计事件的格式设计,是整套方案里最需要提前想清楚的环节。事件模型一旦确定,后期改字段成本很高,尤其是明细类数据基本没办法无痛迁移。我参考了 Kymo 分享里对调用可观测性的强调,结合自己的实践,最终固定成了下面这种 JSON 结构:
{ "schemaVersion": "1.0", "traceId": "d73f2a6c9e0b4f1a", "spanId": "f01b3c7a82d94e6b", "parentSpanId": "a9c44e7d12bf5e80", "sessionId": "chat-20250617-0082", "userId": "u_100234", "source": "customer-service-agent", "modelName": "qwen-plus", "toolCall": { "server": "internal-filesystem", "toolName": "fs_read_file", "argsHash": "sha256:5f9d7e...", "argsPreview": { "path": "/workspace/config/demo.txt", "encoding": "utf8" } }, "policy": { "allowed": true, "matchedRule": "allow_user_whitelist" }, "result": { "status": "success", "summary": "file_size=842, lines=39", "durationMs": 86 }, "time": "2025-06-17T14:33:21.708Z" }存储选型是另一个容易踩坑的地方。网上很多教程直接推荐 Elasticsearch,但实际用过会发现,审计数据写入量大、保留周期长、更新频率极低,用 ES 既贵又重。我更倾向于按数据冷热做分层。下面是我整理的一份选型对照表:
| 存储方案 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 本地文件 + 定时归档 | 最小原型、单机验证 | 零依赖,友好排障 | 不支持检索,无法支撑生产 |
| 关系型数据库 | 小规模、每日万次级调用 | 事务可靠,查询灵活 | 写入吞吐上限低,日志膨胀快 |
| ClickHouse | 中大规模审计分析 | 列式压缩,查询快,写入高 | 运维成本略高,不支持高频单行更新 |
| 对象存储 + 归档 | 合规审计、长期冷存 | 便宜,无限扩展 | 热查延迟高 |
我在内部环境最终选的是 ClickHouse 接热链路,对象存储做冷归档。审计事件先写入消息队列消峰,再由消费者批量写入 ClickHouse,超过 90 天的分区自动转存对象存储。这个方案的写入吞吐足够支撑每日千万级事件,查询一个 traceId 下的完整调用链可以在秒级返回,成本也远低于把全量审计丢给 ES。
3.3 协议限制、旁路方案与成本控制
MCP 审计有个绕不开的难点:你只能审计到 Harness 这一层能看到的东西。MCP 协议本身没有定义审计相关能力,工具的参数是自由 JSON,Server 渲染的结果也是五花八门。更麻烦的是,一个 MCP Server 内部可能还会再调用外部 API,比如一个搜索工具可能同时查询多个上游数据源,这些子调用对 Harness 来说是不可见的。
所以,设计审计方案时一定要认清边界。Harness 层审计的是“Agent 与 MCP Server 之间”的交互。如果审计目标是“全链路总体追踪”,那必须在 MCP Server 内部做埋点或旁路上报。最轻量的做法是让 MCP Server 在收到请求时往日志输出端打一条结构化日志,再由收集器统一采集。这个方案侵入性小,但对 Server 维护方有要求,他们需要愿意配合约定日志格式。
另一个现实问题是成本。全量审计事件很容易在流量高峰时膨胀成海量数据。我的经验是不要一刀切全量或全不录,而是按风险等级划分采样策略。高风险工具如删除类、写入类、外发类必须 100% 全量审计;中风险工具如文件读取、数据查询可以全量记录摘要但不记录参数明细;低风险工具如随机数生成、时间查询做动态采样,比如保留前 20% 的调用明细。这样可以控制存储成本,同时保证安全审计的核心覆盖。
4. 实操复盘:从零搭一个最小审计链路
4.1 组件清单与部署方式
理论讲多了容易飘,我直接把我复现的最小链路完整列出来。这套方案跑在我本地一台 8 核 16G 的 Linux 机器上,用 Docker Compose 编排,整体结构包含四个组件。
- Agent Demo:一个极简对话服务,负责把用户问题交给大模型,模型返回工具调用后提交给 Harness SDK;
- Harness 服务:核心控制层,维护 MCP Client、执行策略、生成审计事件;
- MCP 测试 Server:我用了两个本地 Server,一个模拟文件系统工具,一个模拟外部搜索工具,方便制造“调用成功”和“调用失败”两类场景;
- 审计存储:先用 MySQL 验证模型,确认字段没问题后换成 ClickHouse 跑压测。
组件之间的调用链是:Agent Demo 收到模型决定 → 调用 Harness SDK → SDK 走策略检查 → 转发 MCP Server → 返回结果 → 生成审计事件 → 写入审计存储。整个链路里 Harness 是唯一必经节点,这个定位在我复现过程中验证了一次又一次,只要请求不是刻意绕过 Harness,审计事件就是完整的。
4.2 核心代码路径:拦截一次 MCP 工具调用
代码路径的核心在上文已经给出,这里补充一个更完整的工程视角。我在 Harness SDK 里把处理逻辑拆成了四个阶段。
初始化阶段:读取配置、连接 MCP Server、拉取工具目录、构建策略索引。注意工具目录一定要做本地缓存,否则每次调用前都去远程 Server 执行tools/list,会额外增加 200ms 到 500ms 延迟。我实测在本地环境,缓存工具目录之后单次调用平均延迟下降了约 40%。
评估阶段:对一次工具调用执行三层检查。身份层看调用来源会话是否有效;工具层看该工具是否对当前用户开放;参数层看参数是否包含敏感字段或超出允许范围。三层任一不通过则拒绝调用并写事件,不进入转发。
转发阶段:调用 MCP Client 把请求发给目标 Server。这里要特别注意,MCP Client 不要每次新建连接,最好是连接池复用,否则工具密集调用时握手开销会拖垮吞吐。
收尾阶段:拿到结果后生成摘要、脱敏、封装审计事件,通过异步队列发送,避免阻塞主链路。我在本地实测,异步发送审计事件后,单次工具调用的 P99 延迟比同步写日志的方式降低了约 65%。
4.3 脱敏、白名单与权限控制的落地配置
审计方案里最容易出安全事故的地方,恰恰是审计本身。如果审计事件原样记录 Agent 传给工具的参数,那么模型在对话中提到的用户手机号、密钥片段、内部地址都会原样进入审计库,一旦审计库被读取,反而造成更严重的数据泄露。所以脱敏必须在 Harness 层强制生效,而不是依赖存储端的过滤逻辑。
我给 Harness 配了一份简单的脱敏规则:
{ "sensitive_patterns": [ "password", "token", "secret", "authorization", "api_key", "access_key", "phone_number", "(?<![A-Za-z0-9])[A-Za-z0-9]{32}(?![A-Za-z0-9])" ], "redact_mode": "replace", "placeholder": "***" }这份规则的意思是:字段名命中敏感关键词的,直接替换为占位符;正文里匹配到 32 位连续字母数字的类密钥内容,也替换为占位符。规则本身跑起来很简单,真正要留意的是不匹配的情况。比如一个工具参数是 JSON 字符串,敏感字段嵌套在内部,字段名匹配还要加上深度递归扫描,不能只看顶层。
权限控制方面,我给工具目录打上了安全等级标签,并配了一个最小白名单策略。安全等级为 high 的工具,用户必须属于显式白名单;等级为 medium 的工具,默认开放但要求会话来源是内部系统;等级为 low 的工具,对所有经过身份校验的会话开放。
工具级白名单之外,参数级控制也需要一套规则。比如文件读取工具允许读/workspace/project-a下的文件,但不允许读/etc/passwd。参数级校验不做的话,工具级白名单很容易被绕过——模型只要构造一个越界的路径参数,工具照样执行。我在 Harness 里对每个高敏工具配置了参数校验器,使用的是 JSON-Schema 的 pattern 和 enum 约束,不需要硬编码逻辑,配置驱动即可。
5. 常见问题与排查技巧实录
5.1 一张问题速查表
复现和研究过程中,我遇到了一堆实际问题。这里整理成一张速查表,按现象、可能原因、排查方法、处理建议四列给出,方便你直接对照排查。
| 现象 | 可能原因 | 排查方法 | 处理建议 |
|---|---|---|---|
| 工具调用一直超时 | MCP Server 单次执行耗时长,未做超时控制 | 查看 Server 端日志,对比耗时分布 | 在 Harness 层增加 timeout 配置,对长耗时工具做异步化 |
| 审计事件里敏感字段仍出现 | 脱敏发生在落库阶段,而不是 Harness 层 | 检查脱敏代码位置,确认在事件封装前执行 | 把脱敏逻辑前移到调用结束后立即执行 |
| 多个会话互相串线 | traceId 和 sessionId 映射关系没维护好 | 打开全链路日志,检查会话上下文传递 | 在会话初始化时固定 traceId,子任务统一继承 |
| MCP Server 返回 invalid schema | Agent 生成的参数与 OpenAPI 不一致 | 抓取请求原文,对比 JSON-Schema | 在转发前做参数清洗,丢弃多余字段 |
| 高频调用导致存储膨胀 | 全量审计没有做分级采样 | 查看单日审计事件总量 | 按风险等级分层采样,冷数据定时转存 |
| 某一个工具调用被莫名拒绝 | 参数级白名单冲突 | 查看策略引擎日志中的匹配规则 | 调整校验器的优先级顺序,加测试用例覆盖 |
5.2 几个我反复踩的坑
第一个坑是审计事件丢了溯源信息。最开始我只记录 toolName、参数和结果,没有记录是哪一轮对话的子任务触发的调用。结果等到实际排障时,看到一条异常调用记录完全没有上下文,根本不知道模型为什么会在那个时点调用这个工具。后来我在事件模型里强制要求带上 parentSpanId 和 sessionId,并且每次 Agent 子任务开始时都生成新的 spanId,再通过 parentSpanId 挂到总链路上,排障效率才真正提上来。
第二个坑是 MCP Server 的 transport 兼容。我有两个本地测试 Server,一个走 stdio、一个走 HTTP,最开始为了省事都统一用 HTTP 配置,结果 stdio 那个服务反复启动失败。后来确认是 MCP SDK 的初始化方式不同导致的,stdio transport 要在本地拉起子进程并管理生命周期,HTTP transport 则是建立远端连接,两者的连接池管理和超时配置完全不是一套逻辑。这提醒我,Harness 的配置里必须显式区分 transport 类型,不能默认所有 Server 都支持同一种协议。
第三个坑是审计事件写库阻塞了主链路。我最初实现审计写入时是同步写 MySQL,结果在调用频率高的时候,Agent 响应变慢,模型开始频繁报错。后来改为“先落本地缓冲,再异步批量上报”之后,问题才彻底解决。这是个很老生常谈的性能建议,但在审计场景下特别容易被忽略,因为审计往往被当成“边角料逻辑”,没有多少人会认真设计它的异步机制。
第四个坑是策略规则默认放行带来的“假安全感”。我一开始写的是白名单开白,默认放行所有未匹配策略的工具调用。结果是某个新接入的工具因为忘了配规则,直接对所有人开放。后来我把策略引擎的默认行为从“allow”改成了“deny”,凡是匹配不到明确规则的调用一律拒绝。这个改动虽然让前期接入成本变高了,但安全收益非常明显。默认拒绝应该成为 Harness 策略引擎的核心设计原则。
6. 研究完之后,我自己的几点体会
把 Kymo 的分享和这几天的复现连起来看,我最想通的一件事是:Harness 引擎本质上不是要把 Agent 关进笼子,而是给它在企业内部划一条可信跑道。Agent 的能力边界不是靠限制模型参数画出来的,而是靠工具调用前的策略判断画出来的。模型可以自由思考,但每一次动手都必须经过授权、记录、可追责,这套逻辑对于企业内部助手类的 Agent 几乎是刚需。
MCP 审计方案则是这条跑道的“黑匣子”。没有审计,权限控制做得再好也缺少事后纠偏的依据。我在实际使用中发现,审计数据最值钱的应用场景反而不是“安全追责”,而是反哺策略和模型——当你把被拒绝的调用、异常的参数、超时的工具统计到一起,就能看到 Agent 在真实场景里到底渴望哪些能力、哪个工具的 Schema 设计得让模型困惑。这些信息对优化 Agent 应用的价值,比单纯查日志高得多。
最后再分享一个小建议:如果你们团队刚开始做 Agent,不要一上来就自研完整的 Harness 引擎和审计平台。先找一个统一的调用入口,哪怕只是一个很薄的中间层,把 MCP Client 集中到一个服务里,审计事件先打本地日志文件。等调用量上来、策略需求变复杂了,再逐步往 ClickHouse、策略引擎、默认拒绝那套方向演进。我这次复现就是从最小链路起步的,最终跑通之后,反而觉得“少即是多”这套思路在 Agent 基础设施里同样适用。