我去年参与某头部券商的智能询价系统改造时,合规总监看到 Multi-Agent 架构图,第一句话就问:“如果 Agent A 判断客户风险等级为激进,Agent B 基于这个判断推荐了高杠杆产品,最后客户亏损投诉,责任算谁的?”这一问直接把我从技术乐观里拽了出来。金融行业的多智能体系统,和互联网推荐系统有个本质区别:它不是单纯的技术系统,而是一套需要承担法律责任、接受监管穿透式检查的履职记录系统。后来我们重新梳理设计原则,把合规性设计前置到架构阶段,才真正把项目推进下去。这篇文章把我踩过的坑、验证过有效的东西整理出来,供正在做同类项目的同行参考。
1. 为什么监管对多智能体格外警惕:三条红线的真实业务含义
Multi-Agent 在金融行业不是概念,已经有不少落地场景。比如智能投顾里,一个客户画像 Agent 读取交易流水,一个风险偏好 Agent 做评估,一个产品匹配 Agent 生成推荐;再比如合规审查里,一个事件抽取 Agent 解析公告,一个规则引擎 Agent 判断是否触发披露义务。这些场景的共同点是:多个自主决策单元通过消息传递协作,最终形成一个业务决策。问题也恰恰出在这里。
1.1 单模型时代能说清的事,多智能体时代说不清了
过去用单个模型做风控,还能用 SHAP、LIME 这类工具做特征归因,解释“为什么拒绝了这笔交易”。换成 Multi-Agent 架构后,决策是多个智能体串行协作产生的:A 先做客户分群,B 再基于分群结果做额度测算,C 又参考额度做产品匹配。任何一个中间结果出错,都会顺着链路传导到最终决策上。监管来检查时,问的不是“你的模型准确率多少”,而是“你给我一条完整的、能回溯的决策链路”。
1.2 红线一:可解释性——决策链路不能被当成黑盒
很多团队觉得“加了日志”就算可解释,其实差了很远。可解释性在监管语境下指的是:任何一个业务决策,都能回答“谁做的、基于什么数据、用了什么规则、为什么在这个时间点这么做”四个问题。尤其是当两个 Agent 的结论互相冲突时,必须有冲突消解记录。我们当时就遇到过一个场景:事件抽取 Agent 把某上市公司的公告判定为“重大合同”,但规则引擎 Agent 认为金额未达到披露阈值,两个 Agent 各自输出了结论,最后系统取了一个默认值。后来被内部审计质疑:这个默认值是谁定的?当时的决策依据是什么?翻日志发现根本没记录冲突过程,只有最终输出。
1.3 红线二:数据边界——Agent 之间的通信也是数据传输
这一点最容易被技术团队忽视。很多系统对数据库层做了严格的权限控制,但 Agent 之间通过消息队列传输数据时,完全没做字段级管控。比如客户画像 Agent 在处理完请求后,习惯性地把客户全名、身份证号、手机号都塞进消息体,传给下游的风险评估 Agent,但下游其实只需要客户ID和风险标签。这在监管眼里就是一次越权数据共享,违反了数据最小化原则。更麻烦的是,这种消息通常是 JSON 格式、存在消息中间件里,等发现问题时可能已经留存了好几个月的原始报文。
1.4 红线三:责任归属——出事后必须找到“具名责任人”
多智能体系统天然有“责任扩散”问题。A 说是 B 的错误输入导致它误判,B 说是 C 的数据质量问题,C 说模型训练阶段就存在偏差。监管需要一个明确的说法:到底是哪个 Agent、在哪个时间点、基于哪份数据、做出了哪个决策。这意味着不能只有系统级日志,必须有 Agent 级的决策留痕,并且每条决策都要有关系型的主键,能串联出完整的调用链。我们在设计阶段就把“责任归属”作为核心非功能性需求,后面省了很多事。
2. 从架构上先堵住权限漏洞:角色定义、通信审计与数据域隔离
合规性设计不是后面补的,必须在系统架构图上就能看出来。我建议的顺序是:先做角色定义,再做通信审计,最后做数据隔离。前三件事没想清楚,直接写代码,后面基本都要返工。
2.1 Agent 角色表怎么建模才能让合规看得懂
每个 Agent 都应该对应一个明确的业务角色,角色表至少包含五个字段:Agent标识、业务角色名称、可访问数据域、允许的操作类型、最大调用频次。注意,允许的操作类型必须精确到增删改查,不能笼统地写“读写”。我们当时就给产品推荐 Agent 只配了“读”权限,客户画像 Agent 配了“读客户基础信息和交易流水”权限,两个 Agent 之间的互相调用也单独列了一行权限记录。
CREATE TABLE agent_role ( agent_id VARCHAR(64) PRIMARY KEY, business_role VARCHAR(64) NOT NULL, data_domain VARCHAR(128) NOT NULL, allowed_actions VARCHAR(32) NOT NULL, max_calls_per_minute INT DEFAULT 100, created_by VARCHAR(64), updated_at TIMESTAMP );这里有个容易踩的坑:角色表本身是静态的,但金融业务里的 Agent 权限经常要动态调整。比如市场波动加剧时,合规部门要求风控 Agent 可以临时读取更多维度的数据,但平时不行。这种动态权限如果直接改数据库里的角色表,审计查起来非常乱。建议把“静态角色基线”和“临时授权记录”分开存两张表,临时授权必须带有效期和审批人。
2.2 通信链路上的强制审计点应该怎么埋
所有 Agent 之间的消息必须经过统一网关,网关做四件事:检查发送方是否有该消息类型的发送权限,检查接收方是否有接收权限,检查消息体里是否有超出契约的字段,检查消息头是否携带 RequestID 和 AgentID。四项检查全部通过才放行,任何一项不通过就直接拦截并触发告警。
网关是强制审计点,不能允许某个 Agent 绕过它直接访问另一个 Agent 的接口。我们当时在框架层做了限制,所有 Agent 之间的调用只能走内部服务总线,不能直连 RabbitMQ 或 Kafka 的 topic。刚开始程序员会觉得麻烦,但合规审计时这条链路能省掉太多解释成本。
2.3 数据最小化不是口号,要靠行级安全和动态脱敏落地
数据最小化在实操层面有两个手段:一个是用数据库视图隔离敏感字段,另一个是行级安全策略。比如客户表里有手机号字段和风险等级字段,风险评估 Agent 只需要风险等级,那它的查询账号就应该映射到一个不包含手机号的视图,而不是直接把整张客户表授权给它。
动态脱敏也要考虑在 Agent 间消息传递的环节做一次。我们的做法是:消息网关里配置了一个脱敏规则引擎,识别消息体中符合身份证号、手机号、银行卡号正则的字段,自动替换成掩码或哈希值。这个处理发生在消息发送方提交后、接收方消费前,用中间层来做,其实性能开销很小。
def mask_sensitive_fields(payload: dict) -> dict: masked = {} for key, value in payload.items(): if key in ("phone", "id_card", "bank_account"): if isinstance(value, str) and len(value) >= 8: masked[key] = value[:3] + "*" * 4 + value[-4:] else: masked[key] = "***" else: masked[key] = value return masked3. 可解释性不是空话:决策留痕、推理摘要与调阅输出的三层落地
可解释性要落地,只靠“写日志”三个字解决不了。我建议拆成三层:底层是结构化决策留痕,中间层是为业务人员准备的推理摘要,最上层是面向监管调阅的格式化输出。三层各管各的事,缺一不可。
3.1 决策留痕到底要留哪些字段,少一个都不行
决策留痕的表结构,应该包含 AgentID、节点ID、输入数据的哈希值、输出结果、调用模型的版本号、关键特征的权重值、规则引擎的规则版本号、耗时、业务请求号。其中输入数据的哈希值很有讲究,因为数据库里原始数据可能被更新,如果只记录“输入了 customer_id=1024”,后面用户修改了风险等级,你再回溯时看到的就是新值。记录哈希值,才能证明“当时用的数据是那个版本”。
我们用的方案是:每个 Agent 在处理请求时,先把入参序列化成规范格式,计算 SHA-256,和业务请求号一起写入日志。日志表放在独立的日志库里,业务系统只能写入、不能修改,连开发人员都只给只读权限。这个“不可篡改”很重要,监管调阅时会抽查是否有早期数据被偷偷改动的痕迹。
3.2 推理摘要不是日志压缩,而是面向业务人员的决策说明
原始日志是给机器看的,几千行 JSON 让业务人员去读,他根本看不完也没耐心看。所以中间层要生成推理摘要,至少包含四块内容:操作意图、关键决策因子、模型版本、人工复核状态。比如“评估客户授信额度,关键决策因子为月均收入、历史逾期次数、当前负债率,模型版本 v2.3.1,需人工复核”。
推理摘要可以由规则模板生成,不需要引入额外的生成式模型。这一点我在项目里特别坚持:摘要生成本身也要可解释,如果用大模型去总结,等于又引入了一个新的不可控因素。模板化的缺点是句子枯燥,但胜在稳定、可审计。
3.3 面向监管调阅的格式化输出
监管来调阅的时候,给出一堆日志文件会被打回票,必须按统一的格式整理好。我们当时的做法是,做一个“合规调阅接口”,输入业务请求号,输出一个 PDF 或者标准 Excel:第一页是决策概要,第二页是决策链路图,第三页是全部 Agent 的留痕记录,第四页是冲突消解记录。
这个接口要在开发阶段就做好,否则每逢检查就要临时写脚本去扒日志,既慢又容易漏。格式上注意:时间戳统一用 ISO 8601 格式带时区,模型版本号要能和模型仓库里的版本一一对应,规则版本号也要能追溯。
4. 模型上线前的合规闸门:备案清单、极端场景验证和紧急熔断
模型上线不是写完就部署,在金融行业里至少要过三重门:模型备案、极端场景验证、人工熔断机制。这三件事很多开发团队会觉得是“流程负担”,但每一种能救你一次。
4.1 模型备案清单要包括哪些内容
每个模型(包括规则引擎)上线前要填一份备案清单,至少包含:模型功能描述、训练数据来源、数据敏感等级、算法类型、可解释性方法、性能指标、回测区间、负责团队、评审人。这份清单不用写得多花哨,但要真实。
备案清单的价值在于,它能倒逼团队理清“我的模型到底是怎么工作的”。我们有一个很实际的教训:某模块的规则引擎里埋了好几条老逻辑,文档早丢了,后来审计问起“折算率为什么是 5 倍”,从开发到产品没有一个人能说清。最后查 git 历史才知道是三年前某个临时需求加的。所以我现在强烈建议,备案清单必须和代码仓库绑定,每次改模型阈值都要连带更新备案表。
4.2 验证集设计要覆盖监管关注的极端场景
常规模型验证会看 AUC、KS、准确率,但合规性验证要看的是极端场景。比如投顾系统里,当市场指数暴跌 10% 时,产品匹配 Agent 会不会推荐高杠杆产品?当某个客户一个月内频繁修改风险测评答案,客户画像 Agent 是否会陷入循环?当上游 Agent 返回空值时,下游 Agent 是能正常兜底还是会崩溃?
这些极端场景必须写进验证用例,而且最好按风险等级分类。我列的用例有:系统超时、上游数据缺失、上游数据格式异常、规则引擎版本回滚、模型返回极低置信度、请求量突增十倍。每一条都要有预期的处理行为描述,比如“上游数据缺失时,下游 Agent 放弃本次决策并标记为系统异常,不允许使用默认值继续输出”。
4.3 紧急熔断:不是让系统停机,而是强制切换到人工
多智能体系统一定要有熔断机制,但熔断不是关掉整个系统,而是触发后把决策从 Agent 自动切换为人工审批。我做过的项目里,熔断条件有三类:连续 10 次决策存在异常标记、监控指标超过预设风险阈值、监管发文要求停止相关业务。熔断触发后,编排引擎不再调用 Agent 链路,而是把所有待处理请求转到一个合规审批队列,由人工出结果。
这里有一个细节:熔断切换必须设计成可回退的。市场在变,监管要求也会变,系统不能熔断后就永远只走人工。我们的做法是,熔断记录里带上“可恢复条件”,比如连续 24 小时监控指标恢复正常,可以由合规官手动解除熔断;解除动作也要留痕,因为监管要看到是谁在什么时间解除的、当时的业务状况是什么。
5. 数据安全与隐私保护:脱敏、加密、溯源三个层次的抉择与取舍
数据安全这个话题文章很多,但在 Multi-Agent 场景里,难点在于“数据会在多个 Agent 之间流动”,保护维度比单体应用多很多。我把方案拆成三个层次:脱敏解决“能不能看”的问题,加密解决“泄露了能不能用”的问题,溯源解决“出了问题能不能找回来”的问题。
5.1 脱敏粒度:按数据域选方案,不要一把梭
脱敏方案不能一套走天下。不同数据有不同的敏感度,处理方式也应该不同。我们在项目里把数据分成四类:
| 数据类别 | 示例 | 脱敏策略 | 场景 |
|---|---|---|---|
| 身份标识类 | 姓名、身份证号、手机号 | 前端加掩码,存储用哈希 | 客户画像、KYC |
| 财务交易类 | 交易金额、账户余额 | 金额字段加密存储,查询时动态解密并加审计记录 | 风控、授信 |
| 行为偏好类 | 浏览记录、风险选项 | 保留聚合特征,删除明细 | 客户分群 |
| 策略规则类 | 风控阈值、模型权重 | 不脱敏,但限制访问 IP 和账号 | 模型管理 |
脱敏有副作用,最典型的是“脱敏后下游 Agent 算不准”。某次我们把客户年龄做了分段处理,结果风险评估 Agent 的分数明显失真,后来改为“年龄区间加真实年份组合”的方案,既保证了隐私又保住了模型效果。这个取舍过程一定要让算法团队参与进来,不要只有安全团队拍脑袋。
5.2 Agent 间消息加密的工程取舍
Agent 间通信要不要做全文加密?从安全角度当然要,但从性能角度,每个消息都做非对称加密的成本太高。我建议做混合加密:消息体用 AES 对称加密,对称密钥通过公钥加密传输,这样既能保证保密性,又不会让性能差到不可接受。
不过加密不能解决所有问题。业务链路上的审计日志、监控指标、告警信息这些元数据,如果不加保护,同样会泄露数据模式。我们有一个教训:只加密了消息体,但消息头的 topic 名是明文,观察一段时间就可以推测出某个群组正在集中处理某类高价值客户。后来把所有消息头的业务关键字也做了哈希。
5.3 全链路溯源:数据血缘打通 Agent 与数据库
溯源要回答的问题是:“这个 Agent 输出里的一项指标,源头是哪个系统的哪张表、通过什么加工逻辑、经过了哪几个 Agent。”这需要数据血缘平台和 Agent 编排平台的配合。我们的做法是,在每个 Agent 输出结果时,顺带写出“血缘标签”,例如customer_info.customer_table.risk_score -> agent_risk_eval.v2.3.1 -> agent_product_match.4.1.0。
血缘标签要存到独立图数据库里,支持按业务请求号反查。这样一旦监管问“客户 A 的授信额度为什么被调降”,你可以直接从最终决策节点一路往回追,找到变更阈值的那条规则是哪个版本、哪个人审的。没有血缘,就只能靠人肉翻日志,审计效率极低。
6. 我在某金融机构落地时的踩坑记录与最终检查清单
前面讲了很多理论,最后分享几个真实踩坑和一张检查清单。这些细节文档里基本不会写,但往往决定项目能否过得了内部合规评审。
6.1 踩坑一:只做了角色权限,忘了数据域的行列级隔离
项目上线前,安全团队做渗透测试,发现画像 Agent 的账号可以通过“AND”拼接 SQL 条件,拉出非授权客户的数据。角色权限虽然说“只能看客户基础表”,但表里的所有客户数据它都能看,这明显越界。后来我们在数据库层加了行级安全策略,根据 Agent 所服务的产品线过滤数据,同时把动态脱敏规则集成到查询入口。如果先做这一步,就不会被渗透报告打回。
6.2 踩坑二:日志和 Agent 代码不在同一个版本库
某个版本的规则引擎修了一个 bug,但日志里的规则版本号忘了更新,还是显示 v1.0.0。审计时发现日志显示 v1.0.0,代码仓库里却已经没有这个版本的实现,直接无法回溯。后来我们构建流程里加了强制校验,模型版本号和代码仓库 git commit 一一对应,日志里记录的版本号如果没有对应 tag,系统就拒绝启动该 Agent。虽然略麻烦,但审计零瑕疵。
6.3 落在纸上的最终合规检查清单
我在项目结束时给团队留了一份自查清单,在这里也分享出来:
- 每个 Agent 是否都有明确的业务角色和操作边界,且经过合规部门确认?
- Agent 间通信是否全部经过统一网关,网关检查项是否包含发送权限、接收权限、字段越权?
- 每条业务决策是否能串联出完整的 Agent 调用链,并关联到业务请求号?
- 模型和规则引擎是否有备案清单,版本号是否和代码仓库强制绑定?
- 极端场景验证用例是否有覆盖数据缺失、上游超时、质量回滚?
- 熔断机制是否能在触发后自动切换到人工审批,且熔断/解除操作全部留痕?
- 数据脱敏是否按数据域分类设计了多套策略,而不是一套走天下?
- 数据血缘标签是否存储到独立图数据库,支持按请求号反查?
- 是否存在绕过网关直连消息中间件的接口,如果有,是否已经关闭?
这九项检查清单,每一项看起来都不难,但全部通过的项目其实不多。很多团队是在监管检查前一周才意识到某些项没做,开始疯狂补文档。与其那样,不如在架构阶段就把每一项当成设计约束。
最后再分享一个实际心得:做金融行业 Multi-Agent 合规性设计,最怕的不是技术难度,而是技术团队和合规团队各说各话。技术堆栈里的“日志”在合规语境里叫“留痕”,技术讲的“模型调参”在合规语境里叫“模型变更管理”。两个团队能对着同一张流程图把诉求对齐,项目基本就成了一半。我在项目里最有效的一个动作,是把合规总监请到架构评审会上,让他看着角色表和通信链路图一条条过。他发现自己的问题能被清晰地映射到具体代码模块,态度明显从“提意见”变成了“共同设计”,后面的沟通顺畅了很多。这比任何技术方案都更能省时间。