新闻案例 / NEWS · 案例复盘
Taxbit合规系统避坑手记:把身份焊死在闭包里,AI永远别碰权限
Taxbit在Bedrock AgentCore上搭建角色感知合规问答的工程复盘:靠闭包把用户身份锁在模型视野之外,语料从400多地址砍到70个,切片补头并由代码硬判引用,单次对话模型成本压在20美分以内。
把合规问答系统做进金融和数字资产业务,最容易走的一条弯路是:先搭一个大模型加通用向量检索,再靠系统提示词反复叮嘱「千万别把管理员页面推给普通用户」、「千万别给税务建议」。在真实的采购合规评审和恶意提示注入面前,这条路根本走不通。
Taxbit 在 Amazon Bedrock AgentCore 上落地的这套角色感知合规问答助手(Taxbit Assist),给出的工程解法极其干脆:把身份直接焊死在可信闭包里,让模型根本没有能力表达「以别人身份执行」这件事。从 2026 年 5 月起在美国和欧盟两地生产运行,单次对话的模型成本压在二十美分以内。
01
客户问一句话,真正卡在解释层
这套系统的做法写在 AWS ML Blog 2026 年 11 月的一篇客户案例里,产品叫 Taxbit Assist。Taxbit 成立于 2018 年,做的是数字资产的税务与会计合规。它的客户是交易所、支付服务商和券商,这些机构用它算损益、收集税务文档、批量申报信息表。业务本身高度规整,麻烦出在解释层:要熟练使用这个仪表盘所需的知识,散落在公开官网、接口文档和帮助中心三处,还有相当一部分从来没被完整写下来过。
一个客户问「这张表的状态是什么意思」,他面前只有两条路:自己翻,或者提工单。前者通常翻不到,后者把问题转给人工支持团队。这类问题单个看都不难,但量很大,而且每一条都在消耗同一个稀缺资源——熟悉这套系统的支持人员的时间。
这个窗口要回答的问题类型很特别。它不问「加密货币怎么报税」,也不问「我该选哪种计价方法」。它问的是产品怎么运转:这个按钮做什么、这张表为什么是待处理、我这个角色能不能走完这个流程、这份文档还差什么材料才能提交。答案都存在于产品自身,只是没有被组织成机器能稳定读取的样子。
这一点决定了后面所有的架构设计。如果问题是开放的税务咨询,系统第一件事该是拒答;如果问题是产品机制,那它需要的是能精确指回原文的检索,以及一条判断「这个人有没有权限看这一页」的确定性链路。Taxbit 选的是后者,而这个选择又把四条约束一次性摆上了桌面。
如果问题是开放的税务咨询,系统第一件事该是拒答;如果问题是产品机制,需要的则是能精确指回原文的检索,以及一条判断「这个人有没有权限看这一页」的确定性链路。
四条约束是原文自己列的,每一条都排除了通用聊天机器人的一种常见做法:答案必须可溯源,用户要做申报决策,每个答案得能回到一个他能打开、能读懂的来源,不知道就得说不知道;答案必须区分角色与组织,不能把一个只有管理员能开的页面推给普通支持用户;答案不能构成税务建议,这条线的宽度比外人想的窄得多;同一套代码要支撑不同区域的数据驻留。
其中最难的是第三条。原文把界线画得很清楚:解释 Taxbit 怎么计算成本基础(也就是那个用来算盈亏的会计方法),这是产品文档;告诉用户该选哪种计价方法,这是税务建议。两句话长得几乎一样,性质完全不同。一条主题过滤规则拦不住这种区别,这个问题最后是用一个模型分类器解的。
第四条约束听起来是基础设施问题,实际决定了模型怎么调。区域驻留意味着推理请求必须在指定地理范围内完成,而部署代码只有一套。原文的做法是让推理配置文件的标识带上地理前缀,从部署区域推导出来,欧盟的部署就在欧盟内推理,美国的留在美国。
还有一层不那么技术、但同样影响架构的现实约束:团队规模小。他们没有自建会话状态存储、自己处理会话隔离,而是选了托管的 Agent 运行时。原文给的理由是三条:托管运行时省掉基础设施运维,内置的会话记忆省掉一个独立的会话状态库,按会话隔离让每个用户的上下文待在各自的范围里。省下来的力气全部投进了检索与安全。
02
四条硬约束把通用聊天机器人排除了
把上面四条并排看,会发现它们否掉的是同一个思路:即给一个大模型配一个通用检索,让它自由发挥。可溯源要求检索结果本身就带出处;角色感知要求检索之前先知道这个人是谁;非税务建议要求对输出再做一次性质判断;数据驻留要求整条链路在指定地理范围内闭环。四件事没有一件能靠提示词解决。
先说可溯源这条。它不只是「回答里带上链接」,而是「链接必须打开得了」。这两者之间有个很实际的差别:如果知识库里混进了内部生成的参考材料,模型完全可以忠实地引用它,但用户点不开。所以原文后来把「能不能引用」做成了一条按来源类型在代码里判断的规则,只有三类已发布来源可以产出链接。这条规则把一个语义问题变成了一个枚举问题。
再说角色感知。通用做法是在系统提示词里叮嘱「注意用户权限」,然后寄望模型自觉。原文没有走这条路,它把权限校验做成了两个独立的工具,一个查组织权限,一个查面板角色,模型在推荐任何流程之前必须先调用它们。这样做的效果是,权限从「模型应该记得的事」变成了「模型必须调用的代码」,而且调用记录留下了可审计证据。
数据驻留这条相对干净,但它的实现方式暴露了一个通用问题。如果部署代码只有一套,而部署范围有两个,那就不能让环境相关的值散落在代码各处的判断里。把一个地理前缀做成配置推导出来的常量,比在每个调用点写条件分支可靠得多。自带密钥的账户里,会话历史和其他存着客户数据的存储,用客户自己的 KMS 密钥加密;外部集成的凭据放在 AWS Secrets Manager 里运行时才动态获取,绝不打进镜像也不写进配置。
四条约束之外,原文还列了一组资质,这部分容易被当成合规装饰跳过去,但它其实解释了这套系统为什么敢给受监管金融客户用:整个助手按 ISO/IEC 42001(人工智能管理体系国际标准)开发和运行,机构本身持 ISO/IEC 27001、ISO/IEC 27701 和 SOC 2 Type II 报告。同时明确两条底线:不训练、不微调任何模型,平台也不拿提示词与补全去训练外部模型。对受监管机构来说,「承诺不拿数据训练」比「吹嘘模型多聪明」容易通过法务评审得多。
03
从一个问题到一次回答的六步路径
系统的形态比想象中简单。它是一个基于 Strands Agents SDK 的应用,跑在托管的 Agent 运行时上,以 ARM64 容器镜像的形式从镜像仓库拉取、按需弹性伸缩。用一句话概括这个 Agent:一段系统提示词,一个模型,八个工具。
八个工具的分工极其克制:一个是知识库检索(主工具);两个是访问校验,分别查组织权限和用户的仪表盘角色;四个是工单操作,负责建、列、改、关,而且只针对调用者自己的工单;最后一个是当前时间,用于极少数需要日内精度的提问。没有直接数据库查询,没有报表导出,没有批处理触发。
这里有个容易被忽略的核心边界:在这一版里,Agent 对客户的真实税务数据没有读权限。它回答的是产品知识,唯一的写操作是建自己的支持工单。原文明确说,下一步才是让它去查客户自己的申报数据。这个边界极其关键,它是整套权限模型能成立的现实前提。
六步流水线中最关键的一处位置设计:权限校验必须发生在「检索之后、推荐之前」。如果放最前面,每次提问都白付一次权限查询开销;如果放模型里,模型易受骗越权。
一个请求走六步:
1. 用户在仪表盘里提问,流式对话接口把问题连同已验证的身份(邮箱、组织 ID、面板角色)一起转给运行时;
2. 运行时用 Amazon Bedrock Guardrails 护栏过一遍消息,个人身份信息(PII)就地脱敏,如果被拦截,模型根本不会被调用;
3. 检索与重排:Agent 从 Bedrock Knowledge Bases(底层为 S3 Vectors)取 75 个候选段落,服务端再由 Cohere Rerank 重排取前 12 个;
4. 模型推理前权限校验:模型在重排后的段落上推理,但在推荐任何操作流程之前,必须先调用校验工具核对权限;
5. 流式返回:答案带着来源链接回到仪表盘,会话记忆负责跨轮次延续;
6. 链路追踪:每个请求都发一份 OpenTelemetry 轨迹到 CloudWatch,喂给后续评测管线。
六步里有一步的位置最容易被摆错:权限校验发生在检索之后、推荐之前。放在检索之后,是因为「该检索什么文档」不依赖用户角色;放在推荐之前,是因为「能向用户推荐什么操作」完全依赖角色。如果把它放在最前面,每次普通问答都要先付一次权限查询的成本,而大多数提问根本走不到推荐那一步。
工具做得少还有一个巨大的显性好处:权限审计面极小。八个工具意味着审计一份工具清单只需要看八个入口。入口少,每条权限边界都还能在代码审查里说得清;工具膨胀到几十个之后,权限交叉漏洞就很难管住了。
04
四个决策:身份焊死、语料砍掉八成、切片补头、代码判引用
这四个决策是全篇工程落地含金量最高的部分:
第一个决策:身份不进模型,焊在闭包里。用户邮箱、组织 ID 和角色由可信的调用服务传入,在模型看到之前就通过闭包绑定进每个工具。模型可以决定要不要查权限,但不能决定查谁的权限,因为身份根本不是它可以设置的参数。用户输入在边界处被净化,一次恶意的提示词注入哪怕成功攻陷了模型上下文,也无法伪造那行可信的身份数据。
这个决策的代价是灵活性。如果哪天要支持「代理授权」(让 A 替 B 操作),身份就不能再是一个随请求绑死的常量,闭包这层抽象就得重新设计。但带来的收益是模型在物理上没有能力表达「以别人身份执行」,因为它手里根本没有那个输入槽。
第二个决策:把语料砍掉 80% 以上。站点地图有 400 多个地址,但最终只有大约 70 个被放进回答语料。光是 /blogs/ 目录就占了 300 多个营销页。这些页面本身内容没问题,但它们会和严谨的帮助中心竞争,在向量匹配时争当合规问题的被引来源。
原文把「剔除营销页」称为收益最高的检索改动之一,而且它完全不需要动模型。团队通常会把「回答不准」本能地归因到模型不行或提示词没写好,于是陷入反复调提示词的内耗。但如果语料候选池里混着 300 多页散文营销稿,再厉害的模型也只能在错误的候选池里挑垃圾。
第三个决策:给切片补头,解决 28% 静默截断。他们选择语义切片按自然段落切分,但在税务申报领域,从中段取出的一段话往往会丢掉「这份文档在讲哪国政策」的前置范围信息。一段只适用于欧洲制度的说明,如果被召回到美国 1099 表的问题上,不仅无用还会造成合规事故。
他们的解法是在切片之后运行一个后处理钩子(POST_CHUNKING),给每个切片强行前置该文档的一级标题和导语范围段(前缀约 128 个 tokens,标题 80 字符、范围 300 字符)。由于嵌入模型 Cohere Embed English v3 单次上限为 512 tokens,他们将切片正文硬性压到 384 tokens 以内,两者合并恰好满配 512。
这一设计源于一次生产实测探测:团队发现有约 28% 的切片原本落在 385 到 512 tokens 区间,如果没有正文上限控制,它们的尾部会被嵌入模型静默截断,而且不会有任何报错警告!监控看到的一切都是成功的,但模型拿到的是被腰斩的残篇。补头之后,切片把适用范围一起带进了向量空间。
第四个决策:让代码硬判引用资格。只有三类已发布来源允许产出对外的链接;源码生成的参考和策展内容只用于知识接地(Grounding),但在代码层面绝对禁止产出引用。这条规则用硬编码写在服务逻辑里,而不是写在提示词里。因为如果写在提示词里,大模型在面临用户逼问压力时,会竭尽全力「捏造」一个打不开的 404 伪链接。
05
上线前改掉的设计,与四个问题加一项代价
在上线生产之前,团队踩过的深坑同样值得复盘:
1. 早期原型将身份当参数传:在最初的 PoC 版本中,模型负责把身份作为参数传给工具。上线前团队紧急推翻了这个设计,改为调用服务通过闭包注入。参数是模型能写的东西,闭包是模型碰不到的东西。前者指望大模型时刻守规矩,后者让「越权」在代码语法上无法表达。
2. 28% 切片的静默截断:没有任何错误日志,入库照常、检索照常、回答照常,但尾部关键条件全部丢失。这类静默失效只有深入到底层词元长度对账时才能查出来。
3. 营销页争当引用源:未收窄语料前,营销博客文章屡屡挤占权威合规文档的召回配额,造成回答虚浮。
4. 通用护栏误伤业务:默认的 PII 护栏把 1099 等报税表单编号误判为社保号(SSN)或路由号并直接拦截。团队被迫将表单类识别器调整为「只记录不拦截」,防止防线过严导致正常业务瘫痪。
5. 核心能力边界的取舍代价:当前版本明确不读客户的真实账目数据库,只做产品运转解释。客户最关心的「我还有哪笔款缺材料」暂时无法直接回答,这是为安全与小团队交付周期所付出的必要取舍。
06
如果重来:四条回头清单与落地思考
如果把整个项目推倒重来,Taxbit 架构师给出了四条最应该按顺序先做的事:
第一,先收窄语料,再调提示词。在把索引收窄到支持文档并给切片补头后,回答质量的提升幅度超过了过往所有提示词工程的总和。搞清语料边界永远排在提示词之前。
第二,拿自家生产流量测护栏强度。通用护栏的开箱默认值在金融专业场景里往往过紧,必须用真实样本标定。
第三,让系统提示词逐字节稳定。把所有按请求变化的变量全部剥离到用户消息中,使系统提示词完全静态化。最终生产实测有约 75% 的输入词元直接命中 Bedrock Prompt Caching,模型账单直接腰斩。
第四,从第一个原型开始就把身份绑在闭包里。绝不要抱着「先跑通 Demo、上线前再改鉴权」的侥幸心理,鉴权模式决定了工具接口的基础签名。
语料在提示词之前,真实流量在阈值之前,身份在原型代码里,缓存稳定性在第一天就该规划好。每一条都不是高深新技术,全都是执行次序问题。
最后看运营数据:上线后超过 55% 的用户在 24 小时内会再次发起提问(这是回访率指标,证明工具对用户有用,而非正确率);单次对话的模型平均调用成本压在 20 美分以内。
对于国内正在探索金融级 Agent 落地的团队来说,这篇实践最大的启示在于:不要把安全押注在模型的道德感或提示词的约束力上。把身份锁死在闭包里,把语料清洗到只剩真实文档,把引用资格写进代码规则——只有当约束变成不可逾越的物理结构,大模型才能真正走进受监管的核心业务。
原文出处 / 来源链接:AWS ML Blog 客户案例《Taxbit Assist: A grounded, role-aware AI agent on Amazon Bedrock AgentCore》(2026-11-05)