mrwersa/agentmandate
GitHub: mrwersa/agentmandate
AgentMandate 通过离线分析 AI 代理工具的组合可达性,发现单工具策略检查无法捕捉的权限越界路径,并在版本发布前标记权限范围扩大。
Stars: 2 | Forks: 0
# AgentMandate
[](https://pypi.org/project/agentmandate/)
[](https://www.python.org/downloads/)
[](https://github.com/mrwersa/agentmandate/actions/workflows/ci.yml)
[](#development)
[](https://github.com/mrwersa/agentmandate/blob/main/LICENSE)
一个 Python 库和 CLI 工具,它可以读取代理工具的简短描述(称为 **manifest**),并通过组合单独允许的操作,找出代理可能绕过的限制。它还能在某个版本发布前,告诉你该版本是否扩大了代理的可达范围。
策略引擎每次只决定一个调用。而这个工具会离线查看整个工具图,因此它能捕捉到只有在一个操作序列中才会出现的漏洞。
| 你运行的工具 | 它回答的问题 |
|---|---|
| Cedar, OPA, AgentCore Policy, AgentWard | 此代理现在可以进行此调用吗? |
| **AgentMandate** | **通过组合允许的调用,它可以达到什么范围?这个版本扩大了该范围吗?** |
| [AgentVerity](https://github.com/mrwersa/agentverity) | 被审查的决策路径是否被可重复地执行了? |
| 你的测试和运行时追踪 | 工具是否执行了,声明的控制措施是否生效了? |
想象一个支付争议代理,它可以立案并签发经人工批准的退款。每次退款的上限为每个案件 500 GBP,整个运行过程的上限为 500 GBP。版本 2 添加了一个只读工具:`search_cases`。
这个工具不花费任何东西。然而,它确实让代理能够获取更多的案件,而 500 GBP 的上限是*按每个案件*计算的。在 manifest 下,两笔单独有效的退款变得可达,因此可达的提取金额翻倍达到 1,000 GBP,而每一次单独的调用看起来仍然是允许的。

AgentMandate 构建了工具图,并回答了基于单工具检查无法回答的问题:
- **什么合法的序列打破了限制?** `mandate reach` 返回的是路径,而不是风险评分。
- **这个发布版本带来了什么新可能?** `mandate diff` 比较的是实际权限,而不是配置文本。
- **运行时证据与声明相符吗?** `mandate verify` 在缺少必需的控制字段时会采取失败即关闭原则。
- **测试必须执行什么?** `mandate obligations` 将可达的权限转化为可审查的测试义务。
- **哪些复合风险需要场景?** `mandate scenarios` 将每个反例保留为中立的测试骨架,而不会凭空捏造代理 prompt。
Alpha 阶段。Apache-2.0 许可证。
**查看整体运行情况:**
[**agent-release-gate**](https://github.com/mrwersa/agent-release-gate) 将一个代理从 Python 源码带到门禁决策点,本 README 中的每一个命令都会在它上面运行一次。六次检查,一个退出代码,完全离线。
## 试一试
```
pip install "agentmandate[yaml]"
```
该代码库包含了那个支付争议代理。在版本 1 中,退款上限为每案 500 GBP,每次退款都需要人工批准,整个运行过程的上限为 500 GBP,并且没有任何操作通过 service account 进行花费。它通过了:
```
$ mandate lint examples/dispute-resolver.yaml
no single-manifest findings
$ mandate reach examples/dispute-resolver.yaml
no reachable breach within depth 8. 3 tool(s) reachable, most extractable 500 GBP
```
版本 2 添加了一个只读工具,以便代理可以查找现有案件,而不是总是打开新案件。传统的审查认为没有写入效果:
```
- name: search_cases
effect: read
produces: case
unbounded: true
```
```
$ mandate lint examples/dispute-resolver-v2.yaml
no single-manifest findings
$ mandate reach examples/dispute-resolver-v2.yaml
BREACH cumulative value 1000 GBP exceeds limit 500 GBP
1. open_case(case#1)
2. search_cases(case#2)
3. issue_refund(case#1, 500 GBP)
4. issue_refund(case#2, 500 GBP)
```
Lint 检查依然通过,因为没有哪个单独的工具是错误的。退款上限是针对单个案件衡量的,而新工具让代理能够获取新的案件,因此按案件设定的上限不再能约束整个运行过程。在 CI 中:
```
$ mandate diff examples/dispute-resolver.yaml examples/dispute-resolver-v2.yaml
authority diff v1 -> v2
+ tool: gained search_cases
+ extractable value: 500 -> 2000 GBP
+ reachable breach: gained cumulative_value
verdict: WIDENING
a widening change needs named review before release
```
退出代码为 1。配置的更改是只读的。但实际的权限却不是。
## 为什么配置的 diff 不是权限的 diff
Pull request 展示了别人输入了什么。它没有展示代理现在能做什么,因为可达性是可以组合的,而文本不能。添加一个读取工具、放宽 schema 中的一个枚举,或者移除一个前置条件,都可能打开一条之前不存在的路径,而在审查时,这些看起来都不像是权限变更。
这与 `git diff` 从未取代类型检查是同样的道理。问题不在于改变了什么,而在于这个改变带来了什么可能性。
## 从现有的代理开始
你不必手动编写第一个 manifest。从代理代码生成:
```
$ mandate scan --source src/agent --agent dispute-resolver > mandate.yaml
```
它会读取 `@tool`、`@function_tool` 和 `@ai_function` 声明,以及它们传递给的 `tools=[...]` 列表。这是一个静态读取:没有进行任何导入,没有执行任何代码,也不需要安装框架。
一个 manifest 描述一个代理,因此如果一个源码构建了两个代理,它将被拒绝,直到你使用 `--binding` 指定你想要的那个。联合读取会让 `reach` 跨越永远不会在同一个运行中共享的工具组合出一条路径。读取过程无法枚举的内容会被报告出来而不是丢弃,因为今天 manifest 中缺失的工具,在明天的 diff 中就会显示为从未被添加的权限。参见 [docs/inventory.md](docs/inventory.md)。
或者从 MCP 目录生成:
```
$ mandate scan examples/mcp-tools.json --agent dispute-resolver > mandate.yaml
```
生成的骨架可以直接加载,并且目录无法提供的每一个判断都会被标记出来:
```
- name: issue_refund
# REVIEW: effect guessed from the name. read | write | irreversible
effect: irreversible
# REVIEW: does this spend the caller's authority or a service account?
principal: caller
requires: [case]
# REVIEW: amount looks like a value argument. A ceiling needs scope_key too.
# value_arg: amount
# scope_key: case
# ceiling: { amount: 0, currency: GBP }
requires_approval: true
```
未识别的动词会被保守地建议为 `irreversible`(不可逆),因为低估一个影响的后果更为严重。
## 在 Pull Request 中
```
- uses: mrwersa/agentmandate@v0.8.0
with:
manifest: mandate.yaml
baseline: mandate-released.yaml # optional: did this widen authority?
source: src/agent # optional: has the manifest drifted?
```
反例会作为渲染好的图表出现在作业摘要中,而不是作为一行日志,并且 `sarif-file` 是一个你可以交给 `github/codeql-action/upload-sarif` 的输出,以便它在 diff 上添加注释。
上传故意设计为你的步骤,而不是该 action 的步骤:它需要 `security-events: write` 权限,而一个申请了本可避免的 token 权限的 action,只会给安全团队多一个拒绝的理由。
`fail-on: never` 会进行报告但不阻止流程,这是一种在不让所有人在第一天就停滞的情况下,在现有代码库上启用此功能的方法。
只有你提供了输入参数的检查才会运行。对于 `lint` 和 `reach` 来说,单单一个 manifest 就足够了。
## 在你经常查看的地方显示发现结果
```
# .github/workflows/agent-authority.yml
- run: mandate reach mandate.yaml --sarif > authority.sarif
continue-on-error: true # let the upload happen, then fail the gate
- uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: authority.sarif
- run: mandate reach mandate.yaml # the actual gate
```
然后,违规行为会被标注在引入它的 pull request 上,而不是躺在某个需要有人去打开的日志里。发现结果是 `error`,而不是 `warning`:它们已经以非零状态退出了,而一个与退出代码不一致的 UI 会导致门禁失去可信度。
`--graph` 会输出 Mermaid 图表,GitHub 会在评论中内联渲染它:
```
flowchart LR
s0(["search_cases
case#1"]) s1(["search_cases
case#2"]) s0 --> s1 s2["issue_refund
case#1 · 500 GBP"] s1 --> s2 s3["issue_refund
case#2 · 500 GBP"] s2 --> s3 breach["cumulative value 1000 GBP exceeds limit 500 GBP"] s3 --> breach ``` 每个 **step**(步骤)一个节点,而不是每个工具,因为在不同 binding 上调用同一个工具两次通常才是核心所在。圆角代表读取,方框代表改变了某些东西。 ## 保持 manifest 的准确性 Manifest 是一种声明。有两件事会悄悄地使其失效:有人给代理添加了工具却没人去修改 YAML,或者函数签名发生了改变,导致原本用于计算上限的参数不复存在。 ``` $ mandate drift mandate.yaml --source src/agent UNDECLARED issue_credit_note the agent is given this tool and the mandate does not declare it, so every reach and diff run so far analysed a smaller graph than the real one ARGUMENT issue_refund value_arg names 'amount', which is not an argument this tool takes any more (it takes: case_id, total, currency). A ceiling counted against an argument that does not exist is not a ceiling. REMOVED close_case the mandate declares this tool and the agent is not given it in source, so the analysis is defending authority nobody has. ``` 第二个发现值得引起注意。Manifest 依然能解析,`reach` 依然能运行,但上限却没有实际作用于任何事物。 读取过程无法枚举的工具列表(例如 `tools=load_tools()`)本身就是一个发现结果。如果基于无法看清整个列表的证据来报告“没有偏移”,那就是这个包旨在防止的虚假安全感。 ## Manifest 可达性分析需要每个工具的三个事实,这是普通的工具 schema 所不携带的:影响类别、哪个参数消耗了价值,以及上限是针对哪个 scope 衡量的。 ``` version: 1 agent: dispute-resolver identity: spiffe://bank/agents/dispute-resolver limits: total: { amount: 500, currency: GBP } depth: 8 tools: - name: open_case effect: read # read | write | irreversible produces: case # mints a binding of scope "case" - name: issue_refund effect: irreversible principal: caller # caller | service requires: [case] value_arg: amount scope_key: case # the ceiling is per case ceiling: { amount: 500, currency: GBP } requires_approval: true ``` 要求提供完整的前置条件和后置条件会更具有表现力,但实际上根本不会有人去写。这已经是让复合分析成为可能的最低限度要求了。 上限是一个工具针对其 `scope_key` 的一个 binding 可能消耗的最大**累积**值。`unbounded: true` 标记了一个可以被重复调用以生成新 binding 的工具,这正是将按 scope 的上限变成根本没有上限的罪魁祸首。 ## 命令 | 命令 | 它的功能 | |---|---| | `mandate scan` | 从代理源码(`--source`)或 MCP `tools/list` 目录导出 manifest 骨架,并在每个猜测上带有 `REVIEW` 标记 | | `mandate drift` | 将声明的 mandate 与代理的源码进行比较,如果两者出现偏离则失败 | | `mandate lint` | 单 manifest 的控制检查:职责分离、无门控的不可逆影响、service account 主体、未针对任何 scope 设定的上限 | | `mandate reach` | 在有限范围内搜索突破限制的合法调用序列,并以反例的形式报告 | | `mandate diff` | 对比两个 manifest 的实际权限,包括限制、前置条件、批准、影响和 scope 生成。`--record` 输出变更记录 | | `mandate verify` | 根据 manifest 重放记录的工具调用,如果声明控制所需的证据缺失,则采取失败即关闭原则。使用 `--otel` 读取 [OpenTelemetry traces](https://github.com/mrwersa/agentmandate/blob/main/docs/traces.md) | | `mandate obligations` | 从可达的权限中提取可审查的测试义务,并将已审查的义务渲染为 [AgentVerity](https://github.com/mrwersa/agentverity) 决策套件 | | `mandate scenarios` | 导出可达的违规路径,其中包含空白的运行环境、代理输入和预期控制字段,供人工审查和外部评估框架执行 | 每个分析命令都支持 `--json`,并在发现结果时以非零状态退出,因此它们可以直接原封不动地接入 CI。`scan` 将 manifest 写入标准输出,它是一次性操作,而不是门禁。 | 退出代码 | 含义 | |---|---| | `0` | 通过 | | `1` | 发现问题:lint 错误、可达的违规、扩大的 diff 或不符合规范的重放 | | `2` | 用法错误或格式错误的 manifest | 在 Pull Request 中,实用的门禁是将 `diff` 与默认分支上的 manifest 进行对比,这样扩大权限的更改就会停下来并指定审查人员: ``` - name: Authority diff run: | git show origin/main:mandate.yaml > /tmp/released.yaml mandate diff /tmp/released.yaml mandate.yaml ``` `verify` 是保持其余部分真实性的关键。一个无人检查的 manifest 只是一厢情愿,在有人发布连接器更改的那一刻,声明就会偏离实现。 对于消耗价值的工具,每条追踪记录必须携带 scope、价值、货币、批准状态和执行主体。缺失或格式不正确的控制证据不能作为空值通过。 ## 从权限到评估 AgentMandate 会生成两种不同的测试输入: - `obligations` 命名了经过审查的有界决策测试应该覆盖的关键决策点 - `scenarios` 保留了多步骤场景测试应该尝试的复合反例路径 它不执行任何一种测试。Promptfoo、LangSmith、AgentCore Evaluations、pytest 或内部框架负责行为和结果的评分。在正确性测试通过后,AgentVerity 可以对这些重复的有界决策进行认证。 ``` reachability -> reviewed obligations and scenarios -> external evaluation ^ | | v manifest <- reviewed production incidents <- runtime policy and traces ``` [阅读完整的评估循环工作流](docs/evaluation-loop.md)。 ## 它的定位,以及现有工具的情况 这是一个分析工具,而不是执行工具。它在 CI 中针对 manifest 运行,不会出现在请求路径中。 | 工具 | 功能 | 关系 | |---|---|---| | [Amazon Bedrock AgentCore 中的 Policy](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/policy.html) | 为每次网关工具调用评估所有适用的 Cedar 策略,具有默认拒绝、禁止优先原则,并能标记始终允许和始终拒绝的策略的分析 | 执行每次调用。其文档化的分析是策略层面的,而不是对一系列允许调用的建模 | | [AgentWard](https://github.com/agentward-ai/agentward) | 运行时代理,按调用强制执行策略,对比两个策略文件的差异 | 执行。比较的是声明的文本,而不是可达的权限 | | [AgentShield](https://github.com/affaan-m/agentshield) | 扫描代理配置和 MCP server,基于发现数量的门禁检查 | 扫描。偏移是基于发现数量,而不是权限方向 | | [AgentGuard](https://github.com/WhitzardAgent/AgentGuard) | 用于工具调用的基于属性的访问控制 | 执行 | | [OPA](https://www.openpolicyagent.org/docs), [Cedar](https://docs.cedarpolicy.com/) | 每次决定一个授权 | 执行 | 使用这些工具进行执行。AgentMandate 是离线的那一半:它分析一系列单独允许的调用,并比较跨版本的实际权限。 **如果你已经在运行 AgentCore Policy**,差距是具体的。策略引擎通过评估所有适用的策略来回答“此主体现在是否可以调用此工具”,其文档化的分析可以捕获策略层面的问题,例如无条件允许。它不会去建模四个单独允许的调用是否会组合成 1,000 GBP 的违规,或者某个版本是否扩大了代理的可达范围。AgentMandate 是供应商中立的,在部署前运行在 CI 中,因此它是网关的补充,而不是。 相邻领域中最接近的现有技术是 [IAM Access Analyzer](https://docs.aws.amazon.com/IAM/latest/UserGuide/access-analyzer-concepts.html),它通过自动推理从策略中推导出可达的访问权限,而不是等待日志事件。这就是将那个想法应用到代理工具图上的工具。 `lint` 命令故意与上面的扫描工具有所重叠。一个只报告复合发现的工具,如果旁边没有一个这样的工具在运行,那它根本无法使用。 ## 范围 将每一个发现理解为**在此有限抽象内,受审查的 manifest 所允许的**。它不能证明模型会选择这条路径,也不能证明未声明的下游不变量会接受它。 这个工具故意不做的事情: - **不执行。** 没有代理,没有运行时拦截,没有阻断。 - **没有数据流可达性分析。** 发现读取工具馈送到数据外泄路径需要 manifest 不携带的污点标签。累积值和 scope 生成是当前模型真正支持的功能。 - **不涉及模型行为。** 代理**是否**会走某条路径,与它**是否可以**走某条路径是两个不同的问题。这里衡量的是权限。 - **不推断关键字段。** `mandate scan` 读取代理源码或 MCP 目录并编写骨架,但它无法知道某个影响是否可逆,或者上限是针对什么衡量的。它会保守地猜测,并将每个猜测标记为 `REVIEW`。只提取然后注释,绝不提取后就盲目信任。 搜索受到 `limits.depth` 的限制。在深度 8 没有发现违规,并不能证明在深度 20 不存在违规,并且报告会指出它何时发生了截断。 ## 文档 - [DESIGN.md](DESIGN.md) — 权限模型,为什么搜索要这样设计,以及舍弃了什么 - [docs/evaluation-loop.md](docs/evaluation-loop.md) — 权限分析、场景评估、运行时策略和生产反馈如何保持独立 - [docs/test-obligations.md](docs/test-obligations.md) — 决策点义务和 AgentVerity 桥接 - [CONTRIBUTING.md](CONTRIBUTING.md) — 分支和审查工作流 - [SECURITY.md](SECURITY.md) — 报告,以及 manifest 可以包含什么 - [STABILITY.md](STABILITY.md) — 在 1.0 版本之前有哪些保证 - [ROADMAP.md](ROADMAP.md) — 推广工作、计划的模型扩展,以及通往 1.0 的道路 - [CHANGELOG.md](CHANGELOG.md) ## 开发 ``` python -m pip install -e ".[dev]" python -m pytest -q python -m pytest -q --cov=agentmandate --cov-fail-under=100 ruff check . ``` `main` 分支受保护。每一次更改都必须通过 Pull request 合并,并且 CI 必须为绿色(通过)。 ## 状态 Alpha 阶段。版本号在上面的徽章和 PyPI 中都有显示,因此这里不再重复,以免变得过时。权限模型是最有可能发生变化的部分,因为目前还没有将它应用到足够多的真实工具图上,以了解哪里过于粗糙。提交描述它建模不佳的问题,是你能为该工具做的最有用的事情。
case#1"]) s1(["search_cases
case#2"]) s0 --> s1 s2["issue_refund
case#1 · 500 GBP"] s1 --> s2 s3["issue_refund
case#2 · 500 GBP"] s2 --> s3 breach["cumulative value 1000 GBP exceeds limit 500 GBP"] s3 --> breach ``` 每个 **step**(步骤)一个节点,而不是每个工具,因为在不同 binding 上调用同一个工具两次通常才是核心所在。圆角代表读取,方框代表改变了某些东西。 ## 保持 manifest 的准确性 Manifest 是一种声明。有两件事会悄悄地使其失效:有人给代理添加了工具却没人去修改 YAML,或者函数签名发生了改变,导致原本用于计算上限的参数不复存在。 ``` $ mandate drift mandate.yaml --source src/agent UNDECLARED issue_credit_note the agent is given this tool and the mandate does not declare it, so every reach and diff run so far analysed a smaller graph than the real one ARGUMENT issue_refund value_arg names 'amount', which is not an argument this tool takes any more (it takes: case_id, total, currency). A ceiling counted against an argument that does not exist is not a ceiling. REMOVED close_case the mandate declares this tool and the agent is not given it in source, so the analysis is defending authority nobody has. ``` 第二个发现值得引起注意。Manifest 依然能解析,`reach` 依然能运行,但上限却没有实际作用于任何事物。 读取过程无法枚举的工具列表(例如 `tools=load_tools()`)本身就是一个发现结果。如果基于无法看清整个列表的证据来报告“没有偏移”,那就是这个包旨在防止的虚假安全感。 ## Manifest 可达性分析需要每个工具的三个事实,这是普通的工具 schema 所不携带的:影响类别、哪个参数消耗了价值,以及上限是针对哪个 scope 衡量的。 ``` version: 1 agent: dispute-resolver identity: spiffe://bank/agents/dispute-resolver limits: total: { amount: 500, currency: GBP } depth: 8 tools: - name: open_case effect: read # read | write | irreversible produces: case # mints a binding of scope "case" - name: issue_refund effect: irreversible principal: caller # caller | service requires: [case] value_arg: amount scope_key: case # the ceiling is per case ceiling: { amount: 500, currency: GBP } requires_approval: true ``` 要求提供完整的前置条件和后置条件会更具有表现力,但实际上根本不会有人去写。这已经是让复合分析成为可能的最低限度要求了。 上限是一个工具针对其 `scope_key` 的一个 binding 可能消耗的最大**累积**值。`unbounded: true` 标记了一个可以被重复调用以生成新 binding 的工具,这正是将按 scope 的上限变成根本没有上限的罪魁祸首。 ## 命令 | 命令 | 它的功能 | |---|---| | `mandate scan` | 从代理源码(`--source`)或 MCP `tools/list` 目录导出 manifest 骨架,并在每个猜测上带有 `REVIEW` 标记 | | `mandate drift` | 将声明的 mandate 与代理的源码进行比较,如果两者出现偏离则失败 | | `mandate lint` | 单 manifest 的控制检查:职责分离、无门控的不可逆影响、service account 主体、未针对任何 scope 设定的上限 | | `mandate reach` | 在有限范围内搜索突破限制的合法调用序列,并以反例的形式报告 | | `mandate diff` | 对比两个 manifest 的实际权限,包括限制、前置条件、批准、影响和 scope 生成。`--record` 输出变更记录 | | `mandate verify` | 根据 manifest 重放记录的工具调用,如果声明控制所需的证据缺失,则采取失败即关闭原则。使用 `--otel` 读取 [OpenTelemetry traces](https://github.com/mrwersa/agentmandate/blob/main/docs/traces.md) | | `mandate obligations` | 从可达的权限中提取可审查的测试义务,并将已审查的义务渲染为 [AgentVerity](https://github.com/mrwersa/agentverity) 决策套件 | | `mandate scenarios` | 导出可达的违规路径,其中包含空白的运行环境、代理输入和预期控制字段,供人工审查和外部评估框架执行 | 每个分析命令都支持 `--json`,并在发现结果时以非零状态退出,因此它们可以直接原封不动地接入 CI。`scan` 将 manifest 写入标准输出,它是一次性操作,而不是门禁。 | 退出代码 | 含义 | |---|---| | `0` | 通过 | | `1` | 发现问题:lint 错误、可达的违规、扩大的 diff 或不符合规范的重放 | | `2` | 用法错误或格式错误的 manifest | 在 Pull Request 中,实用的门禁是将 `diff` 与默认分支上的 manifest 进行对比,这样扩大权限的更改就会停下来并指定审查人员: ``` - name: Authority diff run: | git show origin/main:mandate.yaml > /tmp/released.yaml mandate diff /tmp/released.yaml mandate.yaml ``` `verify` 是保持其余部分真实性的关键。一个无人检查的 manifest 只是一厢情愿,在有人发布连接器更改的那一刻,声明就会偏离实现。 对于消耗价值的工具,每条追踪记录必须携带 scope、价值、货币、批准状态和执行主体。缺失或格式不正确的控制证据不能作为空值通过。 ## 从权限到评估 AgentMandate 会生成两种不同的测试输入: - `obligations` 命名了经过审查的有界决策测试应该覆盖的关键决策点 - `scenarios` 保留了多步骤场景测试应该尝试的复合反例路径 它不执行任何一种测试。Promptfoo、LangSmith、AgentCore Evaluations、pytest 或内部框架负责行为和结果的评分。在正确性测试通过后,AgentVerity 可以对这些重复的有界决策进行认证。 ``` reachability -> reviewed obligations and scenarios -> external evaluation ^ | | v manifest <- reviewed production incidents <- runtime policy and traces ``` [阅读完整的评估循环工作流](docs/evaluation-loop.md)。 ## 它的定位,以及现有工具的情况 这是一个分析工具,而不是执行工具。它在 CI 中针对 manifest 运行,不会出现在请求路径中。 | 工具 | 功能 | 关系 | |---|---|---| | [Amazon Bedrock AgentCore 中的 Policy](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/policy.html) | 为每次网关工具调用评估所有适用的 Cedar 策略,具有默认拒绝、禁止优先原则,并能标记始终允许和始终拒绝的策略的分析 | 执行每次调用。其文档化的分析是策略层面的,而不是对一系列允许调用的建模 | | [AgentWard](https://github.com/agentward-ai/agentward) | 运行时代理,按调用强制执行策略,对比两个策略文件的差异 | 执行。比较的是声明的文本,而不是可达的权限 | | [AgentShield](https://github.com/affaan-m/agentshield) | 扫描代理配置和 MCP server,基于发现数量的门禁检查 | 扫描。偏移是基于发现数量,而不是权限方向 | | [AgentGuard](https://github.com/WhitzardAgent/AgentGuard) | 用于工具调用的基于属性的访问控制 | 执行 | | [OPA](https://www.openpolicyagent.org/docs), [Cedar](https://docs.cedarpolicy.com/) | 每次决定一个授权 | 执行 | 使用这些工具进行执行。AgentMandate 是离线的那一半:它分析一系列单独允许的调用,并比较跨版本的实际权限。 **如果你已经在运行 AgentCore Policy**,差距是具体的。策略引擎通过评估所有适用的策略来回答“此主体现在是否可以调用此工具”,其文档化的分析可以捕获策略层面的问题,例如无条件允许。它不会去建模四个单独允许的调用是否会组合成 1,000 GBP 的违规,或者某个版本是否扩大了代理的可达范围。AgentMandate 是供应商中立的,在部署前运行在 CI 中,因此它是网关的补充,而不是。 相邻领域中最接近的现有技术是 [IAM Access Analyzer](https://docs.aws.amazon.com/IAM/latest/UserGuide/access-analyzer-concepts.html),它通过自动推理从策略中推导出可达的访问权限,而不是等待日志事件。这就是将那个想法应用到代理工具图上的工具。 `lint` 命令故意与上面的扫描工具有所重叠。一个只报告复合发现的工具,如果旁边没有一个这样的工具在运行,那它根本无法使用。 ## 范围 将每一个发现理解为**在此有限抽象内,受审查的 manifest 所允许的**。它不能证明模型会选择这条路径,也不能证明未声明的下游不变量会接受它。 这个工具故意不做的事情: - **不执行。** 没有代理,没有运行时拦截,没有阻断。 - **没有数据流可达性分析。** 发现读取工具馈送到数据外泄路径需要 manifest 不携带的污点标签。累积值和 scope 生成是当前模型真正支持的功能。 - **不涉及模型行为。** 代理**是否**会走某条路径,与它**是否可以**走某条路径是两个不同的问题。这里衡量的是权限。 - **不推断关键字段。** `mandate scan` 读取代理源码或 MCP 目录并编写骨架,但它无法知道某个影响是否可逆,或者上限是针对什么衡量的。它会保守地猜测,并将每个猜测标记为 `REVIEW`。只提取然后注释,绝不提取后就盲目信任。 搜索受到 `limits.depth` 的限制。在深度 8 没有发现违规,并不能证明在深度 20 不存在违规,并且报告会指出它何时发生了截断。 ## 文档 - [DESIGN.md](DESIGN.md) — 权限模型,为什么搜索要这样设计,以及舍弃了什么 - [docs/evaluation-loop.md](docs/evaluation-loop.md) — 权限分析、场景评估、运行时策略和生产反馈如何保持独立 - [docs/test-obligations.md](docs/test-obligations.md) — 决策点义务和 AgentVerity 桥接 - [CONTRIBUTING.md](CONTRIBUTING.md) — 分支和审查工作流 - [SECURITY.md](SECURITY.md) — 报告,以及 manifest 可以包含什么 - [STABILITY.md](STABILITY.md) — 在 1.0 版本之前有哪些保证 - [ROADMAP.md](ROADMAP.md) — 推广工作、计划的模型扩展,以及通往 1.0 的道路 - [CHANGELOG.md](CHANGELOG.md) ## 开发 ``` python -m pip install -e ".[dev]" python -m pytest -q python -m pytest -q --cov=agentmandate --cov-fail-under=100 ruff check . ``` `main` 分支受保护。每一次更改都必须通过 Pull request 合并,并且 CI 必须为绿色(通过)。 ## 状态 Alpha 阶段。版本号在上面的徽章和 PyPI 中都有显示,因此这里不再重复,以免变得过时。权限模型是最有可能发生变化的部分,因为目前还没有将它应用到足够多的真实工具图上,以了解哪里过于粗糙。提交描述它建模不佳的问题,是你能为该工具做的最有用的事情。
标签:AI智能体, Python, 人工智能, 无后门, 权限控制, 用户模式Hook绕过, 策略分析, 逆向工具