亚马逊AWS官方博客
在企业环境中为 AI 编程工具构建内容审查层
摘要:本文探讨在企业环境中为 AI 编程工具(如 AWS Kiro)构建内容审查层(DLP)的完整技术方案。文章将问题拆分为两条互斥路径:能改模型端点的自研应用(通过 LiteLLM 统一网关)和不能改端点的闭源工具(通过透明 MITM 拦截)。核心论点是:内容审查只能发生在能拿到明文的点,而内容型 DLP 本质上是 best-effort 检知而非硬预防。后半篇深入架构设计,部署语义审查小模型在自有 VPC 内(避免二次外发),并提出用 RAG 相似度检索补上”新写代码无签名、系统性漏检”的缺口。
目录
一、引言
企业让研发用 AI 编程工具,最先被问的一句话往往是「能不能拦住机密发出去」。本文把这件事拆成两条互斥的技术路径——能改模型端点的自研应用,和改不了端点的闭源工具(如 Kiro)——讲清各自怎么拿到明文、怎么分层拦截、以及有哪些兜不住的盲区。后半篇进入架构设计,回答两个更前沿的问题:语义审查的小模型为什么必须部署在自有 VPC 内,以及能不能用 RAG 相似度检索去补「新写代码无签名、系统性漏检」这个最难的缺口。
二、问题的本质:内容审查只能发生在「能拿到明文」的点
2.1 三个正在发生的泄漏场景
AI 编码助手、Agent、MCP 工具链进入研发日常之后,数据外泄的形态变了。传统 DLP 盯的是文件外传、邮件附件、U 盘拷贝;而现在,泄漏发生在一条条到大模型域名的、加密的、看起来完全正常的 HTTPS 连接里。
- 场景一:手动粘贴。 工程师把一段报错堆栈连同源码贴进 AI 助手求解,内部逻辑、内网地址、甚至密钥就此出境。
- 场景二:工具自动打包。 用闭源 AI 编码工具(如 AWS Kiro)时,为了让补全更准,工具会把代码库上下文自动打包进推理请求——开发者并没有「主动发送」的动作,数据已经走了。
- 场景三:Agent 自主外发。 Agent 执行
git push、curl、调用外部 API、或经扩展进程回传,密钥与业务数据流向任意可达目的地,且完全不经过可观测的推理接口。
这三个场景的共同点:流量默认全部去往云端大模型;出口网关看到的只是「一个到某模型域名的加密连接」,看不到里面是什么。
2.2 三类真实需求
企业口中的「防止信息发给大模型」,其实是三个不同的诉求,缓解手段完全不同:
- 防训练/防留存:不希望内容被用于模型训练或长期留存。这一条最高性价比的手段是合同而非技术,用企业版身份中心(IdC)签订 opt-out(不训练/不留存)条款就能覆盖大部分场景。
- 防误贴/防外发:不希望机密在「发出前」就流出,需要一道同步的内容审查。这才是需要 DLP 引擎的场景,也是本文的主角。
- 防出境:数据不能离开境内。这一条靠 DLP 满足不了:只要用的是 Claude Code、ChatGPT、Kiro 这类模型部署在境外的工具,代码就必然出境。DLP 只能降低出境内容的敏感度,消除不了跨境本身。
上任何技术方案前,先与需求方对齐到底要的是哪一类,否则会用错工具、给错承诺。
2.3 业界现状
讲方案之前,先把业界现在的状况摆清楚。
- 威胁模型已经变形,传统 DLP 系统性失效。 泄漏不再表现为文件外传或异常域名,而是藏在一条条到合法大模型域名的正常加密 HTTPS 连接里。有覆盖上百万员工的遥测显示,源代码已经是被粘贴进公共 AI 助手的第二大数据类型;也有针对官方 MCP server 的公开验证表明,数据可以经一次完全合法的工具调用加一个公开 PR 外泄,全程没有任何异常流量。盯文件、盯网络的传统 DLP,对这类流量看不见也拦不住。
- 内容型 DLP 是 best-effort 检知,不是硬预防。 这一点主流开源和商业检测器的官方文档都直接承认:有的明说「无法保证检出全部敏感信息」,有的自述「不是防止密钥入库的可靠方案」,有的直言检测器「并非完美精确、无法保证合规」。学术界对主流 LLM 护栏的实证更进一步:通过字符注入和对抗样本,部分场景的绕过成功率高达 100%。没有任何权威来源声称内容型 DLP 能硬性、完备地阻止泄漏。
- 厂商的企业级控制有明确边界。 主流工具的企业版普遍默认不拿客户数据训练,但「零数据留存(ZDR)」往往要单独申请,不是默认开启;数据驻留区域基本只覆盖美国和欧洲,没有中国区。防训练、防留存靠合同能拿到,防出境靠地域拿不到。
- 端点和浏览器侧的 AI 感知 DLP 正在补位,但 IDE 侧仍无解。 企业浏览器、端点 DLP 已经开始按「AI 应用」而不是域名来识别和管控粘贴、上传;但第三方研究记录了 AI 编码 IDE 上的数十个漏洞,也指出现有 DLP 栈普遍「没有机制去拦截以自然语言 prompt 形式传输的敏感数据」。补位是真的,盲区也是真的。
所以业界主流的做法是按诉求分治,再加纵深防御。没有任何单层算得上硬预防,DLP 只是护栏之上必要的一层。
2.4 第一性原理:要审内容,必先拿到明文
要审查内容,你必须先看到内容。TLS 之后的信道是加密的,工作在 TLS 之下的设备看不到里面的东西。拿 SNI 透传代理来说,它只读 ClientHello 里那个明文的 SNI 域名,据此决定放不放行,本身从不解密,像个只看信封、从不拆信的邮差。prompt、源码、请求体,它都看不到。
所以「我已经有一台 SNI 透传代理了,加个 DLP 模块就行」这条路走不通。要审查内容,只有两个可以拿到明文的点:
- 客户端进程内(数据加密之前);
- 一个终止 TLS 的代理(把 TLS 在中途解开)。
2.5 决定一切的分叉:客户端能不能改 base_url
[图 1] |
- 能改
base_url(自研 Agent/SDK/AI 应用):让客户端主动把请求发到你的网关。网关是一个 opt-in 的、有名有姓的合法终点,客户端清楚自己在跟网关说话,网关也就自然拿到了明文。这条路不需要伪造证书、不需要分发企业 CA,也不破坏任何 TLS 信任,是干净的路径。 - 不能改
base_url(闭源工具,推理端点硬编码):客户端认定自己在直连某个固定域名。你想在中途看内容,就只能冒充那个域名:伪造证书、让客户端信任你签发证书的企业 CA、半路把信拆开。这就是 MITM(中间人),一条有真实代价的路径。
两者的区别在于:LiteLLM 网关是「你告诉客户端来找我」(opt-in 终止),MITM 是「我假装我是它要找的那个」(拦截)。前者合法透明,后者等于替全体开发者的加密信道背书。
2.6 内容型 DLP 的本质局限
内容型 DLP 是 best-effort 的检知(detection),不是硬预防(prevention)。扫描通过,不等于没有泄漏。
原因是根本性的:一段内容属不属于本公司机密,常常没法只看内容本身,它取决于业务归属和上下文,而不是文本特征。引擎的能力因此并不均衡:
- 对已登记的指纹(密钥格式、内部术语表、EDM 精确数据匹配),召回率高、判定可靠。
- 对新写的源码、改写过的业务逻辑,则会系统性漏检:一段刚写出来的核心算法,没有任何已知指纹能命中它。
这个漏检就是后半篇要处理的缺口:第五节用 VPC 内小模型做语义判断,第六节用 RAG 相似度抓改写变体。
三、路径一 · LiteLLM 统一网关(面向可配置 base_url 的自研应用)
3.1 定位
路径一治的是能改 base_url 的那一类客户端:你自己写的 Agent、SDK、AI 应用。它是一个 opt-in 的 TLS 终止网关,客户端主动把 LLM 请求指向它,它拿到明文后,在转发给上游模型之前做内容审查。
客户端接入只是改一行 base_url(示意):
因为是客户端自愿把流量交过来,网关是合法的明文终点,无需伪造证书、无需企业 CA。
3.2 两道审查钩子
- prompt 审查(
pre_call_hook):请求送往上游模型前,对 prompt 全文过 DLP 引擎,命中则脱敏改写或阻断。 - MCP 工具调用审查(
pre_mcp_callguardrail):LiteLLM 的 MCP gateway 能在工具被调用前审查其参数,防止敏感数据经工具调用参数外泄。
自定义 guardrail 的骨架(示意):
3.3 适用边界
路径一干净、轻量(单 EC2 + Docker 可起)、风险低。但它只对能改 base_url 的客户端有效。碰上 Kiro 这类推理端点硬编码、无法重定向的闭源工具,路径一就直接失效了:Kiro 根本不会把请求发到你的网关。
四、路径二 · 闭源工具的透明拦截(以 Kiro 为例)
4.1 为什么网关方案对 Kiro 无效
Kiro 的推理请求硬编码发往 runtime.us-east-1.kiro.dev,没有 base_url 覆盖入口,你无法让它主动来找你的网关。想在内容出境前看一眼,唯一的技术手段是透明 MITM:在流量路径上终止 TLS、冒充那个硬编码域名。
4.2 推理面协议细节(用于正确解析与改包)
- 推理端点:
runtime.us-east-1.kiro.dev - 操作:
GenerateAssistantResponse(服务AmazonCodeWhispererStreamingService) - 编码:
x-amz-json-1.0;操作名在 HTTP 头X-Amz-Target里 - 响应:event-stream 流式;MITM 侧只读转发,设
flow.response.stream = True
4.3 关键前提:推理面用 Bearer,不是 SigV4
这一点直接决定了能不能改包、能不能阻断:Kiro 推理面用的是 Bearer token(OIDC/SSO),不是 SigV4。
- SigV4 会对 body 和 host 做签名,任何改动都会让签名失效。如果推理面走 SigV4,DLP 就只能原样只读转发,改包和阻断都做不了。
- 但推理面实测是 Bearer,签名不覆盖 body 和 host,所以在推理路径上改写 body、脱敏、阻断都是安全可行的。
- 一个例外要注意:任何可能存在的 SSO 路径或 Bedrock-direct 路径,只要走 SigV4,就必须只读透传、绝不改包。认证和 SSO 域名(
oidc、portal.sso、*.awsapps.com、management.kiro.dev、cli.kiro.dev)继续走 ssl_preread 透传,永不 bump。
4.4 选择性 bump:把「透传管道」局部升级为「中间人」
不要把整台代理都改成 MITM。用 nginx stream 的 SNI map 只对推理端点这一条 bump 到本地 mitmproxy,其余全部继续透传,未知目的地默认拒绝(示意配置):
本地 mitmproxy addon 终止 TLS、解析 CodeWhisperer 协议、调 DLP、决定阻断/放行(示意):
4.5 部署形态:网关式集中拦截
透明拦截的部署形态本文只讲一种:网关式集中拦截。开发机的流量经既有网络路径(VPN、路由或 DNS override)到达同一台共享 EC2 网关,由它统一执行选择性 bump、统一留痕、统一审计。它的取舍很清楚:
| 维度 | 网关式集中拦截 |
| 爆炸半径 | 大,一套 CA、一个节点是全公司推理流量的单一明文枢纽 |
| 集中强制力 | 强,不依赖开发机配合,统一留痕、统一审计 |
| 适用前提 | 端侧受管(MDM/EDR 可验证设备合规),需要统一强制策略 |
| 是否仍需正式评审、不可静默上线 | 是,见第九节合规 |
至于「把拦截逻辑塞进每台开发机本地」的做法,本文不展开。本地形态爆炸半径确实小,但集中强制力弱,中心侧无法确认代理是不是被关掉或绕过了。而且它并不改变核心权衡:只要终止 TLS、注入 CA,就是在为某个信任边界内的加密信道背书。缩小背书范围,不等于免除「要不要背书」这件事。企业如果已有集中式出口网络,网关式集中拦截是唯一能同时给出统一强制和统一审计的形态。
4.6 源头排除:.kiroignore + MDM 统一下发
在流量到达推理面之前,就把敏感文件挡在上下文之外,是和 MITM 互补的一层:它不碰 TLS,却能从源头减少会被打包进推理流的东西。Kiro 场景下,这层落在 MDM(移动设备管理/统一端点管理)上,它承担三件独立的事:
(1)下发忽略清单,缩小上下文范围。~/.kiro/settings 下的 .kiroignore、kiroAgent.agentIgnoreFiles 可以通过 MDM 统一下发到全体受管端点,相当于给 Kiro 的代码库索引和上下文打包划一块禁区:凭证目录、infra/secrets/、生产配置、含客户 PII 的数据样本等,从源头就不进入推理请求。这是成本最低、又不触碰 TLS 的一层,应当无条件先做。
(2)下发企业 CA,让网关式拦截真正生效。 Kiro 不是单一 TLS 栈,而是 3–4 条各自独立信任证书的栈。要让网关式 MITM 的伪造证书被客户端接受,企业 CA 必须进每一条栈的信任库,而 MDM 正是把企业 CA 装进操作系统信任库、并统一配置各栈信任的下发通道(macOS 钥匙串、Linux 的 update-ca-certificates、Windows 证书策略,以及各栈专属的 NODE_EXTRA_CA_CERTS、SSL_CERT_FILE 等环境变量)。没有 MDM 这条集中通道,给每台机器逐栈配 CA 在几百人规模上根本没法运维。
(3)MDM 还是端侧可信的前提。 网关式集中拦截的适用前提是端侧受管,而这就是 MDM/EDR 提供的:能验证设备已装合规配置、企业 CA 在位、没被本地绕过。这样一来,MDM 既是源头排除(.kiroignore)的下发器,又是信任基础设施(CA)的下发器,还是集中拦截合法性(端侧受管)的前提。三件事都压在这一条运维通道上,值得在项目早期就打通。
4.7 MITM 兜不住的盲区
透明拦截只覆盖已知的 HTTPS 推理端点。下面这些路径它一概看不到:
- Agent 自主 shell 外发(
git push、curl); - stdio MCP(本地进程间,不过网络代理);
- Open VSX 扩展进程内独立回传;
- 原生工具数据被打包进推理流(虽走推理端点,但混在正常上下文里,DLP 未必能切出这是不该发的)。
这些盲区只能靠 default-deny 出口白名单兜底:默认拒绝所有出站,只放行白名单里的目的地。它不碰 TLS、不新建明文枢纽,却是唯一的结构性根控制,应当优先、先行部署,而不是等 MITM 上了再回头补。
五、架构核心 · 分层 DLP 引擎
两条路径可以共用同一个 DLP 引擎,只是调用方式不同:路径一同进程 import 直调,路径二解密后把明文 POST 到独立判定服务。引擎本身按「便宜且准 → 贵且模糊」逐层递进,命中即止:
| 层 | 手段 | 抓什么 | 特点 |
| L0 | 正则 / 关键词表 | 固定格式密钥、内网域名、禁用词 | 最便宜、零延迟 |
| L1 | detect-secrets / gitleaks | 各类凭证、token | 成熟、误报可控 |
| L2 | 高熵检测 + 语境 | 未知格式的高熵串 | 抓 L0/L1 漏网的疑似密钥 |
| L3 | Presidio | PII(姓名、邮箱、身份证等) | 开源、可扩展识别器 |
| L3.5 | 内部术语表 / EDM 精确匹配 | 公司专有名词、注册过的机密数据 | 精确、亚毫秒,性价比最高 |
| L3.7 | RAG 相似度检索 | 与已登记机密语义相近的改写变体 | 补 EDM 的「改几个字就绕过」缺口 |
| L4 | 本地/VPC-only LLM | 需语义理解的敏感判断 | 绝不能用第三方 LLM,那是二次外发 |
有一条纪律贯穿全表:同步阻断链路只放确定性、亚秒可解的层,也就是 L0–L3.5。L4 语义判断是秒级、非确定、还可能被 prompt 注入的,默认只做异步告警、不进同步裁决;只有在显式开启的阻断态下,才由判定服务层(而不是引擎)就地合成 BLOCK,且超时即降级放行。L3.7 RAG 落在哪条链路,取决于它的延迟够不够确定,留到第六节展开。
L0–L3.5 这几层,在测试样本上的检测正确性与延迟结果是漏拦 0、误报 0。分层延迟的实测数据如下(78 条分层测试,每条向量对各层 scan() 单独用 perf_counter 计时,暖态单发口径):
| 层 | 单条耗时(暖态单发) | 说明 |
| L0 正则/关键词 | 0.01–0.03 ms | 与 L1/L2/L3.5 合计 <1ms,微秒级 |
| L1 签名检测 | ≈0.01 ms | 同上 |
| L2 熵+语境 | 0.01–0.10 ms | 同上 |
| L3 Presidio NER | 12–15 ms | 同步链路延迟的绝对主体(HTTP 调独立 analyzer 容器;冷启首条 ≈180–196ms) |
| L3.5 术语表/EDM | 0.01–0.31 ms | 亚毫秒 |
同步阻断链路的延迟预算几乎全花在 L3 Presidio 上,L0、L1、L2、L3.5 四层加起来不到 1ms。后文判断「L3.7 RAG 能不能进同步链路」时,会反复拿这条基线来对标。剩下的 L3.7 和 L4 怎么设计,就是后半篇的事。
5.1 规则怎么改:各层定制入口、生效方式与验证闭环
引擎跑起来之后,规则定制就是运营常态:新出现的凭证格式要加签名、新立项的内部代号要进术语表、新登记的机密语料要进向量库、误报要加白名单豁免。工程形态上当前引擎是「代码即配置」+ env 两层,检测规则(正则、词表、白名单)硬编码在 /engine/dlp/ 各层模块的常量里,运行时开关与阈值走环境变量;没有规则配置平台、没有热加载。
按层给出想改什么 、改哪里 和怎么生效 :
| 层 | 典型诉求 | 改哪里(真实入口) | 生效方式 |
| L0 | 加一条正则 / 加白名单豁免 | l0_regex.py:加编译正则 + 在 scan() 里 append 一条 Hit;豁免加进 WHITELIST_TOKENS(token 白名单)或 RESERVED_DOMAINS(保留域,与 L3 邮箱豁免共用) |
重启 dlp |
| L1 | 加新凭证签名 | l1_secrets.py:加编译正则 + _emit(正则, 规则名, 实体名) 一行(自动带占位符豁免);占位符词表在 _PLACEHOLDER |
重启 dlp |
| L2 | 调熵阈值 / 加语境词 | l2_entropy.py:熵阈值(当前 4.0)与语境词表 _CTX 都是模块常量,无 env,改代码 |
重启 dlp |
| L3 | 启停某 PII 类别 / 改动作 / 调置信度 | l3_presidio.py:_ENTITY_ACTION 字典增删键即可(不在字典里的实体直接丢弃),动作就是键值(REDACT/BLOCK);置信度阈值 _SCORE_THRESHOLD(当前 0.5);语言集走 env DLP_PRESIDIO_LANGS,不可达动作走 env DLP_L3_UNAVAILABLE_ACTION=block|keep |
py 改动重启;env 改动使用 up -d 重启;加自定义识别器要动 Presidio 侧配置并重建其镜像 |
| L3.5 | 登记新术语 / EDM 条目 | l35_glossary.py:往 GLOSSARY 列表 append 一个四元组 (短语, 实体名, 动作, 大小写敏感);正则类术语(工单号等模式)仿 _TICKET 加 |
重启 dlp |
| L3.7 | 登记新机密语料 / 调相似度阈值 | 语料:corpus.jsonl 加一行 {"doc_id","chunk_id","text"},重启 RAG 服务(启动时全量重算 embedding,无增量登记 API);阈值走 env DLP_L37_THRESHOLD——改阈值前必须先跑 threshold_sweep.py 重新标定(6.4:阈值是曲线不是点);开关 DLP_ENABLE_L37 |
语料变动需重启 RAG 服务;env 改动使用 up -d 重启 |
| L4 | 改类别集合 / prompt 契约 / 三态 / 模型端点 | 类别与输出契约都在 l4_semantic.py::_CLASSIFIER_PROMPT 一个常量里(五类 + none,Bedrock 与 vLLM 后端共用、避免两处漂移);三态 env DLP_L4_MODE=off|async|sync、同步阻断置信度门槛 DLP_L4_BLOCK_MIN_CONFIDENCE、超时 DLP_L4_TIMEOUT_MS;VPC-local 后端 DLP_L4_BACKEND=vllm + DLP_L4_VLLM_URL/DLP_L4_VLLM_MODEL |
prompt 改动需改 py 重启;env 改动使用 up -d 重启 |
| 出口 | 放行内部 git 远端 / SQL 连接 | egress.py:GIT_REMOTE_ALLOWLIST / SQL_CONN_ALLOWLIST 元组加条目 |
重启 dlp |
docker compose up -d(或 restart)让进程重新 import 即生效;代码里没有热加载。
以下是三个最高频的定制给出的示例。
登记一条新术语(L3.5,最常见),四元组第 4 位就是大小写敏感开关:
调整 PII 类别(L3),停用弱实体 LOCATION、把 CREDIT_CARD 升级为阻断,就是对 _ENTITY_ACTION 字典的两行操作:
登记新机密语料并重标阈值(L3.7),语料进库、阈值必须先扫描后下发:
每加/改一条规则,应当在同一次提交里配一条分层向量(正例 + 豁免各一,进 engine/tests/fixtures_layers/),重跑 78 条分层测试(python3 -m tests.run_layers,跑法见手动复现指南)确认新规则命中、旧规则不回归;L3.7 动了语料或阈值就重跑 threshold_sweep;L4 动了 prompt/类别就重跑 suite6 类别命中。如果没有配套 fixture 的规则改动,等于往引擎里塞了一条没人知道何时失效的规则。
当前引擎无规则外置平台(无 yaml/API/管理界面)、无热加载、无规则级审计与版本化灰度、Presidio 自定义 pattern recognizer 没有注册入口、L3.7 语料无增量登记。如要把这套引擎产品化,应当先补充规则外置与变更审计。
六、架构设计(一)VPC 内小模型:语义审查为什么不能外包
6.1 红线的由来
L4 要判断的是:这段没有任何签名的文本,像不像本公司的专有源码或业务逻辑。这种判断只能靠语义理解,也就是需要一个 LLM。问题在于,如果这个负责审查外发内容的 LLM 本身是第三方托管的,那就等于把要保护的内容又发出去了一次:你为了防止代码泄漏给云端模型,却把同一段代码发给另一个云端模型做判断。这是第二条泄漏通道,而且比第一条更隐蔽。
所以这条红线没有余地:生产环境的 L4 语义模型,必须本地自建或部署在自有 VPC 内。判定服务与嵌入、生成模型同处一个 VPC,待判内容全程不出网络边界。
6.2 演示可以用托管,生产不行
在验证阶段,可以先用 Bedrock 上的托管模型(比如 Qwen3-32B)把 L4 的功能跑通,好处是打开就能端到端验证,不必先搭一套 GPU 推理服务。
生产部署必须在 VPC 内自托管。本文的测试参考是:g6e.xlarge(48G GPU · L40S)用 vLLM 部署 Qwen3-4B,判定服务与它同 VPC 私网直连。这个量级足够做「敏感/不敏感 + 类别 + 置信度」的分类判断,单卡就能容纳,成本可控。
6.3 已有的延迟基线,与「待测」的边界
L4 的判定形态是「输入待扫文本 → 输出结构化 JSON:{sensitive, category, confidence, reason}」——一次短分类,不做长文本生成,maxTokens 只需两三百。
作为量级参照,托管 Bedrock 上的实测数字是:
| 口径 | 模型 | 类别命中 | 实测延迟 |
| 直连 Converse 单条判定 | Qwen3-32B | 4/4 | 193–564 ms |
| 经网关端到端(含转发) | Qwen3-32B | 4/4 | 129–552 ms |
| 判定服务同步阻断态单发(含 RTT) | Qwen3-32B | 4/4 | 908 ms |
VPC-local 小模型实测:分别在 g6.xlarge(L4,24G 显存、300GB/s 显存带宽)和 g6e.xlarge(L40S,48G 显存、864GB/s 显存带宽)上测试,用 vLLM 官方 Docker 起 Qwen/Qwen3-4B-Instruct-2507(纯文本官方 dense、非思考模型)。语料和分类 prompt 都用同一套 suite6(输入约 120–200 token),并由 vllm: 后端哨兵确认是真命中而非回退:
| 口径 (vLLM serving) | 模型 | 类别命中 | 单发延迟 p50 |
L4(g6)BF16,完整契约(带 reason 长文本,max_tokens=128) |
Qwen3-4B-2507 | 4/4 | 2222ms |
| L4(g6)BF16,完整契约 + CUDA graph | Qwen3-4B-2507 | 4/4 | 2182ms |
L4(g6)BF16,精简输出(去 reason 字段,中位仅 20 生成 token) |
Qwen3-4B-2507 | 4/4 | 695ms |
| L40S(g6e)BF16,精简输出 | Qwen3-4B-2507 | 4/4 | 256ms |
| L40S(g6e)FP8( 量化),精简输出 | Qwen3-4B-2507 | 4/4 | 259ms |
- VPC-local 的 4B 小模型类别命中 4/4,与托管 32B 持平。在这套「敏感/不敏感 + 类别」的短分类任务上,4B 量级已经够用,不必上 32B;生产切到 VPC-local 小模型,并不牺牲检出质量
- 从 2222ms 压到 256ms,关键在生成 token 数和显卡带宽。完整契约要模型吐一段中文
reason(100+ token),精简契约只留{sensitive,category,confidence}(中位 20 token),单发延迟就直接降到三分之一,可见延迟的主导因子是输出 token 数,而不是模型或后端。每生成 1 个 token 都要把全部权重从显存读一遍,Qwen3-4B BF16 权重约 8GB ÷ L4 带宽 300GB/s ≈ 27ms/token,这是理论下界;实测 695÷20≈35ms/token,达到峰值的约 77%。换成带宽更高的 L40S 提速约 2.7 倍。FP8 在单发口径上不再提速,真实收益在并发吞吐(KV cache 更省) - 对同步链路意味着什么:L40S 精简输出的 256ms 仍是亚半秒量级,还是不该进同步阻断链路,它比 L0–L3.5 同步链路净成本 p50≈14ms、RAG p50≈16–45ms 差了一个数量级;但拿来做异步告警已经绰绰有余。如果异步告警的延迟预算是秒级,更便宜的 L4 卡就够用了
6.4 为什么 L4 默认走异步
即便模型已经在 VPC 内,秒级的 LLM 判定也不应默认进入同步阻断链路:它延迟不可控、结果不确定,还可能被 prompt 注入操纵。所以默认形态是异步告警。同步链路只跑 L0–L3.5,立即放行或脱敏;L4 在后台跑完,把命中写入告警 sink(只记类别、置信度、所用模型,不落原文片段)。只有当硬阻断是不可退让的要求时,才显式开启同步阻断态,并为它配一个墙钟超时:一旦超时,就降级为异步告警加放行,绝不让 L4 的延迟拖垮一个合法请求。
否决权必须与信号的置信度匹配,这条原则在下一节的 RAG 上同样适用。
七、架构设计(二)RAG 相似度拦截:能不能补上「新写代码漏检」?
7.1 先回到那个最难的缺口
回到第 1.6 节钉下的本质局限:对新写、改写过的专有代码和业务逻辑,内容型 DLP 会系统性漏检。原因是这些东西没有固定签名,它不是密钥格式(L0/L1 抓不到),不是高熵串(L2 抓不到),也不是 PII(L3 抓不到)。
L3.5 的 EDM(精确数据匹配)能抓已登记的机密串,但它做的是精确、词边界匹配:登记了 internal-margin-formula 这个术语,改成 internal_margin_calc 就绕过了;登记了一段核心算法的原文,把变量名换一遍、循环改写一下,就再也命中不了。EDM 的召回率随改写程度断崖式下降。
L4 的通用 LLM 判断能理解语义,但它是一种无参照的泛化猜测:「这段像不像专有逻辑」全凭模型的先验,容易把开源风格的通用代码也误报成专有,因此还得专门想办法抑制告警疲劳。
中间缺一层:它既要像 L4 一样容忍改写(看语义而不是字面),又要像 EDM 一样有确定的参照(锚定在企业真实登记的机密语料,而不是模型的泛化想象)。这一层就是 RAG 相似度检索。
7.2 思路:把 EDM 从「精确匹配」升级为「语义匹配」
RAG(Retrieval-Augmented Generation)在这里的工作方式:
- 建库(离线,一次性 + 增量):把企业已知的机密语料(核心算法源码、内部设计文档、专有业务规则、未公开的定价和风控逻辑)切块后用嵌入模型算成向量,存进 VPC 内的向量库(OpenSearch、pgvector 等)。
- 判定(在线,每请求):把待外发的请求内容同样嵌入成向量,在向量库里查最近邻,算余弦相似度。
- 裁决:相似度超过阈值即命中,说明这段内容和某份已登记机密在语义上高度接近,哪怕一个字都没照抄。
它在分层里的定位是 L3.7「语义 EDM」:在 L3.5(精确注册匹配)和 L4(无参照泛化判断)之间。
[图 2] |
7.3 为什么它比「纯 L4 LLM 判断」更适合补这个缺口
- 有参照,所以更确定:命中的含义是「和你真实登记过的这份文档相近」,不是模型凭空觉得「这看着像机密」。误报的来源从模型先验收敛到语料库里到底有什么,可控得多。
- 可解释、可追溯:命中时能直接指出「与登记文档 X 的第 N 段相似度 0.87」。这对安全审计、对开发者看到的拦截提示、对事后复盘,都远比「LLM 说它敏感」有用。
- 增量更新,不用重训:新登记一份机密,只要把它嵌入后加进向量库就立即生效,不像规则要重写正则、也不像微调模型要重训。
- 嵌入模型比生成模型轻:一个几百 M 到几 G 的嵌入模型(如 bge、gte 系列),单机 CPU 或小 GPU 就能跑。比起跑一个生成模型,它更容易满足 VPC-local 红线,延迟也更有机会进同步阻断链路。
- 天然契合红线:嵌入模型和向量库都在 VPC 内,待判内容和机密语料都不出网络边界。
7.4 四条必须诚实标注的风险
RAG 相似度拦截不是万能钥匙。以下四条风险,设计之初就要写进标定:
- 相似度阈值是又一个机制性误报旋钮。 统计型检测器的误报是机制性的,靠逐例调阈值糊弄不过去。相似度也一样:阈值调高,改写得多的变体就漏检;调低,用了相同框架、相同业务词汇的正常代码又会被报成机密,制造告警疲劳。阈值不是一个能一劳永逸设定的数字,而是一条召回与误报的权衡曲线,必须用真实语料扫出来。
- 依赖机密语料库的完备性。 没登记进向量库的机密照样漏:它把「无签名漏检」缩小成「未登记漏检」,但没有消除。本质仍是 best-effort,不改「扫描通过不等于没泄漏」这条底线。语料库的持续维护(谁登记、多久更新、粒度多细)本身是个治理问题,不是技术上线就完事的。
- 向量库本身是敏感资产。 它存的是企业机密的 embedding,虽然不是原文,但足以做相似度反查、甚至部分重建。所以向量库必须在 VPC 内、静态加密、严格访问控制,安全等级等同于它所保护的机密。把机密语料库放到一个防护薄弱的地方,等于给自己造了一个新的单点。
- 嵌入模型对代码、中英混排的语义表征质量因领域而异。 通用嵌入模型多在自然语言上训练,对源代码的语义相似度判断、对中英混排文本的表征,都可能不如预期:两段逻辑等价但风格迥异的代码,嵌入向量未必接近。实测里,中文自然语言机密场景通用 bge-m3 略胜(0.930 vs 0.897),代码语义克隆场景则是代码嵌入 F2LLM 大幅领先。选嵌入模型,得先看你要拦的是自然语言机密还是源代码,不能一把梭。
在 VPC-local GPU 机上自托管两个嵌入模型,用一套自建评测集跑相似度阈值扫描:
| 嵌入模型 | 参数 | 自建中文机密集 AP | POJ-104 代码克隆集(C/C++) AP | CodeNet Python800 AP / top-1 | 单发 embedding p50 |
| BAAI/bge-m3(通用多语言) | 560M | 0.930 | 0.748(ROC 0.604) | 0.900 / 19/30=0.633 | 15.9ms |
| codefuse F2LLM-v2-4B(代码专用) | 4B | 0.897 | 1.000(ROC 1.000) | 1.000 / 30/30=1.000 | 44.9ms |
7.5 架构接入点
RAG 层有两个现成的挂载位,取决于它的检索延迟够不够确定:
- 进引擎、走同步链路(够快的话,首选):插在 L3.5 之后、L4 之前。嵌入加向量检索如果能稳定在十几到几十毫秒(与 L3 Presidio 同量级),就可以进同步裁决,命中即脱敏或阻断。前提是嵌入模型轻、向量库规模适中、检索用近似最近邻(ANN)索引。
- 走判定服务层旁路、异步告警(较慢或不稳时的保守选择):与 L4 并列,复用同一套后台线程池和告警 sink,命中只落告警、不改裁决。适合语料库大、嵌入模型重、或相似度阈值还在标定期的阶段。
嵌入模型的选型同样是延迟与精度的权衡:代码专用的 F2LLM-v2-4B 单发 p50 约 44.9ms,比 bge-m3(15.9ms)高约 3 倍,但仍稳定在几十毫秒,落在同步拦截链路的预算内。即便为代码场景换上 4B 代码嵌入,RAG 依然进得了同步链路;只有当语料库大到需要 ANN、或嵌入模型再重一个量级时,才需要退回异步旁路。选型顺序是:先按「拦自然语言机密还是源代码」定嵌入模型,再看它的单发延迟够不够进同步拦截链路。
更稳妥的上线路径仍然是先异步、等阈值标定稳了再考虑进同步:延迟够快不代表阈值已经调好。这和 L4 的处置哲学一致:否决权必须与信号置信度匹配。一个还没标定好阈值的相似度信号(bge-m3 实测显示 recall=1.0 时 precision 仅 0.748),不配一上来就拿同步阻断权。延迟允许它进同步,但误报率要求它先在异步告警态里,把阈值和语料库打磨成熟。
八、上 MITM 前的必测项 + CA 硬门槛
8.1 必测项(缺一不可)
- 证书固定:自建 CA 实跑,确认 Kiro 不做证书固定(cert pinning)。信任 CA 后零客户端改动即可拦截
kiro-cli,客户端每次自动更新后都要复核。 - 数据面认证模型:抓包确认推理面确为 Bearer(据此才能改包/阻断),并识别是否存在 SigV4 路径(那些路径只读透传)。
- 索引合规:向 AWS 确认 codebase 索引/embedding 是否遵守
.kiroignore、是否离机计算(对应 3.6 的未验证项)。 - 各栈 CA 信任在目标 build 实际生效:Kiro 不是单一 TLS 栈,而是 3–4 条各自独立信任证书的栈(见下面的矩阵)。必须在目标客户端版本上逐栈实测「装了企业 CA 后握手是否真的通过」,不能假设「装了就信」。
- HTTP 层干扰 / 流稳定性:即使 CA 已被信任、TLS 握手成功,仍要实测代理会不会在 HTTP 层破坏可用性(改写响应头把连接降级、大响应体下 event-stream 被中途 reset)。这是与证书信任相互独立的一类失败模式。
8.2 CA 硬门槛(做不到就不要上 MITM)
CA 信任不是「一个开关」。把企业 CA「装进系统」,远不足以让所有栈接受它。每条栈的信任机制不同,必须按栈分别下发(MDM 是下发通道,见 3.6),再逐栈实测:
| TLS 栈 | 谁在用它 | 让它信任企业 CA 的手段 |
| Chromium / renderer | 界面、扩展市场、登录 | 与推理无关(推理不走这条栈) |
| Node / Electron 扩展宿主 | IDE 的模型推理请求在这里发 | 装进 OS 信任库(首选)或 NODE_EXTRA_CA_CERTS 指向 CA 文件 |
| Rust CLI | kiro-cli 本体 |
SSL_CERT_FILE / AWS_CA_BUNDLE(不读 OS 库、不认 NODE_EXTRA_CA_CERTS) |
| Bun TUI | CLI 的推理子进程 | NODE_USE_SYSTEM_CA=1 + 较新 CLI 版本 |
- IDE 首选把企业 CA 装进操作系统信任库:GUI 应用不继承 shell 环境变量,环境变量法对 IDE 常常「设了没用」,MDM 下发解决的就是这个。
- CLI 与 IDE 要分开配:一套 CA 分发脚本盖不住所有栈。
- 绝对禁止
NODE_TLS_REJECT_UNAUTHORIZED=0:它关闭全部证书校验,等于自废 TLS,制造出比原问题更大的漏洞。 - X.509 Name Constraints:mark critical、permitted 限定
*.kiro.dev、覆盖 iPAddress,加在会被强制校验的中间/叶层,并实测确认 Kiro 的 TLS 栈确实强制执行。 - 私钥保护:根 key 入 HSM/离线;理想形态是节点不持签名 key,仅对单一 host 通过
KMS::Sign现签短期叶子证书,可审计、可秒撤。
九、测试方案
9.1 测试数据集:全部合成、oracle 先行、按层/按场景两套编制
文中所有实测数字背后是四组数据集,编制原则先说清楚,数字才可信:
- 全部合成,零真实机密。 每一条「敏感值」都是数学正确的合成串:身份证过 mod-11 校验、卡号过 Luhn、AKIA 满足格式结构,但都不是真实凭证。同时刻意避开引擎白名单(官方示例 key
AKIAIOSFODNN7EXAMPLE、公开测试卡),以免被静默放行、污染结果。 - oracle 先行,不脑推期望值。 每条 fixture 生成时都对引擎实跑 scan(),期望值与真实行为不一致就报错退出,测试数据本身先过了一道「与被测对象对齐」的关。
四组数据集的构成:
| 数据集 | 规模 | 用途 |
| 分层向量(fixtures_layers) | 86 条(L0×14 / L1×12 / L2×7 / L3×9 / L3.5×10 / EGRESS×11 / NORM×7 / L3.7×8 / L4×8) | 每层每条规则的正例/豁免/边界,直调各层 scan()(4 分层延迟即出自这组) |
| 场景 suite(suite1–7) | 64 条(11/16/10/6/9/4/8) | 端到端注入点×内容形态矩阵;suite6 是 L4 语义语料(5.3 类别命中即用它) |
| k6 压测向量 | 攻击(local×3 + redact×3 + l3only×3)+ 合法(短×8 + 长文本) | 吞吐阶梯/误杀率/fail-closed 对账(4 整机分位与拐点即用它) |
| RAG 评测集 | 自建中文机密 23 条 + POJ-104 派生 50 条 + CodeNet Python800 派生 50 条 | L3.7 阈值扫描 / 嵌入模型对比(6.4) |
分层向量示例,L0 正例,合成 AKIA 真值必须 BLOCK:
场景 suite 示例,suite6 语义敏感语料,没有任何正则可抓的特征,同步必须 PASS、异步 L4 必须标出 business_strategy 类别(这正是「新写内容无签名漏检」的测法):
9.2 VPC 内小模型延迟
- 类别命中 + 单发延迟:复用现有的 L4 标定 harness(同一套无签名语义敏感语料 + oracle 类别标注),对比「VPC-local 内小模型 vs 32B 托管」的类别命中率与单条延迟。
- 判据:小模型的类别命中率是否接近托管 32B 模型、单条延迟是否可控在秒级内。
9.3 RAG 相似度拦截效果
- 语料与 oracle:新建一组对标语义敏感场景的语料,含三部分:正例(已登记机密的改写变体,测召回)、反例(用了相同业务词汇/框架的正常代码,测误报)、阴性对照(通用开源代码,绝不该命中)。
- 阈值扫描(核心):不是取一个固定阈值报一个准确率,而是扫描整条召回与误报曲线(ROC):相似度阈值从高到低,画出「召回率上升对误报率上升」的权衡,找到业务可接受的工作点。
- 可解释性核查:命中时是否能稳定给出「匹配到哪份登记文档、相似度多少」,供审计与开发者提示。
- 嵌入模型对比:通用嵌入模型 vs code-specific 嵌入模型,在代码/中英混排语料上的召回差异(回应风险 3)。
- 判据:沿用现有引擎测试的硬门风格,正例集漏拦为 0(或明确标出改写到什么程度就漏)、反例集误报可接受、阴性对照零命中。
参考 Github repo
十、合规与总结
10.1 合规风险
- 勿静默上线:MITM 在协议层与真实中间人攻击不可区分,未告知的部署就是未授权监听。必须经正式安全评审、告知使用者并取得同意。
- 员工监控合规:涉及员工监控须走对应司法辖区的共同决定/告知程序(如德国 works council 的 Betriebsvereinbarung)。
- 跨境出境:境内到海外的数据出境需走数据出境安全评估或标准合同,并评估美国 CLOUD Act 暴露。先由法务定性,再谈技术。
10.2 三条底线
- 拿明文才能审内容:能改
base_url就走网关(干净),不能改才谈 MITM(有代价,且只做网关式集中形态)。 - 内容型 DLP 是 best-effort 检知,不是硬预防:扫描通过不等于没泄漏。VPC 小模型和 RAG 相似度补的是「新写代码漏检」,但补不等于补全。
- 语义审查的模型和向量库必须在自有 VPC 内:第三方托管就是第二条泄漏通道,这条红线同时约束 L4 和 L3.7 RAG。
10.3 落地顺序
- 第 0 层(立即做,不碰 TLS,覆盖最广):自研可配客户端上 LiteLLM 网关;Kiro 场景用 A(IdC 企业版合同性 opt-out + prompt logging + 事后告警)加 MDM 下发
.kiroignore,再加 B(现有 SNI 代理收紧为 default-deny 出口白名单 + 扩展来源白名单)。多数威胁模型到此为止。 - 第 1 层(仅当「隐私请求必须同步硬阻断」是硬性要求):在第 0 层之上受控引入网关式集中拦截加分层 DLP 引擎,第七节的硬门槛全部到位,失败模式建议降级为透传加告警。
- 第 2 层(补检测深度):VPC 内小模型(L4)默认异步告警上线;RAG 相似度拦截(L3.7)先异步、阈值标定稳了再评估进同步。
只担心训练/留存 → 合同 opt-out;
担心留存要可审计 → opt-out + prompt logging;
担心误贴机密、接受事后告警 → opt-out + prompt logging + default-deny 出口;
担心 agent/扩展乱外发 → default-deny 出口;
隐私请求前必须同步硬阻断且端侧受管 → 网关式集中拦截 + 分层 DLP(L4 同步拦截);
硬约束是「代码不能出境」→ 换不出境的工具。
➡️ 下一步行动:
相关产品:
- Amazon VPC — 隔离云网络
- Amazon Bedrock — 用于构建生成式人工智能应用程序和代理的端到端平台
- Amazon EC2 — 安全且可调整大小的计算容量
- Amazon OpenSearch — 搜索和分析引擎
- Amazon KMS — 托管式密钥管理
相关文章:
- LiteLLM 生产级部署:基于 AWS ECS/EKS 的 AI Gateway 架构
- Bedrock Claude + LiteLLM WebSearch Interception 配置指南
- 通过 LiteLLM 实现 Amazon Bedrock 成本管控:实时限额、多维监控与平台级兜底
- 用 LiteLLM WebSearch Interception 集成 AWS 托管的 Amazon Bedrock AgentCore Web Search 能力
- 接入 Amazon Bedrock 新一代推理引擎 Mantle:用 LiteLLM 网关统一调用 GPT-5.6 与 Claude
*前述特定亚马逊云科技生成式人工智能相关的服务目前在亚马逊云科技海外区域可用。亚马逊云科技中国区域相关云服务由西云数据和光环新网运营,具体信息以中国区域官网为准。
本篇作者
AWS 架构师中心:云端创新的引领者探索 AWS 架构师中心,获取经实战验证的最佳实践与架构指南,助您高效构建安全、可靠的云上应用 |
![]() |



