内容
# Quorum
[](https://github.com/ia64mail/quorum/actions/workflows/ci.yml)
> **关于本项目。** Quorum 是一个案例研究,用于研究 AI 代理驱动的软件开发。它是一个可行的系统,但其主要目的是作为一个实践研究工具,而不是一个生产产品。具体来说,该项目探讨:
>
> - **基于工单的开发** — 将 `tickets/` 目录视为实施时间线知识库,每个工单记录了离散工作单元背后的情况、推理和方法,代码库仍然是 *如何* 的源头。
> - **代码库演化指标** — 在自主、基于角色的代理舰队塑造代码随时间变化时,衡量复杂度、熵和交付速度(参见 `tools/entropy-report` 和 `tools/session-report`)。
> - **Anthropic SDK 和模型上下文协议** — 对 Claude 代理 SDK、MCP 服务器/协议、传输语义(Streamable HTTP、会话、诱导)、工具桥接和按角色权限强制执行进行深入研究。
> - **多代理环境中的上下文管理** — 基于拉取的上下文模型,带有作用域存储(项目/对话/代理)、混合 BM25 + 向量检索、异步嵌入和上下文后处理(引导程序组装、总结、令牌预算控制),以便代理在不传递完整对话历史的情况下进行协作。
>
> 其背后的方法论在 **[基于工单的开发在代理世界中](https://github.com/ia64mail/quorum/wiki/Ticket-Driven-Development-in-an-Agentic-World)** 中有详细介绍 — 从那里开始了解 *为什么*。README 的其余部分描述了从这些实验中涌现的系统。
多代理 AI 协调用于半自主软件开发。Quorum 协调一组基于角色的 AI 代理 — 建筑师、团队负责人、开发人员、QA、产品负责人 — 在由与用户对话的调节器(Moderator)调解下共同完成实际工程任务。调节器作为 Claude Code CLI 容器运行;每个代理都是一个 NestJS 应用程序,通过代理 SDK(`@anthropic-ai/claude-agent-sdk`)驱动 Claude,具有文件系统、bash 和 git 访问权限。它们都通过 MCP 服务器使用基于拉取的上下文模型进行通信,这样没有代理会转发另一个代理的对话历史。
```mermaid
graph TB
U[用户]
subgraph "调节器容器"
MOD[Claude Code CLI]
end
subgraph "MCP 服务器"
REG[代理注册]
BROKER[消息代理]
CTX[上下文存储]
end
subgraph "代理容器(强化)"
A[建筑师]
TL[团队负责人]
D[开发人员]
QA_A[QA]
PO[产品负责人]
end
subgraph "基础设施"
OS[(OpenSearch<br/>BM25 + k-NN)]
OLL[Ollama<br/>嵌入]
end
U <--> MOD
MOD <-->|MCP| BROKER
A <-->|MCP| BROKER
TL <-->|MCP| BROKER
D <-->|MCP| BROKER
QA_A <-->|MCP| BROKER
PO <-->|MCP| BROKER
CTX <-->|混合搜索| OS
CTX -.->|异步嵌入| OLL
```
## 关键思想
使 Quorum 不仅仅是“一堆容器中的代理”。每个链接都深入介绍了 `docs/` 中的内容。
### 代理作为对等体,而非管道
调节器协调,但任何代理都可以调用其他代理在任务中途。开发人员向架构师寻求澄清;架构师将业务问题升级给产品负责人;产品负责人通过调节器的 MCP 查询通道将问题路由回给用户。消息代理通过调用深度限制、循环检测、按角色超时、注册检查等保护措施来限定网格。 → [代理消息](docs/agent-messaging.md),[消息代理](docs/message-broker.md)
### 双向 MCP
标准 MCP 是单向的:主机调用工具,工具返回。Quorum 代理本身就是大型语言模型,需要*发起*调用。MCP 服务器既是工具主机(公开 `invoke_agent`、`context_*` 系列、`wait_invocation`、`new_conversation` 和注册工具 — 总共九个工具和两个资源),又是调度 `POST /invoke` 到每个代理的注册回调 URL 的路由器。会话通过 `mcp-session-id` 固定在每个客户端,空闲时回收,断开时回收。 → [MCP 连接](docs/mcp-connectivity.md)
### 基于拉取的上下文 — 存储决策,而非对话
代理从空白开始。每次调用都带有任务描述、关联 ID 和由代理商预先获取的项目/对话决策的小型启动程序 — 绝不调用者的聊天记录。代理通过 `context_query` 拉取其他内容,记录*命名决策*(`auth_pattern = "JWT with refresh tokens"`)使用 `context_store`,并使用 `context_summarize` 修剪预算长的链条。代理之间没有传输记录;只有提炼的事实。 → [上下文管理](docs/context-management.md)
### OpenSearch 上的混合检索
上下文存储在 OpenSearch 上,具有一个融合 BM25 全文搜索和 k-NN 向量搜索(权重 0.3 / 0.7)的索引。嵌入由本地 Ollama(`mxbai-embed-large`,1024 维)异步计算 — 文档在写入时可通过关键词搜索,在 ~300 ms 内可通过语义搜索。 当嵌入者不可用时,它会顺利降级为仅 BM25,当嵌入者不可用时,它会切换到 `InMemoryStore` 进行测试和无 Docker 开发。 → [上下文存储](docs/context-store.md)
### 按角色划分的权限,机械强制执行
每个代理角色都带有结合 Claude Code `disallowedTools` 禁用列表的工具配置文件和 `canUseTool` 钩子,用于每次调用的 gating。容器本身是强化 - 只读文件系统、丢弃的 Linux 功能、无新权限、tmpfs 临时空间 - 因此权限配置文件缩小了代理*意图*做的事情,而容器强化是实际的安全边界。 → [Claude Code SDK](docs/claude-code-sdk.md)
### 基于工单的知识库
`tickets/` 中的每个工作单元都记录为一个工单,按时间线顺序排列。工单记录了*为什么* — 限制、替代方案、选择背后的推理;代码库仍然是*如何* 的源头。结合 `docs/`(当前系统)和 `tools/session-report`(运行遥测),存储库带有 AI 形状的代码库的完整来源跟踪。 → [知识管理](docs/knowledge-management.md),[工单库](tickets/README.md)
## 30 秒的短片
为了具体地描述:用户要求调节器 *"添加身份验证。"* 调节器调用架构师,带有薄信封 —
```
{ correlationId: "task-auth-001", caller: "moderator", target: "architect",
action: "Design the auth system", context: { constraint: "must support OAuth" } }
```
— 架构师的窗口几乎是空的。它拉取几个项目事实(`context_query(project, "tech stack")`),草拟设计,在任务中途调用产品负责人以澄清 MVP 范围,存储其决策为命名键(`auth_pattern = "JWT with refresh tokens"`、`session_storage = "Redis"`),并返回简明的答案。当开发人员稍后在另一个工单上出现时,它会拉取相同的键,并且永远不会看到架构师的推理字样。上下文存储在存储中,而不是调用链中。
完整的逐步演练 — 带有防护措施、任务中咨询和上下文管理 — 位于 [代理消息](docs/agent-messaging.md) 和 [上下文管理](docs/context-management.md) 中。
## 项目结构
NestJS 单体仓库,包含两个应用程序和一个共享库,以及在一个单独的容器中运行的 Claude Code CLI 调节器(它没有自己的 NestJS 应用程序):
```
quorum/
├── apps/
│ ├── mcp-server/ # MCP 服务器 + 代理注册 + 消息代理 + 上下文存储
│ └── agent/ # 单个 Docker 镜像,通过 AGENT_ROLE 环境变量支持多角色
├── libs/
│ └── common/ # 共享类型、配置、提示、日志记录器和 LLM 实用程序
│ └── src/
│ ├── config/ # 配置工厂(应用程序、Anthropic、日志记录器、MCP)
│ ├── context-store/ # 抽象 ContextStore 类、类型、CompositeKeyBuilder
│ ├── prompts/ # SYSTEM_PREAMBLE + 按角色的提示模板
│ ├── logger/ # LoggerBuilder、QuorumLogger(双重传输)
│ ├── llm/ # 工具映射器(MCP → Anthropic 模式转换)
│ └── messaging/ # 代理角色枚举、InvokeRequest/Response
├── docs/ # 架构文档
├── tickets/ # 实现时间线知识库
├── tools/ # entropy-report、session-report
└── docker-compose.yml
```
## 入门指南
### 先决条件
- Node.js
- Docker & Docker Compose
- **Anthropic API 密钥** — 代理 LLM,计费 API 计费(`ANTHROPIC_API_KEY`)
- **Claude Code OAuth 令牌** — 调节器订阅身份验证,固定速率计费(`CLAUDE_CODE_OAUTH_TOKEN`)。通过在本地运行 `claude setup-token` 生成;需要 Claude.ai Pro/Max/Team/Enterprise 席位
- **GitHub 精细粒度 PAT** (`GH_TOKEN`) — 限定为目标存储库,具有 `Contents: read+write`、`Pull Requests: read+write`、`Metadata: read`。由 `gh` 和 git 在每个容器中用于克隆、获取、推送和 PR 操作
- **目标存储库 URL** (`REPO_URL`) — Quorum 将要处理的项目的 HTTPS 克隆 URL。可以是 Quorum 存储库本身进行自我托管
### 安装
```bash
npm install
cp .env.example .env
# 编辑 .env 并设置:
# ANTHROPIC_API_KEY sk-ant-… (代理)
# CLAUDE_CODE_OAUTH_TOKEN sk-ant-oat01-… (调节器)
# GH_TOKEN github_pat_… (gh CLI + git 身份验证,所有容器)
# REPO_URL https://github.com/<owner>/<repo>.git
```
### 开发
```bash
npm run start:dev # 以监视模式启动默认应用程序
npm run build # 构建所有应用程序
npm run lint # 校验并自动修复
npm run test # 运行单元测试
npm run test:e2e # 运行端到端测试
```
### Docker
```bash
./scripts/start.sh # 构建并启动所有容器
./scripts/start.sh -d # 分离模式
./scripts/moderator.sh # 附加到正在运行的调节器(Claude Code CLI)
```
启动脚本导出当前用户的 `HOST_UID`/`HOST_GID`,因此绑定挂载的日志文件具有正确的所有权,然后运行 `docker compose build` 和 `docker compose up`。额外参数转发到这两个命令。
启动 MCP 服务器、调节器容器(Claude Code CLI)、代理容器以及上下文存储使用的 OpenSearch + Ollama 基础设施。**没有主机绑定挂载您的项目** — 在首次启动时,调节器和每个代理容器都通过 `GH_TOKEN` 和 `git clone $REPO_URL` 进行身份验证和 `git clone $REPO_URL` 到它们自己的命名卷中。所有容器都在它们自己的克隆上操作,并通过 GitHub 远程仓库通过 `git push`/`git pull` 同步;每个代理调用都在请求的分支的隔离 `git worktree` 中运行。代理在启动时向 MCP 服务器注册,并准备接收调用。
## 文档
| 文档 | 内容 |
|----------|----------------|
| [系统设计](docs/system-design.md) | 架构、容器、部署、`quorum.md` 配置 |
| [代理消息](docs/agent-messaging.md) | 双向 MCP、`invoke_agent`、通信模式 |
| [MCP 连接](docs/mcp-connectivity.md) | 代理和调节器的会话生命周期 — 建立、维护、回收、注册、回收 |
| [消息代理](docs/message-broker.md) | 路由、防护措施、传输、可用性 |
| [上下文管理](docs/context-management.md) | MCP 工具/资源 API、使用模式 |
| [上下文存储](docs/context-store.md) | OpenSearch 后端,混合 BM25 + 向量搜索、异步嵌入管道、测试的 InMemoryStore |
| [知识管理](docs/knowledge-management.md) | 知识管理理念、三个领域、KB 概念 |
| [Claude Code SDK](docs/claude-code-sdk.md) | Claude Code SDK 集成、工具桥、权限、强化 |
| [工单库](tickets/README.md) | 工单约定和结构指南 |
## 技术栈
- **运行时**:Node.js + TypeScript
- **框架**:NestJS(单体仓库,webpack)
- **LLM**:Anthropic Claude — 通过 `@anthropic-ai/claude-agent-sdk` 代理;调节器作为 Claude Code CLI 在专用容器中运行
- **协议**:通过 `@modelcontextprotocol/sdk` 的模型上下文协议(Streamable HTTP 传输)
- **检索**:OpenSearch(BM25 + k-NN)与本地 Ollama(`mxbai-embed-large`)用于嵌入
- **容器化**:Docker Compose(多目标 Dockerfile:`default` / `agent` / `moderator`)
- **验证**:Zod
- **测试**:Jest