内容
# 艾吉斯
[English](README.md) · [繁體中文](README.zh-TW.md)
> 艾吉斯描述事实。代理决定采取什么行动。
一个无偏见的观察层,用于LLM驱动的编码工作流。
> **如果你是AI编码代理**(Claude Code、Cursor、Aider等),
> 帮助用户安装艾吉斯,请阅读 [`AGENTS.md`](AGENTS.md) —
> 它包含了您应该运行的安装命令。
📊 **[实验存档](experiments/README.md)** — 九轮
成对的A/B代理比较(Anthropic / OpenAI / Google)驱动
规则库达到其当前状态。图表、运行日志和
从发现到PR的直接血统。
---
## 它是什么(V2)
艾吉斯是一个MCP服务器,发出关于提议的文件写入的**发现**。
它不阻塞、不警告、不允许或做出其他判断。
每个发现都是一个事实 — "这个正则表达式在这里匹配",
"这个信号增加了1", "这个文件导入另一个文件,然后再导入它自己" —
具有足够的结构化上下文,供消费代理(LLM)决定采取什么行动。
**艾吉斯不编写代码,不控制写入,也不评估结果。**
它只描述发生了什么变化。
前一代V1架构(Ring 0/0.5/0.7/R2,具有BLOCK/WARN/PASS
判决,多轮流水线,成本感知回归回滚,停滞/振荡检测器)
在V2中已经消失。判断存在于它应该存在的地方:
在消费代理的推理步骤中。
---
## 为什么存在
LLM系统仍然在三种情况下失败,而周围的工具无法检测到:
1. 多轮重构会无声地积累回归。
2. LLM描述的动作与实际工具调用不同。
3. 结构规则在无人察觉的情况下逐渐被破坏。
艾吉斯存在是为了使这些失败**可见**。
它们是否在此上下文中可接受是一个取决于代理或人类的决定;
艾吉斯只确保数据呈现在桌面上。
---
## 艾吉斯不是什么
艾吉斯是一个狭窄的工具。不要安装它来期望:
- **一个linter或替换ruff / eslint / clippy.**
它只在`crates/aegis-core/src/{security.rs,signals/}`中明确编码的模式上触发。
没有这些模式的普通“坏代码”对它来说是不可见的。
- **一个SAST套件。** 10个安全规则涵盖了众所周知的反模式
(eval、硬编码秘密、弱RNG、TLS-off、CORS错误配置等),
但远远不能覆盖Bandit / Semgrep的范围。
将它们视为一次快速检查,而不是完整的审计。
- **一个适用于绿地工作的有能力模型的保险网。**
如果消费代理是GPT-5级或Sonnet级,编写一个全新的项目,
它通常会自己选择安全的默认值,艾吉斯的发现大多数情况下都是空的。
边际价值在**中级模型在棕地工作中**最高 —
现有代码库有约束(循环图、公共API、已删除的调用者),
代理否则会忽略这些约束。
- **一个自动修复工具。** 艾吉斯描述;代理决定。
发现包含一个`severity_hint`字符串,但没有强制执行。
没有重试,没有重写,没有恢复。
如果你想要一个发布判决的网关,请安装一个linter / SAST /
pre-commit钩子。艾吉斯是其底层的事实层 — 只有事实,
没有判断。
---
## 它如何工作
两个基础设施层为一个MCP工具提供支持。
```
┌─────────────────────────────────────┐
│ MCP Tool: validate_file │
│ (path, new_content, │
│ old_content?, workspace_root?) │
└──────────────┬──────────────────────┘
│ findings[]
▼
┌─────────────────────────────────────┐
│ 发现生成器 │
│ 语法 · 信号 · 安全 │
│ 工作区 │
└──────────────┬──────────────────────┘
│
┌───────┴────────┐
▼ ▼
┌─────────────┐ ┌─────────────────┐
│ 层 1 │ │ 层 2 │
│ parse(file) │ │ 工作区索引 │
│ → 树 │ │ (mtime缓存) │
└─────────────┘ └─────────────────┘
```
**层 1 — parse**:每个文件调用一次tree-sitter,
跨所有发现生成器共享。 不再有每个信号的Parser::new()。
始终返回一个树,即使语法损坏。
**层 2 — 工作区索引**:文件导入和公共符号的反向索引,
mtime缓存,因此重复的MCP调用只重新解析实际更改的内容。
**发现**:四种类型 — 语法、信号、安全、工作区 —
在下面描述。 每个发现包含`file`,可选`range`和`snippet`,
以及结构化的`context`映射。 没有一个包含严重性。
---
## 发现类型
| `kind` | 含义 | 示例 `rule_id`s |
| :--- | :--- | :--- |
| **语法** | Tree-sitter发现ERROR / MISSING节点。 | `ring0_violation` |
| **信号** | 结构计数器(14个)。 当提供`old_content`时,`context`包含`value_before` / `value_after` / `delta`。 | `fan_out`, `max_chain_depth`, `cyclomatic_complexity`, `nesting_depth`, `empty_handler_count`, `unfinished_marker_count`, `unreachable_stmt_count`, `mutable_default_arg_count`, `shadowed_local_count`, `suspicious_literal_count`, `unresolved_local_import_count`, `member_access_count`, `type_leakage_count`, `cross_module_chain_count`, `import_usage_count`, `test_count_lost` |
| **安全** | 特定反模式匹配(16条规则)。`context.severity_hint`是提示,不是判决。 | `SEC001`–`SEC016` (eval/exec, 硬编码秘密, TLS-off, shell注入, SQL拼接, CORS通配符+凭证, JWT不安全, 不安全反序列化, 弱加密, 弱RNG, 硬编码Bearer令牌, 耗时不安全的凭证比较, Python裸`except:`, 硬编码PEM私钥, 静默广泛异常, SSRF在用户输入URL上) |
| **工作区** | 跨文件发现。 仅在提供`workspace_root`时发出。 | `cycle_introduced`, `public_symbol_removed`, `file_role` |
`aegis-allow: <rule_id>`(或`aegis-allow: all`)在同一或
前一行源代码中标记`user_acknowledged: true`,
而不是删除它。 代理看到确认并可以选择尊重它。
---
## 快速入门
V2发布了一个单独的二进制文件:`aegis-mcp`(MCP服务器)。
### 安装
```bash
# 先决条件:git + Rust工具链(1.74+)。
git clone https://github.com/wei9072/aegis && cd aegis
cargo install --path crates/aegis-mcp
```
### 配置您的MCP客户端
将您的MCP感知客户端(Claude Code / Cursor / 您自己的代理)
指向`aegis-mcp`二进制文件,通过stdio。
确切的配置语法因客户端而异;
服务器本身不接受任何标志。
### 一个工具:`validate_file`
```jsonc
{
"name": "validate_file",
"arguments": {
"path": "src/auth.py",
"new_content": "...", // 必需
"old_content": "...", // 可选 — 启用增量
"workspace_root": "/path/to/project" // 可选 — 添加工作区发现
}
}
```
返回:
```json
{
"schema_version": "v2.0",
"findings": [
{
"kind": "security",
"rule_id": "SEC009",
"file": "src/auth.py",
"range": { "start_line": 47, "start_col": 4, "end_line": 47, "end_col": 52 },
"context": { "severity_hint": "block", "message": "弱哈希…" },
"user_acknowledged": false
},
{
"kind": "signal",
"rule_id": "unfinished_marker_count",
"file": "src/auth.py",
"context": { "value_before": 0, "value_after": 1, "delta": 1 },
"user_acknowledged": false
},
{
"kind": "workspace",
"rule_id": "cycle_introduced",
"file": "src/auth.py",
"context": { "cycle": ["src/auth.py", "src/user.py", "src/auth.py"] },
"user_acknowledged": false
}
]
}
```
第一次调用`workspace_root`会构建工作区索引
(解析每个支持的文件一次);
后续调用会重用缓存,
只重新解析mtime更改的文件。
没有单独的“扫描”步骤。
---
## 支持的源语言
通过文件扩展名进行运行时调度。
添加语言是Cargo依赖 +
`crates/aegis-core/src/ast/languages/`下的适配器文件 +
`.scm`导入查询 — 无需其他更改。
| 语言 | 层 1 解析 | 备注 |
| :--- | :---: | :--- |
| Python | ✅ | `.py`, `.pyi` |
| TypeScript | ✅ | `.ts`, `.tsx`, `.mts`, `.cts` |
| JavaScript | ✅ | `.js`, `.mjs`, `.cjs`, `.jsx` |
| Go | ✅ | `.go` |
| Java | ✅ | `.java` |
| C# | ✅ | `.cs` |
| PHP | ✅ | `.php`, `.phtml`, `.php5`, `.php7`, `.phps` |
| Swift | ✅ | `.swift` |
| Kotlin | ✅ | `.kt`, `.kts` |
| Dart | ✅ | `.dart` |
| Rust | ✅ | `.rs` |
---
## 设计原则
- **描述事实,不做判断。** 发现没有严重性字段。
消费代理决定哪些发现重要以及如何反应。
- **一次解析,共享树。** 每个发现生成器都使用一个`ParsedFile`。
没有每个信号的`Parser::new()`。
没有临时文件往返。
- **工作区启动是隐式的。**
带有`workspace_root`的第一个调用会构建索引;
后续调用会命中mtime缓存。
没有单独的扫描工具,没有手动初始化步骤。
- **没有自动学习,没有客观优化。**
艾吉斯不跟踪调用的成功/失败,不适应规则,
不评估结果。 状态仅包含工作区缓存。
- **一个MCP工具,狭窄的表面。** `validate_file`就这样。
没有`retry`,没有`hint`,没有`explain`。
代理推理是代理的工作。
---
## 状态
| 层 | 状态 |
| :--- | :--- |
| 层 1(解析 + 11种语言适配器) | ✅ |
| 层 2(工作区索引 + mtime缓存) | ✅ |
| 发现:语法 + 信号 + 安全 + 工作区 | ✅ |
| MCP服务器(`aegis-mcp`) | ✅ |
| V1二进制文件(`aegis`,`aegis pipeline run`,`aegis check`,`aegis attest`,`aegis scan`) | ❌ 在V2中删除 |
---
## 许可证
MIT — 见 [`LICENSE`](LICENSE)。
V2 — 仅MCP架构。
流水线/运行时/提供者/IR/
决策crate已删除;
判断存在于消费代理中。