
1. 为什么我要给大模型套上一层审计壳1.1 从一次线上事故说起去年秋天我们内部上线了一个基于大模型的工单自动分类助手跑了两周都挺稳直到某天凌晨告警炸了——一个客户投诉工单被错误归类成了“垃圾营销”直接进了自动归档队列客户等了六个小时没人理。事后复盘模型给出的分类理由是一段看起来非常合理的解释但那段解释和它实际输出的标签完全对不上。换句话说模型在“编理由”而我们没有任何手段去验证它到底是怎么得出这个结论的。这件事让我意识到一个很现实的问题大模型的输出是一个黑盒而业务系统需要的是可追溯、可验证、可定责的白盒链路。你可以接受模型偶尔犯错但你没法接受犯错之后连“为什么错”都查不出来。尤其是在审计、风控、合规这些场景里一个无法溯源的AI输出本质上就是一个不可控的风险敞口。所以从去年Q4开始我花了将近三个月的时间给我们的LLM应用层做了一套完整的审计体系。这篇文章是上篇主要讲清楚两件事为什么要做AI-Native审计层以及溯源审计的核心设计思路和落地细节。下篇会讲具体的规则引擎、告警联动和长期运营。1.2 什么是AI-Native审计层先把这个概念说清楚。传统的审计系统审计的是“人操作了什么”——谁在什么时候改了哪条数据、走了什么审批流。但AI-Native审计层审计的是“模型想了什么、说了什么、为什么这么说”。它至少包含四个层次输入审计用户问了什么上下文里带了什么有没有注入攻击的痕迹推理审计模型调用了哪些工具、检索了哪些知识片段、中间推理链是什么输出审计模型最终输出了什么置信度多少有没有触发敏感词或越权内容溯源审计从输出反推回输入和推理过程形成完整的证据链这四个层次里前三层很多团队都在做但第四层——溯源审计——才是真正把“黑盒”变成“灰盒”甚至“白盒”的关键。因为只有能溯源你才能在出问题的时候快速定位、定责、修复。1.3 适合谁来参考这套方案如果你正在做以下任何一件事这套思路应该对你有用把LLM接入到生产业务系统尤其是涉及资金、合规、医疗、法律等高风险场景用RAG或Agent架构做复杂任务编排链路长、环节多、排查困难需要向内部审计或外部监管证明“AI的输出是可控的”团队里已经有人在喊“模型又抽风了但不知道为啥”不需要你是算法专家但最好对LLM的基本调用方式、Prompt结构、RAG流程有一定了解。我会尽量用大白话把每个环节讲透。2. 整体设计思路从“事后查日志”到“事前埋点、事中拦截、事后溯源”2.1 为什么传统日志方案不够用一开始我们也是走常规路线把每次LLM调用的请求和响应打到日志系统里出问题了就去翻日志。但很快发现三个致命问题第一日志是扁平的链路是立体的。一次Agent调用可能涉及3次LLM请求、5次工具调用、2次知识库检索日志里散落在不同地方靠时间戳拼凑排查一次至少半小时。第二日志只记录了“发生了什么”没记录“为什么发生”。你知道模型输出了“拒绝退款”但你不知道它是因为检索到了某条过时的政策文档还是因为Prompt里的某个示例误导了它。第三日志没有防篡改能力。如果日志本身可以被修改那审计就失去了意义。尤其是在多租户环境下谁能保证日志不被误删或恶意覆盖所以我们的设计目标很明确每一次AI输出都必须能沿着一条不可篡改的链路反推到它的每一个决策依据。2.2 核心架构三层埋点 一条溯源链整体架构可以概括为“三层埋点、一条链、一个中心”接入层埋点记录原始请求、用户身份、会话上下文、时间戳推理层埋点记录Prompt全文、检索到的知识片段及来源、工具调用参数与返回、模型原始输出输出层埋点记录最终返回给用户的内容、经过的后处理规则、置信度评分这三层埋点产生的数据通过一个全局唯一的trace_id串联起来形成一条溯源链。每条溯源链在写入时都会计算哈希并链接到前一条记录形成类似区块链的防篡改结构。中心则是一个独立的审计服务负责存储、索引和查询这些链路数据。注意这里说的“类似区块链”是指哈希链式结构不是真的上链。目的是防篡改和可验证不是去中心化。2.3 关键设计决策与取舍在设计过程中有几个决策点值得展开说决策一埋点放在应用层还是网关层我们最终选择了应用层埋点为主、网关层埋点为辅。原因是应用层能拿到更丰富的语义信息比如Prompt模板的版本号、RAG检索的原始query、Agent的中间思考步骤。网关层只能看到HTTP请求和响应信息粒度太粗。但网关层埋点也有价值它可以作为交叉验证的依据防止应用层埋点被绕过。决策二全量存储还是采样存储全量存储的成本很高尤其是Prompt全文和检索片段。我们算过一笔账日均10万次调用平均每次Prompt 2000 token、检索片段5000 token一天就是7亿token的文本量。最终我们采用了分级存储策略高风险场景全量存低风险场景存摘要哈希原始数据保留7天后转冷存储。决策三同步审计还是异步审计同步审计会在调用链路上增加延迟异步审计则可能漏掉实时拦截的机会。我们的方案是关键节点同步、非关键节点异步。比如输出层的敏感词检测和越权判断是同步的检索片段的来源校验是异步的。决策四溯源链的粒度怎么定太粗了查不到细节太细了存储和查询压力都大。我们最终以“一次用户请求”为一条溯源链链内包含多个“推理节点”每个节点对应一次LLM调用或工具调用。这样既保证了链路的完整性又控制了单条链的长度。3. 核心细节解析溯源审计到底审什么、怎么审3.1 输入审计把好第一道门输入审计的目标是回答三个问题谁在问、问了什么、有没有恶意。“谁在问”不仅仅是用户ID还包括用户的角色、权限、历史行为画像。同样一句“帮我查一下上个月的账单”普通用户和财务人员的意图可能完全不同审计策略也应该不同。“问了什么”需要记录原始输入、经过清洗和归一化之后的输入、以及最终拼接到Prompt里的完整内容。这里有个坑很多团队只记录原始输入但实际影响模型的是拼接后的Prompt。如果Prompt模板里包含了系统指令、few-shot示例、检索片段这些都必须记录。“有没有恶意”主要针对Prompt注入和越狱攻击。我们维护了一套注入特征库包括常见的指令覆盖、角色扮演、编码绕过等模式。检测到高风险输入时会打上标记并触发额外的审计规则。# 输入审计埋点示例简化版 def audit_input(user_id, raw_input, context): trace_id generate_trace_id() normalized normalize_text(raw_input) prompt build_prompt(normalized, context) record { trace_id: trace_id, timestamp: now(), user_id: user_id, raw_input: raw_input, normalized_input: normalized, final_prompt: prompt, prompt_template_version: context.template_version, injection_score: detect_injection(normalized), risk_level: assess_risk(user_id, normalized) } audit_store.write(record) return trace_id实操心得Prompt模板一定要版本化。我们踩过一次坑某次更新模板时删掉了一个安全约束导致模型开始回答本不该回答的问题。如果没有版本记录根本定位不到是哪次变更引入的。3.2 推理审计把“思考过程”留下来推理审计是整套体系里最复杂的部分因为不同架构的推理过程差异很大。对于简单的单轮LLM调用推理审计主要记录模型名称、版本、温度参数、top_p、max_tokens、完整的消息列表、模型的原始输出包括被后处理过滤掉的部分。对于RAG架构额外需要记录检索query、检索到的文档ID和片段内容、相似度分数、重排序结果、最终注入Prompt的片段。对于Agent架构还需要记录每一步的工具选择、工具入参、工具返回、中间思考文本如果有CoT、循环次数。这里的关键是每个环节都要有独立的审计记录并通过trace_id和parent_node_id形成树状结构。这样在溯源时你可以从最终输出一路反推到最初的检索query。# 推理节点审计记录结构 class ReasoningNode: def __init__(self, trace_id, parent_node_idNone): self.trace_id trace_id self.node_id generate_node_id() self.parent_node_id parent_node_id self.node_type None # llm_call / tool_call / retrieval self.input_data {} self.output_data {} self.metadata {} self.timestamp now() self.hash None def seal(self, previous_hash): content json.dumps(self.to_dict(), sort_keysTrue) self.hash sha256(content previous_hash) return self.hash注意记录CoT内容时要注意脱敏。有些模型的思考过程可能包含训练数据里的敏感信息或者会泄露系统Prompt的内容。我们会在存储前做一轮敏感信息过滤。3.3 输出审计最后一道防线输出审计不只是敏感词过滤它至少包含四个维度合规性有没有违反行业规范或内部政策的内容一致性输出和检索到的知识是否一致有没有自相矛盾置信度模型对输出的置信程度可以通过logprob或自评得到可解释性输出是否附带了足够的依据还是纯粹的“编造”我们实现了一个输出审计评分卡每个维度打分综合低于阈值的输出会被标记为“需人工复核”或直接拦截。审计维度检测方法阈值处置动作合规性敏感词库分类模型命中即拦截拦截告警一致性与检索片段做NLI矛盾分数0.7标记降级置信度logprob均值0.6标记人工复核可解释性依据覆盖率0.5标记补充检索3.4 溯源链的构建与防篡改溯源链的核心是哈希链式结构。每条审计记录在写入时会计算自身内容的哈希并链接到前一条记录的哈希。这样任何对历史记录的修改都会导致后续所有哈希失效从而被检测到。class AuditChain: def __init__(self): self.records [] self.last_hash GENESIS def append(self, record): record[previous_hash] self.last_hash content json.dumps(record, sort_keysTrue) record[hash] sha256(content self.last_hash) self.last_hash record[hash] self.records.append(record) def verify(self): last_hash GENESIS for record in self.records: content json.dumps( {k: v for k, v in record.items() if k ! hash}, sort_keysTrue ) expected sha256(content last_hash) if record[hash] ! expected: return False, record[trace_id] last_hash record[hash] return True, None实操心得哈希链的验证不需要每次全量跑可以定期抽样验证或者在关键审计场景如监管检查前全量验证。全量验证10万条记录大约需要3-5秒完全可以接受。4. 实操落地从零搭建一套可用的溯源审计系统4.1 技术选型与部署架构我们的技术栈选择如下审计数据存储PostgreSQL结构化元数据 MinIO原始文本和大对象哈希链存储单独一张表只追加不修改检索Elasticsearch支持按trace_id、user_id、时间范围、风险等级等多维查询消息队列Kafka用于异步审计数据的缓冲和分发审计服务Python FastAPI提供写入和查询接口部署架构上审计服务独立部署不和应用服务混在一起。这样做的好处是即使应用服务被攻击或出现故障审计数据依然安全。同时审计服务的写入接口做了限流和鉴权只有持有合法token的应用才能写入。4.2 埋点接入如何做到对业务代码低侵入低侵入是我们非常看重的一点因为如果接入成本太高业务团队就会抵触最后变成“写了但没人用”。我们的方案是装饰器中间件对于LLM调用提供一个统一的AuditedLLMClient业务代码只需要把原来的openai.ChatCompletion.create换成audited_client.chat其余参数不变。对于RAG检索提供一个AuditedRetriever包装原有的检索逻辑。对于Agent提供一个AuditedAgentExecutor在每一步执行前后自动埋点。# 使用示例 from audit_sdk import AuditedLLMClient, AuditedRetriever client AuditedLLMClient(api_key..., audit_endpoint...) retriever AuditedRetriever(base_retrievermy_retriever, audit_endpoint...) # 业务代码几乎不用改 docs retriever.search(query, top_k5) response client.chat(messages[...], modelgpt-4)注意审计SDK本身要足够轻量不能成为性能瓶颈。我们的SDK在同步模式下增加约15ms延迟异步模式下几乎无感。如果业务对延迟极度敏感可以配置为纯异步模式但会牺牲实时拦截能力。4.3 溯源查询怎么快速定位问题溯源查询是审计系统最常用的功能。我们设计了三种查询模式模式一按trace_id精确查询。当你已经知道是哪次请求出问题时直接输入trace_id系统会展示完整的溯源链包括每个节点的输入、输出、耗时、哈希。模式二按条件筛选。支持按用户、时间范围、风险等级、模型版本、Prompt模板版本等条件组合筛选。比如“查一下昨天所有风险等级为高的请求”。模式三反向溯源。这是最有价值的功能。当你发现某条知识片段有问题时可以反查“哪些输出引用了这条片段”从而评估影响范围。-- 反向溯源示例查找引用了特定文档的所有输出 SELECT DISTINCT trace_id, output_content, timestamp FROM audit_records WHERE node_type retrieval AND output_data-doc_id doc_12345 AND timestamp now() - interval 7 days;4.4 性能优化别让审计拖垮业务审计系统的性能优化主要从三个层面入手写入优化批量写入异步刷盘。我们设置了一个内存缓冲区每100条或每500ms刷一次写入吞吐从单条200QPS提升到批量5000QPS。存储优化冷热分离。最近7天的数据存SSD7天到90天的存HDD90天以上的转对象存储。查询时自动路由到对应存储层。查询优化预建索引查询缓存。Elasticsearch的索引按天分片常用查询条件都建了索引。高频查询结果缓存5分钟。优化项优化前优化后提升倍数写入吞吐200 QPS5000 QPS25x单次溯源查询3.2s0.4s8x存储成本/月1200元380元3.2x5. 常见问题与排查技巧实录5.1 审计数据对不上怎么办这是最常见的问题业务日志显示模型输出了A审计记录显示输出了B。排查思路如下第一步检查时间戳对齐。业务日志和审计日志的时间戳可能来自不同服务器存在时钟偏差。我们统一用NTP同步偏差控制在50ms以内。第二步检查是否有后处理环节。模型原始输出可能经过了敏感词过滤、格式化、截断等后处理最终返回给用户的内容和审计记录的原始输出不一致。我们的做法是在审计记录里同时保存raw_output和final_output。第三步检查是否有多个审计写入点。如果应用层和网关层都埋了点可能出现重复记录或覆盖。我们通过trace_id node_id做唯一约束避免重复。5.2 哈希链验证失败怎么处理哈希链验证失败意味着审计数据可能被篡改或损坏。处理流程立即隔离停止对该链的写入防止污染扩散定位断点找到第一个哈希不匹配的记录记录其trace_id和时间戳交叉验证用网关层日志、业务日志、系统日志做交叉比对判断是数据损坏还是人为篡改恢复如果是数据损坏从备份恢复如果是人为篡改触发安全告警并保留证据实操心得我们每个月做一次全量哈希链验证每次大约5分钟。虽然大部分时候都是“验证通过”但有一次真的发现了一条记录被误修改运维人员直接改了数据库及时修复了。5.3 审计系统本身被攻击怎么办审计系统是“审计别人”的但它自己也可能成为攻击目标。我们的防护措施写入接口鉴权只有持有合法token的应用才能写入token定期轮换只追加权限数据库账号只有INSERT权限没有UPDATE和DELETE权限独立部署审计服务和业务服务物理隔离即使业务被攻破审计数据依然安全操作审计对审计系统的所有查询和配置变更也做审计防止“审计员被收买”5.4 常见问题速查表问题现象可能原因排查方法解决方案溯源链断裂埋点遗漏或写入失败检查trace_id是否连续补埋点重试机制查询超时数据量过大或索引缺失查看ES慢查询日志加索引冷热分离哈希验证失败数据篡改或损坏定位断点交叉验证恢复告警审计延迟高同步写入阻塞检查写入耗时改异步批量写入存储暴涨全量存储未分级统计各场景数据量分级存储采样5.5 几个踩过的坑坑一忘了记录Prompt模板版本。有一次模型行为突变排查了半天才发现是模板被改了。后来我们把模板版本号作为必填字段。坑二异步审计丢数据。早期用异步写入时服务重启导致缓冲区数据丢失。后来加了本地磁盘缓冲重启后自动恢复。坑三检索片段没记来源。一开始只记了片段内容没记文档ID和版本。后来发现某条知识过期了但不知道哪些输出受了影响。现在文档ID、版本号、生效时间都是必填。坑四没考虑多租户隔离。早期查询接口没有租户过滤A租户能查到B租户的审计数据。后来在查询层强制加了租户ID过滤。6. 这套方案的实际效果与后续扩展6.1 上线三个月的真实数据系统上线三个月覆盖了内部6个LLM应用日均审计记录约8万条。关键指标问题定位时间从平均45分钟降到平均3分钟审计覆盖率核心链路100%非核心链路85%哈希链验证每月全量验证累计发现2次数据异常均已修复存储成本控制在每月400元以内最直观的变化是以前出问题大家第一反应是“模型又抽风了”现在第一反应是“查一下溯源链”。这个转变本身就说明审计层起到了作用。6.2 后续可以怎么扩展上篇主要讲溯源审计的骨架下篇我会展开讲规则引擎和告警联动。这里先提几个扩展方向实时拦截基于审计数据的实时风险评分高风险输出直接拦截自动归因用LLM分析溯源链自动生成问题归因报告审计报表面向合规部门的自动化审计报表支持导出和签章跨系统溯源把审计链扩展到LLM之外的系统比如数据库操作、API调用6.3 一个实用小技巧如果你刚开始做审计层不要一上来就追求大而全。我的建议是先把trace_id打通再把Prompt和输出记全最后做哈希链。这三步做完你已经能解决80%的排查问题。剩下的20%——比如反向溯源、实时拦截——可以后续迭代。另外审计数据的保留策略要提前想清楚。我们一开始没想好导致存储成本失控。后来定了“7天热存、90天温存、1年冷存”的策略成本才降下来。最后分享一个我们内部用的溯源查询快捷命令基于审计服务的CLI工具# 按trace_id查询完整溯源链 audit-cli trace --id tr_abc123 --format tree # 按用户查询最近高风险记录 audit-cli query --user u_456 --risk high --last 24h # 反向溯源查引用某文档的所有输出 audit-cli reverse --doc doc_789 --since 2024-01-01这套工具我们开源了内部版本核心逻辑不复杂关键是坚持用、持续迭代。审计这件事做一天容易做一年难。但只要坚持下来它带来的确定性和安全感是任何模型能力提升都换不来的。