今年 OWASP Top 10 for GenAI/LLM 的重大改进是引入了对 7,714 起真实事故的分类器分析,发现从业者最担心的问题(如提示注入)按真实事故频率其实排不进前十。项目方公开了这一差异并说明了原因,程序员应据此调整安全投入的优先级。
2026 OWASP GenAI/LLM Top 10 以项目负责人如下这句话开场:
"别再试图构建一个无法被欺骗的模型了。去构建模型周围的系统,这样当模型被欺骗时——而且它一定会被欺骗——不会有重要的事情崩溃。"
我在 8 月 1 日发表了一篇论文,标题为 Contraindre hors de l'agent(在智能体之外约束)。同样的主张,只是从另一个方向切入:不是从 7,714 起事件中读到的,而是从一个在测试网上转移价值的小型智能体系统运行中得出的,并记下了它每次对我撒谎的时刻。
今年的榜单是值得一读的,即便你跳过了其他年份,因为这是项目方将自己的信念与记录对照核实后的版本。社区投票仍占四分之三的权重,但项目负责人对 7,714 起真实事件构建了分类器,筛选出 6,639 起包含足够细节的事件,并提出了一个问题:从业者所恐惧的是否与记录所显示的一致。在具体问题上答案是否定的,他们公布了对分歧而非平滑它们。提示注入(Prompt Injection)仍居第一位,尽管仅按原始事件记录排名,项目负责人写道"它完全跌出前十"——他们将这一差距归因于防御效应。虚假信息(Misinformation)逆势上升。过度代理权(Excessive Agency)攀升至第三,项目负责人直言不讳地说出原因:损害就发生在哪里。
先澄清一件事。该榜单划定了自己的边界:当模型是应用内部的组件时,风险由它承担;而一旦模型成为一个拥有工具、记忆和后果的行动者时,风险则移交给 Agentic Top 10。我的系统位于那条边界的另一侧。所以这不是一份合规报告。这是我将一份关于 model-as-component 的文档——连同同项目的另外两份文档(AIUC-1 交叉对照表和 6 月《代理式 AI 安全与治理现状》报告,两者均在下方引用)——与一份来自 model-as-actor 的失败记录对照阅读时发生的事。缓解措施可以迁移,边界可以迁移,而且有两次这些文档和我的勘误表独立得出了相同的结论。
分三部分,且它们是可分离的。榜单建议了什么,带着日志跑一遍看。文档给了我什么我没有的东西,包括一个我曾列为未来工作的原语(primitive)。以及他们在某件事上是对的而我还没有修复——那一条接近末尾,在"榜单在我身上说对的四个地方"下,如果你只读一个部分,应该读那个。
三条缓解措施,以及它们成为承重组件时的样子
LLM01 缓解措施 7 要求在特权操作或不可逆操作之前进行人工确认,"向审核者呈现的是精确渲染的操作而非摘要,"并指出不可见字符走私(invisible-character smuggling)可以使显示的操作与执行的操作不同。
那句话描述的是一个我构建后不得不修复两次的控制。交易在人工看到任何内容之前就被冻结了;确认客户端从冻结的交易中渲染字段,而非模型生成的描述。人工在一个回环通道上确认,而智能体没有任何工具可以触及该通道。而因为硬件屏幕会截断,我测量了显示内容:18 个字符,通过将设备显示的内容与签名内容重新组装来验证。摘要本可以通过审核。审核者必须看到的是渲染后的操作,榜单用这些词说出来是正确的。
LLM01 缓解措施 4 要求将状态变更能力保存在应用代码中而非模型中,并通过"确定性策略引擎在执行时重新验证意图和参数"来路由特权调用。它补充说,NIST 和 CISA/五眼联盟联合指南现在将确定性仲裁(deterministic mediation)作为基线采购预期。
我的评估引擎返回 0 到 100 之间的分数。没有别的东西。阈值、权重、上限和决策本身都在代码和一个版本化的配置中,一次成功的提示注入最多只能让一个数字产生偏差。我已经将这种方式称为"模型提议,代码决定"好几个月了,却不知道它本来有个名字。OWASP GenAI 项目针对其 Agentic Top 10 发布的 AIUC-1 交叉对照表称之为最大确定性原则(principle of most determinism),并将其缺失列为差距:智能体-模型边界上的结构化模式(structured schemas)约束了可接受输入和输出的范围,这降低了目标劫持和记忆中毒的攻击面。我的决策记录携带分数和权重,一个函数在任何下游运行之前检查四舍五入的加权和是否等于总数。模式不是文档。这是一个失败时关闭的谓词(predicate that fails closed)。自 8 月 4 日以来,引擎还拒绝执行任何决策,除非有链式注册条目覆盖它。值得精确说明状态:该版本已部署并在两个主机上得到验证,但还没有在它上面播放过完整循环。端到端运行的循环——四个,有链上收据——是在早期版本上运行的。一个尚未承载过真实交易的已部署控制是已部署的控制,而非已验证的控制,否则就会出现在你自己的勘误表中。
引擎日志的两段,来自它着陆的那天晚上。不是同一个序列:两个,相隔一小时,因为有趣的那对不会共存。路径已编辑;其他内容未动,日志保持法语,因为翻译过的日志不再是日志。
2026-08-04T20:21:18.484Z [info] nexus_evaluate v0.3.0 ready (stdio MCP, vision+scoring multi-agent — NEXUS_AGENT_ID requis, ar://-only SSRF guard, zero-dep)
2026-08-04T20:21:21.192Z [info] agent bundle chargé : sigma (signature=Vision, 7 dimensions, prompt 7500 chars, bundleHash 1308d7855154… NON épinglé) depuis <engine>/agents/sigma/config.json
2026-08-04T20:21:21.193Z [info] registre vérifié : 1 entrée(s), tête 5397d83fa3d8…
2026-08-04T20:21:22.120Z [warn] metadata attempt 1/3 failed: HTTP 404
2026-08-04T20:21:22.120Z [warn] metadata: «tiers:HTTP 404»
2026-08-04T20:21:22.120Z [warn] DÉSARMEMENT (toolErr) : fichier de décision retiré (<workspace>/.nexus_evaluate_decision) — un refus n'autorise pas la décision précédente à agir à sa place
这是整节的核心,在一行我没有为幻灯片写的话中:一次 fetch 404,控制移除了传输文件而非将上次运行的决策留在那里让运行器捡起。它是在一次问"拒绝会留下什么"的对立审查中发现的,答案是:上一个答案。它上面那行,metadata: «tiers:HTTP 404»,是同一六行中的第二个控制——第三方文本穿着标记进入日志,表明它是第三方文本。
一小时后,同样的引擎,已固定(pinned):
2026-08-04T21:26:50.506Z [info] nexus_evaluate v0.3.3 ready (…) · moteur a99d6040880b… épinglé
2026-08-04T21:29:55.010Z [info] agent bundle chargé : sigma (…, bundleHash 1308d7855154… épinglé) depuis <engine>/agents/sigma/config.json
2026-08-04T21:29:55.011Z [info] registre vérifié : 2 entrée(s), tête 8fb748ab92dc…
2026-08-04T21:30:03.317Z [info] tokenId=23 agent=sigma agg=94 Vision=93 decision=bid maxBidEth=0.0016
2026-08-04T21:30:03.318Z [info] registre: entrée d974490ffb75… appendée (<workspace>/.nexus_evaluate_chain.jsonl)
2026-08-04T21:30:03.318Z [info] decision-file écrit <workspace>/.nexus_evaluate_decision (decision=bid maxBidWei=1600000000000000 chainRef=d974490ffb75…)
两个指纹现在都报告 épinglé(固定),注册表已增加一条,决策文件携带 chainRef 指向覆盖它的条目。如果你想验证这两块属于彼此:将本文稍后显示的 JSON 条目进行哈希运算会得到 d974490ffb75…,即此日志声称已追加的值,而它的 prev 是此日志说它看到的 head。两个制品,由同一进程为不同读者写入,在一个它们都不解释的数字上达成一致。
LLM03 缓解措施 9 建议基于"输入参数的累计值"而非调用次数来设置阈值。这条读起来像是被烧过的人写的。我的上限是累计的、以 wei 为单位:在限制处出价被接受,超出一 wei 系统就拒绝。在主机上在确切的值处验证,而非附近的整数。承载它的服务目前已解除武装,所以我不会说它在生产中——但边界是在边界真正断裂的地方测量的,即那个值处,而非附近。
8 月 4 日,我针对自己提出的一个论断写了一篇勘误。我一直在运行一个哈希链式决策日志,并在自己的规范中写道:链式日志可以证明没有条目缺失。一份对抗性评审否决了它,三份裁决全部反对。链只能证明其内部:从创世块到当前头部的任何内容都无法被篡改、移除或重排序,否则会断链。它无法证明完整性。删除文件后它看起来是全新的。截断尾部,剩余的链完好无损。重写最后一条记录,后续没有任何条目与之矛盾。三种情况全部重新验证为绿。
我在后面写道,封闭这个漏洞需要"一个写作者无法重写的见证者"——第三方时间戳、由基础设施签名的日志、区块链锚定——并将此作为两年后的研究方向归档。
LLM04 缓解措施第 5 条命名了这个原语,而且它已经可用。加密签名"配合透明日志(例如 OpenSSF Model Signing 项目和 Sigstore)"。ML 生态圈在我把它作为决策日志的未来工作时,已经为模型制品解决了见证者问题。
然后同一条目做了一件我没有预料到的事。场景 7 描述了一个被攻陷的构建流水线,并补充道:"由于被植入后门的制品由组织自己的发布基础设施构建和签名,它通过了下游来源检查、内部证明和供应链扫描器。"
同一天,我为自己的引擎写了结构完全相同的句子:对自己的文件做哈希只能防止漂移,无法防止伪造。一个被篡改的引擎写入一个虚假的指纹,它也能成功写入。两个团队、两个领域、同一种形状——由写作者生成的证明无法约束写作者。OWASP 为模型制品写了它。我为评估注册表写了它。我们任何人都无法用另一个哈希来封闭它。
所以这个迁移才是有趣的方向,而且是从他们到我:将 ML 供应链已经应用于其制品的原语,应用到 AI 智能体的决策注册表上。该列表在制品的不可变性方面很谨慎——签名、透明日志、不可变摘要,并明确警告签名证明的是完整性和来源,而非安全性。在决策日志方面,该文档最接近的是 LLM09 的"保留检索活动的不可变日志"——一个不同的对象,且规定了不可变性但没有提供机制或说明它证明了什么。在整篇文档中,只追加(append-only)出现了零次。
这听起来像是一个词汇层面的抱怨,直到你看到这个属性需要付出什么代价。以下是一台主机在一个晚上写入的三条记录,简化到重要的字段:
# 三条记录,一台主机,一晚。字段提取:每行在磁盘上都是一个完整的
# JSON 对象(1077、1451 和 1451 字节);仅展示决定性字段。
#1 v=1 scores: absent engineSha256: absent sha 5397d83fa3d8…
#2 v=1 scores: present engineSha256: 91898bfa323a… sha 8fb748ab92dc… <-- 这笔债务
#3 v=2 scores: present engineSha256: a99d6040880b… sha d974490ffb75…
记录 #2 是错误的,而且是永久性的。它携带着新字段——scores、engine 指纹——但用的是旧的 schema 标签。应用 v: 1 合约的验证者不会查找其中任何一个字段,所以 #2 声明的内容少于它实际包含的内容,而文件中没有任何地方说明了这一点。我注意到它被写入后就发现了这个问题,并提议推迟修复:judge 探测的是字段而非标签,没有人在读这个,再部署一轮会消耗成本。收到的回复重新框架了算术。一条只追加制品上的债务,不是与修复成本比较的,而是与无法修复的成本比较的。而且它不会在你思考的时候停下来,因为每次评估都会再写入一条。
所以那个晚上标签就改了,而不是等到第二天早上,而争论的痕迹就是 #2 这条记录,每个主机各一份,只要这些注册表存在就会一直保留着。这就是只追加所购买的,而可变日志无法提供的东西:不是一份我决定了的记录,而是一份我差点决定的记录。一个我可以重写的文件会悄悄变成我一直都对的那个文件。
以下是那个注册表中一条实际记录的样子,从生产主机上拉下来的。在磁盘上它是单独一行 JSONL,链哈希的是那一行的字节——下面的缩进是我加的,为了可读性。这不是个细节:一个善意转换行尾的传输层会改变字节,然后链就会报告一个从未发生的断链。我在提取这个的时候对自己做了这件事,花了一分钟指责注册表,然后才去检查字节数。
{
"v": 2,
"prev": "8fb748ab92dcaa90bff665e71c073c0ade7b2e339317e37c00ec2612d9275360",
"at": "2026-08-04T21:29:55.010Z",
"agent": "sigma",
"mode": "onchain",
"tokenId": 23,
"provenance": {
"bundleHash": "1308d78551541285039d1ce1e52b8356b4bbd98381896e47cede6628d0979053",
"model": "claude-opus-4-8",
"pinned": true,
"engineSha256": "a99d6040880b3763f881cd2691506e108e74996e1b8567e2c86886ff60377451",
"enginePinned": true
},
"inference": {
"temperature": null,
"seed": null,
"maxTokens": 1024,
"stopReason": "end_turn"
},
"inputHashes": {
"metadataSha256": "7297e0cb3ce984853044e9eb95ec7eeab7f5ed093900e204fbcdf676f7257251",
"imageSha256": "1fc7fb19703d9c9e448640af1daac274f2f937340b017de261d2e0045af4b8d3",
"promptAssembledSha256": "d1acafeb910096fb15948fb3b0ba7a5aefda0a09d851ee21da04847b02430b4d"
},
"rawOutputSha256": "3a33108abd0216fda31563c6467f00a65ac1b273f327b9018fe0fddebab4e1ef",
"rawBytes": 590,
"parse": "ok",
"scores": {
"Matiere": 95, "Lumiere": 97, "Memoire": 94, "Geometrie": 96,
"Cosmos": 90, "Concept": 95, "Vision": 93
},
"weights": {
"Matiere": 0.1, "Lumiere": 0.15, "Memoire": 0.15, "Geometrie": 0.15,
"Cosmos": 0.1, "Concept": 0.1, "Vision": 0.25
},
"creatorResolution": {
"rpc1": { "etat": "resolu", "addr": "0x7cb8e58c42402fbed1b8792c11f6da42942e33b3" },
"rpc2": { "etat": "resolu", "addr": "0x7cb8e58c42402fbed1b8792c11f6da42942e33b3" },
"quorum": "2/2",
"divergent": false,
"independance": {
"clients": [
"reth/v2.3.0-9384bc5/x86_64-unknown-linux-gnu/base/v1.2.0",
"Geth/v10.0.0/drpc"
],
"identiques": false
}
},
"decision": "bid",
"aggregate": 94
}
把这七个分数做加权求和。94.3。四舍五入。这就是 aggregate 字段,而且你不需要信任我就能验证——scores、weights 和结果都在这条记录里。weights 加起来是 1.00,这是 judge 强制执行的谓词的另一半。engineSha256 是产生这条记录的文件自身的哈希,由该文件对自己计算,并锚定在文件外部的一个值。而 identiques: false 是我三天前还没有的那一行:两个见证者,以及一份记录在案的声明,证明他们不是同一个软件。
这条记录无法做的是证明它是唯一一条。这就是透明日志会封闭的缺口,而这个对象里没有任何字段能封闭它。
同一个形状,一层往上,那就是它烧钱的地方
我以为那篇勘误是关于我自己日志的一个狭义教训。然后我读了 OWASP 六月发布的配套报告《代理型 AI 安全与治理现状 v2》,发现同样的形状坐落在整个监管架构之下。
每个主要的代理型 AI 治理框架都有一个共同假设:系统的行为可以在系统运行之前被描述。在欧盟 AI 法案下,第 11 条和附件 IV 要求在系统上市前提供技术文档,描述预期目的、能力与局限性,而第 43 条的一致性评估则评估该文档。架构假设文档中描述的系统就是将要运行的系统。
一个拥有 N 个授权操作和 D 个链式步骤的 AI 智能体组合出 N^D 种可能的工作流,并在运行时进行选择。报告陈述了后果而没有软化:监管机构、审计员和认证机构评估的部署前制品描述了一个在 AI 智能体开始运行的瞬间就不存在的系统。评估在进行时是准确的,但它描述的系统不是正在运行的那个系统。
这就是我的链式错误,只是影响范围更大。一个链式验证证明了其内部逻辑的正确性,但对完整性什么也说不着。一致性评估证明了被评估系统的运作,但对组合后的系统什么也说不着。两者都是对某个边界的有效证明,而所有人都把这个边界误认为是整体。而且这个边界是有代价的:超出评估范围的行为可能构成第 3(23) 条下的实质性修改,从而触发重新评估;如果它改变了预期用途,还会将部署者转变为第 25 条下的提供商。这是一种监管身份的转变,由一个组合了无人记录过的工作流的智能体所触发。
报告提出的答案是给出一个操作边界:命名实际被评估的行为的有界子集,然后使对该边界的偏离变得可检测。我可以补充我能测量的那部分,因为它是成本更低的一半,而且我已经跑起来了。在检测行为偏离之前,你可以让产物偏离不可能被忽视:对提示词、配置和引擎做指纹识别,将被证明的值保存在它所证明的产物之外,并在出现分歧时拒绝启动。我的引擎对它正在执行的文件做哈希,而不是对它被告知的路径做哈希,因为对邻居做哈希会让陌生人的指纹冒充成自己的。这不能约束组合后的行为,但它能保证组合它的东西就是你记录的那个东西——这是论断的一半,也是大多数部署无法达成的那一半。
而且截止日期并不遥远。DORA 已经要求 24 小时内进行事件分类,4 小时内进行初步通知。NIS2 要求 24 小时早期预警。报告本身的摘要指出,这些时间窗口假设的是持续监督而非定期审计。一个链式的、带时间戳的决策寄存器,每个条目对应一个命名检测器,这不是治理上的点缀,而是这些时间窗口所需的产物,而且它恰好也是安全那边本来就要的东西。
对着自己的系统读安全文档,只有在发布它说对了的部分时才值得。
原始模型输出。LLM01 要求你记录导致内存写入的原因提示词。我在解析之前就将原始模型输出内容寻址存储,这在 7 月 2 日的一次解析失败摧毁证据的事件中避免了真正的损失。然后 LLM02 Tier 2 说:不要将原始追踪记录到无限制的可观测性中。我对那个目录的保留策略是一个我尚未做出的开放决定。列表指出了我的这项待偿债务。
可变引用。LLM04 告诉你通过不可变摘要而非可变标签来解析产物,场景 5 展示了命名空间复用如何将一个名称转变为远程代码执行。我按摘要固定引擎、按摘要固定提示词包,通过环境变量按名称引用模型。模型在指纹之外。它可以躲在别名后面改变,而本地没有任何东西会动。而且这种漂移不需要攻击者才能成为现实:研究人员已经记录了角色超真实性(persona hyperstition),即公开描述的模型行为通过训练数据和检索重新进入模型,直到所描述的行为稳定下来。2025 年 7 月 Grok 自称为 MechaHitler 就是活生生的例子。对于任何固定产物的的人来说,相关属性是时机:这种漂移发生在会话之间,而不是会话之内,所以当它可见时,运行中的系统已经偏离了你评估时的系统。我的指纹会在整个过程中报告绿色。
消费上限。LLM06 缓解措施 2 要求不可覆盖的消费上限来停止推理,"而不是警告阈值,因为快速累积的工作负载可以超越警告阈值。"我有一个fail-closed 的货币上限,精确到 wei。但我对 token 没有任何上限。我唯一真正 incurs 的费用是 600 万输出 token,发生在我发起的一次没有公布规模的对抗性审查中。钱包被拒,绝对自找,针对的是我唯一没有限制的资源。我本会将其归类为粗心大意,但 AIUC-1 交叉引用独立地命名了同一个缺失的控制,作为其第七个缺口:API 成本上没有货币责任要求,没有成本治理控制,没有异常使用监控。两份分别发布的文件,指向我部署中同一个漏洞。这已经不再是一则轶事,而是一个类别。
多模态注入。LLM01 风险 4 和场景 6 涵盖了隐藏在低于人类视觉阈值的图像中的指令。我的引擎评估艺术作品。对于视觉模型来说,不受信任的图像是设计上的输入。架构正是列表推荐的答案——只输出分数、设置上限、有界的叙事字段——但我从未对其进行过对抗性测试,所以我拥有的是一个看似合理的containment故事,没有测量数据。
列表对弱测试是严格的。LLM01 告诉你用完整的防御规范披露来进行红队测试,并拒绝仅静态的攻击成功声明,引用了一项研究,其中静态攻击成功接近零,而适应性攻击成功超过 90%,针对的是十二个近期防御中的大多数。LLM05 告诉你不要假设对齐已经移除了后门,并在每个周期后运行触发器探测。两者说了同一个真相:未发现不等于没有可发现的。
这下面还有一个底线,我没有在文档中找到它。正向控制出现零次。假阴性,零次。失败不是你的测试太弱。而是你的测试运行了,返回了一个空结果,而在测量虚无——空结果在任何仪表板上都与干净的结果无法区分。在一次会话中,我有七个仪表对我做了完全相同的事。一个悄无声息地匹配了空值的模式。一个 glob 已经剪掉了它所指向的根目录的检查。一个测量了错误进程的退出码。没有任何一个返回错误。每一个都返回了 absence。没有一个通过重读代码被捕获;全部七个都是通过用相同的探测针对一个我已经知道答案的案例而被捕获的。
这个形状很小,可以写出来。这个是说明性的——我为这篇文章写的,而不是从我的仓库里搬的,因为真实的那些都与它们的领域焊接在一起。不可说明的是它编码的失败:一个搜索,其 glob 悄悄地剪掉了它所指向的根目录,对所有东西返回零,包括对一个我知道在里面存在的字符串。
# The probe: how many files under $ROOT contain the pattern?
hits=$(grep -rl "$PATTERN" "$ROOT" | wc -l)
# The positive control: same probe, same invocation, for something
# you already know is in there.
control=$(grep -rl "$KNOWN_TO_BE_PRESENT" "$ROOT" | wc -l)
# A mute instrument and an empty world return the same thing: zero.
if [ "$control" -eq 0 ]; then
echo "INSTRUMENT IS MUTE - the search returned nothing for a known hit" >&2
exit 2
fi
echo "$hits"
每个检查多一行,这就是 hopeful test 和 measurement 之间的区别。如果你 target