aiquilibria/contAIned
GitHub: aiquilibria/contAIned
一个基于 Claude Code 的受治理编码智能体 CLI,在隔离的 Docker 容器中运行,通过构建时固化的策略、每次工具调用审计日志和 QA 门控,实现对 AI 编码智能体的全生命周期安全管控。
Stars: 4 | Forks: 0
# cont[AIned
## [✦] 夺回对您编码智能体的控制权!
一个基于 [Claude Code](https://docs.anthropic.com/en/docs/claude-code/overview) 构建的编码智能体 CLI。
该智能体在隔离的 Docker 容器内的定义工作区中运行。所有工具调用均经过审计。策略在构建时被固化到容器镜像中,并由钩子脚本在每次工具调用前强制执行——智能体无法更改其运行所依据的规则。高风险操作(例如 git 变更)可以配置为上报给操作员以获取明确批准,而不是直接被允许或拒绝。
## 目录
- [为什么选择 contAIned?](#why-contained)
- [安装](#install)
- [快速入门](#quickstart)
- [命令](#commands)
- [`contAIned`](#contained-1)
- [`contAIned init`](#contained-init-directory)
- [`contAIned verify`](#contained-verify-directory)
- [任务生命周期](#task-lifecycle)
- [工作原理](#how-it-works)
- [隔离机制](#isolation)
- [治理机制](#governance)
- [受 Cedar 启发的策略规则](#cedar-inspired-policy-rules)
- [结合 Claude Code 沙盒的深度防御](#defense-in-depth-with-claude-codes-sandbox)
- [出站过滤](#egress-filtering)
- [Tracer 追踪器](#tracer)
- [自定义策略](#customizing-policy)
- [镜像支持](#image-support)
- [已知缺陷](#known-gaps)
- [QA 钩子覆盖率](#qa-hook-coverage)
- [垃圾回收](#garbage-collection-tracerdb)
- [叙述注入预算](#narrative-injection-budget)
- [针对恶意智能体的出站过滤](#egress-filtering-against-a-malicious-agent)
- [安全模型](#security-model)
## 为什么选择 contAIned?
现有的三个原语处理了相关的问题,但留下了一个关键空白:
### Claude Code 的 `/sandbox`
Claude Code 提供了一个内置的 `/sandbox` 功能,它使用操作系统级别的隔离(macOS 上的 Seatbelt,Linux 上的 bubblewrap)来约束 Bash 子进程——阻止对敏感路径的写入,限制出站网络访问等。它非常有用,而且 contAIned 可以与之并行运行。
它未涵盖的内容:Claude 自身的 SDK 工具调用——`Write`、`Edit`、`Read`、`Glob`、`Grep`——完全在 Claude Code 进程内进行中介。它们永远不会跨越 `/sandbox` 监视的操作系统进程边界。沙盒中“拒绝写入 `.contAIned/`”的策略对 `Write` 工具调用毫无影响;只有 `PreToolUse` 钩子能做到这一点。
除了覆盖范围的空白,`/sandbox` 没有以下概念:
- 关于智能体做了什么以及为什么这么做的审计日志
- 在接受更改之前进行人工审查的任务生命周期
- 阻止智能体在检查通过前宣布成功的质量门控
- 智能体自身无法修改的操作员控制策略
### Docker AI 沙箱
[Docker Sandboxes](https://docs.docker.com/ai/sandboxes/) 在轻量级 microVM 内运行智能体,并带有私有 Docker 守护进程,提供了强大的主机系统隔离。这种隔离保证是真实且宝贵的。
其设计理念明确是“默认 YOLO 模式——智能体工作无需请求许可”。它解决了“智能体会不会损坏我的主机?”这个问题,答案是不会。但它并没有试图回答“智能体到底做了什么,是否正确,以及是否有人类签字确认?”
其他限制:
- 基于 MicroVM 的沙盒需要 **macOS 或 Windows**(Linux 用户回退到传统容器模式——也就是 Docker 一直提供的同种隔离)。
- 没有针对每次工具调用的策略钩子、没有审计日志、没有操作员审查工作流、没有 QA 门控。
### Claude Code devcontainer
Anthropic 提供了一个[参考 devcontainer](https://github.com/anthropics/claude-code/tree/main/.devcontainer)——一个专为 VS Code 的 Dev Containers 扩展设计的预配置 Docker 环境。它提供了一个带有自定义防火墙的 Node.js 环境,该防火墙将出站连接限制在已批准的服务列表(npm 注册表、GitHub、Claude API 等)内。容器边界将智能体与主机隔离,防火墙为列出的域名提供了有意义的出站控制。
其预期用法是 `claude --dangerously-skip-permissions`,这会绕过所有权限提示以实现无人值守操作。这就是治理空白出现的地方:`--dangerously-skip-permissions` 禁用了整个 Claude Code 权限系统——包括 `PreToolUse` 和 `PostToolUse` 钩子、所有 `managed-settings.json` 规则以及每次工具调用的策略强制执行。根本没有任何治理层。Anthropic 自己的文档直接承认了这一后果:该 devcontainer“并不能防止恶意项目窃取 devcontainer 内任何可访问的内容,包括 Claude Code 凭证”。
其他限制:
- **没有治理层。**没有针对每次工具调用的拦截、没有审计日志、没有 QA 门控、没有操作员审查工作流。
- **与 IDE 耦合。**围绕 VS Code Dev Containers 设计;无头和 CI 使用是可能的,但不是主要的设计目标。
- **静态防火墙,而非策略清单。**网络规则存在于 `init-firewall.sh` 中。它们不是由声明式清单驱动的,不能与仓库需求合并,也不能通过单一策略源在 `WebFetch` 和子进程通道之间统一强制执行。
### 三者留下的空白
这些原语都没有涉及治理层:*拦截、记录和选择性地阻止单个智能体工具调用,记录每次更改的内容寻址差异,在智能体可以声明任务完成之前强制执行质量标准,以及在接受工作前要求操作员签字确认。*
也没有任何原语能充分解决出站网络控制问题。智能体可以通过 `WebFetch`、Bash `curl` 或它自己编写然后执行的 Python 脚本泄露工作区数据——这是三个不同的通道,每个通道都绕过了不同的控制集。`/sandbox` 阻止了一些 Bash 网络工具,但无法阻止 Claude Code 自己的 `WebFetch`。Docker Sandboxes 对出站流量没有任何约束。devcontainer 防火墙涵盖了网络出站,但在激活 `--dangerously-skip-permissions` 时,无法阻止通过 Claude Code 会话本身进行的凭证窃取。
contAIned 填补了这一空白:
| 功能 | `/sandbox` | Docker Sandbox | Devcontainer | **contAIned** |
|---|:---:|:---:|:---:|:---:|
| 将智能体与主机文件系统隔离 | ◑ (仅限子进程) | ✓ (microVM) | ✓ (Docker) | ✓ (Docker 容器) |
| 覆盖 SDK 工具调用 (`Write`、`Edit`、`Read`) | ✗ | ✗ | ✗ (`--dangerously-skip-permissions`) | ✓ (PreToolUse hooks) |
| 每次工具调用的只追加审计日志 | ✗ | ✗ | ✗ | ✓ |
| 每个任务的内容寻址差异存储 | ✗ | ✗ | ✗ | ✓ |
| 接受更改前的操作员审查 | ✗ | ✗ | ✗ | ✓ |
| QA 门控阻止智能体过早完成 | ✗ | ✗ | ✗ | ✓ |
| 策略固化到镜像中;运行时防篡改 | ✗ | ✗ | ✗ | ✓ |
| 出站过滤 — 出站网络允许列表 | ✗ | ✗ | ◑ (仅限防火墙;无 WebFetch 控制) | ✓ (沙盒网络阻止子进程;`restrict_network` 钩子硬性拒绝发往非允许列表域名的 WebFetch/WebSearch) |
| 支持 Linux CI/CD 环境 | ✓ | ✗ (MicroVM) | ◑ (专注于 VS Code) | ✓ |
contAIned 和 `/sandbox` 是互补的,而非竞争关系。同时启用两者意味着子进程写入在操作系统级别被阻止,*并且* SDK 工具调用在钩子级别被阻止——来自两个不同信任边界的两个独立执行层。
### 为什么不直接使用原生设置?
Claude Code 提供了一个真正的操作员控制平面:`managed-settings.json` 接受 `allow`、`ask` 和 `deny` 规则、沙盒文件系统和网络约束、钩子注册以及 MCP 服务器允许列表。contAIned 使用了所有这些。问题在于它们自身能表达什么以及不能表达什么。
**原生设置可以(繁琐地)近似实现的功能**
针对 `Read`、`Write` 和 `Glob` 的简单基于路径的阻止在机制上是可表达的。阻止 `.env` 文件需要枚举每种模式——`.env`、`.env.*`、`*.pem`、`*.key`、`id_rsa`、`credentials.json`……——并且要分别针对每种工具:`Read`、`Glob`、`Grep`、`Write`、`Edit`、`MultiEdit`。这需要多达 60 多个单独的 `deny` 条目才能覆盖与一个 Python 正则表达式相同的范围。同样的脆弱性也适用于 Bash 命令限制:`deny: ["Bash(rm -rf*)"]` 对字面前缀有效,但会忽略各种变体,并且无法检查 `cat` 命令是针对敏感文件还是源文件。
**原生设置无法触及的地方**
更深层次的问题是结构性的:
- **`deny` 无条件优先于 `allow`。**Claude Code 的评估顺序是固定的:`deny → ask → allow`。没有办法编写“拒绝 `**/.env*` 除非 `**/.env.example`”的规则——无论任何 allow 规则如何,deny 都会触发。钩子通过在阻止逻辑之前进行显式的安全变体检查解决了这个问题。
- **没有可编程逻辑。**原生规则是模式列表。它们无法解析 bash 命令以提取其文件参数、调用外部工具、查询数据库或应用上下文敏感的判断。`restrict_bash.py` 钩子使用 `shlex.split` 从命令中提取文件路径,并根据机密模式列表检查这些路径;任何原生规则的组合都无法复制这一点。
- **没有审计跟踪。**原生拒绝是无声的——什么也没写。钩子在每次拒绝和每次允许的调用时都会向 `tracer.db` 写入结构化条目,包含时间戳、会话 ID、工具、目标和原因。无论操作员是否在监视,该记录都存在,并且可以通过 **`/contained:tracer`** 技能进行查询。
- **没有生命周期或质量门控。**没有与 `Stop` 钩子事件对等的原生功能。权限系统决定是否允许单个工具调用;它没有拦截智能体“我完成了”的信号以运行测试、检查覆盖率或在接受结果之前构建差异摘要的概念。
- **没有防篡改策略。**`.claude/settings.json` 中的设置文件可由智能体写入。镜像层中的 `managed-settings.json` 更难触及,但它是一个没有来源信息的静态文件。contAIned 在构建时将策略固化到镜像中,在镜像标签中记录清单哈希以进行偏移检测,并可选择使用 Sigstore 对镜像进行签名——为操作员提供了从清单编写到运行时强制执行的保管链,这是单独的原生设置无法提供的。
- **没有工作区隔离。**原生设置约束了智能体*能做什么*,但没有约束*在哪里运行*。不存在在 Docker 容器内运行智能体并仅绑定挂载工作区而其他内容不可见的基于设置的等效方案。
**设计选择**
contAIned 将原生设置用于它们擅长的方面——钩子注册、沙盒文件系统和网络规则、WebFetch 域名允许列表、MCP 服务器限制——而将钩子用于所有需要逻辑、状态或生命周期控制的内容。这种划分是刻意的:声明式规则用于粗粒度结构,可执行的 Python 用于任何需要根据上下文进行推理的内容。
| | 原生设置 | contAIned |
|---|:---:|:---:|
| 阻止对特定路径模式的读/写 | ✓ (冗长) | ✓ |
| 安全变体例外(允许 `.env.example`,拒绝 `.env`) | ✗ | ✓ |
| Bash 语义解析(检测 `cat .env` 与 `cat README.md`) | ✗ | ✓ |
| 每次工具调用的结构化审计日志 | ✗ | ✓ |
| QA 门控 — 阻止智能体在检查通过前完成 | ✗ | ✓ |
| 向智能体提供可操作的拒绝反馈 | ✗ | ✓ |
| 任务生命周期、差异存储、操作员审查 | ✗ | ✓ |
| 运行时防改策略;Sigstore 来源 | ✗ | ✓ |
| 工作区隔离(智能体只能看到项目) | ✗ | ✓ |
### 为什么基于 Claude Code CLI 构建?
Claude Code 是正确的基础,因为它精确地暴露了治理层所需的扩展点——而 contAIned 对每个扩展点都进行了经过深思熟虑的、非平凡的使用。
- **每次工具调用的钩子** ——在每次工具调用和任务完成时进行同步拦截。contAIned 使用这些来强制执行基于路径和语义的策略,编写结构化的只追加审计跟踪,运行 QA 门控,构建内容寻址差异,并阻止智能体在检查通过之前宣布成功。
- **不可变的操作员设置** ——智能体无法读取或覆盖的最高优先级设置层。contAIned 在镜像构建时将策略固化到该层,并在镜像标签中记录清单哈希,为操作员提供了具有 Sigstore 支持来源的防篡改策略平面——一个能够在容器重建和多轮会话中存续的保管链。
- **操作系统级别的沙盒** ——Bash 命令的子进程隔离。contAIned 将此视为第二个独立的执行边界,启用了深度防御架构,其中子进程写入在内核级别被阻止,而 SDK 工具调用在钩子级别被阻止——两个独立的信任边界,同时处于活动状态。
- **原生出站控制** ——对 Claude Code 自己的网络工具的按域名控制。contAIned 将此与清单驱动的网络策略统一起来,以便相同的 `allowed_domains` 列表同时管理内置网络工具和 Bash 子进程流量,从而弥补了多通道数据泄露的缺口。
- **转录和上下文生命周期事件** ——持久的会话上下文以及在上下文窗口填满时发出的信号。contAIned 使用此功能在长任务叙述超过注入预算之前将其压缩回 tracer 数据库,从而在长会话中保持操作员对会话历史视图的连贯性。
Claude Code 的设计使这些扩展点可用;而 contAIned 将它们转化为一个治理系统。
## 安装
**macOS / Linux:**
```
curl -fsSL https://github.com/aiquilibria/contAIned/releases/latest/download/install.sh | sh
```
安装到 `/usr/local/bin`(如果需要,会提示输入 `sudo`)或回退到 `~/.local/bin`。要安装特定版本:`... | sh -s v0.2.0`。
**直接下载:** 从 [GitHub Releases](https://github.com/aiquilibria/contAIned/releases) 获取适合您平台的二进制文件,使其可执行,并将其放在您的 `$PATH` 中。
`contained` 二进制文件是一个独立的可执行文件,除了 Docker 之外没有其他运行时依赖项。
**前提条件:** 必须安装并运行 Docker。`contained init` 会检查所有主机依赖项,并尽可能自动安装可选依赖项:
| 依赖项 | 是否必需? | `contained init` 行为 |
|------------|-----------|---------------------------|
| Docker | 是 | 检测到;如果缺少则显示安装提示 |
| `cosign` | 可选 (Sigstore 来源) | 在 macOS 上通过 Homebrew 自动安装;在 Linux 上提供 apt 提示 |
| `pngpaste` | 可选 (剪贴板图像粘贴) | 在 macOS 上通过 Homebrew 自动安装 |
| `xclip` / `wl-paste` | 可选 (剪贴板图像粘贴) | 在 Linux 上提供 apt 提示 |
如果缺少 Docker,请安装 [Docker Desktop](https://www.docker.com/products/docker-desktop/) (macOS/Windows) 或遵循 [Linux 引擎安装指南](https://docs.docker.com/engine/install/)。
## 快速入门
contAIned 使用两个清单文件:一个控制所有安全策略的**操作员清单**,以及一个提交到每个仓库的**仓库清单**,用于声明生态系统和 QA 检查。
**1. 设置操作员清单。** 从提供的示例开始,其中包含了用于机密文件保护、Bash 限制、网络策略、Sigstore 来源和生态系统定义的合理默认值:
```
cp docs/examples/mainlined.yaml policy.yaml
# 编辑 policy.yaml 以匹配您的环境(模型、允许的域名等)
```
**2. 为您的技术栈生成仓库清单** 并将其提交:
```
cd my-project
contAIned init --ecosystem go > .contAIned_manifest.yaml # Go
contAIned init --ecosystem python > .contAIned_manifest.yaml # Python
contAIned init --ecosystem node > .contAIned_manifest.yaml # Node.js
contAIned init --ecosystem typescript > .contAIned_manifest.yaml # TypeScript
git add .contAIned_manifest.yaml && git commit -m "Add contAIned repo manifest"
```
生成的文件声明了生态系统(安装工具链并打开所需的包注册表域名)和一套完整的 QA 检查——构建、格式化、lint、测试、覆盖率——带有 `when_changed` 守卫,以便每次会话仅运行相关的检查。
**3. 初始化并运行:**
```
# contAIned init 会在本地构建 Docker 镜像——没有可供拉取的镜像。
# 这需要网络连接以安装 Claude Code 和任何已声明的工具链。
contAIned init --manifest policy.yaml
contAIned
```
仓库清单在 `contAIned init` 时合并到操作员清单中。仓库清单中只允许 `ecosystems` 和 `policy.qa.checks`——所有安全策略均由操作员清单拥有,不能被仓库覆盖。有关完整的启动器和混合技术栈示例,请参见 [`docs/examples/`](docs/examples/)。
## 命令
### `contAIned`
在 contAIned 容器内启动交互式 Claude Code 会话。
```
contAIned
```
Claude Code 作为直接子进程运行,并继承您的终端——所有 I/O 均原封不动地通过。智能体可以访问您的项目工作区(绑定挂载在 `/workspace`),仅此而已。
所有输入均原封不动地转发给智能体。使用 **`/contained:tracer`** 技能可直接查询会话历史、审计日志、文件差异和 tracer 数据库。
### `contAIned init [DIRECTORY]`
在目标目录(默认:当前目录)中搭建 contAIned 工作区。
```
contAIned init --manifest policy.yaml # bake a local manifest into the image
contAIned init --mainlined https://mainlined.example.com # fetch manifest from a mAInlined policy URL
contAIned init ./myrepo --manifest policy.yaml # initialize in a specific directory
contAIned init --manifest policy.yaml --force # re-initialise an existing workspace
contAIned init --manifest policy.yaml --rebuild # force-rebuild the Docker image
contAIned init --ecosystem go # print a Go repo manifest starter and exit
```
必须通过 `--manifest`(本地文件)或 `--mainlined`(mAInlined URL)提供清单。在没有任一标志的情况下运行将打印一个初始清单并退出。有关完整的清单架构,请参见 [docs/policy-reference.md](docs/policy-reference.md)。
#### 仓库清单
仓库可以在其根目录提交一个 `.contAIned_manifest.yaml`,用于声明生态系统和 QA 检查。此文件在 `contained init` 时合并到 mAInlined 清单中——镜像就是单个合并后的产物。只允许两个字段;任何其他字段都会导致 `contained init` 失败。
```
# .contAIned_manifest.yaml——提交到仓库根目录
ecosystems:
go: "1.22.5" # installs Go toolchain + adds proxy.golang.org to allowlist
policy:
qa:
checks:
- name: test
command: [go, test, ./...]
when_changed: ["*.go"]
```
每个生态系统键都会根据传递给 `contained init` 的清单中的 `ecosystem_definitions` 进行解析。解析出的工具链会被安装,所需的网络域名会自动添加到允许列表中——无需手动配置网络。
使用 `--ecosystem` 为您的技术栈打印一个预填充的启动器:
```
contAIned init --ecosystem go > .contAIned_manifest.yaml
contAIned init --ecosystem node > .contAIned_manifest.yaml
contAIned init --ecosystem python > .contAIned_manifest.yaml
contAIned init --ecosystem typescript > .contAIned_manifest.yaml
```
有关完整的架构和版本约束规则,请参见 [docs/policy-reference.md](docs/policy-reference.md)。
在构建时将清单固化到 Docker 镜像中——策略在镜像层被强制执行。
**策略在镜像层被强制执行。** 钩子注册和沙盒规则存在于 `/etc/claude-code/managed-settings.json` 中,该文件在构建时被复制到 Docker 镜像中。Claude Code 将此文件视为操作员管理的策略:在那里注册的钩子无法在运行时被智能体覆盖或移除。Mainlined 清单被固化到镜像内的 `/etc/contained/manifest.yaml` 中;钩子专门从该路径读取策略参数。
创建(在工作区内):
```
.contAIned/
tracer.db ← SQLite task + diff store (gitignored)
audit/ ← audit log (gitignored)
manifest.yaml ← merged manifest used by host-side tooling (gitignored)
```
创建(在主机上,工作区外):
```
~/.config/contained//
provenance.yaml ← Sigstore provenance record (only when sigstore.enabled: true)
provenance.bundle ← cosign bundle for offline verification
```
目录键 `` 是 `sha256(abs_workspace_path)` 的前 16 个十六进制字符,因此每个工作区都有自己的槽位,同一工作区的并行会话可以安全地共享它(来源文件在初始化后是只读的)。
固化到 Docker 镜像中(不在工作区内):
```
/etc/claude-code/
managed-settings.json ← hook registration + sandbox rules (highest-precedence settings level)
CLAUDE.md ← contAIned operating instructions (composes with project ./CLAUDE.md)
/etc/contained/
manifest.yaml ← authoritative policy read by hooks at runtime
hooks/ ← enforcement hook scripts (registered in managed-settings.json)
statusline.py ← status bar script
```
如果清单和镜像已经是最新的,不带 `--force` 重新运行 `contAIned init` 将是一个空操作。使用 `--rebuild` 强制进行全新的镜像构建,或使用 `--manifest` 提供更新的清单。
### `contAIned verify [DIRECTORY]`
在启动会话之前验证工作区镜像来源。仅在 `contAIned init` 期间启用了 Sigstore 时才有意义;如果来源被禁用,则干净地退出。
```
contAIned verify # verify current workspace
contAIned verify ./repo # verify a specific workspace
```
检查:
1. 当前的 `contained:latest` 镜像摘要与初始化时记录的摘要匹配——检测会话之间的镜像替换。
2. Rekor 透明度日志中的 Sigstore 签名对于记录的操作员身份仍然有效。
主机上需要 `docker`。在镜像完整性至关重要的环境中运行 `contAIned` 之前运行此命令。
## 任务生命周期
当智能体发出完成信号时,将运行 QA 检查并构建差异摘要。然后任务被自动标记为 `closed`。
```
Agent signals Stop
│
▼
QA checks (qa.py) + diff summary + narrative built
│
▼
Task marked closed
```
随时使用 **`/contained:tracer`** 技能浏览已完成的任务并阅读其叙述。
### 任务状态
| 状态 | 含义 |
|----------|----------------------------------|
| `open` | 智能体正在积极工作。 |
| `closed` | 回合完成;叙述已存储。 |
在多轮会话中,任务会循环:每条新的用户消息将其重新打开为 `open`,当智能体完成该回合时,Stop 钩子会再次将其关闭。
## 工作原理
### 隔离机制
智能体在 Docker 容器内运行。工作区绑定挂载在 `/workspace`;主机文件系统的其余部分是不可见的。任何路径旁路技巧都无法访问工作区外的文件——内核强制执行了这一点。
### 治理机制
每个工具调用都经过三层,全部注册在镜像层托管设置中,因此智能体无法修改:
```
Tool call
│
├── PreToolUse hook (restrict_writes.py / restrict_reads.py / restrict_bash.py / restrict_network.py)
│ Path-based and domain-based enforcement — deny access outside the workspace;
│ block writes to control-plane files (.contAIned/, managed-settings.json);
│ hard-deny WebFetch/WebSearch to non-allowlisted domains
│
├── Deny rules (managed-settings.json)
│ Pattern-based — rm -rf, sudo, curl, git push, etc.
│
├── Allow rules (managed-settings.json)
│ Pattern-based — Read, Glob, Grep, safe Bash patterns
│
└── canUseTool callback
Anything not covered above — surfaces to the operator for approval
```
在每次成功的工具调用之后,`audit.py` 会追加一条结构化的日志条目。
当智能体停止时,`qa.py` 会运行语法和质量检查——如果它们失败,
智能体会收到反馈并继续工作。
### 受 Cedar 启发的策略规则
contAIned 的执行层围绕一个基于 [Cedar](https://www.cedarpolicy.com/en)(Amazon 的开源授权语言)建模的策略引擎构建。该设计并非对 Cedar 的依赖;它借鉴了 Cedar 的思想架构——PARC 元组、`forbid`/`permit`/`unless` 评估算法以及默认在无匹配时拒绝的语义——并将它们精确应用于 contAIned 所关注的实体:文件路径、Bash 命令和网络资源。
**为什么采用 Cedar 的设计?**
Cedar 的核心评估算法具有易于理解的正确性属性。其最重要的保证——**`forbid` 始终覆盖 `permit`,无论规则顺序如何**——消除了一整类困扰正则表达式列表系统的配置错误。这直接解决了 contAIned 最初的顺序问题:Claude Code 的原生设置无条件地将 `deny` 优先于 `allow` 进行评估,使得安全变体异常(允许 `.env.example`,拒绝所有其他 `.env` 文件)无法以声明方式表达。受 Cedar 启发的引擎使这些异常成为规则本身中一等公民的 `unless` 子句:
```
- id: v1:secrets:block-secret-access
effect: forbid
action: [Read, Write, Edit, Glob, Grep]
resource_type: FilePath
when:
- resource.is_secret == true
unless:
- resource.is_safe_variant == true # .env.example always passes
reason: "Secret files (credentials, keys, .env) may not be accessed."
```
无需定制的 Python。没有顺序敏感性。相同的机制对于任何操作员可能编写的任何规则都相同地工作。
**评估如何工作**
每个工具调用都被映射到一个类型化实体——`FilePath`、`BashCommand` 或 `NetworkResource`——并带有计算属性(例如 `in_workspace`、`is_secret`、`verb`、`subcommand`、`in_allowlist`)。引擎评估所有匹配的规则并返回以下四种结果之一:
| 结果 | 含义 |
|---------|---------|
| `ALLOW` | 一个 `permit` 规则匹配,并且没有 `forbid` 覆盖它。 |
| `DENY` | 一个 `forbid` 规则匹配——始终战胜任何 `permit`。 |
| `ESCALATE` | 一个 `escalate` 规则匹配;弹出操作员提示。 |
| `DEFER` | 没有规则匹配。调用传递给 Claude Code 的原生权限系统。 |
`DEFER` 是无匹配时的结果——它不是“允许一切”。清单未明确处理的任何内容都会落回到 `managed-settings.json`,最后落回到操作员提示。
**`define` 规则 ——可重用的分类器**
带有 `effect: define` 的规则声明了实体上的计算属性。然后,强制执行规则通过 `resource. == true` 引用这些属性。分类器和强制执行规则都是独立可读的:
```
- id: v1:define:secret-file-patterns
effect: define
resource_type: FilePath
define:
is_secret:
patterns:
- '(^|[/\\])\.env(\.[^/\\]+)?$'
- '\.(pem|key|p12|pfx|jks|keystore)$'
- '(^|[/\\])(credentials|secrets|service_account)\.(json|yaml|yml)$'
```
**单一可读的真实来源**
所有策略都存在于清单的 `runtime.rules` 中。钩子脚本是轻量级适配器:构建实体 → 调用引擎 → 对决策采取行动。审计 contAIned 部署的安全审查员只需阅读一份文档即可理解完整的权限模型。不需要懂 Python。
每条规则都带有一个稳定的 `id`、人类可读的 `reason` 和 `tags`。规则 ID 在每次决策时记录在 `tracer.db` 中——审计查询可以回答“哪个策略导致了此次拒绝?”,而不仅仅是“运行了哪个钩子?”。
**正确性与性能**
`evaluate()` 是一个没有副作用的纯函数——可独立测试、可审计,并且无论 Claude Code 增加了多少种新工具类型都保持稳定。`tests/engine/` 中的测试套件涵盖了实体构建、条件评估以及针对所有实体类型和效果的端到端场景。
基准测试套件(`tests/engine/test_benchmark`)在每次 CI 运行时,为完整的管线强制执行 **5 毫秒的挂钟时间预算**——即针对完整的生产规则集进行实体构建加上规则评估。在实践中,`evaluate()` 在每次工具调用时的运行时间远低于 1 毫秒。钩子在每次工具调用之前同步触发,因此这里的延迟就是智能体在每次操作中感受到的延迟。
### 结合 Claude Code 沙盒的深度防御
正如在[为什么选择 contAIned?](#why-contained)中所解释的,Claude Code 的 `/sandbox` 涵盖了操作系统级别的子进程隔离,而 contAIned 的 PreToolUse 钩子涵盖了 SDK 工具调用——它们保护不同的信任边界,并且最好协同工作。
contAIned 会自动启用这两者。固化到镜像中的 `managed-settings.json` 包含一个 `sandbox` 块:
```
{
"sandbox": {
"enabled": true,
"filesystem": {
"denyWrite": [".contAIned", ".claude/settings.json"]
}
}
}
```
这防止了任何 Bash 子进程(shell 脚本、通过 Bash 执行的 Python、构建工具等)在操作系统级别写入 `.contAIned/` 控制平面目录或 Claude Code 设置文件——这是在已经在 SDK 级别阻止这些写入的 PreToolUse 钩子之后的第二道执行层。无需手动配置。
### 出站过滤
智能体会话具有多个将数据发送出工作区的通道:Claude Code 的内置 `WebFetch` 工具、Bash 子进程(`curl`、`wget`)以及智能体编写并随后执行的脚本。不同的控制涵盖不同的通道。
contAIned 使用两种互补的机制来解决此问题,这两种机制均由 `manifest.yaml` 中的 `policy.network.allowed_domains` 驱动:
- **`WebFetch` / `WebSearch`** ——对允许的域名的请求通过 `managed-settings.json` 的 allow 规则自动批准。对任何其他域名的请求会在 Claude Code 继续之前被 `restrict_network` 钩子硬性拒绝;不会显示操作员提示,也不会发出请求。
- **Bash 子进程和智能体编写的脚本** ——Claude Code 的内置沙盒在操作系统级别(Linux 上的 bubblewrap)强制执行 `allowedDomains` 列表。无论使用何种工具,发往非允许域名的 HTTP 流量都会被阻止,并返回 `403 Forbidden`。
在 `.contAIned/manifest.yaml` 中配置允许列表,然后重建镜像:
```
policy:
network:
enabled: true
allowed_domains:
- api.anthropic.com # required — Anthropic API
- code.claude.com # Claude Code telemetry / auth
- docs.anthropic.com # documentation lookups
# - pypi.org # add project-specific domains as needed
```
```
contAIned init --rebuild
```
**设计意图——防止意外的数据泄露。** 沙盒网络约束在操作系统级别涵盖了 Bash 子进程和智能体编写的脚本。发往非允许域名的 `WebFetch` 需要明确的操作员批准,而不是静默继续。这些措施共同防止了智能体意外地将数据发送到工作区之外。残余风险——绕过这两层的原始非 HTTP 套接字连接——在下面的[已知缺陷](#known-gaps)中进行了描述。
### 策略层级
contAIned 区分了两个经常被混淆的截然不同的治理关注点:
| 关注点 | 控制什么 | 解决的风险 | 清单键 |
|---|---|---|---|
| **依赖项治理** (TPRM / SCA / SBOM) | 允许智能体安装哪些工具链和包生态系统 | 供应链——安装易受攻击或未经批准的软件 | `ecosystem_definitions`、`runtime.docker.toolchains`、`policy.plugins` |
| **出站治理** | 智能体及其子进程可以访问哪些出站域名 | 数据泄露——将工作区数据发送到项目之外 | `policy.network.allowed_domains` |
这两个关注点都完全由传递给 `contained init` 的清单所拥有。仓库清单中的生态系统声明根据该清单中的 `ecosystem_definitions` 进行解析——团队无法安装清单未批准的工具链或访问包注册表。
**生命周期管理——组织下限与团队固定版本**
针对组织的预期执行模型是 `mnlined`(配套的策略服务,目前正在开发中):一个集中管理的清单分发给团队,确保每个工作区都根据相同的批准基线进行初始化。在 mAInlined 可用之前,可以通过在外部共享 `policy.yaml` 并让团队在 `contained init` 时引用它(`contained init --manifest policy.yaml`)来实现相同的模型。
集中拥有的清单将最低可接受的工具链版本设置为下限约束。各个团队在这些范围内固定其所需的版本:
```
# 集中管理的 manifest(通过 mAInlined 或共享的 policy.yaml 分发)
runtime:
docker:
toolchains:
go: ">=1.22" # floor: 1.22 or later; any version below fails at init
python: ">=3.13" # floor: Python 3.13+
node: ">=18"
ecosystem_definitions:
go:
toolchain: go
network_domains: [proxy.golang.org, sum.golang.org]
python:
network_domains: [pypi.org, files.pythonhosted.org]
```
```
# 仓库 manifest(.contAIned_manifest.yaml)——由各团队所有
ecosystems:
go: "1.22.5" # satisfies >=1.22 ✓
python: "3.13.1" # satisfies >=3.13 ✓
```
低于下限的版本会立即失败并显示可操作的错误——不会构建镜像,也不会运行容器:
```
ecosystem "go": version "1.21.0" does not satisfy constraint ">=1.22" for toolchain "go"
```
这使组织能够同时控制两件事:满足合规性要求的最低工具链版本,以及允许这些工具链访问的包注册表。即使团队声明了有效的生态系统,也无法访问未经批准的注册表,因为 `ecosystem_definitions` 完全由集中管理的清单所拥有。
### Tracer 追踪器
`tracer.db`(SQLite,WAL 模式)记录每个任务、子智能体调用、文件差异(内容寻址 blob 存储)和 QA 结果。使用 **`/contained:tracer`** 技能查询会话历史、审计日志和文件差异。
#### 来源标记
每次任务关闭都会在 `tasks.summary` 中写入一个 `provenance_log` 条目。每个条目捕获镜像摘要、Sigstore 操作员身份、Rekor 日志索引和 `closed_at` 时间戳——将任务记录与强制执行其策略的确切签名镜像绑定在一起。
这在问责方面有两个重要意义:
**数据库中的任何任务都是完全可追踪的。** 给定一个已关闭的任务,您不仅可以确定智能体做了*什么*(审计日志+差异),还可以确定是*哪个签名镜像约束了它*——然后从那里通过 Rekor 独立验证操作员身份和策略内容。链条是:任务 → 镜像摘要 → Rekor 条目 → 操作员 OIDC 身份 → 策略清单。
**恢复的任务会积累完整的签名历史。** 当任务在容器重建(新的 `contAIned init`,新的镜像签名)之后重新打开时,下一次关闭会向 `provenance_log` 追加第二个条目,而不是覆盖第一个。跨越三次构建恢复的任务带有三个条目——每个条目都记录了管理该部分工作的镜像和操作员身份。无论经历了多少次重建周期,都会保留完整的保管链。
该日志可直接查询:
```
-- Which image ran this task?
SELECT json_extract(summary, '$.provenance_log[0].image_digest'),
json_extract(summary, '$.provenance_log[0].operator_identity'),
json_extract(summary, '$.provenance_log[0].rekor_log_index')
FROM tasks WHERE session_id = ?;
-- Tasks that ran under a specific image digest
SELECT session_id, started_at
FROM tasks
WHERE json_extract(summary, '$.provenance_log[0].image_digest') = 'sha256:...';
```
当 Sigstore 被禁用时(`sigstore.enabled: false`),`provenance_log` 将写入为空列表——该字段始终存在,使得来源的缺失是清晰可见的,而不是模糊不清的。
## 自定义策略
策略在构建时固化到 Docker 镜像中。要更改它,请编辑 `.contAIned/manifest.yaml` 并重建:
```
contAIned init --rebuild # rebuild with existing manifest
contAIned init --manifest policy.yaml # rebuild with a new manifest
```
在会话中使用 `#policy` 查看有效策略(只读)。
当清单哈希更改时,镜像会自动重建——在编辑 `manifest.yaml` 之后运行 `contAIned init` 将检测到更改并触发重建,而无需显式使用 `--rebuild`。
**镜像标签。** 默认情况下,`contAIned init` 将构建的镜像标记为 `contained:`——这是从目录名自动派生的。在 `~/projects/api-service` 中运行 `contAIned init` 会生成 `contained:api-service`;在 `~/projects/data-pipeline` 中运行它会产生 `contained:data-pipeline`。这两个镜像共存;互不覆盖。要改用固定名称,请在 `manifest.yaml` 中显式设置 `runtime.docker.image`。清单哈希和包版本作为镜像标签存储,用于决定是否需要重建——在编辑 `manifest.yaml` 之后运行 `contAIned init` 会自动触发重建。
### 项目指令 (CLAUDE.md)
contAIned 针对智能体的操作指令固化在镜像的 `/etc/claude-code/CLAUDE.md` 中。Claude Code 会将其与您项目的 `./CLAUDE.md` 一起加载——两者在每个会话中自动组合,无需任何配置。
**`./CLAUDE.md` 是您的。** 在您的项目根目录中创建它,以为智能体提供项目特定的上下文:架构说明、编码约定、常见工作流、领域术语。镜像中受管理的指令始终存在;您是在添加它们,而不是替换它们。
```
# 我的项目
This is a Go service that ... (your project context here)
```
受管理的 `CLAUDE.md` 与 `managed-settings.json` 一样是防篡改的——它不能被项目或用户设置排除或覆盖。这确保了治理指令(在 QA 失败时停止、不要修改控制平面文件、不要重试被拒绝的工具调用)始终生效,同时将智能体的项目知识完全留给操作员掌控。
### 策略架构概述
所有治理设置都位于 `manifest.yaml` 的 `policy:` 键下。完整参考位于 [docs/policy-reference.md](./docs/policy-reference.md);常见项目类型的示例位于 [docs/examples/](./docs/examples/)。关键部分如下:
#### `policy.secrets.rules` ——机密文件保护
一个控制哪些文件路径被视为机密的单一有序规则列表。每个规则具有 `name`、`patterns`(正则表达式列表,不区分大小写)、`action`(`allow` 或 `block`)和可选的 `reason`。**首次匹配生效**——将 `allow` 规则列在 `block` 规则之前以创建安全变体豁免。
```
policy:
secrets:
rules:
- name: safe-variants
patterns: ['\.(example|sample|template)']
action: allow # .env.example is always permitted
- name: dotenv
patterns: ['(^|[/\\])\.env(\.[^/\\]+)?$']
reason: "Secret files may not be accessed."
action: block
```
#### `policy.bash.rules` ——Bash 命令限制
一个控制智能体可以运行哪些 Bash 命令的有序规则列表。每个规则支持 `action: allow`(自动批准)、`action: block`(直接拒绝)或 `action: escalate`(弹出操作员提示)。首次匹配生效——首先列出 `allow` 规则,这样安全的命令就不会被后面的阻止规则捕获。
```
policy:
bash:
rules:
- name: safe-reads
patterns: ['^git\s+status\b', '^ls\b', '^cat\b']
action: allow
- name: destructive
patterns: ['^rm\s', '.*\brm\s+-rf\b.*']
reason: "Destructive deletion is not permitted."
action: block
- name: docker-run
patterns: ['^docker\s+run\b']
reason: "docker run requires operator approval."
action: escalate
```
#### `policy.qa.checks` ——QA 门控
当智能体发出完成信号时运行的命令列表。每个条目要么是一个简单的 exec 形式数组(名称从 `command[0]` 推断),要么是带有可选 `when_changed` 全局保护符的命名对象。如果 `checks` 为空,则 QA 平凡通过。
```
policy:
qa:
checks:
- ["ruff", "check", "."] # bare — name inferred as "ruff"
- name: tests
command: ["pytest", "tests/", "-x", "-q"]
when_changed: ["*.py"] # skip if no .py files were touched
- name: go-vet
command: ["go", "vet", "./..."]
when_changed: ["*.go"]
```
命令使用 exec 形式数组并在 `shell=False` 下运行。未安装其二进制文件的检查将被自动跳过。退出码 5(未收集到测试)被视为通过。
### 插件市场强制执行
Claude Code 支持插件——通过*市场*分发的技能、智能体、钩子、MCP 服务器和 LSP 服务器。contAIned 为操作员提供了三个杠杆来治理插件的使用,均在 `manifest.yaml` 的 `policy.plugins` 下配置:
**`strict_marketplaces`** ——当为 `true` 时,将 `strictKnownMarketplaces` 发射到 `managed-settings.json` 中。只能添加已解析列表中的来源;所有其他市场来源在任何网络或文件系统操作之前都会被阻止。默认值:`false`(无限制)。
**`preinstall`** ——在 `contained init` 时固化到容器镜像中的插件,从第一次会话开始就可用,无需任何手动安装步骤。将其用于 GitHub 集成等跨领域工具,无论智能体正在处理哪个项目,这些工具都应始终存在。
**生态系统 `plugins`** ——`ecosystem_definitions` 中每个生态环境的插件列表,只要该生态系统在工作区中活跃,就会预安装。这是 LSP 服务器(`go-lsp`、`python-lsp`、`typescript-lsp`)的自然归属,它们应该只在相关语言正在使用时才存在。
```
# 在您的 operator manifest 中:
ecosystem_definitions:
go:
plugins:
- marketplace: claude-plugins-official
plugin: go-lsp # installed only when a repo declares the go ecosystem
policy:
plugins:
strict_marketplaces: true # block unknown marketplace sources
builtin_marketplace: true # include the Anthropic official marketplace
preinstall:
- marketplace: claude-plugins-official
plugin: github # always installed regardless of ecosystem
```
**按插件允许列表。** Claude Code 没有 `/plugin install` 的钩子事件——插件安装斜杠命令完全绕过了钩子系统。如果您需要限制市场内的单个插件,推荐的方法是创建一个仅包含已批准插件索引的私有市场仓库,然后专门绑定到它:
```
policy:
plugins:
strict_marketplaces: true
builtin_marketplace: false # disable the Anthropic official marketplace
extra_marketplaces:
- source: github
repo: your-org/approved-plugins
ref: main
```
这将智能体限制为您组织已审查的确切插件,而不依赖于上游市场。有关完整的 `policy.plugins` 架构,请参见 [docs/policy-reference.md](docs/policy-reference.md)。
## 镜像支持
contAIned 智能体在 Docker 容器内运行,无法访问主机文件系统或剪贴板。支持两种与智能体共享图像的手势,以匹配主机上原生 Claude Code 的体验:
**拖放** ——将任何图像文件从主机操作系统拖放到终端窗口上。contAIned CLI 通过括号粘贴模式拦截拖放,将文件复制到 `/.images/` 中,并将容器端路径注入到组合缓冲区中。终端会打印一条 `[contAIned] image copied →` 确认行。
**Ctrl+V(剪贴板粘贴)** ——将图像复制到主机剪贴板后,按 Ctrl+V。后台监视器将剪贴板图像保存到 `/.images/clipboard.png`,并且 CLI 拦截该击键以注入该路径。一行 `[contAIned] clipboard image ready` 确认了该操作。
这两条路径在输入到达 Claude Code 之前完全由 CLI 处理。智能体只看到容器端路径并通过其正常的 `Read` 工具读取。
### 平台先决条件
| 平台 | 所需工具 | 安装 |
|---|---|---|
| macOS | `pngpaste` | `brew install pngpaste` |
| Linux (Wayland) | `wl-paste` | 通常包含在 `wl-clipboard` 中 |
| Linux (X11) | `xclip` | `apt install xclip` / `dnf install xclip` |
如果未找到剪贴板工具,拖放仍然有效。监视器在没有剪贴板支持的情况下启动,并记录一次性警告。
### 不支持的方法
| 方法 | 为什么不支持 |
|---|---|
| 手动输入主机路径 | PTY 仅拦截粘贴事件,而不拦截单个击键 |
| 通过 URL 引用图像 | 如果模型需要远程图像,请使用 `WebFetch` |
| 广泛的目录挂载 | 不必要——它们暴露了超出需要的主机文件系统部分;显式共享就足够了 |
## 已知缺陷
### QA 钩子覆盖率
QA 检查现在完全与语言无关。`policy.qa.checks` 列表接受任何 exec 形式的命令数组——`go vet`、`npx tsc --noEmit`、`cargo clippy` 或任何其他工具。不需要具备 Python 知识;有关 Go、TypeScript 和混合项目的即用型清单,请参见 [docs/examples/](./docs/examples/)。
### 垃圾回收 (`tracer.db`)
`tracer.db` 会无限制地增长。旧的 `contAIned gc` CLI 命令已随所有其他子命令一起移除;目前无法在会话内触发 GC。
**待处理:** 将 `#gc [--keep-days N]` 实现为 `UserPromptSubmit` 钩子中的哈希命令。在此之前,可以手动修剪数据库:
```
-- connect via: #db
DELETE FROM blobs WHERE sha256 NOT IN (SELECT sha256 FROM diffs);
DELETE FROM diffs WHERE task_id IN (
SELECT id FROM tasks WHERE created_at < unixepoch('now', '-30 days')
);
DELETE FROM tasks WHERE created_at < unixepoch('now', '-30 days');
VACUUM;
```
### 叙述注入预算
**`/contained:tracer`** 技能从 `tracer.db` 检索存储的叙述,并通过 `additionalContext` 机制将其注入到 Claude 的上下文中。Claude Code 对注入的上下文施加了大小上限(大约 10,000 个字符)。对于长会话——大约 30 轮或更多轮——叙述的每轮结束声明会积累到超出此预算。线束会静默截断注入的文本,丢弃叙述的尾部。会话的最终结论(通常是最重要的部分)通常位于尾部。
`tracer.db` 中存储的数据是完整的,不受影响;这纯粹是一个显示问题。
**待处理:** 在 `PreCompact` 钩子中实现叙述压缩。当会话上下文增长到足以值得压缩时,Claude Code 会触发 `PreCompact`——同样的信号表明叙述也变得很大。该钩子将从 `tracer.db` 读取积累的结束内容,让 Claude 将它们总结成一个紧凑的段落,并将结果写回。这完全反映了 Claude Code 压缩其自身上下文窗口的方式:相同的触发器、相同的机制、有限的输出大小。
### 针对恶意智能体的出站过滤
Bash 子进程网络访问由 Claude Code 的沙盒(bubblewrap)在操作系统级别强制执行,因此无法通过操纵环境变量来绕过。发往非允许域名的 `WebFetch` 需要操作员批准——在无人值守的会话中它不会静默进行。
残余的空白是绕过这两层的出站连接:打开原始非 HTTP 套接字(例如通过 `socket.connect` 连接到硬编码 IP)的代码不受沙盒基于域名的规则的约束。完全强制执行所有协议需要在 Docker 网桥上设置 iptables DNAT 规则,以便在内核级别将所有端口流量重定向到过滤代理。这些规则需要在主机上具有 `CAP_NET_ADMIN`。
**待处理:** 调查轻量级 Docker 网络插件是否可以在 `contAIned init` 时安装重定向规则,从而消除用户面对的 `sudo` 需求。在此之前,需要针对此场景进行硬性边界的操作员可以在初始化后手动应用规则:
```
BRIDGE=$(docker network inspect contAIned-net \
--format '{{index .Options "com.docker.network.bridge.name"}}')
iptables -t nat -A PREROUTING -i "$BRIDGE" -p tcp --dport 80 -j REDIRECT --to-port 3128
iptables -t nat -A PREROUTING -i "$BRIDGE" -p tcp --dport 443 -j REDIRECT --to-port 3128
```
## 安全模型
**镜像共享与 contAIned 的隔离模型保持一致。** 该设计刻意避免了广泛的主机目录挂载:只有操作员明确共享的文件(通过拖动或粘贴)才能进入容器。PTY 包装器在输入边界进行拦截,因此智能体永远不会看到原始的主机路径——它只接收 `/workspace/.images/` 下的容器端路径。文件是被复制的(而不是符号链接),因此智能体的视图完全包含在 `/workspace/` 中,并且无法遍历到意外的主机位置。`restrict_reads` 钩子在策略层强制执行 `/workspace/.images/` 下的仅限图像访问,阻止非图像文件,即使它们由于某种原因被放置在那里。唯一的 `clipboard.png` 条目反映了原生剪贴板的心智模型——工作区中不会积累意外图像的历史记录。
contAIned 的信任边界、执行架构和威胁分析记录在 [docs/security-model.md](./docs/security-model.md) 中。该文档涵盖了主要的信任层次结构、每个遏制层的演练、具体的威胁场景(提示注入、对抗性智能体、数据泄露、供应链攻击、操作员过度依赖)以及关于 contAIned 不保证什么的确切声明。交叉引用表将分析映射到 [OWASP Top 10 for LLM Applications](https://owasp.org/www-project-top-10-for-large-language-model-applications/) 和 [MITRE ATLAS](https://atlas.mitre.org/),以供在这些框架内工作的读者参考。
标签:AI代理, AI安全, Anthropic, Chat Copilot, CIS基准, Claude Code, CLI, DevSecOps, DNS 解析, Docker, IP 地址批量处理, LLM, NIDS, QA自动化, RBAC, SOC Prime, Streamlit, Unmanaged PE, Web报告查看器, WiFi技术, 上游代理, 人工智能, 代码审查, 代码生成, 出口过滤, 合规治理, 大模型安全, 威胁情报, 安全防御评估, 容器化, 容器隔离, 开发工具, 开发者工具, 权限控制, 沙箱, 渗透测试工具, 用户模式Hook绕过, 端到端安全, 策略即代码, 策略引擎, 纵深防御, 编码助手, 网络安全, 网络安全挑战, 网络过滤, 聊天机器人安全, 自主智能体, 访问控制, 请求拦截, 质量保证, 逆向工具, 隐私保护