跨组织 Agent 对接收官:12 条硬条款 + 一个能跑的门检校验器

「跨组织 Agent 协作周」Day 5 · 收官篇。前四天我们拆了门票(全景)、名片(Agent Card 与发现层)、握手(委托链)、责任(审计与归责),今天把结论压成一份可执行的 checklist,并交付一个纯标准库的门检校验器 gate_check.py------输入一份对接档案 JSON,逐条机检,输出 PASS/FAIL。

TL;DR

  • 收官清单四条主线:凭证不过手 / 防线在边界外 / 注册表当索引不当背书 / 出事时十分钟三问
  • 本文含 12 条硬条款的完整定义 + 可运行代码(纯标准库,Python 3.x 直接跑)
  • 每条条款都给了本周新闻里的现场注脚,不是纸上谈兵

一、为什么收官日给的是清单而不是新知识点

因为本周最后 72 小时的新闻,恰好把前四天的每一条判断都「现场验证」了一遍:

本周判断 现场注脚(来源见文末)
凭证不过手 1Password for Agents:Agent 登录但密码在端点侧注入,Agent 拿到一次性授权(10-22)
智能的供应与权力分离 Nadella 发文 + Red Hat×NVIDIA OpenShell/Sentry:内核层划界、DPU 内存取证(10-19~20)
注册表当索引不当背书 RedMonk:MCP 注册表收录不审查、A2A 卡片签名可选(10-19);AWS:「Registry 提供连接元数据,不是认证材料」(10-20)
治理意愿强、能力弱 OutSystems×KPMG 调研:82% 自信 vs 26% 会解冲突,仅 5% 有自动化控制(10-22)

反面样本也同场出现了:那起「$50 Hack」------一人一开源框架攻破 100+ 家组织,全战役 token 账单约 5,000 美元。防御侧输的不是模型,是边界:出站不设防、凭据混用、没有硬性消费上限。印度 CERT-In 的通报(10-15)给出第一号缓解要求:出站默认拒绝在网络层做,不靠提示词说明。

二、12 条硬条款

对接前(问清楚再开门)

  • P1(HIGH) 对方给出 subject,指到自然人。仅凭 agent_id,出事后指不回人。
  • P2(HIGH) 对方给出 policy_ref。策略没有版本号,等于无法回答「按哪一版规则放行的」。
  • P3(HIGH) 凡要求把长期凭据交给对方 Agent 托管的条款,一律驳回。参照 1Password 的做法:短期、窄域、受信执行点代用。
  • P4(MED) 总量账本在令牌体系之外。IETF 委托链草案自己承认:父令牌持 100 上限可铸任意多各持 100 的子令牌------单笔上限管不住扇出。

握手时(验证再放行)

  • H1(HIGH) 委托链有可验证的衰减证明(del_depth / par_hash 一类)。act 链只是家谱(RFC 8693 §4.1 明说嵌套 Actor 不参与访问控制),链深 ≥2 就没人能强制「子不宽于父」。
  • H2(HIGH) 出站控制落在网络层。提示词说明不算控制。
  • H3(MED) 卡片验签且公钥来源已知。签名只在事先已知对方公钥时才有意义------先安全换钥,再谈签名。
  • H4(MED) 发现层收录仅作索引。能力与权限另行验证,参考 AWS 的治理设计:发布者与审批者 IAM 分离,DRAFT → APPROVED 全程可审计。

运行与出事时(日志能指回一个人)

  • A1(HIGH) 审计写入路径与 Agent 执行身份分离。日志要防住它所记录的 Agent。
  • A2(MED) 撤销事件记「验证结果」而非「发出指令」。父令牌撤销不级联子令牌是现实。
  • A3(MED) 留存按「证据」而非「排障」设定:trace 可采样、可七天;审计不可采样、按年。
  • A4(MED) 审计带第八列 initiated_by,区分「操作者行为」与「设计者行为」------出事时这两类的追问对象完全不同。

验收方法(从 Safeguard 的清单抄来的):把护栏逐个关掉,看沙箱还挡得住什么------那个缺口才是你的真实暴露面。

三、门检校验器:gate_check.py

纯标准库、无第三方依赖、可离线运行。以下代码在 Python 3.13.14 实测通过(文末附实测输出)。

python 复制代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
gate_check.py ------ 跨组织 Agent 对接门检校验器(纯标准库,可离线运行)

用法:
    python gate_check.py profile.json
    cat profile.json | python gate_check.py
退出码:0 = 通过;1 = 存在 HIGH 缺项,或 MED 缺项 >= 2
"""
import json
import sys


def _nonempty(v):
    return isinstance(v, str) and v.strip() != ""


# 每条: (编号, 分段, 严重级, 判定函数, fail提示)
CHECKS = [
    # 对接前
    ("P1", "对接前", "HIGH", lambda p: _nonempty(p.get("subject")),
     "subject 未指向自然人。仅凭 agent_id 无法在出事后指回到人"),
    ("P2", "对接前", "HIGH", lambda p: _nonempty(p.get("policy_ref")),
     "policy_ref 缺失。策略没有版本号,等于无法回答'按哪一版规则放行'"),
    ("P3", "对接前", "HIGH", lambda p: p.get("credential_held_by") != "counterparty_agent",
     "长期凭据由对方 Agent 托管。凭证不过手原则被违反,应改为短期窄域令牌或受信执行点代用"),
    ("P4", "对接前", "MED", lambda p: p.get("ledger_type") != "token_only",
     "额度账本只存在于令牌内。单笔上限管不住扇出,总量账本必须在令牌体系之外"),
    # 握手时
    ("H1", "握手时", "HIGH", lambda p: p.get("decay_proof") == "del_depth_par_hash",
     "委托链无可验证的衰减证明。act 链只是家谱,无法强制'子不宽于父'"),
    ("H2", "握手时", "HIGH", lambda p: p.get("egress_control") == "network_layer",
     "出站控制在提示词层。出站默认拒绝必须落在网络层,提示词说明不算控制"),
    ("H3", "握手时", "MED", lambda p: p.get("card_signature") == "jws_known_key",
     "卡片未验签或验签密钥来源不明。签名只在事先已知对方公钥时才有意义"),
    ("H4", "握手时", "MED", lambda p: p.get("discovery_role") == "index_only",
     "发现层收录被当作背书。注册表只担保名字归属,能力与权限必须另行验证"),
    # 运行与出事时
    ("A1", "出事时", "HIGH", lambda p: p.get("audit_write_isolation") is True,
     "审计写入路径与 Agent 执行身份未分离。日志要防住它所记录的 Agent"),
    ("A2", "出事时", "MED", lambda p: p.get("revocation_records") == "verification_result",
     "撤销事件记录的是'发出指令'而非'验证结果'。父令牌撤销不级联子令牌"),
    ("A3", "出事时", "MED", lambda p: p.get("retention_policy") == "evidence",
     "留存策略按排障而非证据设定。trace 可采样、可七天;审计不可采样、按年"),
    ("A4", "出事时", "MED", lambda p: p.get("initiated_by_enabled") is True,
     "审计缺 initiated_by 列。操作者行为与设计者行为无法区分"),
]


def check(profile):
    results = []
    high = med = 0
    for cid, phase, sev, ok, hint in CHECKS:
        passed = ok(profile)
        if not passed:
            if sev == "HIGH":
                high += 1
            elif sev == "MED":
                med += 1
        results.append((cid, phase, sev, passed, hint))
    return results, high, med


def render(results, high, med):
    lines = []
    phase = None
    for cid, ph, sev, passed, hint in results:
        if ph != phase:
            lines.append(f"--- {ph} ---")
            phase = ph
        mark = "PASS" if passed else "FAIL"
        lines.append(f"[{mark}] {cid} ({sev})" + ("" if passed else f" -- {hint}"))
    verdict = "PASS" if (high == 0 and med < 2) else "FAIL"
    lines.append(f"\n结论: {verdict}  (HIGH 缺项: {high}, MED 缺项: {med})")
    return "\n".join(lines), verdict


def main():
    if len(sys.argv) > 1:
        with open(sys.argv[1], encoding="utf-8") as f:
            profile = json.load(f)
    else:
        profile = json.loads(sys.stdin.read())
    results, high, med = check(profile)
    text, verdict = render(results, high, med)
    print(text)
    sys.exit(0 if verdict == "PASS" else 1)


if __name__ == "__main__":
    main()

档案示例(合规版)

json 复制代码
{
  "agent_name": "good-partner-agent",
  "subject": "emp-0182:zhang.wei(法务合规负责人)",
  "policy_ref": "agent-policy-v2.3.1",
  "credential_held_by": "gateway",
  "ledger_type": "external_ledger",
  "decay_proof": "del_depth_par_hash",
  "egress_control": "network_layer",
  "card_signature": "jws_known_key",
  "discovery_role": "index_only",
  "audit_write_isolation": true,
  "revocation_records": "verification_result",
  "retention_policy": "evidence",
  "initiated_by_enabled": true
}

实测输出(Python 3.13.14)

合规档案:

scss 复制代码
--- 对接前 ---
[PASS] P1 (HIGH)
[PASS] P2 (HIGH)
...
--- 出事时 ---
[PASS] A4 (MED)

结论: PASS  (HIGH 缺项: 0, MED 缺项: 0)

把 credential_held_by 改回 "counterparty_agent"、egress_control 改成 "prompt_only" 再跑:

scss 复制代码
[FAIL] P3 (HIGH) -- 长期凭据由对方 Agent 托管。...
[FAIL] H2 (HIGH) -- 出站控制在提示词层。...

结论: FAIL  (HIGH 缺项: 2, MED 缺项: 0)

退出码 1------可以直接挂进 CI,对接档案不过门检不进评审会。

四、设计取舍与局限

为什么做成「声明式档案」而不是连对方真实系统探测? 因为跨组织场景下你通常没有权限探测对方内部实现,评审阶段能拿到的是对方签署的对接档案。这份校验器检的是「档案与清单的一致性」,真实执行强度仍要在验收环境实测(scope escape test:逐个关护栏看沙箱)。

局限表(避免被误用):

局限 说明
不验证对方声明真实性 档案是自我声明,签名/公证是下一步,本工具不做
判定是枚举值匹配 decay_proof 只认 "del_depth_par_hash" 这类约定值,自定义方案需自己扩展 lambda
退出码策略保守 2 个 MED 也判 FAIL,团队可按风险容忍度调整阈值
不含网络/运行时探测 属于静态门检,动态验收需另建测试环境

五、踩坑记录

  1. is True 不是抠门。 p.get("audit_write_isolation") 若写 if p.get(...),任何 truthy 值都过------对接档案里 "true" 字符串是 JSON 反序列化的常见坑,"false" 字符串也是 truthy。显式 is True 配合上游 schema 校验。
  2. 枚举值用 != 反向判。 P3 的条件是 credential_held_by != "counterparty_agent",意思是「任何不等于违规值的取值都算过」------缺字段也过。这是有意的:字段级缺失应该由 P1/P2 这类 HIGH 项兜底,避免一份数据被重复扣分掩盖主因。
  3. 中文提示别写进异常。 fail 提示放数据结构里而不是 raise ValueError,是为了让 CI 采集 stdout 时格式稳定;退出码承载判定结果,文本只承载解释。
  4. 分段渲染先看 phase != phase。 最初版本用 enumerate 对齐分组索引,插入新检查项就错位;改成「与上一条 phase 不同则输出分组头」后增删项零维护。
  5. 退出码语义要写进 docstring。 0/1 的含义不写清楚,CI 接入的人一定会问第二遍。

六、收官:本周判断的最终形态

  • 协议解决怎么传,不解决凭什么信 ------所以对接前先问 subject 与 policy_ref,不问「支不支持 A2A」。
  • 防线在边界外------OpenShell/Sentry 把控制推进到内核与 DPU,1Password 把凭证收回到端点,方向一致:执行点离模型越远,防线越可信。
  • 注册表当索引不当背书------发现与授权分层是 AWS 亲口确认的正确姿势。
  • 责任要能十分钟三问------谁定的目标、谁给的额度、谁点的确认;答不出来,说明你治理的是系统不是人,而系统不会赔钱。

下周进入新主题:AI 应用的记忆与上下文持久化怎么工程化------本周讲的是「信任怎么建」,下周讲「状态怎么存」。欢迎在评论区留下你对接过的最离谱的第三方 Agent 档案。


来源:TechCrunch / The Verge(1Password for Agents,10-22);BidSense Daily AI Intelligence Report(10-23);cherumbu.com(Red Hat×NVIDIA GTC Berlin);dev.to「The $50 Hack」;safeguard.sh「Guardrails vs Sandboxes」;NHIMG Multi-Agent A2A Security Guide(10-20 更新);RedMonk(10-19);AWS ML Blog(10-20);OutSystems×KPMG×CIO Dive 调研;notesfromtheterminal.com(Visa 试点,10-22)。厂商自报数据均已标注口径。

相关推荐
程序员老赵1 小时前
Docker 部署 MaxKB:轻松搭建企业级智能体平台
前端·docker·agent
用户4015823324621 小时前
对话一长,AI 就忘了你说过的话?Claude Code、Codex、Aider、WES Code 怎么压缩上下文
人工智能·go·ai编程
qinglongcn1 小时前
如何安全地停下一个正在工作的 Agent——中断与崩溃恢复
agent·ai编程
李溪白1 小时前
RAG 进阶:Agentic RAG 与多智能体协作(系列三·第5篇·收官):生产级 Agentic RAG 系统全景
agent
Bryceee2 小时前
DeepSeek Harness (dsh) 会话管理架构深度剖析:为什么他们砍掉了 SQLite,选择 Zstd 分帧 JSONL?
agent
摩羯12302 小时前
AI提示词备查与原始输入输出记录:生成式人工智能合规的可验证实现
人工智能·aigc
136096757232 小时前
把模型调用抽成 4 个对象,换厂商只改一行配置
agent
没事写点啥2 小时前
WeKnora 上下文管理
后端·ai编程
武子康2 小时前
Coding Agent 的知识也会漂移:用固定快照检查 Vercel Plugin 升级
人工智能·后端·agent