AGT 外部评估器交接(External Evaluator Handoff)依赖审计:有界依赖、SDK 摘要助手与零权限交接边界

原创2026-09-19 21:19:101,374 阅读
文章标签:人工智能AI AgentAI 安全治理策略引擎认证鉴权Agent 沙箱可观测性

AGT 外部评估器交接(External Evaluator Handoff)依赖审计:有界依赖、SDK 摘要助手与零权限交接边界

本篇技术指南围绕 agent-governance-toolkit(AGT)仓库中 docs/dependency-audits/2026-09-03-external-evaluator-handoff.md 这份依赖审计记录展开,解读 examples/external-evaluator-handoff 独立示例新增的两项有界依赖(AGT core v5 与 pytest v8+)的来龙去脉,并深入到示例源码、测试用例与底层 SDK 实现,说明「外部评估器交接」为何必须站在公共 DecisionBOM 模型与 sha256_jcs 摘要助手之上,以及该边界如何做到只读、零授权、不影响任何现有包与运行时行为。读完本文,你将掌握该示例的依赖约束原理、交接请求的构造与校验方式,以及如何安全地运行、测试和评估其变更风险。

一、审计背景:一次针对独立示例的依赖变更

docs/dependency-audits/ 目录是仓库的依赖审计专区,其 README.md 规定:任何 PR 只要改动 lockfile(requirements.txt、Cargo.lock、package-lock.json、go.sum、packages.lock.json 等)或 vendored 内容,必须附带一份按 YYYY-MM-DD-<short-description>.md 命名的审计文档,并包含三个必备小节:依赖变更及原因、安全公告相关性、破坏性变更风险评估。

2026-09-03 这份审计记录的变更对象是新增的独立示例 examples/external-evaluator-handoff/requirements.txt,其内容仅两行:

agent-governance-toolkit-core>=5.0.0,<6.0
pytest>=8.0.0,<10.0

关键结论在文档开头便已点明:这两项依赖被隔离在示例内部,不改变任何 AGT 包或仓库级运行时依赖图。它既不是安全公告的修复,也不引入新的密码学实现。

二、两个依赖各自的职责边界

1. agent-governance-toolkit-core>=5.0.0,<6.0:复用公共 SDK 面

该依赖向示例供给两个现成的 SDK 构件:

  • DecisionBOM 模型:决策物料清单模型,用于承载一次治理决策的全部可重建因素;
  • 公共 sha256_jcs 摘要助手:基于 RFC 8785 JSON 规范化(JCS)的 SHA-256 内容摘要。

审计文档特别强调:v5 下限是必需的,目的是让示例不要绕过 SDK 边界、直接教读者使用裸密码学原语。也就是说,示例中任何摘要、任何序列化都应当复用 AGT 公共面,而非自造实现。

2. pytest>=8.0.0,<10.0:仅测试期依赖

pytest 只用于运行示例的本地回归测试,不被可运行示例本身 import。这是一个典型的「测试依赖与运行依赖分离」实践:运行示例无需安装测试框架,测试环境才需要。

三、为何是 v5:DecisionBOM 与 sha256_jcs 的源码级依据

要理解 v5 下限的含义,需要看这两个构件在仓库中的真实定义。

DecisionBOM:可重建的决策视图

DecisionBOM 定义于 agent-governance-python/agent-mesh/src/agentmesh/governance/decision_bom.py,其模块注释明确其设计哲学:可重建视图(reconstructible view)——不要求在决策时预构建 BOM(那会与 action 管线强耦合),而是在事后按需查询审计日志、信任分、策略评估、OTel trace 等可观测信号,重建完整的决策上下文,遵循「不完整但非侵入 > 完整但侵入」的原则。

模型核心包括:

  • BOMFieldCategory 枚举:identity、trust、policy、action、context、outcome、lineage 七类,与治理关注点对齐;
  • BOMField 数据类:name、category、value、source(信号来源)、confidence(0.0-1.0 置信度,默认 1.0)、inferred(是否为重建推断值);
  • DecisionBOM 数据类:decision_id、timestamp、agent_id、action_requested、outcome(allow/deny/alert)、fields、reconstructed_at、sources_queried、completeness_score,并提供 get_fields_by_category() 与 to_dict();
  • DecisionBOMReconstructor:按 trace_id 或 agent_id+timestamp 从四个信号源(audit/trust/policy/trace)分阶段重建 BOM,并依据 REQUIRED_FIELDS(agent_identity、trust_score_at_decision、policy_rules_evaluated、action_type、decision_outcome)计算 completeness_score。

交接示例正是围绕这一模型构建的,而不是另起炉灶引入第二套审计/策略模型。

sha256_jcs:RFC 8785 规范化摘要

sha256_jcs 实现于 agent-governance-python/agent-mesh/src/agentmesh/governance/approval_protocol/digest.py,属于 ADR-0030 批准协议的一部分:每个批准都通过对请求的规范化序列化做哈希,绑定到被审查的精确动作上。该模块不依赖第三方库,聚焦实现 RFC 8785 的核心要求:

  • 对象成员按键的 UTF-16 码元排序(_utf16_units);
  • 无多余空白(仅 , 与 : 分隔符);
  • 字符串采用最小 JSON 转义,非 ASCII 保留为字面 UTF-8;
  • 整数无小数点序列化,有限浮点用最短往返 repr,整数型浮点归一化为整数,NaN/Infinity 一律拒绝(_format_number);
  • canonicalize() 返回规范 UTF-8 编码,sha256_jcs() 返回 "sha256:<小写十六进制>" 前缀形式。

模块注释明确指出:字节级与非 Python JCS 实现的完全一致被跟踪为跨 SDK 一致性工作(ADR-0030 第 6 步);而在 Python 治理面内部,序列化自洽稳定——在 AGT 内部计算出的摘要,在 AGT 内部一定可以复现。这正是动作摘要绑定所要求的最小成立条件。

四、交接示例如何使用这两个构件:零权限只读边界

示例主程序 examples/external-evaluator-handoff/external_evaluator_handoff.py 的模块注释声明了它的边界纪律:示例刻意停在互操作边界上——不调用评估器、不修改 AGT 记录、不把评估结果变成治理决策。

核心函数 build_external_evaluation_request(decisions, *, generated_at, allowed_field_names=()) 的行为要点:

  • 非空校验:决策集为空直接抛 ValueError;
  • 时区强制:_utc_timestamp 拒绝无时区的时间戳,统一转 RFC 3339 UTC 格式(+00:00 替换为 Z),保证重放无歧义;
  • 字段白名单默认空集:默认不导出任何可选字段,避免把任意策略、上下文或 trace 数据误带出边界;
  • 严格 JSON 检查(fail-closed):_export_field 用 json.dumps(..., allow_nan=False, ensure_ascii=False, separators=(",", ":"), sort_keys=True) 序列化再反序列化,无法严格 JSON 化的字段值直接抛 ValueError;
  • 权威边界固定:请求体中的 authority_boundary 恒为 read_only: True、source_records_mutated: False、execution_authorized: False、policy_decision_overridden: False、evaluation_result_is_governance_decision: False——下游评估器收到的是观测,既不能授权动作、不能覆盖策略,也不能修改源记录;
  • 内容派生 request_id:先对完整请求(不含 request_id)计算 sha256_jcs,再以 eval_<hex> 形式挂到请求上,因此标识符本身被排除在自身哈希输入之外。

请求的 source 声明固定为 system: "agent-governance-toolkit"、representation: "decision_bom",明确交接的是「决策 BOM 表示」,框架中立,任何外部评估器都可以消费同一观测边界。

五、测试如何锁定这些承诺

示例测试 examples/external-evaluator-handoff/test_external_evaluator_handoff.py 用 pytest 逐条锁定了上文的承诺,这解释了为何 pytest 是测试必需依赖:

  • 确定性:test_handoff_is_deterministic_allowlisted_and_read_only 用同一输入两次构造请求,断言输出完全相等,且源 DecisionBOM 的 to_dict() 前后一致(源不可变);
  • 摘要正确性:test_request_id_uses_the_public_sdk_digest 弹出 request_id 后,用公共 sha256_jcs 对剩余 payload 重算,断言 request_id == "eval_" + digest,验证 request_id 确实来自公共 SDK 助手而非私有实现;
  • 默认零导出:test_handoff_exports_no_optional_fields_by_default 断言未传白名单时 fields == [];
  • 值脱离(detach):test_handoff_detaches_mutable_allowlisted_values_from_source 修改导出值不影响源 BOM,修改源 BOM 也不影响已导出值——白名单值被拷贝为脱离的严格 JSON 副本;
  • fail-closed 校验:空决策集、无时区时间戳、非 JSON 可序列化值分别触发三类 ValueError(test_handoff_rejects_empty_decision_set、test_handoff_rejects_timezone_free_timestamps、test_handoff_rejects_non_json_allowlisted_values)。

六、安全公告相关性:不引入新密码学实现

审计文档对此节结论明确:本次变更不是安全公告修复,也没有新增任何密码学实现。内容摘要全部委托给 AGT 既有公共 SDK 助手(sha256_jcs);pytest 是纯测试依赖;仓库既有的依赖审查与漏洞检查(如 scripts/ 下的 check_* 系列脚本与 CI 门禁)对解析后的依赖图仍然权威有效。因此该变更的安全面为零增量。

七、破坏性变更风险评估:low 且 example-local

审计结论为 风险低、仅限示例内部:

  • 示例要求 AGT core v5,是因为公共摘要助手属于受支持的 v5 表面;钉在 AGT core v4 的用户若想运行该示例,必须先升级;
  • 但没有任何既有包、API、策略或运行时行为被改变;
  • 上界(<6.0、<10.0)将解析约束在目前受支持的主版本区间内,防止未来主版本漂移导致意外解析。

八、验证与回滚:可复现的完整闭环

审计文档列出的验证覆盖面包括:示例测试套件、格式与 lint 检查、严格 JSON 输出、文档链接,以及仓库的依赖门禁与「未授权密码学(unauthorized-crypto)」门禁。你可以按示例 README examples/external-evaluator-handoff/README.md 的指引自行复现:

# 前置:Python 3.11+,运行时无需 API key 或网络服务
python -m venv .venv
source .venv/bin/activate
pip install -r examples/external-evaluator-handoff/requirements.txt

# 从仓库根目录运行示例(打印一个合成请求)
python examples/external-evaluator-handoff/external_evaluator_handoff.py

# 运行回归测试
PYTHONPATH=agent-governance-python/agent-mesh/src \
  pytest -q examples/external-evaluator-handoff/test_external_evaluator_handoff.py

运行示例时请留意输出中的 authority_boundary 段,其五个布尔字段恒为固定的只读/零授权声明;同时注意 README 中的数据与安全提示:

  • Decision BOM 字段可能包含策略、上下文或 trace 数据,因此除非调用方显式白名单精确字段名,否则不导出任何可选字段;
  • source_completeness 只描述 BOM 重建覆盖率,不证明真实世界中某事件正确、已授权或完整;
  • sha256_jcs 内容是哈希,不是签名、不是证明、不是真实性证明;
  • 示例使用合成标识符与合成值,导出真实记录前需评估租户、隐私、留存与跨境合规要求。

回滚方案则极为轻量:移除该独立示例与这条审计记录即可,不涉及任何生产数据或迁移——因为变更从未触及仓库级运行时依赖图。

九、总结:一次「接口优先」依赖审计的范本

这份审计记录的价值在于演示了 AGT 的一个原则性实践:接口优先(interface-first)边界。运行时治理(AGT 回答动作是否被允许、应用了哪条策略、观测到哪些治理信号)与执行后评估(下游评估器跨时间比较多次决策并产出可审查的评估)是两个不同的问题;评估结论只是独立审查流程的输入,不是许可,也不覆盖 AGT。本次依赖变更以最小的、被充分论证的两项有界依赖,让独立示例在不破坏任何现有面的前提下,复用了公共 DecisionBOM 与 sha256_jcs,并辅以测试锁定确定性、白名单、不可变与零授权边界——这正是依赖审计应当达到的信息密度:变更动机、安全影响、破坏性风险、验证与回滚,一应俱全。

登录后查看全文
agent-governance-toolkit