Compare commits

...
Author SHA1 Message Date
weichen 228cf74468 Merge authoritative main into the task branch 2026-10-11 02:40:54 +00:00
weichen 0f5df5147e Add the approved collaboration storage and distribution plan r1
Why: freeze the direction for 10-100 user multi-project collaboration storage (PostgreSQL team tier, migration, staged multi-instance) before U1+U2 implementation starts.

Evidence: static review record and SELF audit receipt included; user approval recorded in Dev task card 2026-10-11 (instruction to start the transformation per the plan).

Impact: docs only - new proposal, index entry, review record, audit receipt; no code or runtime change.

Refs: Dev .task/feature-collaboration-storage-plan.md; plan section 5 names U1+U2 as the first increment.
2026-10-11 02:40:02 +00:00
weichen b3ca409cfc Merge pull request 'Backfill the S0-1 completion in the plan and index' (#58) from feature/s0-progress into main 2026-10-11 02:29:57 +00:00
weichen 4a3ca0eb5a Backfill the S0-1 completion in the plan and index 2026-10-11 02:29:16 +00:00
weichen 9a46785ee0 Merge pull request 'Backfill the E4 completion in the plan and index' (#57) from feature/e4-progress into main 2026-10-11 01:34:48 +00:00
weichen cc54cdac76 Backfill the E4 completion in the plan and index 2026-10-11 01:34:07 +00:00
weichen b37f45ff61 Merge pull request 'Backfill the E5 MiniMax-family A/B result' (#56) from feature/e5-progress into main 2026-10-10 17:28:06 +00:00
weichen 077ebdf184 Backfill the E5 MiniMax-family A/B result in the plan and index 2026-10-10 17:27:25 +00:00
weichen e986a85436 Merge pull request 'Backfill the E3 completion in the plan and index' (#55) from feature/e3-progress into main 2026-10-10 16:39:56 +00:00
weichen 19b77a28bc Backfill the E3 completion in the plan and index 2026-10-10 16:39:02 +00:00
weichen 3a82eec74c Merge pull request 'Backfill the E1 increment completion in the plan and index' (#54) from feature/e1-progress into main 2026-10-10 10:03:49 +00:00
weichen 352b7aa540 Backfill the E1 completion status in the index row too 2026-10-10 09:47:50 +00:00
weichen a379a9fbe6 Backfill the E1 increment completion in the plan and index
Why: The E1 quick-fix increment landed (delegate full text, MiniMax
reasoning split, sandbox deps layer, parallel tool execution with kernel
ordering) and the proposal is the master record, so its stage table and
the index row must reflect the actual progress with links.

Evidence: Agent-Native #42/#43/#44, Backend #127, Dev task
feature/agent-e1-parallel-fixes (live verification receipts: F2 zero
think-leak, F3 sandbox IMPORTS-OK, F5 concurrent overlap; eval
toolFailures 19->11, unverifiedFacts 7->12 recorded honestly).

Impact: Documents-only progress backfill; E2-E5/S0 remain unauthorized.
2026-10-10 09:47:25 +00:00
weichen 0f6d6974ef Merge pull request 'Propose the delegation, model-routing and task-sandbox plan' (#53) from feature/agent-exec-routing-sandbox-plan into main 2026-10-10 07:40:41 +00:00
weichen 3947a12700 Propose the delegation, model-routing and task-sandbox plan
Why: The user asked for a researched design covering subagent use,
tiered model routing (Claude via dmxapi for complex work, MiniMax for
the rest), a millisecond/sub-second task sandbox (record-only), and a
verdict on why the agent still feels weaker than codex/claude-code/kimi.
Why: Four research threads (external frontier survey, subagent/routing
evidence, harness-vs-model attribution, local code audit and probes)
converge on role/stage routing instead of whole-task routing, and on
three corrections to the sandbox assumptions (overlay is not a snapshot;
snapshots defer conflicts to merge time; destroy must follow sealing).

Evidence: Local measurements are SELF receipts in the Dev task card
(bwrap namespace ~7ms, overlay same, docker ~142ms; MiniMax reasoning
split; dmxapi /v1/messages cache_read; EXT-16 sandbox missing the
python-docx layer). External claims carry per-item source URLs in the
proposal's section 8.

Impact: Documents-only addition: new proposal 2026-10-10 (E1-E5/S0
increments, D/R workstreams, sandbox tiers L0-L2) plus INDEX rows for it
and the previously unlisted G1 package. No product code changes; all
implementation increments stay unauthorized until approved.

Refs: proposals/2026-10-09-agent-quality-improvement-plan.md (P4/P5,
model A/B), 2026-10-09-ext-g1-b1-b3-exec-backend.md (EXT-16),
Dev task feature/agent-exec-routing-sandbox-plan.
2026-10-10 07:39:56 +00:00
weichen 3e43193f45 Merge pull request 'Propose the G1 extension package: EXT-13..16 (shared retrieval, commands-as-tools, shared project list, exec backend)' (#52) from feature/g1-ext-b1-b3-proposal into main 2026-10-09 14:50:07 +00:00
weichen 1f8ad3b99b Propose the G1 extension package: shared in-run retrieval, commands-as-tools, shared project list, exec backend
Why: RDM G1/G2 are blocked on four base capabilities - the member agent
cannot see published project knowledge inside a run, plugin commands are
unreachable as tools, members cannot see or open conversations in shared
projects, and the isolated exec tier needs a backend seam.
Evidence: SELF - code citations verified at Dev main (runmgr.go:726-736
owner-only Retrieve; hostcaps.go:64-66 owner-only search; server.go:438-441
owner-only listProjects; store/projects.go:124-130 owner-scoped conversation
creation); bwrap availability tested on this host (2026-10-09).
Impact: Docs only. New proposal docs/proposals/2026-10-09-ext-g1-b1-b3-exec-backend.md
registering EXT-13..EXT-16 per the compat-policy template; no code change.
Refs: RDM plan V1.1 sections 3.3/3.4/5.3; owner Q1-Q8 (Q4=option A, Q2)
2026-10-09 14:49:56 +00:00
weichen 9206e0d77c Merge pull request 'Record the agent quality improvement plan and six user rulings' (#51) from feature/agent-quality-plan into main 2026-10-09 14:12:56 +00:00
weichen 238f90e92a Record the agent quality improvement plan and its six user rulings
Why: The 2026-10-09 diagnosis on real run transcripts identified the direct
conversation path's failure chain and a phased plan was approved with six
explicit user rulings (open up tools, kernel-transcript-authoritative context,
markdown-source deliverables with all file types retained, gate demotions,
uncapped stronger-model evals, real-model eval required for behavior changes).
The plan needs a durable main record per the long-term-direction policy, and
the 2026-09-30 preview proposal's "fs tools hidden by default" ruling is
superseded by ruling 1, which must be discoverable from the index.

Evidence: SELF - the record cites the diagnosis session, the transcript
sources (pg1 + legacy SQLite, 62 assistant turns) and the before-baseline
report location on the task card; no code or pointers changed in this repo.

Impact: New proposal 2026-10-09-agent-quality-improvement-plan.md plus one
index row. Subsequent phase tasks reference this record instead of restating
the plan.

Refs: Dev task feature/agent-quality-phase01; supersedes the fs-tool default
in 2026-09-30-file-preview-and-reserved-capabilities.md §2.3.
2026-10-09 12:42:33 +00:00
weichen d3c9b4554e Merge pull request 'Record the v0.2.0 base release scope and owner tag steps' (#50) from feature/v020-release-notes into main 2026-10-09 10:46:32 +00:00
weichen 176db44c4b Record the v0.2.0 base release scope and owner tag steps
Why: v0.2.0 is the hard prerequisite for RDM shared work, and the release
checklist referenced a stale SurfaceVersion and tag message.
Evidence: SELF - scope lines are tied to merged Backend PRs (#66-#110);
Backend #110 CI success; UI composite run 1369 shows 14 files/103 tests.
Impact: Adds docs/records/2026-10-09-base-v0.2.0-release.md and points the
compat policy and the EXT-1 proposal at the actual 2.5.0-experimental line.
Tag creation and push stay with the owner (dual-key rule).
Refs: Dev feature/base-v020-release; Backend #110
2026-10-09 10:44:33 +00:00
weichen 69360de5bb Merge pull request 'Propose the C1-C7 base extension package for RDM shared contracts' (#49) from feature/ext-c1-c7-applications into main 2026-10-08 15:22:39 +00:00
weichen 9995552152 Propose the C1-C7 base extension package for RDM shared contracts
Why: U0 (merged in RDM) rules that domain logic stays downstream and the base needs seven minimal generic capabilities; the formal four-element applications are the only legal path to base changes.
Evidence: All current-state citations re-verified at Dev 34a2bbe (backend fe7c245); RDM requirement source pinned at RDM main ca72cfc.
Impact: New proposal doc + one INDEX row; no code, no surface change in this PR.
Refs: RDM U0 proposal section 5 / ruling D1; extension-compat-policy; phase-plan section 3.3.
2026-10-08 14:44:56 +00:00
weichen 4e57196a8e Merge pull request 'Add architecture guides with worked examples' (#48) from feature/architecture-guides into main 2026-10-08 09:37:36 +00:00
weichen 4ce8155161 Expand guides with worked examples for intents, runs, and squads
Why: 补充实例让导览可对照实操:意图打分/填槽/多意图裁决给出
intentctl explain 实测轨迹(含样例意图、歧义、优先级 tie-break、
能力目录),任务执行给出工具清单与典型回合走查(含 ClaimGate
三类拦截),小队给出 agent-squad demo 全流程实测与真实工作区
目录树/TASK.md 实拍;并修正事实:默认意图目录为空、插件 overlay
与 registry loader 两种文件形态不同。

Evidence: intentctl explain/catalog 与 agent-squad demo 为 2026-10-08
本地实测输出(命令见各节);锚点仍为 Dev R0 218fc4b1;git diff
--check 通过;五篇互链全部可解析。SRC=SELF。

Impact: 仅文档增补(02/03/04/05 四篇),无代码影响。

Refs: Dev 任务分支 feature/architecture-guides。
2026-10-08 09:33:13 +00:00
weichen 09d968cb2f Add architecture guide set for overview, intent, runs, and squads
Why: 系统缺少面向新人与新任务的现状导览;把结构/意图识别/任务执行/小队
四条主线的架构、模型与调用链整理成五篇参考文档,降低跨仓定位成本。

Evidence: 锚点核验于 Dev 组合 218fc4b1ba00b0a03b5bb79f292b3eba6c987a58
(2026-10-08,引用的函数入口行号逐一 grep 定位);git diff --check 通过;
五篇互链相对路径全部可解析。SRC=SELF(本地核验,任务卡有记录)。

Impact: 纯文档新增(docs/guides 五篇),无代码/契约/配置影响。

Refs: Dev 任务分支 feature/architecture-guides(R0 218fc4b1)。
2026-10-08 09:33:13 +00:00
weichen 9505c7eadc Merge pull request 'Backfill the RDM baseline review and MS1 acceptance audit' (#47) from feature/rdm-status-record into main 2026-10-08 09:22:43 +00:00
16 changed files with 2158 additions and 3 deletions
+213
View File
@@ -0,0 +1,213 @@
# Agent-System 系统结构与架构
> 基准:Agent-System-Dev 组合 `218fc4b1ba00b0a03b5bb79f292b3eba6c987a58`(R0,2026-10-08 锚点核验)。
> 行号为该提交下的函数入口位置(个别内部区段为约略范围),随代码演进漂移;文件与函数名是稳定锚点。
> 文中路径均为 Dev 组合仓内相对路径。
---
## 1. 总体形态:一个父仓 + 12 个子模块仓的组合系统
Agent-System 是**多仓组合(gitlink 固定指针)**的开发集成父仓,不是单一代码库。
`.gitmodules` 定义了 12 个子仓,分两类:
**modules/(5 个产品模块仓)**
| 仓 | 目录 | 定位 | 技术栈 |
|---|---|---|---|
| Agent-System-Core | `modules/core` | 核心契约库:纯数据类型 + 线格式权威,零第三方依赖 | Go(附 `ts/types.ts` TS 镜像) |
| Agent-System-Backend | `modules/backend` | 后端模块化单体,默认单二进制(内嵌前端 + 嵌入式 SQLite) | Go |
| Agent-System-Frontend | `modules/frontend` | Web 前端 | React 19 + Vite + TypeScript |
| Agent-System-UI | `modules/ui` | 纯展示组件库:props 进 events 出,不持状态不调 API | React + CSS 令牌 |
| Agent-System-Documents | `modules/documents` | 文档过程仓(设计提案/记录/报告),无代码 | Markdown |
**deps/(7 个基础库仓)**
| 仓 | go module 名 | 一句话职责 |
|---|---|---|
| agent-native | `agent-native` | Agent 执行内核:ReAct 循环、LLM 客户端、工具注册表、会话转录、MCP、技能 |
| agent-squad | `agent-squad` | 小队编排底座:规划器、Leader 委派、黑板、HITL 挂起恢复、调度 |
| agent-intent | `agent-intent` | 确定性意图识别:四级漏斗(Guard→Expectation→Rules→Adjudicate) |
| agent-command | `agent-command` | 命令总线基础库:命令 spec、危险分级、审计信封(零依赖) |
| error-code-system | `error-code-system` | 跨系统错误码目录 + HTTP 错误信封 + 查询服务 |
| logging-system | `logging-system` | 结构化日志库 + 采集服务 |
| development-guidelines | —(非 Go) | 人与 Agent 共用的开发规范文档基线 |
**依赖方向铁律**(`README.md`):
```
frontend → core ← backend → { agent-native, agent-squad, agent-intent, agent-command }
↘ error-code-system / logging-system
```
core 与各生态仓互不依赖;backend 对 8 个库的引用经自己仓内 `deps/` 子模块 +
`modules/backend/go.mod` 的 `replace` 收敛(父仓顶层 `deps/` 与 `modules/backend/deps/`
是同一批仓的两份 gitlink,`scripts/check-pins.sh` 核对一致)。仓库**不使用 go.work**
(避免工作区最小版本选择抬高 backend 单独构建的依赖版本,见 `PROJECT.md`)。
**三态一致的契约机制**:core 的每个契约同时存在 ① Go 类型(camelCase JSON)、
② `modules/core/testdata/golden/*.json` 黄金样本(线格式权威)、③ `modules/core/ts/types.ts`
手工 TS 镜像。父仓 `make contracts` 把 TS 镜像生成到 frontend 与 ui 的 `src/contracts.ts`。
---
## 2. Backend 功能分布(产品核心)
单二进制入口:`modules/backend/cmd/agent-system/main.go`(子命令 serve/version/apikey/user/config)。
组合根与装配顺序在 `modules/backend/internal/app/app.go` 的 `Run()`:
存储 → 总线 → 工具注册 → settings/prompts/skills/profiles → runmgr(三个 runner + SquadRunner 注入)→
intentgate → 插件宿主 + hostapi → workflow 注册表 → RAG → 可选 intentsem →
cardsvc/cmdbus → server + 限流 + 认证 → HTTP 监听(默认 `127.0.0.1:8787`)。
### internal 包职责一览(功能 → 代码分布)
| 包 | 主文件 | 职责 |
|---|---|---|
| app | `internal/app/app.go` | 组合根:装配一切组件 |
| config | `internal/config/config.go` | 启动配置单一管道:内置默认→`--config` JSON→环境变量→CLI |
| store | `internal/store/store.go` | 持久化唯一入口;SQLite(默认,modernc 纯 Go)/ PostgreSQL 双档;`dialect.go` 方言收敛、`migrate.go` 版本化迁移 |
| blobstore | `internal/blobstore/blobstore.go` | 工件对象存储抽象,默认本地目录 `<data>/artifacts/` |
| bus | `internal/bus/bus.go` | 进程内事件总线:按会话转发已持久化事件给 SSE 订阅者 |
| pgbus | `internal/pgbus/pgbus.go` | PG 档跨实例扇出:`NOTIFY/LISTEN agent_events` |
| cmdbus | `internal/cmdbus/cmdbus.go` | 命令总线宿主:button/card/slash/chat 四入口归一分发、危险命令确认卡、审计 |
| cardsvc | `internal/cardsvc/cardsvc.go` | 收件箱任务卡服务(card.* 事件投影) |
| runmgr | `internal/runmgr/runmgr.go` | **Run 生命周期枢纽**:状态机、事件持久化+广播、HITL、子代理任务树、工具执行策略、取消级联 |
| route | `internal/route/route.go` | 唯一意图 Router:命令总线 > 工作流 > 普通回合 |
| intentgate | `internal/intentgate/gate.go` | 意图闸门:接入 agent-intent 库,插件意图 overlay |
| intentsem | `internal/intentsem/scorer.go` `generator.go` | 意图慢通道:embedding 语义重排 + 轻量模型命令生成(默认关) |
| runtime | `internal/runtime/runner.go` `llm.go` `llm_direct.go` `sub_loop.go` `workflow_compiled.go` `squad_runner.go` `model.go` | 四类 Runner(LLM/Mock/Sub/Squad)+ 工作流执行入口 + 模型路由 |
| wfcompile | `internal/wfcompile/run.go` `compiler.go` | 工作流编译执行驱动:编译图 → 小队引擎装配 → 步进循环 → critic 终判 |
| ctxasm | `internal/ctxasm/ctxasm.go` `finish.go` | 上下文组装与预算裁剪 |
| prompts | `internal/prompts/service.go` | 提示词模块库:embed 内置 + DB 用户覆盖链 |
| skills | `internal/skills/skills.go` | 内置技能种子(go:embed recipes → `<data>/skills/`) |
| tools | `internal/tools/registry.go` | 统一工具注册表:内置/声明式 HTTP/插件工具同形态 |
| plugins | `internal/plugins/host.go` `host_v2.go` | 插件宿主:fail-closed 热加载;sidecar 进程拉起 |
| hostapi | `internal/hostapi/server.go` | sidecar 本机回调面 `/host/v1`(127.0.0.1 随机端口 + 随机 token) |
| workflow | `internal/workflow/registry.go` | 工作流**定义**注册表(编译执行器在 wfcompile) |
| ingest / docgen / rag / quality | 各主文件 | 文档摄取 → ParsedDocument IR;Markdown→docx;混合检索 rerank;交付物六维打分 |
| profiles / settings / ratelimit | 各主文件 | 画像;system→user→project 分层设置;租户限流(内存/Redis) |
### 其他目录
- `modules/backend/pkg/extension/`:对下游仓的**公开扩展面**(下游只许依赖本包与 core,
`surface.txt` 锁导出面);`link/` 把 extension.Main 接回组合根。
- `modules/backend/web/embed.go`:`go:embed` 内嵌前端构建产物。
- `modules/backend/deps/`:8 个库的 submodule 副本(replace 目标)。
---
## 3. 前端与 UI 分工
- **frontend**(`modules/frontend/src/`):`App.tsx` 四栏布局 —— 左(项目/会话树
`TaskSidebar.tsx`)、中(流式对话 `Conversation.tsx` + `ProcessTimeline.tsx`)、
预览区(`PreviewPane.tsx` / `ArtifactsPanel.tsx`)、右(工作台卡流 `Workbench.tsx`)。
职能文件:`api.ts`(HTTP 客户端)、`live.ts`(SSE 到达序投影纯逻辑)、
`store.ts`、`runstate.ts`、`pluginHost.ts`、`outbox.ts`。
- **ui**(`modules/ui/src/`):只渲染词汇有限的 UI schema —— `UIRenderer.tsx`、
`InboxList.tsx`、`InlineConfirmCard.tsx`(危险命令确认卡)、`ToolBlock.tsx`、
`tokens.css`(设计令牌唯一来源)。frontend 经 `@ui` 别名直接源码组合(`vite.config.ts`)。
---
## 4. 通信架构:HTTP + SSE,无 WebSocket
- **全部 HTTP 路由**集中在 `modules/backend/internal/server/server.go` 的 `Handler()`
(约 90 条 `mux.HandleFunc`):auth/users、projects/conversations/messages、cards、
artifacts/uploads、profiles/prompts/settings、tools/plugins、commands/audit、
plan/todos、knowledge、hitl 等。
- **SSE** 是唯一增量通道:`GET /api/events?conversationId=...`(`server.go:171` 注册,
`server.go:275` 处理器),带 `Last-Event-ID`/`since` 游标重放;项目级流用伪会话键
`project:<id>`(`modules/core/events.go:30` `ProjectChannelKey`)。全仓 grep 无 websocket。
- **事件三层转发**:
1. `runmgr/events.go` 事件缓冲:高频 delta 合并(150ms/64 条,`events.go:42-43`),先落
store(会话内单调 seq)再 `bus.Publish`(`events.go:83`);
2. `internal/bus` 内存总线 → 在线 SSE 订阅者;
3. `internal/pgbus`(仅 PG 多实例档):事务提交后 NOTIFY,其他实例 LISTEN 后按 seq 从库
取回转发本地 bus。
- **事件词汇**全部定义在 `modules/core/events.go` 的 `StreamEvent` 信封
(message.delta / thinking.delta / tool.* / run.phase / hitl.request / plan.update /
agent.spawn / agent.progress / card.* / artifact.* 等 20+ 类型)。
---
## 5. 持久化与磁盘布局
- 主库:`<data>/agent-system.db`(SQLite,FTS5 全文 + CJK LIKE 兜底)或 PostgreSQL
(`--dsn`,pgx 驱动);迁移双方言可回滚;租户隔离靠 owner 列硬过滤。
- 工件:`<data>/artifacts/`(blobstore 本地档,Put 原子落盘)+ store 登记。
- 技能:`<data>/skills/`(内置种子 + 用户覆盖)。
- 工作区沙箱:`<data>/workspaces/<owner>/<conversationID>/`(fs 工具根)。
- 小队运行态:编排目录下 `runs/<run_id>/*.json`(磁盘即权威,见小队篇)。
- 本机服务实例(`scripts/svc.sh` 管理):`runtime/dev/`(agent 后端 + SQLite)、
`runtime/pg1/`(PostgreSQL);`data/`、`data-test/` 为默认/测试数据目录。
---
## 6. 插件系统
- 契约:`modules/core/plugin.go`(manifest v1:tools/workflows/intents/prompts/panels/
cards/commands 等 provides);`modules/core/plugin_v2.go`(2.0-experimental 窗口:
PluginScope 可见性、PluginBackend{inproc|sidecar}、技能/MCP/知识集/解析器/代理声明)。
- 宿主:`modules/backend/internal/plugins/host.go` —— 「manifest + 声明资产 + 可选代码」,
任一资产解析失败整批拒绝(fail-closed),支持热加载 `Reload`;`host_v2.go` 拉起 sidecar
进程(注入 `AGENT_HOST_API`/`AGENT_HOST_TOKEN` 环境变量)。
- sidecar 回调经 `internal/hostapi/server.go` 的 `/host/v1`(kv/audit/commands/llm/search);
进程内插件走 `pkg/extension`(EXT-1)+ `link` 包。
- 示例:父仓 `plugins/example/`、`examples/sidecar-demo/`、`examples/downstream-rdm/`。
---
## 7. 端到端数据流(用户输入 → 结果返回)
```
用户输入(中栏)
│ POST /api/conversations/{id}/messages server.go:574 postMessage
├─ "/" 前缀 → cmdbus 命令总线(不启 run,server.go:623)
├─ 附件 → ingest.Parse 文档摄取(并行,互不依赖,server.go:643)
├─ 意图识别 → intentgate(agent-intent 四级漏斗,server.go:655)
├─ 路由裁决 → route.Decide route.go:60
│ ├─ KindCommand → cmdbus.Dispatch(直接执行命令)
│ ├─ KindWorkflow → runmgr.StartClaimedWorkflowRun(:996)
│ └─ KindTurn → runmgr.StartClaimedConversationRun(:991)
├─ runmgr 建 Run(pending)落库 → 选 Runner(runmgr.go:918)→ go execute()
│ ├─ LLMRunner → agent-native 内核 ReAct 循环(默认)
│ ├─ CompiledWorkflowRunner → wfcompile 编译执行(可由规划子代理生成)
│ └─ SquadRunner → squadadapter → agent-squad 引擎
├─ 一切增量皆事件 → runmgr 缓冲 → store(seq 权威)→ bus → SSE
└─ 前端 live.ts 到达序投影 → 中栏对话 / 右栏卡流 / AgentTracker
```
- HITL:`hitl.request` 卡片(choice/input/confirm/form)→ 用户 `POST /api/hitl/{id}/resolve`
→ `runmgr.ResolveHITL`(runmgr.go:1261)恢复或转轨。
- 工件:产物落 blobstore + store → `artifact.created` 事件 → 预览区;quality 六维打分。
---
## 8. 设计文档索引
架构权威文档在 `modules/documents/docs/proposals/`:
- 整体:`2026-09-29-system-skeleton-design-and-plan.md`(骨架设计 + D1–D16 决策,主设计)、
`2026-09-30-dual-tier-storage-and-multitenancy.md`(双档存储/多租户)、
`2026-10-01-base-extension-roadmap.md`(基座不可改、下游二开)。
- Agent 栈:`2026-10-01-agent-harness.md`(回合循环、多代理编排)、
`2026-10-02-agent-stack-completion.md`(通用智能体/小队/意图)、
`2026-10-07-agent-stack-n1q1i1-plans.md`、`n2-plan.md`、`n3-plan.md`(N 系列阶段方案)。
- 其他:卡片流/收件箱/命令总线/工作区(2026-09-30 三篇)、插件契约 v2、UI 重构等。
---
## 9. 关键文件速查
| 想了解什么 | 看哪里 |
|---|---|
| 程序入口与装配 | `modules/backend/cmd/agent-system/main.go`、`internal/app/app.go` |
| 全部 HTTP 路由 | `modules/backend/internal/server/server.go`(`Handler()`) |
| Run 状态机与事件 | `modules/core/model.go`、`modules/core/events.go`、`internal/runmgr/runmgr.go` |
| Agent 执行循环 | `deps/agent-native/runtime/loop.go`、`internal/runtime/llm_direct.go` |
| 意图识别 | `deps/agent-intent/intent/`、`internal/intentgate/gate.go`、`internal/route/route.go` |
| 小队引擎 | `deps/agent-squad/squad/`、`internal/squadadapter/adapter.go`、`entry.go` |
| 工作流 | `internal/workflow/registry.go`(定义)、`internal/wfcompile/`(编译执行)、`internal/runtime/workflow_compiled.go`(入口) |
| 工具体系 | `modules/core/tools.go`、`internal/tools/registry.go`、`internal/runmgr/runmgr.go:740` |
| 提示词/技能 | `internal/prompts/service.go`、`internal/skills/skills.go` |
| 前端投影 | `modules/frontend/src/live.ts`、`src/store.ts` |
+292
View File
@@ -0,0 +1,292 @@
# Agent 的意图识别:架构、模型与调用链
> 基准:Agent-System-Dev 组合 `218fc4b1ba00b0a03b5bb79f292b3eba6c987a58`(R0,2026-10-08 锚点核验)。
> 行号为该提交下的函数入口位置(个别内部区段为约略范围),随代码演进漂移;文件与函数名是稳定锚点。
> `internal/` 指 `modules/backend/internal/`。
---
## 1. 一句话结论
意图识别是**"确定性规则漏斗为主体、语义向量与 LLM 为可选慢通道(默认关闭)"的混合
分层架构**:识别主体在独立库 `deps/agent-intent`(零 LLM 依赖),backend 通过
`internal/intentgate` 闸门接入,识别结果由 `internal/route` 统一裁决为
命令 / 工作流 / 普通回合三分支。
---
## 2. 识别架构:四级漏斗
核心识别器 `FunnelRecognizer`(`deps/agent-intent/intent/recognizer.go:23`,
入口 `Recognize` 在 `recognizer.go:41`,内部 `recognize` 在 `:71`),四级依次短路:
| 级 | 名称 | 行为 | 代码 |
|---|---|---|---|
| Stage 0 | 守卫(guard_hit) | 守卫域 spec 命中即短路,表达"识别即拒绝" | `recognizer.go` 守卫分支 |
| Stage 1 | 挂起预期(expectation_hit) | 多轮澄清场景:宿主注入 `ExpectationProvider` 声明上轮预期意图 | 接口 `intent/expectation.go:17` |
| Stage 2+3 | 规则计分 + 置信度裁决 | 全部非守卫 spec 线性扫描(`intent/registry.go` 规则计分与裁决),`score = base + (正则命中 ? pattern_bonus : 0)`,上限 1.0;唯一过阈或前两名分差 ≥ margin(默认 0.1)→ **matched**,同分比 priority → 否则 **ambiguous**,无命中 → **fallback** | `intent/spec.go:17-29`(默认值)、`recognizer.go:146` 附近同分裁决 |
| Stage 4 | 慢通道(可选) | ambiguous → 语义向量重排 `semanticRerank`(`recognizer.go:292`);fallback → LLM 命令生成 `generateCommands`(`recognizer.go:333`) | 见下节 |
识别分支共五种(`deps/agent-intent/intent/result.go`):
`guard_hit / expectation_hit / matched / ambiguous / fallback`。
---
## 3. 用什么模型:三层各自不同
| 层 | 是否 LLM | 实现与模型来源 | 状态 |
|---|---|---|---|
| 规则层(守卫/计分/裁决) | **否,永不引入** | 纯 Go 确定性计算。设计文档明确规则层永不引入 LLM(`deps/agent-intent/docs/records/2026-09-29-decoupling-and-llm-layering.md` §2.1) | **主体,默认生效** |
| 语义重排(SemanticScorer) | 向量模型(非生成式) | `modules/backend/internal/intentsem/scorer.go:18` `EmbeddingScorer`:对输入与 spec 的 examples 各取向量算最大余弦相似度。Provider 走 OpenAI 兼容 `/embeddings`(`internal/rag/embed.go`),模型/端点来自 settings `models.embedding.*`;默认阈值 0.8(`intent/spec.go`) | 可选,**默认关**(settings `runtime.intentSemantic`) |
| 命令生成(CommandGenerator) | 生成式 LLM | `modules/backend/internal/intentsem/generator.go:18` `LightGenerator`:走 `agentllm.Client.Stream`,用**轻量档模型**(`internal/runtime/model.go:84` `ResolveLightweightModel`,settings `models.lightweight.model`,缺省回落主 chat 档),MaxTokens 默认 1024;system prompt 在 `generator.go:100-129`(只暴露 id/描述/槽位键,防提示词注入) | 可选,**默认关**(settings `runtime.intentGenerator`) |
| Classifier(LLM 重打分,L2) | 生成式 LLM | 接口在 `intent/classifier.go:9` | **接口预留,backend 未接线** |
慢通道装配点:`modules/backend/internal/app/app.go:351,357`(开关开启才
`gate.SetSemantic(...)` / `gate.SetGenerator(...)`)。关闭时行为与纯规则版完全一致。
**失败安全**:agent-intent 库本体零 LLM 依赖,只定义 `SemanticScorer`
(`intent/semantic.go:9`)与 `CommandGenerator`(`intent/generator.go:14`)窄接口由
宿主注入;所有慢通道失败均回退规则结论(fail-open/closed 到 safe 分支),识别永不因
LLM 失败而失败。生成器幻觉有校验兜底:`validateGenerated`
(`recognizer.go:368`)要求 action.id/槽位键必须都在目录内,否则 fail-closed 回 fallback。
---
## 4. 意图目录(Intent Catalog / Schema)
意图定义 `IntentSpec`(`deps/agent-intent/intent/spec.go:74` 附近,Go 注册与 JSON overlay 共用):
- **标识**:`id`(必须 `domain.助记` 多段,`spec.go:122` 校验)、`domain`、`version`、
`description`、`priority`、`disabled`。
- **matcher**(`spec.go:32-39`):`includes_any / includes_all / excludes_any` 词表 +
`patterns` 正则 + `case_sensitive` + `boundary`(词界匹配,压制中文子串误命中)。
- **slots**(`spec.go:44-62`):`key/required/pattern(抽取正则)/group`,加 HITL 字段
`label/question`(建议澄清提问)与校验链 `enum/format/normalize/validator`。
- **confidence**(`spec.go:66-70`):base 默认 0.7、pattern_bonus 默认 0.2、threshold 默认 0.6。
- **动作绑定**:`action`(kind ∈ command/workflow/blueprint/handler,`intent/command.go:32-43`)
优先,其次 `metadata["blueprint"]`、再次 `handler`,否则 auto(宿主在命令总线与工作流间裁决)。
- **risk**(low/medium/high)+ `requires_confirm`(透传宿主做授权确认)。
- **examples**:示例句数组——语义慢通道按它们算相似度。
配置文件形态(`intent/loader.go:12-19`):
```json
{ "intents": [ { …IntentSpec… } ], "disable": ["demo.run.status_query"] }
```
插件可经 `intents/*.json` overlay 注入(`internal/plugins/host.go:216-220` 采集)。
样例:`deps/agent-intent/examples/specs/demo.json`。
识别产物命令集(`intent/command.go:11-26`):`start / set_slot / change_slot / cancel /
clarify / chitchat / handoff`;规则漏斗只产出 start/set_slot/clarify(`deriveCommands`
在 `command.go:85`)。能力目录 `Snapshot.Catalog()` / `CatalogMarkdown()`
(`intent/catalog.go:36,68`)把启用意图渲染为 Markdown,供小队规划者/Leader 提示词注入
(`deps/agent-squad/adapters/intent/catalog.go` 消费)。
## 4.5 内置与默认意图:默认是空目录
**系统默认不内置任何意图**:backend 组合根 `intentgate.New()`(`app.go:309`)起步是空注册表,
`g.Match` 在空闸门下直接透传(未启用 = 行为回退,所有消息走普通回合)。意图只能经两条
overlay 路径进入,且**两种路径的文件形态不同**:
| 路径 | 文件形态 | 装载方 |
|---|---|---|
| 插件 overlay(产品路径) | **裸数组** `[ {…IntentSpec…}, … ]` | `plugins/host.go:216-220` 按元素透传 → `gate.Rebuild`(gate.go:104)逐个 unmarshal 成 IntentSpec |
| registry/loader(库路径:intentctl、BuildFS) | 包装对象 `{"intents":[…], "disable":[…]}` | `intent/loader.go:21` `overlayDoc`,严格 JSON(未知字段报错) |
仓库自带两份样例意图(即"默认能看到的意图"上限——装载对应插件/配置后生效):
- `plugins/example/intents/demo.json`(插件形态):`demo.show`(命中"示例/演示",无槽位)、
`demo.brief`(命中"简报",必填槽位 `topic`,抽取正则 `主题[::]\s*(\S+)`,缺槽问"主题是什么?")。
- `deps/agent-intent/examples/specs/demo.json`(loader 形态):`demo.run.status_query`
(命中"进度/状态"、排除"配置",槽位 `run_id`,base=0.8/threshold=0.7,handler=`taskrun.query_status`)、
`demo.run.restart`(需同时含"重启"+"任务",`requires_confirm=true`,risk=medium)。
---
## 4.6 实测:一句话怎么被打分、填槽、命中
以下全部为 `intentctl explain` 在样例配置上的**真实输出**(2026-10-08 实测;例 5/6 的配置目录
= 两份样例意图合并)。打分公式:`score = base + (正则命中 ? pattern_bonus : 0)`(封顶 1.0),
前两名分差 < `margin`(默认 0.1,`intent/registry.go:55`)判 ambiguous。
**例 1|命中 + 抽到槽位**(输入"查一下任务 run_42 的进度"):
```
分支:matched
Top:demo.run.status_query 置信度=0.8000 风险=low 确认=false handler=taskrun.query_status
槽位 run_id="run"
候选(1):
0.8000 demo.run.status_query terms=进度
裁决轨迹:
margin=0.1000
守卫:未评估
规则 命中 demo.run.status_query 0.8000 进度
规则 排除 demo.run.restart no_positive_match
```
要点:`terms=进度` 命中词表加 0 分(base 已含);`restart` 因 `includes_all:["任务"]` 缺"重启"
被排除(`missing_includes_all`)。槽位 `run_id="run"` 是真实结果——槽位正则
`(?:run|任务)[:# ]*([A-Za-z0-9-]+)` 的字符类不含下划线,捕获在 `run` 后被 `_` 截断:
**规则识别完全确定性,槽位 pattern 的质量决定抽取质量**(写 `[A-Za-z0-9_-]+` 即可取到 `run_42`)。
**例 2|命中但缺必填槽位**(输入"写一份简报",`demo.brief` 装载时):
```
分支:matched
Top:demo.brief 置信度=0.7000
缺失必填槽位:topic
裁决轨迹:
规则 命中 demo.brief 0.7000 简报
规则 排除 demo.run.restart missing_includes_all:任务
规则 排除 demo.run.status_query no_positive_match
规则 排除 demo.show no_positive_match
```
`Missing=[topic]` 连同 spec 的 `question:"主题是什么?"` 一起随 route 进入工作流 run,
由 HITL 输入卡向用户澄清(见调用链第 10 步)。
**例 3|槽位填上**(输入"主题:核能 写一份简报"):同一意图 matched,
`槽位 topic="核能"`,Missing 为空——正则 `主题[::]\s*(\S+)` 命中,直接启动。
**例 4|什么都不像**(输入"随便聊聊今天的天气"):
```
分支:fallback
裁决轨迹:
规则 排除 demo.run.restart missing_includes_all:任务
规则 排除 demo.run.status_query no_positive_match
```
无命中 → fallback;若装载了语义慢通道(`runtime.intentSemantic`,默认关)会先尝试 embedding
重排,仍不达阈才 fallback。gate 把 fallback 折叠后 `route.Decide` 判 `KindTurn` → 普通对话回合。
**例 5|能力目录实测**(`intentctl catalog`,这段 Markdown 就是注入小队规划者/Leader
提示词的内容,`squad/engine.go:44` 注释明示用途):
```
# 系统能力目录(意图快照 revision=1)
以下能力可直接调用,无需派智能体执行。子任务命中其一时,输出携带 intent_id 的步骤并填齐必填槽位;
必填槽位信息不足时不要编造,改为向用户澄清(优先使用各槽位的提问建议)。
## demo.brief — 写一份简报。必填槽位 topic,没写就问「主题是什么?」。
- 风险:low;执行前需人工确认:否
- 槽位 topic(必填,topic)
- 提问建议:主题是什么?
```
---
## 4.7 一句话里有多个意图怎么办
意图层的契约是**一条输入只产出一个 Decision**(top-1),不做多意图拆分。多个候选的
裁决顺序固定为:
1. **分差裁决**:唯一过阈,或第一名领先 ≥ margin → 直接 matched。
2. **同分且 priority 不同 → 高优先级直接胜出**(I5,不标歧)。实测("帮我生成一份报告",
两个意图 base 都是 0.7、词表都命中"报告"):template 设 `priority:10` 后
`分支:matched,Top:demo.report.template 置信度=0.7000`。
3. **分差 < margin(含同分同优先级)→ ambiguous**,先走可选语义重排 `semanticRerank`
(`recognizer.go:292`,默认关)→ 再走 Classifier L2 重打分(backend 未接线)→ 都没有则
**保持 ambiguous**。实测同配置去掉 priority 后:
`分支:ambiguous,候选(2):0.7000 demo.report.generate / 0.7000 demo.report.template`。
4. ambiguous 经 gate 折叠 → `KindTurn` → **交给普通 LLM 回合**:模型在一次 ReAct 循环里
自行处理多个请求(这也是多数"一句话多个事"的实际归宿——意图层只接管高确定性的
命令/工作流类请求)。
5. 多轮场景:Stage 1 挂起预期(`ExpectationProvider`)让澄清中的输入优先回到挂起意图;
HITL 自由输入命中其他意图时会**取消挂起 run 并转轨**(`runmgr.go:1261` `ResolveHITL`),
相当于允许用户中途改道。
---
## 5. 端到端调用链:用户输入一条消息后发生了什么
| # | 步骤 | 调用方 → 被调方 | 位置 |
|---|---|---|---|
| 1 | HTTP 入口 | 路由注册 `POST /api/conversations/{id}/messages` | `internal/server/server.go:168`(注册)、`:574` `postMessage` |
| 2 | slash 短路 | `/` 前缀非技能命令 → `handleCommand`(`server.go:756`)→ 命令总线,**不进意图识别** | `server.go:623` |
| 3 | 附件摄取 | `ingest.Parse(name, mime, content)` → `core.ParsedDocument`(与意图识别并行、互不依赖) | `server.go:643`;`internal/ingest/ingest.go` |
| 4 | 识别调用 | `s.recognize(ctx, text)`(端口 `NLIntentGate`,`server.go:79-85`) | `server.go:655` |
| 5 | 进入闸门 | `intentgate.Gate.Recognize` | `internal/intentgate/gate.go:137` |
| 6 | 四级漏斗 | `FunnelRecognizer.Recognize(ctx, input, reg.Snapshot())` | `gate.go:144` 附近 → `deps/agent-intent/intent/recognizer.go:41` |
| 7 | 结果投影 | 库 `Result` → `MatchResult{Branch, ID, Slots, Missing, Commands}`;guard/expectation 折叠为 fallback;缺槽位附 spec 的 Question | `gate.go:148-176` |
| 8 | 统一路由 | `route.Decide(recognized, route.Env{HasCommand, WorkflowFor, SquadReady})` | `server.go:656`;`internal/route/route.go:60` |
| 9a | 命令分支 | KindCommand → `s.Bus.Dispatch`(Entrance="chat",槽位透传 Params) | `server.go:701-717` |
| 9b | 工作流分支 | KindWorkflow → `mgr.StartClaimedWorkflowRun`(缺槽位列表随行) | `server.go:725`;`internal/runmgr/runmgr.go:996` |
| 9c | 回合分支 | KindTurn → `ClaimConversation` 抢槽 → `AppendMessageWithRequest` 落库 → `mgr.StartClaimedConversationRun` | `server.go:679, 727`;`runmgr.go:980, 991` |
| 10 | 澄清/HITL | 工作流缺必填槽位 → 逐个发 `HITLCard{Kind:Input, AllowFreeInput}` 人工输入卡 | `internal/wfcompile/run.go:47` 起 `RunCompiled` 槽位补齐段 |
| 11 | HITL 转轨 | `POST /api/hitl/{id}/resolve` → `mgr.ResolveHITL`:自由输入以 `/` 开头 → pivot=command;否则再过 `gate.Recognize` + `route.Decide`,命中其他意图则取消挂起 run 并 pivot | `server.go:883`;`runmgr.go:1261`(转轨判定 `:1293` 附近) |
### 启动期装配链
| # | 内容 | 位置 |
|---|---|---|
| A1 | `app.Run` 建 `gate := intentgate.New()` 并 `mgr.SetGate(gate)` | `internal/app/app.go:309-310`;`runmgr.go:142` |
| A2 | 插件装载回调:`pluginHost.OnIntents = overlay → gate.Rebuild(overlay)`(fail-closed:解析失败保留上一快照) | `app.go:324`;`internal/plugins/host.go:216-220`;`gate.go:96` |
| A3 | 开关开启时接线慢通道:`gate.SetSemantic(intentsem.EmbeddingScorer{…})` / `gate.SetGenerator(intentsem.LightGenerator{…})` | `app.go:351,357`;`gate.go:66,75` |
---
## 6. intentgate 与 intentsem 的分工
- **intentgate**(`internal/intentgate/gate.go`):宿主闸门适配器。持 Registry +
FunnelRecognizer;负责插件 overlay 装载(`Rebuild`,`:96`)、分支折叠(非
matched/ambiguous 一律折叠为 fallback,`gate.go:148-155`)、补槽位 Question、
类型投影(让 runmgr/route 只依赖本包表面)、慢通道接线(`:66,75`)。
**不做缓存**——识别是纯函数 Query,快照不可变无锁读(`intent/registry.go`)。
- **intentsem**(`internal/intentsem/`):慢通道实现包。`EmbeddingScorer` 出错原样上抛
(库保持 Ambiguous);`LightGenerator` 解析失败/模型不可达返回错误(库保持 fallback)。
---
## 7. 识别结果的去向与失败路径
- 识别结果 `Result`(`intent/result.go`)是**纯内存 Query 结果,不落库**(store 无
intent 表)。去向仅三处:① HTTP 响应;② logsys 结构化日志 `intent recognized`
(`recognizer.go:62` 附近,不记原文);③ 作为 run 启动参数(workflow slots/missing、命令 ID)。
- **ambiguous**(重排后仍歧义)→ gate 折叠 → `route.Decide` 返回 `KindTurn`
(`route.go:61-63`)→ 走普通 LLM 回合自由对话兜底(不是强制澄清)。
- **matched 但缺必填槽位** → Commands=[start, clarify](`intent/command.go:110` 附近)→
workflow 路径把 Missing(含建议 Question)传入 run → 发 HITL 输入卡澄清。
- **guard_hit / expectation_hit** → 折叠为 fallback → turn。
- **低置信度**(未过 threshold)→ fallback → turn。
route 决策细节(`internal/route/route.go:55-114`):优先级
命令 > 工作流 > 普通回合;`blueprint` → `KindSquad`,但**当前不可达**——
`server.go:664` 明确注释 `SquadReady 保持 false`(per-run entry 覆盖未就位),
小队的实际启动走别的路径(见[小队与子 Agent 启动](squad-and-subagents.md))。
声明目标不可解析时不静默错配,回落 turn 并写 Reason。
---
## 8. 辅助工具与测试
- `deps/agent-intent/cmd/intentctl/`:只读诊断 CLI(`lint` 校验配置+词条冲突、
`explain` 单条输入裁决轨迹(`recognizer.go:226` `Snapshot.Explain`)、
`eval` 语料评测 Precision/Recall、`catalog` 输出目录)。
- `deps/agent-intent/conformance/conformance.go`:识别器公开契约行为验收
(分支/槽位断言 + 确定性 + 并发冒烟)。
- `deps/agent-intent/catalogcheck/align_test.go`:断言库错误码与 Error-Code-System
intent 域种子码逐个相等,防两套平行权威。
---
## 9. 关键文件清单
| 文件 | 职责 |
|---|---|
| `deps/agent-intent/intent/recognizer.go` | 四级漏斗识别器、语义重排(:292)、生成兜底(:333)、Explain 诊断(:226) |
| `deps/agent-intent/intent/spec.go` | IntentSpec/MatchSpec/SlotSpec/ConfidenceSpec 定义与校验 |
| `deps/agent-intent/intent/registry.go` | 编译、不可变 Snapshot、双源合并、原子 Reload |
| `deps/agent-intent/intent/loader.go` | JSON overlay 加载 |
| `deps/agent-intent/intent/command.go` | 命令模型 + deriveCommands(:85)展开 |
| `deps/agent-intent/intent/catalog.go` | 能力目录与 Markdown 渲染(:36/:68/:128) |
| `deps/agent-intent/intent/semantic.go` `generator.go` `classifier.go` `expectation.go` | 四个可注入端口 |
| `deps/agent-intent/cmd/intentctl/main.go` | 诊断 CLI |
| `modules/backend/internal/intentgate/gate.go` | 闸门适配器(overlay :96、投影、慢通道接线 :66/:75) |
| `modules/backend/internal/intentsem/generator.go` `scorer.go` | LLM 命令生成(含 system prompt :100)/ embedding 重排 |
| `modules/backend/internal/route/route.go` | 唯一 Router(Decide :60 三分支裁决) |
| `modules/backend/internal/server/server.go` | 消息入口 postMessage(:574)、recognize(:655)、resolveHITL(:883)、SSE events(:275) |
| `modules/backend/internal/plugins/host.go` | 插件 intents/*.json overlay 采集(:216-220) |
| `modules/backend/internal/app/app.go` | 组合根装配(:309-310、:324、:351/:357) |
+133
View File
@@ -0,0 +1,133 @@
# 何时启动小队或子 Agent:触发条件与调用链
> 基准:Agent-System-Dev 组合 `218fc4b1ba00b0a03b5bb79f292b3eba6c987a58`(R0,2026-10-08 锚点核验)。
> 行号为该提交下的函数入口位置(个别内部区段为约略范围),随代码演进漂移;文件与函数名是稳定锚点。
> `internal/` 指 `modules/backend/internal/`。
---
## 1. 一句话结论
系统有两条"委派"线,都发生在**普通 run 的执行过程中**(小队不是独立的一层):
- **子代理(sub-agent)**:模型在回合中调用 `delegate_task` 工具 → runmgr 派生
`Kind=subagent` 的子 run(任务树)。
- **小队(squad)**:三条触发路径 —— ① 设置 `runtime.entry=squad` 让**对话本身**
走 SquadRunner;② 模型调用 `squad_orchestrate` 工具;③ 演示消息含 `#squad`。
三者最终都汇入 `deps/agent-squad` 引擎。**成员执行统一走 agent-native 内核**
(legacy 的 agent-system 子 run 压缩路径已退役,`internal/squadadapter/adapter.go:289-292`
注释明示;后端缺席时 fail-closed,不静默降级)。
另有第三种"规划委派":`run_workflow` 触发的**规划子代理**(workflowPlannerID),
见[任务规划与执行](task-planning-and-execution.md)第 7 节。
---
## 2. 子代理:`delegate_task` 触发链
### 触发条件
主对话模型判断某段工作适合独立完成(如长调研、批量整理)时,在 ReAct 循环里发出
`delegate_task` 工具调用(工具名常量 `internal/runtime/llm.go:58`)。工具声明与门禁在
`internal/runtime/llm.go:267-315`(编排型工具随上下文声明;子代理 spec 由 prompts 包提供
tools/model/maxTurns 约束,**禁止子代理再持有派发类工具**,`internal/prompts/service.go`)。
### 调用链
| # | 步骤 | 位置 |
|---|---|---|
| 1 | 内核循环解析出 `delegate_task` 调用 → `runTool` 分派 | `internal/runtime/llm_direct.go:471` `runTool`,case 在 `:493` 起 |
| 2 | 后台模式:经 rc 端口断言调 `SpawnBackground`;前台:`SpawnAgent` | `llm_direct.go:502, 516` 附近 |
| 3 | runmgr 派生子 run | `internal/runmgr/runmgr.go:1348` `spawnSubagent` → `:1358` `startChild`(**深度检查**在此,注释 `:1356`) |
| 4 | 建 `Kind=subagent` 子 run,`ParentRunID` 指向父 run(**任务树**) | `runmgr.go` startChild 内;契约 `modules/core/model.go:188` |
| 5 | 发 `agent.spawn` 事件(前端 AgentTracker 可见) | `runmgr.go:1375` |
| 6 | 独立 ctx 执行(后台模式 detach) | `runmgr.go` startChild 内 |
| 7 | 子代理执行器 = `SubLLMRunner.Execute` —— 同样进入 `nruntime.Run` 内核循环(独立转录、按子代理 spec 收窄工具) | `internal/runtime/sub_loop.go:31` |
| 8 | 完成回填 `AgentResultPayload`;结果截断 32000 字 | `runmgr.go:1401` `finishChild`;`runmgr.go:1780` `capSubagentResult` |
### 后台子代理
- `delegate_task` 可请求后台模式 → `spawnBackground`(`runmgr.go:1475`),
**每会话上限 3 个**(`runmgr.go` 常量)。
- 后台子代理完成后不直接打扰当前回合,而是进通知队列:`deliverNotices`
(`runmgr.go:1560`)在会话空闲时以 system-reminder 消息启动一个新父回合汇报结果
(`startNoticeRun`,`runmgr.go:1616`)。
- 取消级联:子 run 的 ctx 派生自父 run,`CancelRun`(`runmgr.go:1222`)取消根 run
即级联取消全部子 run。
---
## 3. 小队:三条触发路径
### 路径 A:`squad_orchestrate` 工具调用(工具路径)
| # | 步骤 | 位置 |
|---|---|---|
| A1 | 模型在回合中调用 `squad_orchestrate goal=...` | 工具名常量 `internal/runtime/llm.go:62`;声明 `llm.go:268` 附近 |
| A2 | 声明门禁:settings `runtime.squad`(默认 true)开启才声明;**entry 已是 squad 时不再提供,避免套娃** | `internal/runtime/llm.go:467` `squadToolEnabled`;`:266` 注释 |
| A3 | runTool 分派 → `squadadapter.Orchestrate(rc, dataDir, goalText)` | `internal/runtime/llm_direct.go:479` → `internal/squadadapter/adapter.go:47` `Orchestrate` |
| A4 | 之后进入 agent-squad 引擎(规划→调度→质检) | 见[小队工作机制](squad-mechanics.md) |
### 路径 B:对话入口模式(entry=squad)
| # | 步骤 | 位置 |
|---|---|---|
| B1 | 灰度开关 `runtime.entry=squad`(默认 `direct`) | `internal/settings/settings.go:90` |
| B2 | runmgr 选 Runner:`selectConversationRunner` 检查 `runtime.entry=="squad"` → 返回 SquadRunner | `internal/runmgr/runmgr.go:918`(注入点 `:908` `SetSquadRunner`,装配 `internal/app/app.go:306`) |
| B3 | `SquadRunner.Execute`(`internal/runtime/squad_runner.go:26`)先做 **LLM 分诊**:`modelRouter.Route`(`:146`,轻量模型输出 JSON `direct|plan`,`parseRoute` :167)—— direct = 单步 assistant 直通(`deps/agent-squad/squad/router.go:55` `directAssistantPlan`);plan = 进 Leader 委派(`router.go:29` `applyRoute` 设 `metadata["mode"]="leader"`) | |
| B4 | plan 模式 → `squadadapter.RunEntry` → 引擎 leader 模式 | `internal/squadadapter/entry.go:42` `RunEntry`;`deps/agent-squad/squad/engine.go`(`RunOptions.Mode="leader"`,Options 注释 `engine.go:99-105`) |
### 路径 C:演示/脚本
用户消息含 `#squad` → MockRunner 走同样 `Orchestrate`(`internal/runtime/mock.go`)。
### 一条"不可达"的路径(设计预留)
意图识别绑定 `blueprint` 动作可路由到 `KindSquad`(`internal/route/route.go:96-97`),
但当前**双重不可达**:`server.go:664` 明确注释 `SquadReady 保持 false`(per-run entry
覆盖未就位);且系统默认意图目录为空(见[意图识别](intent-recognition.md) 4.5 节),
不装插件就没有意图可命中 blueprint。小队的实际启动走上面 A/B/C 三条路径;意图目录
对小队的现实作用是注入规划者/Leader 提示词的能力目录(`deps/agent-squad/adapters/intent/catalog.go`)。
---
## 4. 装配(启动期)
| 内容 | 位置 |
|---|---|
| runmgr 注入三个 runner(LLMRunner / SubLLMRunner / MockRunner),SquadRunner 经 `SetSquadRunner` | `internal/app/app.go:287-306` |
| 角色指令来源接提示词库(`agent.squad.<kind>`) | `app.go:176` → `squadadapter.RoleInstructions`(`adapter.go:303`) |
| agent-native 内核选项:模型路由、Tavily 搜索、解释器、MCP、技能根、Subagent 桥接、PermissionAsk、检查点 | `app.go:196` `squadadapter.AgentNativeOptions` |
| 小队后端解析器:**只注册 `"agent-native"` 内核工厂**(legacy `"agent-system"` SpawnSubagent 压缩路径已退役删除;工厂缺席解析 fail-closed) | `internal/squadadapter/adapter.go:292` `BuildBackends`(退役注释 `:289-291`);`entry.go:103` DefaultBackend="agent-native" |
| 内核工厂注册 | `deps/agent-squad/adapters/native/register.go:61` `RegisterFull` |
---
## 5. 两条线的对比小结
| | 子代理 delegate_task | 小队 squad |
|---|---|---|
| 触发者 | 主对话模型 | 设置开关 / 主对话模型 / 演示脚本 |
| 执行单位 | 一个子 run(`Kind=subagent`) | 一组角色化步骤(每步一个 agent-native 内核 agent) |
| 编排者 | 主对话(单层委派,子代理不可再委派) | agent-squad 引擎(DAG 调度 / Leader 循环) |
| 并发 | 后台最多 3/会话 | 无写集冲突的步骤可并行 |
| 深度 | 1 层(startChild 深度检查,`runmgr.go:1356-1358`;子代理 spec 禁派发工具) | 成员内核里的 `delegate_task` 经 `SubagentFactory`(`adapter.go:234`)桥回产品 `SpawnSubagent`,**深度恒为 1,子 run 不再嵌套小队** |
| 汇总 | `AgentResultPayload` 回父 run | 交付物聚合 `output.md` → 主 run 工件(见[小队工作机制](squad-mechanics.md)) |
---
## 6. 关键文件清单
| 职责 | 文件 |
|---|---|
| delegate_task 分派 | `internal/runtime/llm_direct.go:493` 起 |
| 子 run 派生与任务树 | `internal/runmgr/runmgr.go:1348-1475`、`1560-1616` |
| 子代理执行器 | `internal/runtime/sub_loop.go` |
| 工具声明与门禁 | `internal/runtime/llm.go:267-315, 466-467` |
| squad_orchestrate 分派 | `internal/runtime/llm_direct.go:479` |
| 小队适配器(工具路径 + 后端组装 + Subagent 桥接) | `internal/squadadapter/adapter.go`(Orchestrate :47、SubagentFactory :234、BuildBackends :292、builtinRoles :306) |
| 小队适配器(入口路径) | `internal/squadadapter/entry.go`(RunEntry :42、kernelRoles :177) |
| 对话入口 Runner + LLM 分诊 | `internal/runtime/squad_runner.go`(Execute :26、Route :146) |
| Runner 选择 | `internal/runmgr/runmgr.go:918` |
| 灰度开关 | `internal/settings/settings.go:90` |
| 装配 | `internal/app/app.go:176, 196, 287-306` |
+226
View File
@@ -0,0 +1,226 @@
# 小队(Squad)工作机制:拆解、执行、汇总的完整调用链
> 基准:Agent-System-Dev 组合 `218fc4b1ba00b0a03b5bb79f292b3eba6c987a58`(R0,2026-10-08 锚点核验)。
> 行号为该提交下的函数入口位置(个别内部区段为约略范围),随代码演进漂移;文件与函数名是稳定锚点。
> `internal/` 指 `modules/backend/internal/`;`squad/` 指 `deps/agent-squad/squad/`。
---
## 1. 一句话结论
小队是 run 内的一种**编排执行模式**:由 agent-squad 引擎(`deps/agent-squad`)把目标
拆成**角色化步骤 DAG**(规则规划器或蓝图模板),每一步交给一个"成员"执行——成员是
按角色装配的 **agent-native 内核 agent**(独立执行循环;legacy 的 agent-system 子 run
压缩路径已退役),无写集冲突的步骤并行推进,产出落子工作区并经**黑板**共享,收口时做
六维质检与自动修复,交付物聚合回主 run。
触发时机(何时启动):settings `runtime.entry=squad`(对话即小队)、模型调用
`squad_orchestrate` 工具、演示消息 `#squad`——详见[小队与子 Agent 启动](squad-and-subagents.md)第 3 节。
---
## 2. 概念模型(类型全部在 `deps/agent-squad/squad/models.go`)
| 类型 | 位置 | 含义 |
|---|---|---|
| `RoleSpec` 角色 | models.go:25 | role/name、绑定执行后端 `Backend`(stub/llm/agent-native/…)、角色级 Model、工具白名单 Tools、技能 Skills、**BashPermission 三态 allow/ask/deny**、Keywords(规则规划器匹配用)、Instructions、HITL 策略、`MaxConcurrentTasks` 并发限流 |
| `Goal` | models.go:48 | 目标文本 + Materials 附件 + Constraints + HITL 覆盖 |
| `Blueprint` | models.go:64 | 任务模板:关键词命中即走基线步骤 |
| `StepSpec` 步骤 | models.go:86 | Role、Targets(期望交付物文件,缺省 output.md)、`Writes` 写集(写集相交强制串行)、`Needs` 依赖、`Parallel`、`Critical`(失败即终止)、`MinScore` 质检门槛、`RequiresApproval`、`IntentID/IntentSlots`(系统功能步,不走智能体)、Attempts |
| `WorkflowPlan` + `SquadConfig` | models.go:168, 179 | 拆解结果(与产品 squad workflow.json / squad.json 兼容) |
| `BlackboardState` 黑板 | models.go:225 | Facts/Gaps/Implications/Assumptions/Fields —— 成员间协作的共享状态载体 |
**小队成员 ≠ 常驻 agent**:成员 = 角色(RoleSpec)。每条步骤由
`BackendResolver.Resolve`(`squad/backend.go`)解析出一个 Executor 并调用一次
`Execute`。产品侧**只注册 agent-native 后端**(`internal/squadadapter/adapter.go:292`
`BuildBackends`;`deps/agent-squad/adapters/native/register.go:61` `RegisterFull`),
每步构造一个独立内核 agent 执行循环(模型/工具/搜索/MCP/技能根按 RoleSpec 装配)。
---
## 3. 小队 Run 状态机
`squad/models.go:284` 起(`RunStatus`):
```
created → planning → (awaiting_clarification | awaiting_confirmation) ← HITL 挂起
→ executing → (waiting_human | quality_review) ← HITL 挂起
→ completed | degraded | failed | cancelled ← 终态
```
- 挂起态判定与适配:适配器 `isHoldStatus`(`internal/squadadapter/adapter.go:221`)据此进 HITL。
- 挂起上下文 `HitlGate`(Request+Plan+PrevStatus)落盘 `pending.json`;`Respond`
(`squad/engine.go:637`)按多种 Kind 分派恢复:Clarification 答案并入 Goal 重新规划、
Confirmation、Gap、Escalation、Adjustment、Permission、QualityReview;执行态恢复走
`Resume`(`squad/engine.go:1127`)。
**事件体系**:引擎 `squad.Event` 13 类(`squad/progress.go:10-24`:run_started /
phase / step_started / completed / failed / skipped / detail / deliverable /
human_input_required / human_responded / run_completed / failed / cancelled)。
产品侧不直连 bus:`onSquadEvent`(`internal/squadadapter/adapter.go:185`)把 step_* 映射为
`core.EvAgentProgress`,计划经 `mapPlan`(`adapter.go:200`)映射为 `rc.UpdatePlan` →
`core.EvPlanUpdate`,成员子 run 发 `core.EvAgentSpawn` —— 统一进事件流,**前端可看到
小队进展**(`modules/frontend/src/store.ts`、`contracts.ts` 消费 agent.spawn/plan.update/agent.progress)。
**运行状态持久化(磁盘即权威)**:`workspace.json / workflow.json / squad.json /
blackboard.json / pending.json / runstate.json / usage.json`
(`squad/runstate.go:13` 注释、`:81` `persist`);崩溃恢复 `Resume → recoverInterrupted`
(产物已齐判完成防副作用翻倍)。支持运行中 Steering 与黑板字段注入自动唤醒 `SweepWakeups`。
---
## 4. 端到端调用链(以 `squad_orchestrate` 工具路径为主线)
| # | 步骤 | 位置 |
|---|---|---|
| 1 | 触发:`llm_direct.go:479` case toolSquad → `squadadapter.Orchestrate` | `internal/runtime/llm_direct.go:479` → `internal/squadadapter/adapter.go:47` |
| 2 | 编排进度卡(可选):创建 `CardKindProgress` 卡,终态置态 | adapter.go `Orchestrate` 内(CardSink 端口 `:40`) |
| 3 | 注册内置角色 `builtinRoles()`(`adapter.go:306`):规划员/调研员/撰稿员/审校员,**全部 Backend=agent-native**;工具白名单按角色最小化(全员含 write_file,步骤须自己落盘交付物);撰稿员 `BashPermission=ask` + docx/xlsx/pptx-author 技能,其余角色 Bash=deny;指令来源 `RoleInstructions`(:303,接提示词库 `agent.squad.<kind>`) | |
| 4 | 组装后端 `BuildBackends`(`adapter.go:292`):只注册 `"agent-native"` 内核工厂(组合根 `AgentNativeOptions`,`internal/app/app.go:196`);legacy `"agent-system"` 已退役;工厂缺席 fail-closed | |
| 5 | 组装引擎 `squad.NewEngine(Options{…})`:Planner=RulePlanner、Checker=quality.Checker、RepairBudget、HitlPolicy、Sink=ProgressFunc(onSquadEvent);引擎默认 `DefaultBackend="stub"`(`squad/engine.go:169-170`)、单步超时默认 10 分钟(Options 注释 `:32`) | `adapter.go` Orchestrate 内;`squad/engine.go` |
| 6 | `SquadEngine.Run`(`squad/engine.go:234`):生成 `run_<UTC时间戳>` ID → `acquire`(`:213`,进程内 mutex + flock 文件锁,单 run 单控制操作)→ `EvRunStarted` → 可选**意图闸门**一次裁决(GuardHit→Reject;命中 blueprint→定向模板;否则不干预;实现 `deps/agent-squad/adapters/intent/gate.go`) | |
| 7 | **任务拆解(规划)** `planAndExecute`(`squad/engine.go:335`):先 Router 分诊;未 direct 则蓝图匹配 + `Planner.Plan`(`squad/planner_rule.go:20`)。产品用 **RulePlanner(确定性规则,非 LLM)**:命中 Blueprint 直接用模板步骤/编制(`fromBlueprint`,planner_rule.go:27);未命中走四阶段骨架 **梳理(organizer)→专项(specialist)→成文(writer)→终审(leader)**(planner_rule.go:85 起),注册表缺角色时 `Registry.Derive` 现场派生并置 `NeedsConfirmation`。LLM 规划器已实现(`squad/planner_llm.go`,严格 JSON+重试+Fallback)但产品未接。拆解结果持久化 `runs/<run_id>/workflow.json + squad.json`(`squad/runstate.go:81` `persist`) | |
| 8 | 澄清/确认挂起:规划歧义 → `awaiting_clarification`;派生编制 → `awaiting_confirmation`(leader 模式豁免)。适配器驱动循环(`adapter.go` Orchestrate 内):hold 态 → `askHuman`(`adapter.go:150`,HumanRequest→core.HITLCard→`rc.AskHITL`)→ `engine.Respond`(`engine.go:637`);执行态 → `engine.Resume`(`engine.go:1127`)。每次推进后 `mapPlan`(`adapter.go:200`)把 WorkflowPlan 映射为右栏计划快照 | |
| 9 | **工作区物化**:`PrepareWorkspace`(`squad/workspace.go:27`)+ `BuildSubWorkspaces`(workspace.go:127):每步建 `tasks/NN_<role>/` 子工作区,把 `context/`(goal.md、材料原文 + textextract 抽取文本、index.json)与 template.md 快照进去(`snapshotContextInto` workspace.go:160),并写 **TASK.md**(`writeTaskFile` workspace.go:182:黑板快照、权威等级资源表、产出要求——成员靠它读前序产出)与 **AGENTS.md**(`writeAgentsFile` workspace.go:224:角色指令) | |
| 10 | **调度执行** `flowDriver.advance`(`squad/workflow.go:25`):每轮从权威 Steps 现场编译调度图 `compileGraph`(`squad/graph.go:23`:Needs 依赖或相邻 Parallel 同层,环 fail-closed)→ 分并行/串行层 → **无写集冲突的步骤并行**(`runParallel` workflow.go:216,goroutine+WaitGroup,关键失败 cancel 全组),写集冲突按声明序串行 → `gateBeforeRun`(workflow.go:175:bash ask 角色授权 / 步骤审批的 HITL 门)→ `runOneStep` | |
| 11 | **单步执行** `runOneStep`(`squad/workflow.go:262`):置 running、Attempts++ → 写 TASK.md → 组装 ExecRequest(ToolAllowlist/Skills/SessionScope/Budget/OnEvent 过程帧)→ 后端 `Execute`(系统功能步改走 IntentRunner)→ 瞬态失败重试并注入重试提示 → **交付物落盘校验** `missingTargets`(workflow.go:1071)→ 完成:`mergeStepArtifacts` 把子工作区 `fields.json/gaps.json` 并入黑板(`squad/blackboard.go:43`)→ `checkBlockingGaps`(workflow.go:652:interactive 挂 HitlGap 卡、auto 记兜底假设继续)→ 每步后可动态调整(评估器加步/跳步)。superstep 边界写检查点快照,Resume 时校验图指纹防形态漂移 | |
| 12 | **成员执行(统一 agent-native)**:`RegisterFull`(`adapters/native/register.go:61`)按角色构造独立内核 agent(模型/工具/搜索/MCP/技能根按 RoleSpec+BashPermission 装配),过程帧经 eventTap 桥为 `EvDetail`。内核里的 `delegate_task` 经 `SubagentFactory`(`adapter.go:234`,SpawnBackground `:266` / SpawnSubagent `:273`)桥回产品 SpawnSubagent,**深度恒为 1,子 run 不再嵌套小队** | |
| 13 | **Leader 委派模式**(entry=squad 的 plan 分诊):`RunOptions.Mode="leader"`(`squad/engine.go:99-105` Options 注释;轮次上限 `MaxLeaderTurns` 默认 8,`squad/leader.go:48` `DefaultMaxLeaderTurns`)——规划被委派循环取代:每轮重写 leader 的 TASK.md(目标+黑板+委派历史)与 AGENTS.md(协调契约),leader 后端执行产出 **delegation.json**(action=delegate/escalate/close/failed);delegate:临时建 StepSpec、跑 runOneStep、交付快照进 leader 的 `inbox/<stepID>/`、再唤醒 leader 评估(一轮只委派一个成员);escalate → HitlEscalation 挂起;超限按现有产出降级收口。核心状态与提示词装配:`LeaderState`(leader.go:57)、`DelegationRecord`(:81)、`delegation`(:91)、`leaderPrompt`(:126)、`leaderExecRequest`(:16) | |
| 14 | **汇总与质检(收口)** `finalize`(`squad/workflow.go:784`):最后 completed 步骤的主产出复制为 `output.md` → `Checker.Check`(`internal/quality/checker.go:16` 附近 → `score.go`:六维启发式评分——结论先行/结构/完整性/证据/可执行性/可读性;**Passed = 总分≥70 且无单维<40**,score.go:28,72;docx 走段落提取;可选 housestyle lint)→ 未过写 `quality-feedback.md`(checker.go:29)并自动修复 `rerunForRepair` 重跑交付步骤(workflow.go:913)→ 仍不过:interactive 转 `quality_review` 人工裁决(approve=manualPass、retry 追加预算但受上限 `MaxRepairGranted`,`squad/termination.go:14`)→ 写 `manifest.json`(全文件 sha256 防篡改)→ `EvRunCompleted` | |
| 15 | **聚合回主 run**:`Orchestrate` 返回 summary,交付物经 `rc.WriteArtifact("squad-<runID>.md")` 登记为主 run 工件;`RunEntry` 返回 `EntryResult{Summary, Body}`(`entry.go:34`),SquadRunner 做**虚假交付检查**(`internal/runtime/squad_runner.go:95` 附近 `claimsFalseDeliverable`)后落工件并 `SetAssistantParts`(squad_runner.go:107)。所有 usage 按角色聚合进 `RunResult.Usage` | |
---
## 4.5 实测:离线小队演示(真实输出)
`deps/agent-squad` 自带离线演示(`cmd/agent-squad demo -keep`,stub 脚本成员 + 规划脚本 +
自动应答 HITL,无需 LLM/网络),以下为其 2026-10-08 实测输出摘录。**引擎机制与产品完全
同一套**,差异只有三处:成员后端是脚本(产品是 agent-native 内核)、规划器是 MockPlanner
(产品默认 RulePlanner)、HITL 自动应答(产品走 HITL 卡片)。
```
═══ agent-squad 离线演示(stub 智能体 + mock 规划器 + HITL)═══
[1] 注册角色(手动注册,backend=demo 脚本智能体)
+ researcher 调研专员 + writer 撰写专员 + reviewer 审核专员
[2] 发起运行:调研便携式超声诊断仪并产出报告
✔ step_01_research researcher 市场与技术调研,回填事实与缺口
step_02_draft writer 汇总调研产出,撰写报告初稿
step_03_review reviewer 一致性终审并产出终版
[3] ⏸ HITL 挂起(gap):发现 1 个阻塞缺口,需要人工补充信息后才能继续:
- [gap_1] 目标年产量未提供,影响成本测算(owner=agent)
→ 自动回应:choice="resolved" answer="年产量 10 万套"
✔ step_01_research ✔ step_02_draft ✔ step_03_review
[4] 运行终态:completed
步骤 3 个(完成 3/跳过 0/失败 0)。质检得分 100。
交付物: <dataDir>/runs/run_20261008T090457.882598947/output.md
黑板字段: {"annual_volume":"100000","budget":"15万","device_class":"二类",
"review":"passed","target_market":"中国"}
```
对应真实落盘的工作区(`runs/<run_id>/`)——与第 4 节调用链逐项对应:
```text
runs/run_20261008T090457.882598947/
├── context/goal.md, context/index.json # 9. 目标与材料快照
├── tasks/01_researcher/ # 9. 每步一个子工作区
│ ├── TASK.md # 步骤任务书(角色、期望产出、资源权威等级、黑板快照、产出要求)
│ ├── AGENTS.md # 角色指令
│ ├── context/ # 快照进来的目标/材料
│ ├── notes.md fields.json gaps.json # 成员产出(黑板来源)
│ └── execution.log
├── tasks/02_writer/{TASK.md,AGENTS.md,context/,output.md,...}
├── tasks/03_reviewer/{TASK.md,AGENTS.md,context/,output.md,fields.json,...}
├── blackboard.json # 11. 黑板(字段在步骤间累积)
├── workflow.json squad.json runstate.json # 7. 拆解结果与运行态(磁盘即权威)
├── checkpoints/{001,002,latest}.json # 11. superstep 检查点
├── output.md manifest.json # 14. 聚合交付物 + sha256 清单
└── .lock # 6. acquire 的 flock 文件锁
```
`tasks/01_researcher/TASK.md` 实拍(节选):
```markdown
# 步骤任务:市场与技术调研,回填事实与缺口
- 角色:调研专员(researcher)
- 期望产出:[notes.md fields.json gaps.json]
## 任务上下文资源与权威等级(已快照到本目录,只引用本地路径)
| 资源路径 | 权威等级 | 用途说明 |
| `context/goal.md` | **authoritative** | 运行目标 | 不可推翻 |
## 黑板快照(共享状态)
## 产出要求
1. 按期望产出文件名落盘到本子目录;
2. 结构化事实回填 `fields.json`(字符串键值);
3. 发现信息缺口且无法假设时写 `gaps.json`([{"item":"…","blocking":true}])。
```
产品的默认拆解(RulePlanner 四阶段骨架,`squad/planner_rule.go:79` 起)与演示计划同构,
真实代码里的步骤与交付约定:
```go
// 通用四阶段骨架:梳理 → 专项 → 成文 → 终审
{"step_01_organizer", "梳理目标背景与输入材料,产出事实与缺口", "organizer"},
{"step_02_specialist", "围绕目标开展专业分析与推演", "specialist"},
{"step_03_writer", "汇总各角色产出,撰写交付物初稿", "writer"},
{"step_04_leader", "一致性终审与交付把关", "leader"},
// 非末步 targets = ["notes.md","fields.json"](事实经黑板传给后步);末步 = ["output.md"]
```
注册表缺角色时 `Registry.Derive` 现场派生(名为"organizer 专员"等)并置
`NeedsConfirmation` → `awaiting_confirmation` 挂起等用户确认编制。意图目录注入规划器/
Leader 的形态见[意图识别](intent-recognition.md) 4.6 节例 5 的 catalog 实测输出。
---
## 5. 小队如何"协作"
- **黑板(Blackboard)**是协作中枢:Facts(已确认事实)/Gaps(信息缺口)/
Implications/Assumptions/Fields。每步完成后子工作区的 `fields.json`/`gaps.json`
合并进黑板(`squad/blackboard.go:43` `mergeStepArtifacts`);下一成员的 TASK.md 带
黑板快照,据此读前序产出。字段注入可自动唤醒挂起的 run(`SweepWakeups`)。
- **材料溯源**:输入材料经 `deps/agent-squad/textextract/` 提取纯文本并注入页码锚点,
成员引用材料时可标注出处。
- **防失控上限**:`squad/termination.go` 集中管理修复预算、Leader 轮数等终止条件。
---
## 6. 与 runmgr / workflow 的关系
- 小队**不是独立的一层**:它是 conversation run 内的一种执行模式/入口(`runtime.entry`)
或 run 内 LLM 的一次工具调用,在 runCtx 上同步驱动。
- 成员执行的子 run 经产品 `SpawnSubagent` 进任务树(仅限内核 `delegate_task` 桥接);
工作流(`run_workflow` → wfcompile 编译执行)是另一条平行机制——注意 wfcompile 执行时
**也装配到 squad 引擎上步进**,entry=squad 时不再提供 `run_workflow` 声明(互斥,
`internal/runtime/llm.go:266-268`)。
- 依赖方向:agent-squad 核心只依赖 error-code-system(不感知内核);依赖由 adapters
层单向引入——`agent-squad/adapters/native` 同时依赖 agent-native 与 agent-squad;
backend require 两者并 replace 到自己 `deps/` 下的库副本。
---
## 7. 关键文件清单
| 职责 | 文件 |
|---|---|
| 全部类型与状态机 | `deps/agent-squad/squad/models.go`(RoleSpec :25、StepSpec :86、BlackboardState :225、RunStatus :284) |
| 门面(Run/Respond/Resume/Inspect/Steer) | `deps/agent-squad/squad/engine.go`(Run :234、acquire :213、planAndExecute :335、Respond :637、Resume :1127) |
| 调度/单步/收口 | `deps/agent-squad/squad/workflow.go`(advance :25、gateBeforeRun :175、runParallel :216、runOneStep :262、checkBlockingGaps :652、finalize :784、rerunForRepair :913、missingTargets :1071) |
| DAG 编译与图指纹 | `deps/agent-squad/squad/graph.go`(compileGraph :23) |
| Leader 委派 | `deps/agent-squad/squad/leader.go`(DefaultMaxLeaderTurns :48、LeaderState :57、leaderPrompt :126) |
| 规划器(规则/LLM/端口) | `squad/planner_rule.go`(Plan :20、fromBlueprint :27)、`planner_llm.go`、`planner.go` |
| direct/plan 分诊 | `squad/router.go`(applyRoute :29、directAssistantPlan :55) |
| 状态持久化(磁盘即权威) | `squad/runstate.go`(persist :81) |
| 工作区/TASK.md/AGENTS.md | `squad/workspace.go`(PrepareWorkspace :27、BuildSubWorkspaces :127、writeTaskFile :182、writeAgentsFile :224) |
| 黑板合并 | `squad/blackboard.go`(mergeStepArtifacts :43) |
| 事件 | `squad/progress.go`、`store/eventsink.go` |
| 后端解析器 / 防失控上限 | `squad/backend.go`、`termination.go` |
| backend 工具路径适配 | `internal/squadadapter/adapter.go`(Orchestrate :47、askHuman :150、onSquadEvent :185、mapPlan :200、isHoldStatus :221、SubagentFactory :234、BuildBackends :292、builtinRoles :306) |
| backend 入口路径适配 | `internal/squadadapter/entry.go`(RunEntry :42、kernelRoles :177) |
| 对话入口 Runner + LLM 分诊 | `internal/runtime/squad_runner.go`(Execute :26、Route :146) |
| 质检打分(六维) | `internal/quality/checker.go`、`score.go` |
| 内核接成小队执行器 | `deps/agent-squad/adapters/native/register.go`(RegisterFull :61) |
| 意图闸门/能力目录 | `deps/agent-squad/adapters/intent/gate.go`、`catalog.go` |
| 独立 CLI(serve/run/demo) | `deps/agent-squad/cmd/agent-squad/` |
+246
View File
@@ -0,0 +1,246 @@
# Agent 的任务规划与执行:架构与调用链
> 基准:Agent-System-Dev 组合 `218fc4b1ba00b0a03b5bb79f292b3eba6c987a58`(R0,2026-10-08 锚点核验)。
> 行号为该提交下的函数入口位置(个别内部区段为约略范围),随代码演进漂移;文件与函数名是稳定锚点。
> `internal/` 指 `modules/backend/internal/`;`core/` 指 `modules/core/`;
> `nruntime/` 指 `deps/agent-native/runtime/`。
---
## 1. 一句话结论
系统有**两套并存的规划机制**:
1. **默认主线 = LLM 自主规划(ReAct 循环)**:没有预置 DAG。普通消息由 backend
`LLMRunner` 驱动,进入 `deps/agent-native` 自研内核的单轮循环
(组装上下文 → 调 LLM → 解析工具调用 → 执行工具 → 回填 → 直到模型不再调工具),
模型可用 `update_plan` / `update_todos` 工具自主维护任务列表(右栏 Plan/Todos 面板)。
2. **可选结构化路径 = WorkflowDef(数据化步骤列表)**:可由意图绑定启动,也可由模型
调用 `run_workflow goal=...` 触发"规划子代理生成步骤 → 用户 HITL 确认 → 编译执行"
(`internal/runtime/workflow_compiled.go:91` `planCompiledWorkflow`)。工作流的
编译与步进执行在 `internal/wfcompile` 包,执行时**装配到小队引擎上步进**
(挂起 → HITL 卡 → Respond;执行态 → Resume),收口带 critic 终判。
执行主体分两层:backend 的 `runmgr`(Run 生命周期/事件/持久化/策略)+
`internal/runtime`(各 Runner);真正的智能体循环内核在 `deps/agent-native/runtime/loop.go`。
---
## 2. Run 生命周期与状态机
契约定义在 `modules/core/model.go`:
- `RunStatus`(model.go:166):
`pending → running → (waiting_hitl ⇄ running) → completed | failed | cancelled | interrupted`
- `RunPhase`(model.go:178):`thinking → acting → responding → finalizing → done`
- `RunKind`(model.go:158):`conversation | subagent | tool`;
`Run` 结构体(model.go:188)含 `ParentRunID`(任务树)、Usage、Error。
状态迁移代码位置(均在 `internal/runmgr/runmgr.go`):
| 迁移 | 代码位置 |
|---|---|
| 创建(pending) | `:1023` `startClaimedRun`(建 Run + `store.CreateRun`,`internal/store/messages.go:120`) |
| 运行 | `:1055` 附近 `execute`(置 running/thinking) |
| HITL 挂起/恢复 | `:449` `AskHITL` 置 waiting_hitl;`:1261` `ResolveHITL` 恢复 running |
| 终态 | `:1087` `finishRun` → `internal/store/recovery.go:125` `SettleRun`(条件 UPDATE + 同事务写权威助手消息树,防并发双终态)→ 发 `run.completed/failed` |
| 取消 | `:1222` `CancelRun`(根 run ctx 取消,子 run 派生自父 ctx 天然级联;并唤醒 HITL 等待) |
| 停机中断/重启对账 | `:1655` `InterruptRunning`、启动时 `:1689` `ReconcileInterrupted` |
| 会话并发控制 | `:980` `ClaimConversation`(一会话至多一个在途 run,忙返回 409,`server.go` 错误映射) |
---
## 3. 端到端调用链(普通消息 → 结果落库)
| # | 步骤 | 位置 |
|---|---|---|
| 1 | HTTP 入口 `postMessage`:附件经 `ingest.Parse`;`/` 命令不启 run;意图识别 + `route.Decide` 三分支 | `internal/server/server.go:574`;路由调用 `:656` |
| 2 | 抢会话槽位 + 消息落库(幂等 requestId) | `server.go:679` → `runmgr.go:980`;`server.go:686` 附近 `AppendMessageWithRequest` |
| 3 | 启动 run:工作流回合 `StartClaimedWorkflowRun`(定义深拷贝进 Launch,插件卸载不影响在跑副本);普通回合 `StartClaimedConversationRun` → `startClaimedRun`:建 Run 落库 → 注册 runHandle → `selectConversationRunner` → 附件原件落工作区 → `go m.execute(rc, runner)` | `server.go:725,727`;`runmgr.go:991, 996, 1023-1043`;runner 选择 `runmgr.go:918` |
| 4 | `LLMRunner.Execute`(`internal/runtime/llm.go:198`)准备阶段:模型路由 `ResolveChatModel`(`internal/runtime/model.go:54`,settings `models.chat.*` + 画像 ModelRoute,密钥经 SecretLookup);`@knowledge` 显式引用触发 RAG(llm.go:216 附近);system 段 `prompts.Service.ResolveSystem`(`internal/prompts/service.go:290`);上下文组装 `ctxasm.AssembleInput`/`FinishAssembly`(`internal/ctxasm/ctxasm.go:45`、`finish.go:97`,产出 `ContextObservation` 随 run.completed 下发);工具声明(编排型内置 + 注册表过滤,llm.go:267-315;计划模式过滤 `filterPlanDecls` llm.go:332) | |
| 5 | 进入内核:`l.runDirect(...)`(`internal/runtime/llm_direct.go:95`)把声明包成内核工具注册进 `agenttools.NewRegistry`、打开转录 journal(`<sandbox>/.agentx/transcript.jsonl`)、接线 Stop 钩子(交付物门禁 + 验证器)、调 `nruntime.Run(...)` | |
| 6 | **内核 ReAct 单轮循环** `nruntime.Run`(`deps/agent-native/runtime/loop.go:196`,`for turn < cfg.MaxTurns` 默认 12):① ctx/预算检查 → ② `prepareRequest`(loop.go:787:转录投影 + 窗口清理 + 记忆注入 + reminders)→ ③ `consume` 流式消费 LLM 事件(loop.go:492)→ ④ 持久化 assistant → ⑤ 有工具调用:`executeTools`(loop.go:595,可并行),每个 `runOne`(loop.go:709)走完整性校验 → `gateTool`(hooks PreToolUse,ask 决策交宿主 PermissionAsk)→ commitToolIntent(崩溃恢复配对)→ `cfg.Tools.Execute` → AfterTool,结果按 Role=tool 回填 → `continue` 下一轮 → ⑥ 无工具调用:`ContinueOnLength` 续写(loop.go:399)+ follow-up drain + Stop 钩子决定续跑(`stopDecision` loop.go:645;宿主侧做交付物门禁与验证,最多 3 次续跑)→ `settle` 结束;轮数耗尽报 max turns exceeded(loop.go:447) | |
| 7 | 工具执行回落宿主:编排型工具在 runner 内消化——`squad_orchestrate` → squadadapter.Orchestrate(case 在 `llm_direct.go:479`)、`delegate_task` → rc 端口 `SpawnBackground`/`SpawnAgent`(`llm_direct.go:493` 起)、`update_plan/update_todos` → `rc.UpdatePlan/UpdateTodos`、`run_workflow`、`submit_card/card_*`(各 case 依次在 `llm_direct.go:471` `runTool` 内);其余 → `rc.ExecuteTool`(`runmgr.go:740`):画像三清单 → policy.Decide(计划模式 deny / ask→HITL 确认卡)→ `registry.Execute`(`internal/tools/registry.go:94`)→ 结果截断转工件 | |
| 8 | 子代理 run(任务树):`spawnSubagent/startChild`(`runmgr.go:1348/1358`,startChild 做深度检查)建 `Kind=subagent` 子 run(ParentRunID 指父)→ 发 `agent.spawn`(`runmgr.go:1375`)→ `finishChild`(`runmgr.go:1401`)跑 `SubLLMRunner.Execute`(`internal/runtime/sub_loop.go:31`,同样调 `nruntime.Run`)→ 子转录独立落盘 → 结果截断 32000 字(`runmgr.go:1780` `capSubagentResult`)。后台子代理:`spawnBackground`(`runmgr.go:1475`,每会话上限 3);完成后 `deliverNotices`(`runmgr.go:1560`)在会话空闲时以 system-reminder 启动新父回合汇报(`startNoticeRun`,`runmgr.go:1616`) | |
| 9 | 终态收敛:`finishRun`(`runmgr.go:1087`)usage 汇总 → 终态消息树(tool_call/tool_result/thinking/card 部件)→ `SettleRun` 单事务落库 → submit_card 回填锚点 → 发 `run.completed`(含 ContextObservation)/`run.failed`。工件:`runCtx.WriteArtifact` → `store.CreateArtifact` → `artifact.created` 事件 → 文本进检索索引;工件作为 `PartArtifactRef` 挂进助手消息树 | |
完成判定者 = 内核循环 + 宿主 Stop 钩子组合:模型不再调工具且无 follow-up → Stop 钩子
(交付物门禁说谎检测、可选验证器)决定续跑或放行 → 可选用主模型 `composeFinal` 重写
最终回复(`nruntime/compose.go`)→ `finishRun` 落终态。
---
## 4. LLM 调用与流式
- **不用第三方 SDK**,自研 HTTP 客户端 `deps/agent-native/runtime/llm/client.go`:
默认 OpenAI 兼容 `POST {base}/chat/completions` + `stream:true`;
`Protocol=anthropic` 走原生 Messages(`anthropic.go`,带 cache_control 断点与签名思考块)。
- **Provider 预设**:`llm/providers.go`(anthropic/openai/google/deepseek/minimax/
openrouter/groq/azure)+ `models.json` 自定义;`ParseModelRef` 支持 `provider/model:thinking`。
- **流式解析**:`llm/sse.go` `ParseSSE` → `emitParsed` 产出统一事件
(text_delta / reasoning_delta / tool_call_delta / message_end)。
- **模型配置链**:settings 覆盖链(defaults→system→user→project)+ 画像 ModelRoute
(`internal/runtime/model.go:30-62`);轻量档回落主档(model.go:84);
`models.chat.fallbacks` 故障切换(`llm/fallback.go`,在 runDirect 内接线);
重试(内核 `WithRetry`)。
- **事件到前端**:内核事件 → `directRun.onEvent`(`llm_direct.go:278`)转 `rc.Emit(...)`
→ `runmgr.emit`(`runmgr.go:1217`)→ 事件缓冲合并(150ms/64 条,`internal/runmgr/events.go:42-43,83`)
→ `store.AppendEvents`(事务内分配会话内单调 seq,`internal/store/messages.go:243`)
→ `bus.Publish` → SSE `GET /api/events`(`server.go:275`,Last-Event-ID 重放,15s 心跳);
PG 多实例经 `internal/pgbus` NOTIFY/LISTEN 扇出。
---
## 5. 工具 / 技能 / 提示词体系
**工具系统**
- 契约:`modules/core/tools.go:16` `ToolDesc`(JSON Schema 参数、ReadOnly/Edits/Risk 权限声明)、
`tools.go:33` `HTTPToolSpec` 无代码声明式 HTTP 工具。
- 注册:`internal/tools/registry.go:50` `Register`(后注册覆盖)、`:112` `RegisterBuiltin`
(time_now、knowledge_search、write_artifact、quality_review、跨会话 peers、ask_user 等);
另有 fs / exec / search / 声明式 HTTP / `run_tool_script`(需开关)各文件。
- 执行策略在 runmgr:`ExecuteTool`(`runmgr.go:740`)deny→错误、计划模式 deny、
confirm/ask→HITL 确认卡、执行后超长截断转工件。
- 结果回填:内核侧作为 `Role=tool` 消息进转录;UI 侧由 onEvent 聚成 `PartToolCall`+
`PartToolResult` 终态落库;下一轮由 ctxasm `renderAssistantTurns`(`internal/ctxasm/ctxasm.go:174` 附近)
还原成 provider 原生配对消息。
**skills 包 = 内置技能文件**:go:embed `recipes/**`(`internal/skills/skills.go:23` 附近),
启动 `Seed`(skills.go:53)落 `<data>/skills/`,带 manifest 哈希做升级/drift 保护;
供内核的 SKILL.md 加载器与 `load_skill` 渐进披露工具消费
(`deps/agent-native/runtime/skills/skills.go`)。
**prompts 包 = 提示词模块库**:embed `library/*.md` + DB 用户覆盖链。
`ResolveSystem`(`internal/prompts/service.go:290`):base/env 层按画像引用 →
task 层全员触发匹配(command/intent/attachment 三类触发)→ 正文点名的 `method.*`
辅助模块跟随注入 → 占位符填充(`{{date}}/{{model}}/{{skillsDir}}`)。
另提供子代理 spec(tools/model/maxTurns,`service.go:81` 附近;**禁止子代理持有派发类工具**)、
AgentPrompt、工具描述覆盖。
**ctxasm 包 = 上下文组装**:`AssembleInput`(`internal/ctxasm/ctxasm.go:45`)=
sys 段 → 历史窗口(默认 40 条,超出注入抽取式摘要)→ 卡片/工件正文回填
(防"下一轮失忆")→ 当前轮文本 + 附件解析全文(默认 48000 字符截断)+ knowledge 块;
`FinishAssembly`(`finish.go:97`)追加条件注入;产出 Report(token 估算、裁剪记录、
prefixHash)作为可观测 `ContextObservation`。记忆注入在内核按请求完成(`loop.go` prepareRequest 段)。
---
## 5.5 主对话模型手上有哪些工具
模型每轮请求能看到的工具分三类(全部在 `LLMRunner.Execute` 声明阶段组装,`llm.go:267-315`):
| 类别 | 工具名(实测常量/注册名) | 来源 |
|---|---|---|
| **编排型**(在 runner 内消化,不走通用执行器) | `update_plan` / `update_todos`(维护右栏计划/待办)、`delegate_task`(派子代理)、`squad_orchestrate`(发起小队)、`run_workflow`(发起工作流)、`review_findings`、`submit_card`、`card_begin`/`card_append`/`card_commit`(流式产卡三段式) | `internal/runtime/llm.go:56-70` 常量 |
| **内置产品工具** | `time_now`、`knowledge_search`(项目/插件知识全文检索)、`write_artifact`(写工件)、`quality_review`(交付物打分)、`ask_user`(主动提问=HITL)、跨会话 `peers` 系列 | `internal/tools/registry.go:112` `RegisterBuiltin` |
| **内核工作区工具**(经 hostbridge 注入内核) | `read_file` / `write_file` / `edit_file` / `multi_edit` / `list_dir` / `grep` / `glob` / `load_skill` / `run_python` / `run_command` / `web_search` / `tavily_search` / `fetch_url` / `delegate_task` | `internal/runtime/hostbridge.go:15-21` `kernelToolNames`;`web_search`/`tavily_search` 仅在配置了搜索密钥时注册 |
实际可见集 = 以上与**画像三清单**(allow/deny/confirm,`core/profile.go`)过滤后的交集;
计划模式下 `filterPlanDecls`(llm.go:332)会再收窄。
---
## 5.6 实例:一个典型回合的机制走查
> 走查基于上文全部代码锚点;模型输出为示意,机制、工具名与顺序均为代码实测。
用户输入:"把我会话里刚上传的季度数据整理成一份摘要文档,并告诉我三个关键结论。"
1. **进入回合**:`postMessage` → 意图 fallback(没有装匹配意图)→ `route.Decide` 判
`KindTurn` → 抢会话槽位 → `startClaimedRun` 建 `Run{pending}` → 选 `LLMRunner` →
`go execute()`(`runmgr.go:1023, 918`)。
2. **上下文组装**:`ctxasm.AssembleInput`(ctxasm.go:45)——system 段(base/env/task 提示词
模块,`prompts.ResolveSystem`)→ 历史窗口(默认 40 条)→ 当前轮文本 + **附件解析全文**
(ingest 已把 xlsx/csv 解成 ParsedDocument,默认 48000 字符截断)→ knowledge 块。
3. **第 1 轮 LLM**:模型看到数据后先调 `update_todos`(拆 3 个待办)+ `read_file`(读附件
解析物)。两个工具经内核 `runTool` 回落宿主执行,结果以 `Role=tool` 消息回填转录
(loop.go `executeTools` 段),UI 侧聚成工具过程部件。
4. **第 2 轮 LLM**:模型调 `write_artifact`(把摘要写成工件)。宿主执行 → `artifact.created`
事件 → 工件登记进会话。
5. **第 3 轮 LLM**:模型输出三个结论的纯文本(无工具调用)→ 内核判定无工具调用 →
follow-up drain → **Stop 钩子**:宿主做交付物门禁(`deps/agent-native/runtime/gates/gates.go:52`
`ClaimGate`)——它抓三类"说谎":① 回复正文出现工具调用标记但本轮没有真实调用;
② 引用了不存在的工件 ID;③ **声称"已生成/已登记文件"但这轮没有产出对应工件**。
本例模型确实产出了工件,门禁放行;若模型只写了结论却声称"文件已生成",
门禁拒绝结算并附原因让模型续跑(最多 3 次续跑,`loop.go` stopDecision + 宿主接线)。
6. **收口**:可选用主模型 `composeFinal` 重写最终回复 → `finishRun`(runmgr.go:1087)→
`SettleRun` 单事务落 run 终态与助手消息树 → 发 `run.completed`(带 ContextObservation)。
子代理变体:若第 2 轮模型改调 `delegate_task {agent:"researcher", task:"分析季度数据"}`,
则走 `llm_direct.go:493` → `runmgr.go:1348` 派生 `Kind=subagent` 子 run(任务树 + `agent.spawn`
事件)→ `SubLLMRunner` 用同一内核循环独立跑完 → 结果回填父轮。
---
## 6. HITL(人工介入)
- 契约:`modules/core/hitl.go:33-36` 四种卡(choice/input/confirm/form)。
- 挂起:`runCtx.AskHITL`(`runmgr.go:449`)存卡 → run 置 waiting_hitl → 发
`hitl.request` → 收件箱投影 → `waitHITL` 阻塞等恢复(支持超时)。
- 恢复:`POST /api/hitl/{id}/resolve` → `runmgr.ResolveHITL`(`runmgr.go:1261`)
三分支:选项回填恢复;自由输入命中其他意图 → 取消挂起 run 并转轨;否则作澄清回答回填。
- 触发时机:a) 工具确认(画像 confirm 清单 / policy ask,`runmgr.go:757` 附近与内核
`PermissionAsk` 端口);b) 工作流缺槽位提问(`internal/wfcompile/run.go` 槽位补齐段)、
显式 hitl 步骤(`internal/wfcompile/host.go:138` `doHitl`)、动态计划执行前确认
(`RunOptions.RequireConfirm`,`internal/wfcompile/run.go:34`);c) `ask_user` 工具。
---
## 7. workflow 体系与"规划器"
- `internal/workflow/registry.go`:工作流**定义注册表**(只依赖 core)。
`Registry.Set` 整体替换快照、`ByIntent` 按意图查定义、`Launch` 产启动副本。
- **编译执行器在 `internal/wfcompile/`**(Q3 WP1):
- `run.go:39` `RunCompiled`:缺槽位逐个 HITL input 卡收齐 → `compiler.go:76` `Compile`
编译 WorkflowDef → **装配到小队引擎(squad)步进执行**(挂起 → HITL 卡片 → Respond;
执行态 → Resume)→ critic 终判 → outcome 汇总(run.go 文件头注释即此流程)。
- `RunOptions`(run.go:27):`DynamicLimits`(模型生成上限:≤8 步、禁 script)、
`RequireConfirm`(模型生成工作流执行前强制 HITL 确认;注册的声明式工作流免确认)、
`ModelScript`(脚本步为模型提供,执行前单独确认)。
- **"先规划任务列表再执行"的 planner 真实存在**:`internal/runtime/workflow_compiled.go:91`
`planCompiledWorkflow` —— `rc.SpawnAgent(workflowPlannerID, "规划", …)` 派**规划子代理**
输出 WorkflowDef JSON("只输出 JSON,不要完成目标本身")→ `wfcompile.ParseWorkflowDef`
解析校验 → `runLaunch` 以 `DynamicLimits:true, RequireConfirm:true` 执行(用户 HITL
确认计划后步进)。另有 `registeredWorkflowLaunch`(:117,注册定义 + 快照脚本正文)、
`modelScriptLaunch`(:138,模型脚本,单独确认)。入口 Runner 是
`CompiledWorkflowRunner`(workflow_compiled.go:30,`Execute` :39)。
---
## 8. deps/agent-native 内核的其他部件
- `runtime/` 是**进程内自研内核**(不派子进程):`loop.go` 循环、`llm/` 客户端、
`tools/` 内核注册表(deferred 延迟暴露 + tool_search)、`session/`(JSONL 转录树,
可 fork/clone)、`session_agent.go`(headless AgentSession:steer/follow_up/abort/换模型)、
`hooks/`、`memory/`、`skills/`、`promptx/`、`mcp/`、`checkpoint/`、`subagent/`、
`gates/`(验收+守卫)、`codeorch/`(Python 依序调工具的代码编排)、`policy/`。
- `rpc/` 是本地 JSONL RPC(一行一命令,双向 pipe),把内核暴露给子进程/外部宿主;
**产品后端主路径不走 rpc**,直接进程内调 `nruntime.Run`。
- `adapters/native/backend.go` 把内核包成 `exec.Backend`(`exec/backend.go` 接口),
供小队成员执行使用(见[小队与子 Agent 启动](squad-and-subagents.md)、
[小队工作机制](squad-mechanics.md));`adapters/pitransport/` 是远程 pi runner 的
HTTP/NDJSON 传输适配(产品未用为主)。
---
## 9. 关键文件清单
| 职责 | 文件 |
|---|---|
| Run/消息/计划/工件契约 | `modules/core/model.go`、`events.go`、`hitl.go`、`tools.go` |
| Run 生命周期枢纽 | `modules/backend/internal/runmgr/runmgr.go` |
| 事件缓冲与 seq | `modules/backend/internal/runmgr/events.go` |
| Runner 端口与四实现 | `internal/runtime/runner.go`、`llm.go`、`sub_loop.go`、`workflow_compiled.go`、`squad_runner.go` |
| 内核桥接(工具/事件/Stop 钩子) | `internal/runtime/llm_direct.go`(runDirect :95、onEvent :278、runTool :471) |
| 模型路由 | `internal/runtime/model.go` |
| 工作流编译执行 | `internal/wfcompile/run.go`(RunCompiled :39)、`compiler.go`(Compile :76)、`host.go`(doHitl :138) |
| 工作流入口与 planner | `internal/runtime/workflow_compiled.go`(CompiledWorkflowRunner :30、planCompiledWorkflow :91) |
| 工作流定义注册表 | `internal/workflow/registry.go` |
| 内核 ReAct 循环 | `deps/agent-native/runtime/loop.go` |
| LLM 客户端/协议/预设 | `deps/agent-native/runtime/llm/client.go`、`providers.go`、`sse.go`、`anthropic.go` |
| 上下文组装 | `internal/ctxasm/ctxasm.go`、`finish.go` |
| 提示词库 | `internal/prompts/service.go` |
| 技能种子 | `internal/skills/skills.go` |
| 工具注册表 | `internal/tools/registry.go` |
| run/事件/工件落库 | `internal/store/messages.go`、`recovery.go` |
| HTTP 入口与 SSE | `internal/server/server.go` |
@@ -8,7 +8,7 @@
| 面 | 标识 | 规则 |
|---|---|---|
| 插件清单 | `apiVersion` | 接受 `1.0` 与 `2.0-experimental`。其他值整包拒绝(fail-closed) |
| 公开 Go 包 | `extension.SurfaceVersion` | `2.0.0-experimental`。实验窗口内允许破坏性调整,但必须改契约快照并在提案里写一句 |
| 公开 Go 包 | `extension.SurfaceVersion` | 实验系列 `2.x.0-experimental`(2026-10-09 v0.2.0 时为 `2.5.0-experimental`,每次加法升次版本号)。实验窗口内允许破坏性调整,但必须改契约快照并在提案里写一句 |
| HTTP API | 路径前缀 `/api/` 与 `/host/v1/` | 已发布路径只加字段,不改语义、不删字段。新能力用新路径 |
弃用:公开符号先标记注释 `Deprecated:`,至少一个下游可升级的版本之后才删除。实验窗口内的删除仍要改快照测试,不能静默。
@@ -45,10 +45,11 @@ replace agent-system-core => ../Agent-System-Dev/modules/core
## 5. 发版命令(owner 执行,本次未执行)
tag 名以 V1.2 实施推进方案批复的 `v0.2.0` 为准(2026-10-05 对齐;五仓 backend/core/frontend/ui/Dev 同打):
tag 名以 V1.2 实施推进方案批复的 `v0.2.0` 为准(2026-10-05 对齐;五仓 backend/core/frontend/ui/Dev 同打)。
具体提交、tag 消息与核对命令以 [v0.2.0 发版记录](../records/2026-10-09-base-v0.2.0-release.md) 为准(消息里的 SurfaceVersion 写发版时的实际值):
```
git tag -a v0.2.0 -m "extension surface 2.0.0-experimental"
git tag -a v0.2.0 <该仓对应提交> -m "<发版记录里的消息>"
git push origin v0.2.0
```
@@ -9,6 +9,7 @@
- **希望符号**:`pkg/extension` 新增 `Request` / `Handler` / `Host`(owner 作用域宿主句柄最小集)与 `Registry.AddCommandHandler(spec, h)`;基座侧配套 `cmdbus.Bus.RegisterPluginCommand`。
- **为何现有注册点组合不出来**:`RegisterPluginCommands` 仅登记 spec;`HostClient` 走 sidecar HTTP 且需要进程内不可得的 host token;owner 作用域必须由基座在分发时构造,插件无法自造。
- **兼容**:只加符号与方法,不改既有路径语义;`SurfaceVersion` 保持 `2.0.0-experimental`,实验窗口内的接口调整以本提案为记录。
(2026-10-09 注:此后 EXT-8/9/11/12 与执行原语硬开关按 compat-policy 逐次加法升级,v0.2.0 发版时为 `2.5.0-experimental`,见 [v0.2.0 发版记录](../records/2026-10-09-base-v0.2.0-release.md)。)
## 2. 公开面变更(surface.txt 12 → 15 项)
@@ -0,0 +1,166 @@
# EXT 申请包 · C1–C7:RDM 多人共享契约的基座最小扩展(EXT-6…EXT-12)
| 项目 | 内容 |
|---|---|
| 状态 | proposed(2026-10-08);owner:weichen;任务:Dev `feature/ext-c1-c7-applications` |
| 性质 | 扩展点申请包(extension-compat-policy 流程第 2 步:基座侧评审)。**本文只立申请,不实施**;各 EXT 实现另立任务卡 |
| 需求依据 | RDM 仓 U0 主记录 `docs/proposals/2026-10-08-U0-baseline-and-shared-contracts.md` §5 契约 C1–C7(RDM main `ca72cfc` 已合入,含 D1–D9 裁定);来源=用户 2026-10-08 会话批准的 U0 实施计划 |
| 现状证据 | 均为 SELF·源码级,引用对象:Dev `34a2bbe`(backend gitlink `fe7c245`);RDM pin backend `cb26e4c`(v0.1.0)相同结论者已注明。运行态 NOT_RUN |
| 编号 | 按既有序列(EXT-1/3/4/5 在案)顺延 **EXT-6…EXT-12**;最终编号以受理评审为准 |
## 0. 总则
- **推荐路线**(U0 裁定 D1,修订 V2.1 草稿 J-09「基座不改动」):领域规则(成员、角色、三权、
发布状态)住 RDM 下游仓;基座只补**通用、无领域词汇**的最小扩展面。每条申请遵守 INV-2:
基座代码与 diff 不出现 RDM 领域字样,全部以「资源授权/受管配置/投影过滤」等通用语义表述。
- **事实依据(为何纯下游组合不出来)**:基座一切资源访问按 owner 硬边界
(`internal/store/projects.go:22-34`),EXT-1 Host 强制 owner 归属且**无知识删除/更新**
(`pkg/extension/host.go:43-46`、`internal/app/hostcaps.go:101`)。RDM 增加成员表不能改变
基座过滤;给每人复制知识库被 U0 §4 明确禁止。
- **SurfaceVersion 影响**:EXT-6/7/9 触及 `pkg/extension` 导出面(Registry/Options/Host 方法),
属加法变更——按 extension-compat-policy 升 SurfaceVersion(2.0.0-experimental 内加法,
surface.txt 同步);EXT-8/10/11/12 为 internal/HTTP 行为扩展,不动导出面。
- **分期建议**(对齐 U0 §10):EXT-8(授权适配器)与 EXT-9(索引发布)为序 4「项目范围与
授权基础」的硬前置;EXT-6/EXT-12 支撑序 4–5;EXT-7/EXT-10 可随后。EXT-8 建议最先评审。
- **通用默认不变量**:每条扩展的缺省行为=现状(未注册/未配置时与今日基座完全一致),
RDM 产品部署显式启用;通用基座与既有下游不受影响。
---
## 1. EXT-6(←C1)可信身份:账号停用与会话吊销
- **场景**:RDM R01 唯一管理员移交后旧凭证须立即失效;成员移除同理。现状基座账号只有
创建/改密/列表(`internal/server/accounts.go:184-261`),无停用语义,撤销账号后其 API key
与会话仍有效到自然过期。
- **期望公开面**:①`POST /api/admin/users/{owner}/disable|enable`(requireAdmin 保护);
②users 表加 `disabled` 布尔(迁移,default false);③认证链(API key 校验与会话校验,
`internal/server/auth.go:29-64`)与 `requireAdmin` 拒绝 disabled 账号并吊销其在途会话。
「全局唯一管理员」由 RDM 产品策略约束(部署约定+台账),不强制基座单管理员。
- **为何组合不出**:无任何现有端点或设置键能改变账号认证状态;RDM 无法经 EXT-1 Host 触达
认证层(Host 无账号域能力,也不应有一—属平台面)。
- **兼容方式**:纯加法(新端点+新列 default false);未停用=现状。SurfaceVersion 不变
(不动 pkg/extension)。
- **验收**:停用后该账号 API key 与 cookie 即刻 401;管理员功能不受影响;启用可恢复。
## 2. EXT-7(←C2)受管系统配置:最终生效值受策略约束
- **场景**:RDM R01——模型端点/密钥、工具上限、系统插件由管理员控制;个人偏好保留。现状
设置链 `defaults → system(启动注入) → user → project`(`internal/settings/settings.go:131-147`),
user/project 可覆盖一切;system 层不落库且组合根只注入 dataDir
(`internal/app/app.go:130`)——只藏设置页或只禁今后写入都处理不了既有覆盖。
- **期望公开面**:①`extension.Options` 增受管注入:`ManagedSettings map[string]any`(键集+
值,由宿主二进制启动注入);②设置服务按受管键集跳过 user/project 覆盖(Effective 尊重
策略);③受控键的 user/project 写入返回结构化拒绝并附依据;④`GET /api/settings` 标注
哪些键受管。RDM 在自有二进制 main 里以管理员配置注入。
- **为何组合不出**:Options 无注入口;`SetSystemOverride` 是组合根私有;下游无法让 Effective
忽略 user/project 的既有覆盖。
- **兼容方式**:`ManagedSettings` 缺省 nil=现状(无受管键,行为不变);pkg/extension Options
加字段=导出面加法,SurfaceVersion 与 surface.txt 同步升。
- **验收**:预置 user 覆盖的受控键,注入策略后生效值=受管值;普通用户写受控键被拒且
`GET /api/settings` 可解释;未受管键的个人偏好不受影响。
## 3. EXT-8(←C4)资源授权适配器:项目共享的读取通道 ⭐关键路径
- **场景**:RDM R03/R04/R05——成员 B 依 RDM 成员表读取成员 A 提交的项目资料(工件原件、
检索、引用、下载、图查询);撤销后停止。现状一切资源入口按 owner 过滤
(`internal/store/projects.go:22-34`;工件/知识/卡片同理),Host 读取族同样强制本人
owner(`pkg/extension/host.go:43-46`)。
- **期望公开面**:①基座提供**授权适配器注册点**(HTTP 组合根):`RegisterResourceAuthorizer(
func(owner, resourceProjectID core.ID) (allow bool, err error))` 形态的进程内回调,经
`extension.Options` 或 Registry 注入(具体挂点评审定);②资源入口(项目/工件/知识检索/
卡片读取的 HTTP 层与 Host 读取族)在 owner 自有判定未命中时回调适配器:返回 true 则按
「项目共享」放行,false/未注册=现状 NotFound(不泄露存在性);③适配器只回答布尔,
不接触基座内部状态;④放行日志入审计(owner×resource×decision)。
- **为何组合不出**:owner 过滤在 SQL 层硬编码;无任何回调或配置可改变跨 owner 可见性;
回环 HTTP 模拟他人身份被认证中间件阻止(V1.1 教训,10 号方案已证)。
- **兼容方式**:未注册适配器=现状零差异;回调失败按拒绝(fail-closed)。pkg/extension
加法→SurfaceVersion 升。**注意**:本 EXT 使基座首次具备跨 owner 授权读取语义,评审须
重点核对回调契约的最小性(仅 owner×projectID 两参,不透出对象细粒度——细粒度由 RDM
在自己的命令层做)。
- **验收**:同项目两人+另一项目一人(RDM 成员表桩):共享资源四入口(原件/检索/引用/下载)
一致放行;跨项目与替换引用拒绝且 NotFound 不泄露;撤销成员后新访问立即拒绝;审计可查。
## 4. EXT-9(←C5)索引发布与撤销:留存 ≠ 可检索
- **场景**:RDM R07——存疑资料确认前不得进入任何正式检索;发布有稳定版本、撤销即时生效。
现状基座上传端点在下游相关性判定**之前**即写检索索引
(`internal/server/server.go:1039-1046`;RDM pin 相同);Host 知识面只有
`KnowledgeWrite`(强制 owner),无删除/更新(`pkg/extension/host.go:43-46`)。
- **期望公开面**:①基座上传预索引开关(`Options` 或项目设置键 `uploads.autoIndex`,缺省
true=现状;RDM 部署关闭,索引决定移交其命令层);②Host 增 `KnowledgeDelete(owner,
sourcePrefix string, versions …) / KnowledgeUpdate`(或等价发布令牌语义:写入带稳定
`PublishID`,删除按 PublishID 幂等);③删除仅作用于插件来源(`Source` 前缀
`plugin:<id>`),公共 manual 来源不可被插件删。
- **为何组合不出**:检索索引无删除面;上传索引行为硬编码;RDM 无法撤回已入索引的存疑内容。
- **兼容方式**:开关缺省现状;Host 删除方法为导出面加法(surface.txt/SurfaceVersion 同步)。
- **验收**:相关/存疑/拒绝三份样本:仅获准发布版本可检索;重试不重复建索引;撤销后检索与
上下文注入立即停止(含 Host 读取族);manual 来源不可删。
## 5. EXT-10(←C3)分发前校验钩子与会话归属
- **场景**:RDM R18——对话/按钮/卡片/API 四入口同源且对象-会话-项目关联可校验;旧卡片/
重复确认不得操作新版本。现状 `POST /api/commands/{id}` 不校验 conversationId 归属
(`internal/server/server.go:823-836`),确认卡锚定客户端自报会话
(`internal/cmdbus/cmdbus.go:294-316`);插件命令注册不写 slash 别名,对话入口不可达
`rdm.*`(`cmdbus.go:145-153`,v0.1.0 与 main 同——阶段总案 §2.3 已登记)。
- **期望公开面**:①分发前校验钩子:`RegisterDispatchGuard(func(owner, conversationID string)
error)`(未注册=现状放行;RDM 注入「会话属该项目且属于该 owner」判定);②
`RegisterPluginCommand` 增可选 slash 别名参数(或 manifest 字段),对话入口 `/rdm.*`
可解析;③别名与 intents 命中路由到插件命令的打通(阶段总案既有缺口,并入本 EXT 或
单列由评审定)。
- **为何组合不出**:execCommand 对 conversationId 零校验也无回调点;slash 表只由内置与
用户命令登记写入(`cmdbus.go:109-127`),插件路径不写。
- **兼容方式**:钩子/别名均缺省=现状;internal 行为扩展为主(Registry 签名若变则属
pkg/extension 加法,SurfaceVersion 升)。
- **验收**:伪造他人会话/项目引用的命令被拒;`/rdm.ping` 在对话入口可达且回执与按钮入口
同源(同一审计信封);陈旧卡片确认不生效(既有 ErrNotAwaiting 之上叠加对象版本校验)。
## 6. EXT-11(←C6)按 owner 重建受限 Host:后台任务恢复
- **场景**:RDM R10/R21——慢任务跨重启不丢不重;撤权后不越权续跑。现状 RDM 慢通道是进程
内存队列并把分发时 Host 句柄留在队列(RDM `internal/rdmapp/slow.go:53,66`,重启即丢、
撤权不重验);基座任务台账仅记账(`pkg/extension/host.go:48-50`)。
- **期望公开面**:基座侧缺口小——提供「按 owner+pluginID 构造受限 Host」的公开工厂
(如 `extension.NewScopedHost(owner, pluginID)` 形态或 Registry 上的查询),供下游在
恢复任务时重建执行上下文;任务体、输入版本、幂等与重验策略由 RDM 自管(U0 §7)。
**不序列化旧 Host**——重建受限上下文,天然以当前权限为准(配合 EXT-8 的授权适配器,
撤权即拒绝)。
- **为何组合不出**:inprocHost 是 internal 类型按分发请求私有构造
(`internal/app/compiled.go:42`);下游无任何入口可在分发期之外获得合法句柄。
- **兼容方式**:纯加法工厂函数;不改变分发期语义。pkg/extension 加法→SurfaceVersion 升。
- **验收**:下游以工厂重建的 Host 执行读/写,owner 作用域与分发期一致;重启后同任务不重复
执行(RDM 侧幂等)+ 撤权后重建 Host 的读取被适配器拒绝。
## 7. EXT-12(←C7)卡片/事件投影过滤:私人事件与公共投影分离
- **场景**:RDM R04/R18——私人会话卡不得进项目公共流;公共投影只含获准发布的状态;撤权
后重放不泄露。现状会话卡**全量镜像**到项目频道(`internal/cardsvc/cardsvc.go:41-60`,
注释自述「全量镜像换取口径简单」)——单 owner 下无害,多人项目即隐私泄露路径。
- **期望公开面**:卡片镜像策略可配置:①按卡属性过滤(kind/来源)的内置开关,或②复用
EXT-8 适配器语义的投影可见性回调(`func(owner, card) bool`)——评审择一,倾向①最小。
RDM 部署关闭会话卡镜像,只用项目级卡承载公共投影。
- **为何组合不出**:emitCard 镜像逻辑硬编码;无配置面。
- **兼容方式**:缺省=现状全量镜像;internal/设置键扩展,不动导出面。
- **验收**:关闭镜像后会话卡仅出现在本人会话流;项目流只含项目级卡;既有单用户部署行为
不变;SSE 重放遵循同一过滤。
## 8. 不在本申请包(显式排除)
- 需求条目化/模板/法规双通道/三权流程等 RDM 领域能力(U0 §2 R08–R16)——全部住下游。
- 飞书渠道 EXT-3:既有在案申请,节奏不变(MS4 前)。
- 命令审计跨租户修复:**不是扩展**,是缺陷修复,已另行(Backend PR #104 + Dev PR #262)。
## 9. 受理建议
| EXT | 前置的 U0 序 | 建议评审优先级 | SurfaceVersion |
|---|---|---|---|
| EXT-8 授权适配器 | 序 4 硬前置 | **P0 最先** | 升(加法) |
| EXT-9 索引发布/撤销 | 序 4 硬前置 | P0 | 升(加法) |
| EXT-6 账号停用 | 序 4–5 | P1 | 不变 |
| EXT-11 Host 工厂 | 序 4 | P1 | 升(加法) |
| EXT-10 分发守卫/别名 | 序 3 剩余半边(基座侧) | P1 | 视形状 |
| EXT-7 受管配置 | 序 4–5(R01) | P2 | 升(加法) |
| EXT-12 投影过滤 | 序 5 | P2 | 不变 |
每条受理后按 extension-compat-policy 走「基座任务卡实现→发版→RDM 升钉」;预计随 v0.2.0
之后的 v0.3.x 分批落地(对齐 RDM 阶段总案 §3.3 与风险 R-5)。
@@ -0,0 +1,86 @@
# 智能体工作流质量改进:诊断、裁定与分期计划
状态:**已获实施授权(2026-10-09,六项裁定见 §3)**;owner:weichen;
主任务:Dev 仓 `feature/agent-quality-phase01`(Phase 0+1 首个增量)。
撰写日期:2026-10-09。诊断依据:dev 实例真实运行转录(PG pg1 13 次 run + 旧
SQLite 53 次,共 62 条助手消息、263 次工具调用;模型 MiniMax-M2.7-highspeed,
direct 入口),对照本机 ZCode v3.14.3 源码与 codex/opencode/pi/Kimi CLI/Cursor
公开资料(2026-10-09 抓取)。诊断证据级别 SELF(本地观察与复算)。
本文件是该改进方向的主记录:目标、裁定、分期与验收以本文为准;
各阶段实施细节与结果记在对应任务卡,不回写复述。
---
## 1. 问题陈述
与 codex / pi / kimi / cursor / zcode / opencode 相比,本系统直连对话路径在
「工作细致、详细、完善」上存在可测差距。真实转录中定位到的失败链(均已复核):
1. **信息逐轮衰减**:跨轮上下文由界面消息有损重建——工具结果只回放 160 字
摘要、参数截断、二进制原件占位、思考内容丢弃;上传材料只在「本轮」注入。
后续轮次在摘要的摘要上工作,细节靠编造补齐(实测:「详细方案」中 85%/95%
准确率、8000–10000 字等数字在原始转录中不存在;日期星期错配)。
2. **修改模型倒挂**:交付物由一次性整段生成脚本产出(docx 无源文件),任何
修改=整篇重写重跑(实测一轮 24 次工具调用 9 次失败、6KB 脚本重跑 8 次)。
3. **检查失效且误报**:终轮评分的 lint 子进程无 PYTHONPATH 而静默失败(同一
docx 带 PYTHONPATH 9 个 error,评分器里 0 个);终轮检查重评已被替代的旧
工件导致死循环;可读性规则把表格单元格「项目概述撰写」判成栏目式标题;
历史工件渲染格式 `[产出工件 …]` 与伪造标记检查同形,模型照抄即被拦。
4. **最终答复与事实脱节**:composeFinal 只见 160 字摘要,产出了不存在的章节
列表和「我会调整后再提交」类未兑现承诺。
5. **过程性根因**:无任务级评测。任务卡验证=单测+mock,真实模型被列为非目标;
evalgold 只有 1 条规则,eval/ab.go 无运行记录。
## 2. 根因归纳(按影响排序)
1. 上下文保真度不足(影响最大)——产品没有把内核的追加式会话记录当权威上下文。
2. 工具与修改流程不适合交付物领域——没有「读源码→精确改→编译→看诊断」的对应物。
3. 质量控制是事后把关而非让模型自己做好,且把关本身不可靠。
4. 行为指引薄弱(何时建计划/失败换路/坚持到完成),工具描述只有一行。
5. 没有以真实模型结果为准的评测回路。
内核能力(read/edit/grep/glob、超长落盘、压缩、会话树)大多已在 agent-native
就位,问题主要在产品组装层(`modules/backend/internal/runtime|ctxasm|quality`)。
## 3. 用户裁定(2026-10-09,授权实施)
| # | 事项 | 裁定 |
|---|---|---|
| 1 | 直连对话开放工具与能力 | **开放,尽可能实现和开放所有的工具和能力**(推翻 2026-09-30 预览案「fs 工具实现但不默认提供」的默认关闭裁定) |
| 2 | 跨轮上下文以内核会话记录为准 | **同意**(重新生成/排除/回滚映射为会话树分支操作) |
| 3 | 交付物 Markdown 源码化 + 构建器 | **可以,但必须保留生成各类文件(docx/xlsx/pptx/pdf/图片等)的能力** |
| 4 | composeFinal 与关键词评分降级 | **按建议**:Phase 2/3 落地后 composeFinal 默认关闭(仅异常兜底);关键词评分移出拦截路径、只作评测指标 |
| 5 | 更强模型评测 | **可以,预算无上限** |
| 6 | 行为面改动须附真实模型评测 | **按建议**:写入 Dev 仓 policy/delivery.md |
## 4. 分期与验收
每阶段独立任务卡 + PR;改动提示词/工具/上下文/门禁的 PR 须附 eval/live
真实模型前后对照(裁定 6)。评测口径:`modules/backend/eval/live/`
(golden 任务集 + 指标:工具失败率、整篇重跑数、是否读原件、计划使用率、
检查误报/死循环、终轮与工件一致性、未核实数字)。
| 阶段 | 内容 | 验收 |
|---|---|---|
| **P0 评测基线** | eval/live 运行器 + tasks.json + 指标报告;before/after 各跑一次 | 基线报告能复现 §1 失败链 |
| **P1 缺陷修复** | §1.3 的检查失效与误报七项 | 检查误报清零;两条死循环链消失 |
| **P2 上下文保真** | 内核会话记录为权威跨轮上下文;窗口按模型真实窗口(token,实测校准);被清结果落盘留路径+预览;上传材料持久 `inputs/*.md`;交错思考模型回传 `<think>`;接上 9 段压缩模板 | 后续轮 100% 读原件;「更详细」不整篇重写;未核实数字显著下降 |
| **P3 工具开放 + 交付物源码化** | 内核 read/edit/grep/glob 接入直连(沙箱=会话工作区);交付物 = Markdown 源文件 + `housestyle.build` 确定性渲染(自动 lint,诊断指向源行);写入即诊断;`housestyle.api` 签名查询;保留 run_python 全类型生成能力(裁定 3) | 工具失败率 ≤8%;修改请求整篇重建 ≤1 次;API 幽灵调用消失 |
| **P4 行为指引** | 基座提示词「工作方法」段(建计划/失败两次换路/先查 API/坚持到完成/结论先行终稿);重写高频工具描述;计划提醒常开;按模型分提示词(先 MiniMax) | 多步任务计划使用率 ≥80%;终稿承诺未兑现清零 |
| **P5 门禁精简** | 拦截型检查只留硬事实(工件存在/lint 零 error/引用存在);新增事实一致性检查(数字/日期/人名回溯材料);验证器按需触发+异模型 | 检查死循环 0;未核实数字显著下降 |
| **模型 A/B** | 同一评测集换更强主模型对照(裁定 5,无预算上限) | 区分模型与 harness 各自的贡献,输出选型建议 |
## 5. 不在本文范围
- 操作系统级沙箱、多租户隔离策略(沿用现状与既有声明)。
- 小队(squad)路径的同批改造——先修直连主路径,squad 经 A/B 对照跟进。
- AGENTS.md/DESIGN.md 层面的仓库治理变更。
## 6. 记录与证据
- 诊断细节与复现命令:2026-10-09 会话记录(本文件只留结论与指针)。
- before 基线:`eval-live/before-main-1ebd46e.json`(任务卡 evidence 引用)。
- 关联:[Agent 栈总案](2026-10-02-agent-stack-completion.md)(库分期不受本文影响)、
[文件预览与保留能力](2026-09-30-file-preview-and-reserved-capabilities.md)(其 §2.3
的 fs 工具默认关闭裁定被本文裁定 1 推翻,以本文为准)。
@@ -0,0 +1,124 @@
# EXT 申请包 · G1:回合内共享检索、命令即工具、项目列表共享与执行后端接口(B-1/B-2/B-3 + B-5②)
状态:**proposed(2026-10-09)**;owner:weichen;需求依据 = RDM 评审与实施方案 V1.1
(`Agent-System-RDM docs/proposals/2026-10-09-V22-review-and-implementation-plan.md` §3.3/§3.4/§5.3,
owner 2026-10-09 Q1–Q8 批复;Q4=方案 A,Q2=暂时禁用+隔离档)。
撰写日期:2026-10-09。配套:[扩展面兼容与演进](2026-10-01-extension-compat-policy.md)、
[EXT 申请包 C1–C7](2026-10-08-ext-c1-c7-rdm-shared-contracts.md)(EXT-6…EXT-12)。
## 0. 总则
- 本包四条申请按既有序列顺延 **EXT-13…EXT-16**;最终编号以受理评审为准。
- 分期(对齐 RDM 方案 §5.2):B-1(EXT-13)与 B-3(EXT-15)为 **G1 序 4 的硬前置**
(成员智能体看不到项目知识 = R04 路径不通);B-2(EXT-14)为 G2/V0.1 端到端的硬前置
(对话入口无法触达 `rdm.*`);B-5②(EXT-16)为 G2-8 原型的前置。
- SurfaceVersion 影响:EXT-14 触及 `pkg/extension` 导出面(命令规格新字段)→ 需升
SurfaceVersion 并同步 surface.txt;EXT-13/15/16 为 internal 行为扩展,不改导出面
(EXT-16 若只加 `Options` 字段则属加法,见 §4)。
## 1. EXT-13(←B-1)回合内共享检索:智能体的检索遵循项目共享授权
- **场景**:RDM R04/R05——成员 B 的智能体在**回合内**(ReAct 循环的 `knowledge_search`
工具与自动知识注入)必须能检索到成员 A 已发布的项目知识;现状只能看到本人 owner 的
知识。HTTP 面(`/api/…/search`)已走 `RetrieveShared`(EXT-8 余项,Dev #269/#273),
但回合内两条路径仍是 owner-only:
- `internal/runmgr/runmgr.go:726-736` `runCtx.Retrieve` → `retriever.Retrieve(ctx, c.run.Owner, q)`(owner-only);
- `internal/app/hostcaps.go:64-66` `hostcaps.search`(进程内 `Host.KnowledgeSearch` 底座)→ `pipe.Retrieve(owner)`(owner-only)。
结果:同一个成员,页面搜索能看到共享知识,**智能体对话里看不到**——「所有用户的智能体
应当能够看到项目级的共享信息」(用户补充材料 §2树1图)走不通。
- **希望公开面**:组合根把 EXT-8 适配器(`Server.ProjectAuthz` 同款判定函数)注入
runmgr 与 hostcaps:两条检索路径在本人 owner 两路(全文+向量)如常之外,追加
「经适配器放行的共享两路」——与 HTTP 面 `rag.Pipeline.RetrieveShared` 完全同一实现与
同一授权判定(复用 `sharedVisible`/`SearchKnowledgeShared`,不另造第二套语义)。
进程内 `Host.KnowledgeSearch` 同步走共享版。
- **为何组合不出**:`RetrieveShared` 已存在,但 runmgr/hostcaps 没有适配器的引用——
适配器槽在 `internal/app/hostcaps.go` 的 `projectAuthz`,由组合根在装载扩展后注入
Server;runmgr 的构造早于扩展装载,且 `Manager` 无该依赖。下游无法经任何公开面把
「按 owner 判定共享」的回调递进回合执行路径。
- **兼容方式**:适配器未注册=现状(本人两路);注册后共享命中追加、RRF 融合。
不动 `pkg/extension` 导出面,SurfaceVersion 不变。
- **验收**:同项目两人+另一项目一人(RDM 成员表桩):成员 B 发起对话,模型经
`knowledge_search` 命中 A 已发布的知识切片(共享命中出现在工具结果);未授权者与
跨项目者为零命中;`Host.KnowledgeSearch`(hostapi 与进程内两形态)行为一致;
未注册适配器时与现状逐字节一致(回归)。
## 2. EXT-14(←B-2)插件命令即工具:`exposeAsTool`(Q4 方案 A)
- **场景**:RDM V0.1 端到端(C-11)要求「从空项目创建到生成项目建议书,全对话完成」;
RDM 的全部能力(两树一网读写、上传、起草、追溯查询)都登记为 `rdm.*` 命令,而智能体
回合的工具清单里没有任何插件命令——模型既不能检索笔记树,也不能发起 `rdm.draft.compose`。
U0 §3.3/方案 §3.3 裁定走**方案 A**:命令经命令总线暴露为工具,四入口同源
(button/card/slash/chat)自然延伸出第五入口「智能体」。
- **希望公开面**:命令规格(`agentcommand.CommandSpec` 经 `Registry.AddCommandHandler`
登记的 JSON)新增可选字段 `"exposeAsTool": {"risk": "low|medium|high"}`(缺省不暴露)。
runmgr 装配工具清单时,把带该字段的命令投影为工具描述(名称=命令 id,参数=命令
paramsSchema);模型调用该工具时,**不分发为工具执行,而是构造 `cmdbus.Request` 经
命令总线分发**(Entrance="agent",Actor=会话 owner,ConversationID=当前会话)——
同一审计信封、同一 HITL 内联确认卡(`risk=high` 或命令规格要求确认时照常弹卡)、
同一危险分级。
- **为何组合不出**:`Registry` 只有 AddSkill/AddCommand/AddCommandHandler/Note/
RegisterResourceAuthorizer;工具注册表(`tools.Registry`)与命令总线之间没有任何
桥接;MCP 通道(方案 B)绕过命令总线与确认卡,与 SR-E6/HITL 门禁冲突,已被 Q4 否决。
- **兼容方式**:`pkg/extension` 命令规格加可选字段=导出面加法,SurfaceVersion 升
(建议 2.6.0-experimental)+ surface.txt 同步。缺省(不写 exposeAsTool)=现状零差异。
- **验收**:带 `exposeAsTool` 的 `/rdm.ping` 出现在模型工具清单且调用返回回执,
审计信封 Entrance="agent" 与按钮入口同源;`risk=high` 的暴露命令在对话内触发内联
确认卡;不带字段的命令不出现;RDM 侧 `rdm.trace`/`rdm.baseline` 等只读命令可在
回合内被模型调用(成员 B 场景结合 EXT-13)。
## 3. EXT-15(←B-3)项目列表与建会话的共享放行(SR-G10)
- **场景**:RDM R03——成员 B 被加入项目后「界面自动出现该项目,可直接开始工作(新建
对话)」。基座 `projects` 表按 owner 硬隔离(`store/projects.go:22-34`):
- `GET /api/projects`(`server.go:438-441` `listProjects`)只返回本人 owner 的项目——成员看不到共享项目;
- `POST /api/projects/{id}/conversations`(`store/projects.go:124-130`)校验「项目属于请求者」——成员在共享项目下建会话被 404。
EXT-8 已放行的入口(工件三入口、SSE 项目流、检索)不含这两处。
- **希望公开面**:这两处入口在 owner 自有判定未命中时咨询同一 EXT-8 适配器:
`listProjects` 追加经适配器放行的项目(去重、标注 `shared: true` 字段供前端区分
展示);`CreateConversation` 的归属校验放宽为「本人自有 **或** 适配器放行」。会话
本身仍归属创建者(owner 不变),只是创建权扩展到成员。
- **为何组合不出**:`ListProjects(owner)` 的 SQL 按 owner 过滤,无回调;建会话校验在
store 层硬编码。与 C4(EXT-8)同因:纯下游无法改变基座 owner 过滤。
- **兼容方式**:未注册适配器=现状;放行结果带审计。internal 行为扩展,SurfaceVersion
不变。
- **验收**:成员 B 的 `GET /api/projects` 含共享项目(shared 标注)且不含未授权项目;
B 在共享项目下 `POST conversations` 成功、会话 owner=B;撤销成员后两者立即拒绝;
项目拥有者视角无任何变化。
## 4. EXT-16(←B-5②)执行后端接口:隔离执行档(Q2 阶段二,G2-8 原型)
- **场景**:Q2 裁定「暂时禁用,但后续执行任务时肯定还是要代码执行能力」。v0.2.0 已
交付启动级硬关闭(`Options.DisableExecTools`,Backend #110);隔离档是恢复执行能力的
路径:执行的是**模型生成的代码**,须按不可信代码对待(方案 §3.4):只读系统根、仅挂
会话工作目录与技能目录、默认无网、cgroup 资源限额、硬超时、按项目 venv 只读挂载;
本机已实测 `bwrap --unshare-all` 可用(无特权 userns 被 AppArmor 限制,bwrap 专门
策略放行)。
- **希望公开面**:`internal/tools/exec_interpreter.go` 的进程启动点抽象为「执行后端」
接口(`ExecBackend`:`Run(spec) → result`),启动参数选择后端:
`ExecBackend = direct | sandbox`(缺省 direct=现状)。sandbox 后端以 bwrap 实现
(资源限额经 systemd-run/cgroup v2)。`extension.Options` 增加
`ExecBackend string`(加法;空=direct)。`DisableExecTools` 保留为最终兜底,优先级
高于后端选择。
- **为何组合不出**:现实现直接 `exec.Command`(`runInterpreter`),无后端抽象;沙箱
参数组装(bind 清单、网络命名空间、限额)属平台面,下游插件无法注入。
- **兼容方式**:缺省 direct=逐字节现状;`Options` 加字段=SurfaceVersion 升。RDM 在
G3-7 才切换 sandbox;G1–G2 期间维持 `DisableExecTools=true`。
- **验收**:sandbox 后端下逃逸负例全部被拦——读 `~/.secrets`、读其他会话目录、访问
外网、fork 炸弹、超时不可杀均为拒绝/杀灭;会话工作目录内 docx 生成任务成功且产物
可登记;direct 后端与现状一致(回归)。
## 5. 不在本申请包(显式排除)
- EXT-6/7/10/12 完整形态:仍在 C1–C7 包(EXT-6…12)受理轨道,节奏不变。
- 提示词随执行可用性切换(`base.deliverables` 在禁用期的降级文案):基座自有任务,
不占 EXT 编号。
- UI 仓 nightly runner 修复:与代码无关(rolldown 原生绑定缺失),另立任务。
## 6. 受理建议
| 申请 | 内容 | 优先级 | 兼容 |
|---|---|---|---|
| EXT-13 回合内共享检索 | G1 序 4 硬前置(R04/R05) | **P0 最先** | 不变(internal) |
| EXT-15 项目列表/建会话共享 | G1 序 4(R03/SR-G10) | P0 | 不变(internal) |
| EXT-14 命令即工具 | G2/V0.1 端到端硬前置(Q4 方案 A) | P1 | 升(加字段) |
| EXT-16 执行后端接口 | G2-8 原型(Q2 阶段二) | P1 | 升(加字段) |
@@ -0,0 +1,242 @@
# 子代理分工、模型分级路由与任务沙箱:调研与方案
状态:**proposed(2026-10-10)**;owner:weichen;方向来源:用户 2026-10-10 会话
(①充分利用子代理;②复杂任务用 dmxapi 的 Sonnet/Opus,常规任务用 MiniMax-M2.7-highspeed;
③记录并规划毫秒/亚秒级任务沙箱,**暂不实现**;④对「体感不如 codex/claude code/kimi」的判断)。
本文是这四件事的主记录;未经评审的部分一律为「待确认」,不因被记录而视为已批准。
依据级别:本机实测为 **SELF**(命令与输出见任务卡 evidence 与 §7 复现脚本);外部资料为
一手页面抓取(访问日 2026-10-10,URL 在 §8);推断明确标注「推断」。
## 0. 结论先行
1. **方向对,落点要改。** 子代理只在「读多写少、可并行、输出冗长」的环节净收益为正;
交付物的写作与关键决策保持单线程(单一写者)。把复杂任务**整单**交给 Claude,会把最贵、
最慢的模型放在 token 最多的工具循环里——A/B 里 opus-5-5 耗时约 4.7 倍、工具失败更多。
证据更支持**按阶段/角色路由**:Claude 只做 token 少、杠杆大的环节(复杂任务的规划简报、
终稿综合、独立核查),检索、阅读、抽取、跑 Python、排版留给 MiniMax。
2. **「体感差」不能主要归因于 MiniMax。** 同一模型换 harness 的分差与相邻代际换模型同量级
(MiniMax-M2.1 换 harness 差 7.0 分;同 harness 换成 Opus 4.5 差 3.9–7.4 分 [H11])。
本机实测还发现两处直接拖累体感的协议层问题(§1.2),应先修再下结论。
3. **「编造数字」更像模型属性,但可以用机制压住。** 同 harness 下 7→0 支持这一点;但
24 个相关观测里「0 处」的 95% 置信上界约 12.5%,不足以宣称清零(§1.3)。数字溯源闸门
比换模型便宜,且两者可叠加。
4. **沙箱:现在的需求表述里有三处需要修正**(§4.1)——overlay 不是快照、快照只是把冲突
推迟到合并、「立即销毁」必须先留存审计材料。修正后,本机条件下 **bwrap + 冻结基底 +
overlay** 能做到约 7ms 启动(实测),足够作为默认档;microVM 档只在需要更强隔离时启用。
## 1. 现状核实(SELF,2026-10-10)
### 1.1 已有能力(源码,backend `421fee8`,其 deps/agent-native `3b92cb6`)
| 能力 | 现状 | 位置 |
|---|---|---|
| 子代理委派 | 模型可调 `delegate_task`,参数 `agent∈{general,explore,research,plan,verify,writer}`、`task`、`background`;深度 1(子不能再派) | `internal/runtime/llm.go:310`、`internal/runtime/llm_direct.go:668`、`internal/runmgr/runmgr.go:1462` |
| 子代理模型 | 由画像 frontmatter 决定:`writer` 用 `chat` 档,其余 7 个用 `lightweight` 档;调用方**不能**指定模型 | `internal/prompts/library/agent-sub-*.md`、`internal/runtime/sub_loop.go:48` |
| 模型档位 | 只有两档:`models.chat` 与 `models.lightweight`(轻量档空则回落主档),另有 `models.chat.fallbacks` 回退链 | `internal/runtime/model.go:54,84`、`internal/settings/settings.go:73-84` |
| 评审/验证 | `review_findings` 并行 1–3 个审查子代理再逐条验证;验证子代理输出 `VERDICT` | `internal/runtime/llm.go:311`、`agent-sub-verify.md` |
| LLM 协议 | 后端恒走 OpenAI chat 协议(`openLLMClient` 不设 `Protocol`);内核已实现 Anthropic Messages 客户端(含 `cache_control`)但**产品未接线** | `internal/runtime/llm.go:491`、`deps/agent-native/runtime/llm/anthropic.go` |
| 工具并行 | 内核支持 `ExecParallel`,直连路径未设置 → 默认**串行**执行同一回合的多个工具调用 | `deps/agent-native/runtime/loop.go:203,596`、`internal/runtime/llm_direct.go:268` |
| 执行隔离 | EXT-16:`run_shell`/`run_python` 可选 bwrap 档(断网、只读根、tmpfs 遮蔽 `/home` `/tmp`、仅会话目录可写、ulimit、超时杀命名空间);缺省 direct | `internal/tools/exec_backend.go` |
### 1.2 本机实测发现的问题
| # | 发现 | 影响 | 证据 |
|---|---|---|---|
| F1 | **子代理结论被压成 160 字**:直连路径 `delegate_task` 只把 `res.Summary` 回给父,而 `Summary` 是子代理全文的前 160 字(全文在 `res.Text` 里,被丢弃) | 子代理做完的检索/调研结论,父代理只看到开头一句;这与「上下文防火墙」的用法正好相反,也会诱发父代理凭摘要补细节 | `llm_direct.go:697-700`、`runmgr.go` finishChild(`Summary = truncate(fullText, 160)`) |
| F2 | **MiniMax 思考内容混在正文流里**:OpenAI 兼容流式接口默认把思考放进 `content` 的 `<think>` 块;后端在回合结束时才剥离 | 回合进行中前端收到的 `message.delta` 先是一大段思考原文,界面上「正文」区域先显示推理过程;思考计时也不准。官方提供 `reasoning_split=true`,实测开启后思考改走独立的 `reasoning_content`/`reasoning_details` 字段(3/3 次),TTFT 中位数 0.69s | `llm_direct.go:418-475`;probe2 实测(§7) |
| F3 | **sandbox 档下交付物生成跑不通**:python-docx/openpyxl/python-pptx 装在 `~/.local`,被 `--tmpfs /home` 遮蔽;`run_python` 描述里「缺失时 pip install --user」在断网档也失效 | RDM 计划 G3-7 切 sandbox 时,docx/xlsx/pptx 生成会直接失败;EXT-16 的「docx 生成任务成功」验收用的是 `printf` 桩,未覆盖真实库 | bench2/bench3 实测:EXT-16 同形参数下 `import docx` → ModuleNotFoundError;只读挂载依赖层后 `import docx, openpyxl, pptx` 成功 |
| F4 | **Claude 走兼容协议丢能力**:经 dmxapi/lmuai 的 OpenAI 兼容接口,4 个 Claude 型号都不返回思考字段;改走 dmxapi 的 `/v1/messages` 原生接口,第二次调用 `cache_read_input_tokens=5043`(缓存生效) | A/B 中 Claude 一侧每步按全价重算前缀、没有思考回传——47 分钟的耗时与工具失败部分可归因于此(推断,需日志确认) | anthropic_probe 实测(§7) |
| F5 | 直连路径工具串行 | 模型一次发出多个独立检索时逐个等待;4 个被测模型都能在一个回合里给出 2 个并行 tool_calls | probe 实测(§7) |
### 1.3 A/B 数据的正确读法
内部 A/B(`.task/model-ab/ab-opus.json` 对 `.task/phase05-eval/after-p5.json`,4 任务 6 回合):
MiniMax 编造数字 7、约 10 分钟、工具 97/失败 19;opus-5-5 编造 0、约 47 分钟、工具 99/失败 33。
- 样本小且相关:「0 处」的 95% 置信上界约 3/24≈12.5%(rule of three)[R39],不能宣称清零。
- 两侧都不在最佳状态:Claude 一侧走兼容协议(F4);MiniMax 一侧思考回传已实现
(`context.thinkReplay=auto`,`ctxasm.go:206`),但流式分流有 F2 问题。
- 速度:公开测速两者相近(opus-5-5 约 96 t/s [H25],M2.7-highspeed 官方约 100 TPS [H4];
本机短文本实测 48.9 对 50.7 t/s),所以 4.7 倍耗时更可能来自思考量、步数、重试与无缓存(推断)。
- Vectara 有据摘要幻觉率 M2.7 12.9%、Opus 4.6 12.2%、Sonnet 4.6 10.6% [R40]:「读懂原文」差距
不大;差距更可能出在「缺数据时去猜」(推断)。
### 1.4 其余现状(补充核实,SELF)
- 评测:`eval/live` 是 go test(env:`AGENT_EVAL_BASE` 实例地址、`AGENT_EVAL_TASKS` 过滤、
`AGENT_EVAL_TURN_TIMEOUT`),golden 任务 4 个(`tasks.json`);A/B 工具 `eval/ab.go`、`switch.go`。
- 打断/插话:有 `CancelRun`(停止按钮)与子代理中断续跑;**没有**「运行中用户输入排队、
当前工具结束后本轮送达」的 steering 队列(未找到实现)。
- 检查点:文件写入有改前快照日志(`runmgr/rewind.go` `NoteFileCheckpoint`)。
- 直连路径声明 13 个工具;提示词库 base-* 共约 6.7KB(base-deliverables 5.2KB 最大)。
## 2. 子代理分工:证据与方案
### 2.1 证据要点(外部,来源见 §8)
- **有益面**:广度优先检索(Anthropic 多智能体研究系统:主+子比单 Opus 高 90.2%,复杂查询
耗时最多降 90%,代价 token 约 15 倍 [R13]);上下文防火墙——18 个模型都随输入变长退化
[R14],子代理只回 1–2k token 摘要 [R17];fresh-context 审阅(Devin Review 均 2 bug/PR,
58% 严重 [R20])。
- **有害面**:写入耦合(并行写手风格冲突,「多代理贡献智能、写入保持单线程」是当前可行解
[R19][R20]);顺序任务 −39%~−70%、单代理基线 >45% 时加代理为负收益、无中心校验时误差
放大 17.2 倍 [R21];Claude Code 官方也建议迭代密集/共享上下文/延迟敏感时留在主会话 [R1]。
- **契约**:委派带目标/输出格式/工具面/边界/预算;回传浓缩结论 + 证据指针(文件/行),
大产物落盘只回引用 [R13][R17];限额:禁嵌套、并发 3–5、按复杂度分档 [R7][R13][R21]。
### 2.2 方案(增量,每项独立任务卡)
| # | 内容 | 验收(可判定) |
|---|---|---|
| D1 | **修 F1**:`delegate_task` 回传改为「子代理全文(上限 4k 字)+ 超长落盘 `subagent-result.txt` 只回指针 + 工件摘要行」——内核 `subagent.CapParentText` 已有此语义(backend 侧没走它) | eval/live 前后对照:父代理引用子代理结论的回合,其终稿中来自子代理的事实不再凭摘要补齐;单测覆盖超长截断 |
| D2 | **委派简报规范**:`delegate_task` 描述与 base-method 提示补「简报四要素」(目标、输出格式、工具/来源边界、完成判据),禁止把大块原文粘进 task | 提示词 diff + eval/live 计划/委派质量指标不回退 |
| D3 | **并行工具执行**:直连路径设置 `ToolExecution=parallel`(读类工具并发、写类串行的区分后续再议) | 同回合多工具调用 wall-time 下降(评测记录);事件序号仍单调 |
| D4 | **证据账本子代理**:research/explore 画像的输出格式改为「结论 + 原文引用(文件/页/行)+ source_id」,数字必须带 source_id 或 Python 计算 ID;终稿前由确定性校验拦截(接质量计划 P5 的事实一致性检查) | 编造数字在闸门处被拦且计数;子代理回传含引用率 100% |
| D5 | **角色×模型矩阵**(依赖 §3 R2):新增/调整画像的 `model:` 支持 `claude-*` 档;`writer` 保持 chat 档,新增 `composer`(终稿综合)与 `planner-complex`(复杂任务规划简报)画像走强模型档 | 画像 frontmatter 生效(SubagentSpec 解析);评测 A3/A5 组可配置 |
边界:子代理深度维持 1(现状即禁嵌套);并发上限维持产品侧 3;本方案不引入子代理间通信。
## 3. 模型分级路由:证据与方案
### 3.1 证据要点
- **整单路由不划算**:我们的 A/B 中 opus-5-5 耗时约 4.7 倍、工具失败 33 vs 19;公开测速两者
相近(§1.3)。Devin Fusion 的教训:强模型当全程主控只降本 33%,且「判断即交付物」的难题
上掉分 [R30][R31]。
- **按阶段/角色路由有正证据**:Sonnet 执行 + Opus 顾问 +2.7pp 且降本 11.9%;Haiku + Opus 顾问
在 BrowseComp 19.7%→41.2% [R32];Aider「强规划+便宜编辑」$13.29 得 64.0% 胜 o1 单跑 $186.5
得 61.7% [R12]。反例也存在(BrowseComp-Plus 上弱编排+强子代理更好 [R21])——**放哪一层必须
实测**,这正是 R3 实验的目的。
- **协议与缓存**:Claude 经 OpenAI 兼容层不回思考、不支持缓存、忽略 strict(官方文档 [H37],
本机 F4 实测缓存恢复);缓存按 tools→system→messages 前缀匹配,中途改工具集会整缓存失效
[R35][R36] → 模型切换只应发生在阶段边界,每角色固定 system/tools 前缀。
### 3.2 方案
| # | 内容 | 验收 |
|---|---|---|
| R1 | **Claude 原生协议接线**:`models.chat.protocol = openai-chat \| anthropic`(设置项),anthropic 时用内核现成的 `llm/anthropic.go` 客户端(base 指向 dmxapi `/v1/messages` 根),回传 thinking 块与 signature、挂 `cache_control` 断点、usage 记缓存读写(N3 计费拆分已有字段) | 同会话第二次调用 `cache_read_input_tokens>0`(SELF 复现 F4 的探针即验收集);thinking 块原样回传(删改会 400 [H24]) |
| R2 | **角色路由表**:设置新增 `models.<role>`(role∈composer/planner/verifier,缺省回落 chat 档);画像 frontmatter `model: <role或具体型号>`。默认建议:chat=MiniMax-M2.7-highspeed(不变)、composer=claude-sonnet-5(dmxapi,原生协议)、verifier=异模型(deepseek-v4-flash 或 claude)、复杂任务 planner=claude。Opus 系列留给最难任务手动指定 | 角色解析链单测;`models` 列表来自各端点 `/v1/models`(已核实 dmxapi 有 sonnet-4-6/sonnet-5/opus-4-6/opus-5-5 等,见任务卡 evidence) |
| R3 | **路由 A/B 实验**(扩展现有 eval/ab.go):组别 A0 全 MiniMax / A2 MiniMax+数字闸门 / A3 A2+Claude 终稿 / A4 A2+Claude 核查 / A5 Claude 规划+终稿+核查、MiniMax 执行;关键组加 Sonnet 变体。指标:无来源数字占比、含编造交付物比例、pass^3、p50/p95 耗时、分角色成本(含缓存)、工具失败分类。样本:30–50 任务×3 次(宣称编造率<5% 需 ≥60 份零编造样本 [R39]);<3pp 差异慎下结论 [R49] | 选型报告入任务卡,来源 GATE/SELF 标注 |
| R4 | **升级规则进 harness**(不依赖模型自觉 [R20]):连续 N 次工具失败、闸门拦截无来源数字、检索「未找到」三类事件触发 advisor 咨询或角色升级 | 评测中升级触发可观测;误升级率记录 |
运维事实(2026-10-10,SELF):lmuai 月额度已耗尽($1303.32/$1300,2026-11-05 重置)——R1/R3
的 Claude 侧建议先走 dmxapi(余额 ¥0.48,**需充值**,此前 A/B 卡估算全量对照 ¥50–100)。
## 4. 任务沙箱:记录与规划(**不立即实现**)
### 4.1 对原始思路的三处修正
1. **「overlay 隔离他人变更」不成立**:内核文档明确「下层在挂载期间被修改的行为是未定义的」
[S1];本机实测运行中的沙箱能看到下层后续修改(bench2 第 5 段)。**一致视图必须来自不可变
基底**(冻结副本或 git commit 物化),而不是叠在活动目录上的 overlay。
2. **快照只是把冲突推迟到合并**:Codex cloud/container-use/Cursor 都以 diff/PR 回收,冲突在
写回时用基线校验暴露 [S23][S28][S26]。「互不影响」的成本是写回时的三方合并与人工兜底,
设计里必须显式包含这一步,否则快照只是把失败挪到了最后。
3. **「立即销毁」与审计有张力**:销毁的应是计算资源与可写层;转录、diff、工件哈希、环境
清单要按 TTL 封存(Codex 删前存快照 [S23]、container-use 留命令史 [S28]、Cursor 留工件
[S26])。本仓的证据门禁(AGENTS.md)同样要求可复核输出——封存记录是硬约束。
另一处简化:硬链接克隆(cp -al)对「原子替换写入」是安全的(rename 换 inode),但对原地
写入会穿透(共享 inode)——本机实测(bench3 第 3 段)。只可作为「可检测」方案并配
sha256/mtime 清单复核,不可当作完整快照。
### 4.2 目标形态(任务工作区沙箱)
一次任务 = 冻结基底(会话/项目材料的不可变视图)+ 私有可写层 + 确定性依赖层 + 出口管控 +
回收(diff 与工件清单)+ 封存后销毁。智能体在其中任意折腾不破坏原始资料;产出经校验写回。
### 4.3 分层架构与延迟预算(本机实测,SELF)
| 层 | 机制 | 启动 | 适用 | 备注 |
|---|---|---|---|---|
| L0 进程沙箱 | bwrap(EXT-16 现状)+ 依赖层只读挂载 | **p50 7.3ms**(/bin/true,n=30);带 overlay 同为 ~7ms | 默认:解释器执行、交付物生成 | 修 F3 后 python-docx 全链路可用(import 60ms、1 页 docx 76ms) |
| L0.5 工作区沙箱 | L0 + `--overlay-src <冻结基底> --overlay <宿主upper>` | ~7ms(实测) | **多任务并发、需要一致基底与可回收 diff 时** | upper 即增量;快照语义来自冻结基底而非 overlay |
| L1 容器 | docker(overlayfs 驱动) | `run --rm` p50 142ms(redis:7 true,n=10) | 依赖隔离更强、镜像化环境 | 可加 warm pool 压冷启动;单机自托管无障碍 |
| L2 microVM | Firecracker/gVisor | 未实测;E2B 等托管方当前文档只称 "fast Linux VM"(量化数字已不可得,**未经证实**) | 不可信代码、最强隔离 | 本机有 /dev/kvm;引入与维护成本高,暂缓 |
支撑件(与层正交):
- **冻结基底**:git 材料以 commit SHA 物化(同 SHA 只物化一次、多任务只读共享);非 git 材料
用 cp -al + sha256 清单(写回前复核,变了即判基底失效)。git worktree add 实测 192 文件
11ms,overlay 直接盖工作树 ~10ms,两者都够快。
- **依赖层**:项目 venv / site-packages 只读 bind(实测修复 F3;RDM 方案 §3.4 原有此意)。
- **数据库**:SQLite 用 `VACUUM INTO` 取一致快照 [S32];PG 用零连接模板库克隆(PG18 XFS 上
有 CLONE,ext4 上是复制 [S11][S37]——量级以库大小为准,必要时评估独立 XFS 卷给 reflink)。
- **网络**:默认无网(现状);将来要放开时,按 sandbox-runtime 的形态做出口代理白名单 +
代理侧凭据注入(密钥不进沙箱)[S-RT]。
- **回收与写回**:沙箱退出后读 upper → 生成 `git diff --binary` 补丁 + 工件 sha256 清单;
写回用 CAS(目标当前哈希==基底哈希 → 原子替换;不等 → `git merge-file` 三方合并,冲突停给
人);写回前留前像。git 目标用 `update-ref <ref> <new> <old>`(本身就是 CAS)[S46]。
- **封存**:base SHA、补丁、工件哈希、转录指针、环境清单(依赖层版本)、退出码,按 TTL 留存
后销毁工作区目录。
### 4.4 与现网的关系
- EXT-16 sandbox 档是**进程级**隔离(单次命令);本节是**任务级工作区**,两者叠加而非替代。
首个落地形态:ExecSpec 增加工作区概念,sandbox 档把「会话目录 bind」换成「冻结基底 overlay
+ 私有 upper」。
- 已知缺口顺带记录:小队内核 ExecConfig、工作流 ScriptCommand 仍 direct(EXT-16 卡内遗留);
沙箱档覆盖它们是前置条件之一。
### 4.5 非目标与开放问题
- 不做多租户硬隔离与 OS 级安全声明(沿用质量计划 §5 边界)。
- 开放:是否划独立 XFS 卷换 reflink;gVisor 试用;warm pool 是否值得(L0 已 7ms,大概率不值);
Windows/macOS 不在范围。
## 5. 分期与验收总表
| 增量 | 内容 | 依赖 | 备注 |
|---|---|---|---|
| E1(P0 快修)✅ | F1 委派回传、F2 reasoning_split、F5 并行工具、F3 依赖层 | 无 | **已完成(2026-10-10)**:Agent-Native #42/#43/#44 + Backend #127 + Dev PR(任务卡 feature/agent-e1-parallel-fixes)。验证:F2 实测正文 0 泄漏、F3 sandbox 实测 IMPORTS-OK、F5 实测同回合并发重叠;eval 对照工具失败 19→11,未核实数字 7→12(E3 待做,见任务卡) |
| E2 | R1 Claude 原生协议 + 缓存观测 | E1(F2 同类协议改动可同批) | dmxapi 需充值 |
| E3 ✅ | D4 证据账本 + 数字闸门 | 无(接质量计划 P5) | **已完成(2026-10-10)**:Backend #130、Dev #311(任务卡 feature/agent-e3-evidence-gate)。标注感知豁免(分句判定)+ 两轮修正(变化才二拦)+ base.deliverables v6 数字溯源规则 + 子代理引用格式;eval 对照未核实数字 12→5(未标注 3)、工具调用 93→63 |
| E4 ✅(R4+账本) | R2 角色路由 + D5 画像 | E2 | **R4 与账本已完成(2026-10-11,MiniMax 版)**:Backend #131–#134、Dev #315(任务卡 feature/agent-e4-escalation-ledger)——滑窗失败提醒(端到端日志实证)、检索空命中提醒、source_id 账本(83 标记);Claude 角色档按模型指示暂缓;eval 四轮对照无恶化 |
| E5 ✅(R3 部分) | R3+R4 路由 A/B 与升级规则 | E2–E4 | **R3 已完成(2026-10-10,MiniMax 家族版)**:Dev #313(任务卡 feature/agent-e5-model-ab)三臂对照(hs / 全M3 / hs+M3轻量)——建议默认 chat=MiniMax-M3(未标注编造 1、失败最少、时长持平、单价减半);R4 升级规则仍待做 |
| S0-1 ✅ | §4.4 首个落地形态:冻结基底 overlay + CAS 回收 + 变更摘要(--exec-workspace overlay) | 用户 2026-10-11「继续完成下一步」 | **已完成(2026-10-11)**:Backend #135/#136、Dev #318(任务卡 feature/agent-s0-workspace-overlay)——bwrap 集成 3 + live 端到端(失败零落地/新增/修改/-ws 清理);S0-2(fs 联合读/任务级工作区/封存)待授权 |
## 6. 与既有规划的关系
- [智能体工作流质量改进](2026-10-09-agent-quality-improvement-plan.md):本文是其「模型 A/B」
结论的后续与 P4/P5 的延伸(D4 与 P5 合流;E5 继承 A/B 方法论并扩大样本)。
- [EXT 申请包 G1](2026-10-09-ext-g1-b1-b3-exec-backend.md):E1-F3 修复其 sandbox 档的交付物
缺口;§4 工作区沙箱是 EXT-16 的演进方向。
- [RDM 阶段总案](2026-10-07-rdm-phase-plan.md):G3-7 切 sandbox 前置依赖 F3 修复。
- Agent 栈 N2 WP5(子代理/后台派生):D1–D5 在产品层消费其内核端口,不改内核契约。
## 7. 本机实测的复现要点(SELF,脚本存于任务卡 evidence 同目录说明)
- 启动延迟:bwrap 命名空间 / +overlay / +持久 upper 各 30 次(bench.sh、bench2.sh §3)。
- F2:`reasoning_split=true` 前后各 3 次流式(probe2.py,观察 delta 字段分布)。
- F3:EXT-16 同形参数下 `import docx` 失败 vs 只读挂载 `~/.local/.../site-packages` 后成功
(bench2 §1–2、bench3 §1–2)。
- F4:dmxapi `/v1/messages` 连续两次调用,第二次 `cache_read_input_tokens=5043`。
- overlay 非快照:运行中沙箱读到下层并发修改(bench2 §5);cp -al 对 rename 安全、对原地写
穿透(bench3 §3)。
- docker 冷启动 10 次(bench.sh)。
## 8. 来源(除标注 SELF 外均为 2026-10-10 抓取的一手页面)
- [S1] Linux 内核 overlayfs 文档:https://docs.kernel.org/filesystems/overlayfs.html
- [S11] BoringSQL instant database clones:https://boringsql.com/posts/instant-database-clones/
- [S23] Codex environments/git-worktrees:https://learn.chatgpt.com/docs/environments/git-worktrees
- [S26] Cursor cloud agent:https://cursor.com/docs/cloud-agent
- [S28] container-use CLI:https://container-use.com/cli-reference.md
- [S32] SQLite VACUUM INTO:https://www.sqlite.org/lang_vacuum.html
- [S37] PostgreSQL 18 release notes:https://www.postgresql.org/docs/release/18.0/
- [S46] git-update-ref:https://git-scm.com/docs/git-update-ref
- [S-RT] Anthropic sandbox-runtime(Linux=bwrap+seccomp、网络默认全拒+代理白名单+凭据注入、
Apache-2.0):https://github.com/anthropics/sandbox-runtime
- [S-CX] Codex sandboxing(Linux 用 bwrap;read-only/workspace-write/danger-full-access):
https://learn.chatgpt.com/docs/sandboxing
- [H4] MiniMax 定价与速度:https://platform.minimax.io/docs/pricing/overview 、
https://platform.minimax.io/docs/guides/text-generation
- [H24] Claude thinking 块回传要求:https://platform.claude.com/docs/en/build-with-claude/extended-thinking
- [H25] Artificial Analysis(opus-5-5 96.4 t/s 等):https://artificialanalysis.ai/models/claude-opus-5-5
- [H37] Claude OpenAI SDK 兼容层限制:https://platform.claude.com/docs/en/api/openai-sdk
- [R1][R12][R13][R14][R17][R19][R20][R21][R30][R31][R32][R35][R36][R39][R40][R49] 子代理与
路由调研来源清单见任务卡「调研来源」节(Claude Code docs、Anthropic/Cognition 工程博客、
arXiv 2512.08296 等,URL 全量随卡保存)。
@@ -0,0 +1,253 @@
---
title: 多人协作存储与分布式演进方案 - Plan
type: refactor
date: 2026-10-11
execution: code
product_contract_source: ce-plan-bootstrap
---
# 多人协作存储与分布式演进方案 - Plan
修订:r1;确认状态:**已批准(2026-10-11 用户指示「按方案开始改造」)**;推进状态:方案获批发布,实施自首选增量 U1+U2 启动;业务实施/迁移/压测/部署仍为 NOT_RUN,逐项以实施任务卡为准。
需求来源:2026-10-11 用户要求完善 SQLite、PostgreSQL/pgvector、Redis 分工;随后确认“10–100 人,多人多项目多会话并行,未来支持多服务分布式”。
需求负责人:weichen;方案编制:Codex;实施及运维负责人:待指定。更新时间:2026-10-11(Asia/Shanghai)。
关联任务:Dev `.task/feature-collaboration-storage-plan.md`(方案任务,已发布);后续 U1+U2 实施由独立任务承接。
正文中的源码路径以 Agent-System-Dev 为根;工作包中的 `internal/` 简写指 `modules/backend/internal/`;文内相对链接以本文为根。
## 1. 推荐结论与边界
团队部署以 PostgreSQL 作为共享业务状态的唯一权威,pgvector 承担数据库内向量查询,Redis 承担可丢失的缓存与限流。SQLite 保留给个人嵌入式使用、离线工具及可以重建的本机状态,不承载团队共享写入或跨主机运行所有权。
第一阶段交付单应用实例的可靠团队部署;第二阶段补齐执行所有权、持久命令、共享工件与事件追赶,再开放多个应用实例和 Worker。通过接口和数据归属为服务拆分做准备,当前不要求微服务化、Kubernetes、分片或引入另一套消息中间件。
“不会出现错误”不能成为无条件承诺。工程目标是:在定义的故障模型下,不静默丢失已确认数据、不覆盖他人的并发更新、不产生未经识别的重复副作用、不越权;异常可发现、可追踪、可恢复。冲突可以返回明确的 409,超载可以返回 429,依赖故障可以返回 503,它们必须有清楚的重试语义。
本方案是[双档存储 D17–D24](2026-09-30-dual-tier-storage-and-multitenancy.md)的候选增补,批准前不改写原已采纳决策。拟调整的是团队部署的默认与必需条件,以及 D24 原来排除的跨实例执行能力;保留个人 SQLite 档、非容器部署、现有检索契约与序号语义。
## 2. 当前事实与差距
源码调查基线:Dev `73c901fa341a3653d6e0826d0da3497507995482`,Backend `eda90ed310f37f7352e24a8096d932dbf320cd73`,Documents `9a46785ee0f19540c79f83b4e4544572b5217ac2`,Agent-Squad `490b0b2dfa1567cb16ff7a7230fd78e52bc2938e`,Agent-Native `c348441691e8bdab784a03aa9be64d53dacff341`。
来源均为 **SELF·源码/本机只读观察**;没有真实并发故障重放。命令、对象 SHA 和原始输出见[调查收据](../records/2026-10-11-collaboration-storage-audit-receipt.json),启动收据见任务卡。
| 对象 | 当前可核实事实 | 方案含义 |
|---|---|---|
| Dev 运行实例 | `runtime/dev/bin/agent-system`、8788 端口打开 `runtime/dev/data/agent-system.db` 及 WAL;二进制 build revision 为 `017b40f76156905eabe8323ae2bfe800f47fb8bd` | 当前服务确实使用 SQLite;运行版本与上述源码基线不同,源码缺陷不冒充已在该二进制重现 |
| 本机安装 | Homebrew 列出 PostgreSQL 18.6、pgvector 0.8.7、Redis 8.10.2 | 仅证明安装;目标 PG 实例、目标库扩展启用、角色与可用性未验证,不据此批准切换 |
| Backend 存储 | `internal/app/app.go:105` 按 DSN 选 PG,否则 SQLite;`store/store.go:50` SQLite WAL、busy_timeout=5000、单连接;PG 池固定 16/8 | 无需重写全部存储层;需团队档显式配置、连接预算和真实 PG 测试 |
| 已有一致性基础 | `store/dialect.go:49` PG 事务咨询锁;消息 seq 唯一约束、requestId 去重和 HITL 条件更新 | 保留并扩展,不把已有能力列作全新建设;这些能力不等于跨实例运行互斥 |
| Run 所有权 | `runmgr/runmgr.go:1113` 用进程内 map 占用会话;`store/messages.go:120` 直接创建 run | 两实例可能各自认为空闲;需共享占用和有效执行代次 |
| 取消与审批 | `runmgr/runmgr.go:1326,1422` 依赖本实例 active 句柄;HITL 先落卡,再查句柄 | 请求落在非执行实例时存在命令不生效或卡已处理但执行未被唤醒的风险 |
| 跨实例事件 | `pgbus/pgbus.go` 每批通知末 seq,Listener 只 `EventAt` 取末条;断线后对原连接继续 Wait | 静态可见批内中间事件遗漏及重连缺口;`server/server.go:376` 的 SSE 重放/去重不能修复已越过的游标 |
| schema 迁移 | `store/migrate.go:migrate` 每次 Open 执行迁移;DDL 事务提交后另行插入迁移版本 | 并发启动与两步间崩溃存在风险;切 PG 前先改为单迁移入口与原子版本登记 |
| 向量查询 | `store/vectors.go` 读取 BYTEA,应用内计算余弦;`migrate.go` 未把向量列建成 pgvector 类型 | 安装 pgvector 不代表启用;复用 RRF 管线,分阶段改为数据库 top-k |
| Redis | `ratelimit/ratelimit.go:65` 独立 INCR/EXPIRE,EXPIRE 错误未处理;请求异常默认放行;app 默认限额 0 | 当前只是可选共享限流,需原子窗口、鉴权/TLS配置、容量保护与可观察的降级 |
| 本地文件 | `blobstore` 默认本地目录;Backend Squad 入口未传 Store,回落 FileStore;checkpoint 写 `.agentx/checkpoints.jsonl` | PG DSN 不会迁走这些状态;工件、执行工作区、checkpoint 要分别处理 |
| 独立 Squad | 独立 CLI 使用 `adapters/sqlite/runstore.go` 的 `orchestration.db`;这不是 Backend 实际接线 | 不把迁移该 SQLite 文件当作 Backend 分布式改造完成 |
| 验证 | PG/Redis 集成由 `AGENT_PG_TEST_DSN`/`AGENT_REDIS_TEST_ADDR` 控制;当前 CI YAML 没有显式准备这两种服务 | 缺变量会跳过关键测试;CI 通过不能自动证明真 PG/Redis 或双进程链路通过 |
已有规划衔接:Q1 已记录 Store 收敛但仍留 platform→squad 队列桥接等余项;N3 原案与归档卡的 JournalOnly 落地不同,应以当前接线确定权威。共享权限沿用 EXT-8 授权适配器与 RDM 的成员/角色职责,不新建第二套成员模型。历史报告用于定位,不能替代当前验收。远端调查发现 Backend [#135](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Backend/pulls/135) 正在增加 S0 工作区 overlay/frozen base/CAS apply;U5 必须复用并核对其契约,不能另造冲突的工作区实现。该 PR 尚未作为本案已合入依赖。其他被查仓当时未返回 open PR,详见收据;实施启动时重新核对。
## 3. 需求与可判定结果
| ID | 需求 | 优先级 | 验收条件 |
|---|---|---|---|
| R1 | 支持 10–100 人、多项目、多会话并行 | P0 | 在固定环境与下述负载下完成真实业务链路;容量结果绑定组件 SHA、配置和数据集 |
| R2 | 团队数据有唯一共享权威 | P0 | 团队档无有效 PG 配置或连接失败时拒绝就绪,不自动建立本地 SQLite 继续服务 |
| R3 | 并发提交和编辑不静默覆盖 | P0 | 同 requestId 返回同一逻辑结果;同版本竞争写入最多一个成功,其余明确冲突;不同会话并行 |
| R4 | 跨实例运行与命令可恢复 | P1,多实例前置 | 同会话主 run 单一有效 owner;旧 epoch 写入拒绝;任意 API 实例取消/HITL 均到达执行者 |
| R5 | 事件有序、可追赶 | P0/P1 | 批处理、掉通知、重连和慢消费者后可按游标补齐全部已提交事件;不因收到末 seq 而跳过中间事件 |
| R6 | 项目权限在所有数据入口一致 | P0 | 读取、写入、SSE、检索、下载与工具入口均校验当前授权;撤权后的新请求无泄漏 |
| R7 | 数据和运行状态可恢复 | P0 | SQLite→PG 演练无遗漏;备份恢复含文件与权限映射;未知副作用停在可见恢复状态,不盲目重试 |
| R8 | 高负载下有界且可观察 | P0 | 队列、活跃 Agent、DB 连接、工具/模型请求都有上限;超载行为、等待时间和错误分类可观测 |
| R9 | 向量检索随数据增长扩展 | P1 | 数据库 top-k 与原精确基线对照;授权过滤正确;模型/维度/版本不混用;ANN 有召回率与延迟证据 |
| R10 | 支持未来多服务部署 | P1/P2 | 先验证两个真实 API/Worker 进程共享服务;业务表有模块 owner,后续服务通过契约操作他方数据 |
R1 的人数及未来分布式方向由用户确认;下列量化指标是**建议的验收合同,待批准,不是当前性能结论**:
- 基准环境建议:Linux、8 vCPU、32 GiB RAM、NVMe,记录 PG/应用的实际配额与网络;两实例验证分为同机多进程和后续双主机两种证据。
- 阶梯负载:10→30→100 在线用户,最多 300 SSE 连接;初始全局活跃 Agent 执行预算 20(包含子代理)、每用户 2、索引任务 4。待执行队列最多 200,按用户/项目公平分配。已接纳 run 数与实际执行许可分别计数;父 run 等待子代理/HITL 时让出执行许可、保留会话所有权,恢复执行须重新获许可,避免额度用尽时父子互等。以上阈值由实测调整。
- 数据量建议:100 万条消息/事件量级、10 万知识分块、多个项目与交叉授权用户;另测 100 万分块扩展档。记录数据分布与热点,不能只测空库。
- 非模型普通 API:P95≤300ms、P99≤1s;SSE 持久事件可见延迟 P95≤1s;非预期失败率≤0.1%。预期限流/冲突单列,不能排除后不报告。
- 先跑 60 分钟稳定负载,再跑 2 小时混合长稳;持续记录实际成功吞吐、排队、CPU/内存/IO、连接等待和模型配额。模拟模型用于容量基线,另保留真实模型少量全链路验证。
- 数据正确性不变量在全部测试中零违反。数据库/Worker 故障恢复建议 RTO≤15 分钟;灾难恢复建议 RPO≤5 分钟、RTO≤60 分钟。主机/存储全毁不在单机“零丢失”承诺内;若要求该场景 RPO=0,需另行批准同步复制、故障域与可用性取舍。
## 4. 技术决策与目标结构
KTD1. **区分 local 与 team 部署档。** local 保留 SQLite;team 强制 PG、认证、独立实例 ID、受控工具执行和启动诊断。PG 暂时不可用时返回未就绪,不切回 SQLite。个人档不因浏览器人数变多而自动升级。
KTD2. **PG 保存持久业务与调度事实。** 用户/项目/会话/消息/卡片/运行/幂等记录/命令/事件/checkpoint 元数据以 PG 为准。Redis 不成为任务完成、审批通过或永久幂等的唯一证据。初期使用同一 PG 实例和有归属的逻辑表/角色;后续拆服务时明确表的唯一写入模块,避免所有服务无约束共享整库。
KTD2 的全部状态收敛以 U5 完成为界;此前有明确的单实例过渡边界,不能提前把本地状态当成缓存:
| 数据 | 首期单实例团队版 | U5 后多实例版 |
|---|---|---|
| Backend 业务记录、幂等与恢复隔离标记 | PG 权威 | PG 权威 |
| 尚未改造的 Squad FileStore、checkpoint、续跑标记 | 唯一实例的本地权威文件,联合备份,禁止跨机接管 | 版本/归属元数据在 PG,大内容在共享对象存储 |
| 工件 | 本地对象目录,持久备份与 DB 清单绑定 | 共享不可变对象与 PG 发布记录 |
| 临时执行目录 | 单实例/单 owner;先分类再清理 | owner+epoch 控制的可恢复工作区 |
KTD3. **一个会话一次主执行,不同会话并行。** 推荐独立 conversation_execution 占用行,以 owner/会话唯一键、run_id、epoch、lease_until 约束;不对所有 runs 加“会话唯一”,以免禁止正常子代理。事务内完成权限检查、请求幂等、消息、主 run 和调度记录,接收成功以事务提交为准。
KTD4. **短事务领取,租约与 fencing 配套。** Worker 通过 PG 行锁和 SKIP LOCKED 领取到期任务;事务提交后执行,模型/工具等待不得持有数据库事务。续租、事件追加、checkpoint、终态写入都校验 run+epoch。租约用 DB 时间,执行实例 ID 与每次启动的 generation 分开。旧 Worker 恢复后不能提交;租约过期也不等于外部命令已经停止。
KTD5. **持久命令与提交事件同事务。** 取消/HITL/调度命令写入 PG inbox/outbox,带唯一 command_id、逻辑 run、执行代次与处理状态。运行级取消意图交给当前 owner,即使接受后发生接管也不能丢失;HITL 绑定稳定 gate_id+gate_version,新 owner 消费时重验 gate 与权限,过期审批明确拒绝而不静默丢弃。执行结果及确认仍须校验当前 epoch。批准数据与待投递命令原子提交,执行者幂等消费,处理确认与执行效果分别记录。关键外部动作必须有目标端幂等键、可查询凭据或人工恢复路径;超时结果未知时进入 recovering,不宣称任意工具 exactly-once。
KTD6. **事件表是重放依据,NOTIFY 仅作唤醒。** 事件提交与 outbox 同事务;需要时在同事务使用 pg_notify。监听端维护每流连续 cursor,从 EventsAfter 分页追赶到通知水位;不能直接跳到末 seq。重建连接→LISTEN 生效→补查水位/积压,再转实时。增加周期补查,覆盖通知丢失、断线与通知前后竞争。只有落库事件才能发布可恢复的 SSE ID;纯瞬时进度明确可丢失,不冒充已确认业务数据。
KTD7. **数据库乐观并发控制。** 项目设置、卡片、文档元数据等读改写带 version/expected_version;冲突返回 409 及新版本引用。重要唯一性/外键落在数据库,事务失败按错误类型有界重试整笔事务;提交结果未知先按幂等键查询。不能无限重试模型调用或发送动作。[PG 事务隔离与重试](https://www.postgresql.org/docs/current/transaction-iso.html)。
KTD8. **数据库迁移独立于普通启动。** 单一 migrator 取得数据库锁,执行 append-only 版本;可事务化 DDL 与版本登记同事务。应用仅核对兼容版本和就绪状态。不能放入事务的在线索引变更单列可恢复运维步骤,不夹入普通迁移后声称原子。使用扩展→回填→切读写→收缩,保留兼容窗口。
KTD9. **工件共享,工作区有执行 owner。** 先保留 blobstore 接口;跨主机上线前实现并验收 S3 兼容对象存储,DB 保存不可变对象键、校验和、版本与就绪状态。发布前先完整写对象再提交引用,失败孤儿可清理;删除采用标记和延迟 GC。工作区、检查点与 Squad 状态分开设计:checkpoint/调度版本入 PG,大内容入对象存储,本地工作区是受 owner/epoch 控制的暂存执行目录;需要接管时按清单恢复。共享目录和文件 rename 不能替代跨机所有权。
KTD10. **Redis 只保存可恢复的加速状态。** 保留共享限流,改为 Lua 原子计数+过期或等价原子算法,键包含实例群/完整时间窗/owner,支持 ACL 凭据/TLS、连接与请求超时。故障时普通读取可降到本地保守限流;创建 Agent/昂贵工具等写入口仍受 PG admission 硬预算保护,不能无界放行。缓存带授权/内容版本,撤权同步失效或重新校验;先证明命中收益再加缓存。[Redis INCR 限流模式](https://redis.io/docs/latest/commands/incr/)、[分布式锁限制](https://redis.io/docs/latest/develop/clients/patterns/distributed-locks/)。
KTD11. **pgvector 按“正确→测量→ANN”推进。** 复用现有全文+RRF+可选 rerank,先补 embedding profile/model/revision/dimension/content_hash/generation 身份,禁止同维不同模型混查;迁移为 vector 列并在 PG 做过滤和精确 top-k,再用基线对照决定 HNSW。权限适配器不能直接变成 SQL 时,由适配器提供受限授权候选集合或采用有界分批取候选+最终实时鉴权,不得无界加载所有 owner 后在内存过滤。授权过滤后的召回率单独验证;ANN 需要迭代扫描/分区等策略时以模型维度和版本实测选择。[pgvector 官方说明](https://github.com/pgvector/pgvector)。
KTD12. **先管理连接池,再决定 PgBouncer。** 使 MaxOpen/Idle/IdleTime/Lifetime、查询超时可配置并输出脱敏有效值;总连接预算包含所有 API、Worker、LISTEN、migrator、监控与维护预留。100 在线用户不等于 100 DB 连接。未来接 PgBouncer transaction pooling 时 LISTEN 必须使用直连或 session pool 独立 DSN,迁移也走直连,并验证 pgx prepared statement 配置。[PgBouncer 兼容表](https://www.pgbouncer.org/features.html)。
KTD13. **共享可读不等于可写或可批准。** 当前 EXT-8 `ProjectAuthz` 是只读 bool 契约。U3 先列 read/edit/approve/execute/admin 的动作授权矩阵,复用下游角色,不另建成员权威;需要共享写入时以版本化公开契约扩展 action-aware 授权,旧只读适配器不得自动获得写入能力。未配置的动作默认拒绝,Host/工具/API 均走同一授权结论;审批职责分离仍由下游规则判定。SSE 发送后续数据、异步任务开始和工件下载时重新校验当前授权,缓存失效不承担唯一安全责任。
KTD14. **迁移副本按原数据敏感级别保护。** 备份、导出、WAL、工件和测试副本使用专用角色及受限目录;传输与静态备份加密,解密材料保存在独立受管秘密存储并纳入恢复演练。日志/报告只留统计与哈希,禁止正文、令牌及密钥明文;测试使用脱敏数据。U1 固定保留期限、存放位置、访问者与清理责任,未知项阻塞正式导出。正式恢复演练同时验证凭据可用性与非授权者无法访问副本。
```mermaid
flowchart LR
U[10–100 用户] --> G[入口与认证]
G --> A[API 实例 A / B]
A --> P[(PostgreSQL + pgvector)]
A --> R[(Redis 限流 / 可重建缓存)]
P --> W[Worker 领取任务 / 持久命令]
W --> P
W --> X[受控工具工作区 / 模型服务]
W --> O[(共享对象存储)]
A --> O
P --> E[事件追赶 / 本地订阅]
E --> U
```
```mermaid
sequenceDiagram
participant C as 用户
participant A as 任意 API
participant P as PostgreSQL
participant W as 执行 Worker
C->>A: 提交 requestId / expected_version
A->>P: 短事务:鉴权、幂等、占用、消息、run、任务
P-->>A: commit / 冲突
A-->>C: 已受理与 runId / 明确冲突
W->>P: 领取任务并取得 epoch
W->>W: 事务外执行模型与工具
W->>P: 按 epoch 提交结果与事件
P-->>A: 唤醒后按 cursor 补齐
A-->>C: SSE 已提交事件
```
```mermaid
stateDiagram-v2
[*] --> queued
queued --> running: 原子领取并分配 epoch
running --> waiting: 持久 HITL
waiting --> running: 幂等命令消费
running --> completed: 有效 epoch 原子结算
running --> recovering: 失联或副作用结果未知
waiting --> recovering: owner 失效
recovering --> queued: 已核对可安全重试
recovering --> interrupted: 需人工处理或不可恢复
queued --> cancelled: 取消命令生效
running --> cancelled: 执行者确认停止
```
## 5. 实施顺序与工作包
每个阶段评审后再进入下阶段;以下均为待批准计划,不因写入文档取得实施授权。新测试文件标“新增”;现有测试只作为扩展入口,本次未运行。普通基座改动在 Backend;公开持久接口确需变化时才同步 Native/Squad/Core,并由 Dev 固定组合测试,不全库改造。
### U1. 冻结目标、数据归属与基准环境
对应 R1/R2/R7/R8。先确定目标为 Dev 团队实例及其实际发布制品,记录脱敏配置来源、DB 实例/版本/扩展、数据数量与归属、文件清单、磁盘容量、峰值和恢复目标。RDM 独立业务库、旧 Agent-System 治理库另列,不能把一次 DSN 切换宣称覆盖它们。
范围:`runtime/dev` 部署配置(实施时核对文件位置)、`modules/backend/internal/config/config.go`、文档运行手册。
产物:固定部署清单、迁移映射、容量合同(区分接纳与执行许可)、隔离测试库/目录/端口方案、KTD14 的副本保护与保留合同;确认暂态 Worker 文件哪些可重建,哪些必须迁移。
验收:仅操作批准的实例和测试资源;共享数据凭据不打印;未知 owner 或未定位的权威文件阻塞迁移。测试:脱敏配置诊断及缺项校验,业务压测依后续 U7。
### U2. 团队 PG 档、迁移器与真实数据库门禁
依赖 U1;对应 R2/R3/R7。修改 `internal/config/config.go`、`internal/app/app.go`、`internal/store/store.go`、`internal/store/migrate.go` 及 CLI;按 KTD1/KTD8/KTD12 实施。
CI 增加隔离原生 PG/Redis 生命周期及明确必跑标记,固定组件/扩展版本;缺少真实测试服务时该 job 失败而非跳过。
测试:扩展 `internal/config/config_test.go`、`internal/store/store_test.go`;新增 `internal/store/migrate_atomic_test.go`。覆盖 team 缺 DSN/PG 故障拒绝启动、local SQLite 正常、两个 migrator 竞争、DDL 与账本中间崩溃、旧 schema 升级、PG bool/时间/空 JSON/唯一约束与全文扩展。
出口:真 PG 完整 CRUD/认证/会话/SSE/检索/卡片链路通过;仅该出口不宣布多实例安全。schema 变更的组合测试与业务迁移是不同动作。
### U3. 请求原子性、版本冲突与受控容量
依赖 U2;对应 R3/R6/R7/R8。修改 `internal/server/server.go`、`internal/store/messages.go`、`internal/store/cards.go`、`internal/cardsvc/cardsvc.go`、设置读改写及 `internal/runmgr`。收口 KTD3/KTD7 的事务边界;保留现有 seq 约束。按 KTD10 完成 Redis 原子限流、配置与异常策略;PG 记录共享 admission 预算。按 KTD13 完成动作授权矩阵与所需公开扩展的兼容计划;涉及共享编辑时 Core/Host 与下游适配器一起验收。
先补最小持久恢复隔离:为未知副作用记录 recovery hold,`ResumeInterruptedChildren` 等所有启动续跑入口必须跳过,人工核对后显式解除。概念状态 recovering 可以先由独立隔离记录表达;扩展 Core 枚举时走公开契约版本,不假定当前已有此状态。完整租约接管留 U5。
测试:扩展 `internal/server/admission_test.go`、`internal/ratelimit/ratelimit_test.go`;新增 `internal/store/admission_pg_test.go`、`internal/store/card_version_test.go`、`internal/runmgr/recovery_hold_test.go`。覆盖相同请求重放、事务部分失败、同版本竞争、动作越权、子代理预算计入全局、所有父 run 占满额度后同时派生仍能前进、未知副作用重启不自动续跑、队列满、Redis 断连/恢复/键过期。
出口:单 API 团队模式达到不变量与有界行为;不会把 20 个 Agent 配置值当作已证明容量。
### U4. 可切换的迁移与恢复演练
依赖 U2/U3;对应 R2/R7。新增 `modules/backend/cmd/storage-migrate/` 或等价受管运维入口,明确只处理 Backend 库;不得直接执行 SQLite dump 作为 PG SQL。迁移实例以 migration_id、source_snapshot_id、source_schema_version 固定来源,记录表级完成与最终验证完成标记;仅同一冻结快照可续传,其他非空目标拒绝。迁移报告包括每表计数、主键映射、按主键分批规范化内容校验和、外键/owner/权限检查及工件哈希。
测试:新增迁移工具的 `main_test.go` 与 `integration_test.go`,固定脱敏 SQLite 夹具覆盖全部当前 schema、布尔/空值/时间/JSON/Unicode/二进制向量/秘密字段、幂等重复导入、缺文件、未知 owner、断点重试及目标已含数据拒绝。
操作顺序与回退见第 6 节。出口:隔离环境迁移与恢复验证完成,由用户批准窗口后才切正式服务。
### U5. 执行所有权、持久命令与共享状态
依赖 U3,团队现网切换不必等待整个 U5;对应 R4/R6/R7/R10。修改 `internal/runmgr`、`internal/store/recovery.go`、`internal/squadadapter`、`internal/runtime/squad_runner.go`、`internal/blobstore`;新增 Store 的 lease/inbox/outbox 与 PG RunStore 适配器。
按 KTD4/KTD5/KTD9 设计 Native checkpoint 接口,继续支持 local 文件实现;不把 JournalOnly 日志误当作业务上下文权威。Worker 工具沙箱与访问范围沿用现有方向,在执行端检查当前身份和 epoch。
测试:新增 `internal/runmgr/multi_instance_test.go`、`internal/store/lease_pg_test.go`、`internal/blobstore/object_store_test.go`;扩展 `internal/runmgr/recovery_test.go`。必须启动两个真实进程:同会话争抢、不同会话并行、跨实例取消/HITL、命令提交后消费前 owner 换代、旧 gate 的审批明确失效、Worker 被杀、旧 Worker 迟到结算/写事件/写文件、失联重连、副作用结果未知、文件缺失/重复发布。
出口:所有写入和工具副作用的失效 owner 处理有实证后,才允许第二实例承接写请求;故障状态可见,不能仅靠粘性路由。
### U6. 事件追赶与数据库向量检索
U6 内分三个交付切片,稳定工作包 ID 不变:事件追赶/重连修复依赖 U2,可与 U3 并行;事务 outbox 与投递闭环依赖 U5;向量 schema 与查询改造依赖 U2 迁移器及 U3 授权合同。对应 R5/R6/R9。
事件:修改 `internal/pgbus/pgbus.go`、`internal/runmgr/events.go`、`internal/server/server.go`、Store 事件接口。扩展 `internal/pgbus/pgbus_test.go`,新增 `internal/server/sse_gap_test.go`:批次 64 条全部可见、断线补齐、通知丢失、订阅与补查竞争、1000+ 分页、慢消费者、撤权后新事件拒绝。第一切片完成只证明追赶修复,不声称 KTD6 的事务 outbox 已就位。
向量:修改 `internal/store/vectors.go`、`internal/store/migrate.go` 与现有知识索引管线;新增 `internal/store/pgvector_test.go`。验证模型身份/维度隔离、内容更新/删除原子性、回填中重启、索引未完成可降级、中文全文混合、共享授权后 top-k、撤权、与精确检索 Recall@10 对照(建议≥0.95,待批准)。索引构建不得拖垮前台连接/IO。
出口:先验证正确与有界,再按容量结果决定 HNSW 与缓存;绝不以 extension 存在替代查询计划/召回证据。
### U7. 压测、故障验收与分布式放行
依赖 U2/U3/U4;双实例验收另依赖 U5/U6。对应全部 R。首期单实例放行必须完整重放现有入口的 R6 权限矩阵(含 SSE 后续事件、共享检索、下载、Host/工具与撤权),并证明 U3 恢复隔离;不能等 U5/U6 才验证这些 P0 要求。跨实例传播与接管的新增验证随 U5/U6。新增 Dev `tests/load/collaboration/` 与 `tests/integration/multi-instance/`,由隔离测试入口负责启动/回收自己创建的资源。
按第 3 节负载合同和第 7 节矩阵测量,记录原始 k6/pgbench、服务指标与数据不变量;pgbench 只测数据库能力,不能替代业务 API/SSE/Agent 验收。正式测试不打生产服务。
出口:单实例团队、双进程、双主机分别出具结果;CI 身份/链接/SHA 或独立 REPLAY 可核验才用于放行。本地 SELF 只能作辅助。变更智能体行为时补真实模型前后任务级对照。
远期 P2:需求达到后再拆 API/Worker/索引服务,按表归属收紧角色;引入 PG 高可用与自动故障转移、对象存储冗余、可选 PgBouncer。服务拆分与 DB 复制是两件事;Redis 集群、读副本和缓存不能替代主库一致性,读后写敏感路径保持主库读取或显式一致性令牌。
## 6. SQLite → PostgreSQL 切换和恢复
1. 固定源服务制品、全部数据库/文件路径与目标空库;指定迁移 owner、窗口、可接受中断和回退判据。先做只读盘点与隔离演练,不接触其他服务的数据。
2. 为 SQLite 做一致性备份(应用已有 Backup/VACUUM INTO 或官方备份路径),同时生成工件/checkpoint/工作区清单。活跃 WAL 数据库不能只复制 `.db` 文件;检查备份可打开且内容与清单匹配。
3. 预演使用独立测试目标 PG,由 migrator 建 schema/扩展,再按依赖顺序导入。显式保留 ID、幂等键、事件 seq、哈希/秘密、用户归属、共享范围、审批状态,调整序列值和类型。预演库不成为正式目标;所有副本遵守 KTD14。
4. 最终窗口暂停新写与调度,等待或安全收口在途 run;无法确认外部副作用的 run 写入 U3 的持久 recovery hold。停止全部旧写者后取最终一致性快照,导入新空正式目标;按 U4 的同快照迁移身份续传。全量对账与验收抽样完成并写入完成标记后才可切入口,保留只读源备份及过渡期权威文件。目标已服务或身份不同的非空库不得覆盖。
5. 仅一个入口切到 PG,检查版本/配置指纹、库身份与数据归属,执行用户登录、项目、会话、审批、检索、下载的冒烟;应用 fail-closed,不能故障后回 SQLite。原库不再写,不使用临时双写制造两个权威。
6. **PG 接受新写入前**可撤回切换,确认目标未产生业务写后恢复 SQLite。**PG 接受新写入后**禁止直接指回旧 SQLite;优先保留 PG 回退兼容应用或向前修复。确需反向迁移,重新冻结写者、导出 PG 增量、对账并批准专门回迁方案。
7. 建立 pg_dump 逻辑备份,以及 pg_basebackup(或等价物理基础备份)+ 连续 WAL 归档/PITR,并与对象存储版本/清单联合恢复;pg_dump 不能用作 WAL 回放的基础备份,保留足够不可变对象。每次演练恢复到新目标,核对应用可用与权限/文件完整性,不能把“备份命令成功”当作可恢复。
SQLite 本身支持可靠事务与多人经同一应用访问;WAL 提升读写并发,但同一数据库仍只有一个 writer,且不适合多主机通过网络文件系统共享 WAL。取舍基于写负载、进程拓扑、恢复与运维目标,不用“超过某人数 SQLite 就出错”的判断。[SQLite WAL 官方文档](https://sqlite.org/wal.html)。
## 7. 最小验收矩阵
| 场景 | 必须观察到的结果 | 对应 |
|---|---|---|
| 同 requestId 并发/网络超时后重试 | 一个逻辑请求和同一 run,不重做已经确认的副作用 | R3/U3/U5 |
| 两实例同会话不同请求 | 仅一个主执行;另一个排队或明确冲突,子代理不受错误唯一约束阻断 | R3/R4/U5 |
| 多项目并行、热点会话、满额度父子派生 | 公平、有界;等待父 run 不阻塞子 run 获得许可,所有任务能前进或得到有界拒绝 | R1/R8/U3/U7 |
| 并发修改同卡/设置 | 成功与冲突可辨认,无最后写入静默覆盖 | R3/U3 |
| 在 B 提交 A 所有 run 的取消/HITL,消费前换 owner | 命令持久、唯一消费;取消随逻辑 run 生效,HITL 重验 gate 版本,卡片与 run 最终一致 | R4/U5 |
| Worker 崩溃/暂停超过租约后恢复 | 新 owner 可受控接管;旧 epoch 不能提交;不明副作用不自动重放 | R4/R7/U5 |
| PG 断连/提交响应丢失 | 依赖不可用有界失败;恢复按幂等记录核对,不自动切 SQLite | R2/R3/U3 |
| Redis 断连/INCR 后崩溃 | 不改变业务事实;昂贵任务仍受硬预算;恢复无永久计数键错误 | R8/U3 |
| 批事件/断开 LISTEN/通知丢失 | 所有已提交事件按连续 cursor 追赶,重放不跳洞 | R5/U6 |
| 撤销成员权限/跨 owner 猜测 ID | 新请求、流后续事件与下载/检索/工具均不泄漏 | R6/U3/U5/U6 |
| 多模型向量/过滤后 top-k | 不混模型,权限正确,召回/延迟达批准阈值 | R9/U6 |
| 迁移崩溃/坏文件/未知 owner | 阻止切换;重试不重复导入,不隐式公开历史数据 | R7/U4 |
| 磁盘满/备份恢复/整机失效 | 清楚拒绝写入和报警;在约定故障模型与 RPO/RTO 下恢复 | R7/U4/U7 |
## 8. 工具、观测与交付
本轮未安装软件、修改配置、运行迁移、重启服务或压测。brew 已有三项服务软件,当前瓶颈主要是接线与一致性机制,暂不重复安装。未来压测可通过 [brew 安装 k6](https://formulae.brew.sh/formula/k6);PgBouncer 只在连接测量证明有必要后安装。安装授权不等于对共享数据库升级、创建扩展或变更服务的授权;执行前核对目标、版本与既有占用。
必需观测:API P95/P99与预期/非预期错误,DB pool wait/连接数/慢查询/锁等待,队列长度/最老年龄,run 租约/过期写拒绝/未知副作用,SSE cursor lag/补查次数,Redis 错误与降级,检索耗时/召回抽样,备份时间与恢复结果。日志只记录 request/run/owner 的安全标识,不记录 DSN、密钥或文档正文。
当前交付仅为 r1 方案与 SELF 调查记录。需求人数已确认;容量阈值、正式数据/部署目标、迁移窗口、灾难恢复等级和实施阶段尚待人工评审。没有业务测试 PASS、独立 REPLAY、CI GATE 或已部署声明。
方案静态审阅及修订处置见[审阅记录](../records/2026-10-11-collaboration-storage-plan-review.md)。Agent 审阅不替代人工批准或实现验收。
首选下一增量是 **U1 + U2 的详细实施与测试合同**:固定目标实例与 PG 测试环境,解决团队档、迁移原子性和真实数据库测试门禁。U6 的事件正确性修复可在单独授权后并行;不要先增加应用副本。各实现任务启动前复核在途 PR 与依赖 SHA,再按 Dev 的 evidence/task-sync/组合验证流程交付。
+5
View File
@@ -12,6 +12,7 @@
| 基座与下游如何分工,如何申请扩展 | [基座路线图](2026-10-01-base-extension-roadmap.md),配套插件契约与兼容规则 | 公开扩展面、插件 v2、质量回路、工程基座 | weichen |
| 三个智能体库如何继续演进 | [Agent 栈总案](2026-10-02-agent-stack-completion.md),阶段细化见 N1/Q1/I1、N2、N3、Q2/Q3 | R0/R1/D1、N1–N3、Q1–Q3、I1–I2、阶段评测 | weichen |
| RDM 下游的大目标和消费者里程碑是什么 | [RDM 阶段总案](2026-10-07-rdm-phase-plan.md) | P0–P4,对应 MS0'、MS1–MS4及发布前置 | weichen |
| 10–100 人协作与未来分布式如何演进 | [协作存储与分布式方案](2026-10-11-collaboration-storage-and-distribution.md) | 团队 PG 档 → 迁移恢复 → 多实例执行与故障验收;r1 已批准(2026-10-11),实施自 U1+U2 启动 | 需求:weichen;实施/运维待指定 |
以上是依据文内显式范围和引用形成的阅读入口,不是新增优先级或批准结论。N2 阶段案未单列 owner,
应由负责人补齐;不能仅凭任务创建人推断全部规划的负责人。
@@ -74,6 +75,10 @@ Dev 链接均指向对应任务 PR,可从其任务卡继续追溯组件 PR;
| [N2 实施案](2026-10-07-agent-stack-n2-plan.md) | 栈总案 N2 的六 WP:上下文、协议、工具、skills、子代理、装配 | 未单列确认状态及 owner;正文称 N2 开工 | Documents [#42](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Documents/pulls/42) 已合入;Dev [#244](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/244) 已合入(merge `7e3ba10`),闭卡 [#247](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/247)。卡内自述六 WP 落地;本索引未重放 |
| [N3 实施案](2026-10-07-agent-stack-n3-plan.md) | 栈总案 N3:持久与可观测 | 页首未单列确认状态及 owner;正文称 N3 开工 | Documents [#43](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Documents/pulls/43) 已合入;Dev [#248](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/248) 已合入(merge `2836a87`),闭卡 [#249](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/249)。卡内自述六 WP 落地;本索引未重放 |
| [Q2/Q3 实施案](2026-10-07-agent-stack-q2q3-plans.md) | 栈总案 Q2 拓扑与 Q3 收敛互通 | 页首未单列确认状态及 owner | Documents [#44](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Documents/pulls/44) 已合入;Dev [#250](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/250) 采纳方案,[#251](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/251)(merge `452c00a`)与 [#253](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/253)(merge `c8ce4eb`)已合入。卡内自述 Q2/Q3 落地;本索引未重放 |
| [智能体工作流质量改进](2026-10-09-agent-quality-improvement-plan.md) | 直连主路径质量:诊断→六项裁定→P0–P5 分期 + 模型 A/B;推翻 fs 工具默认关闭裁定 | 已获实施授权(2026-10-09 六项裁定);首个增量 feature/agent-quality-phase01 | 随本提案 PR;诊断 SELF(真实运行转录);基线报告见任务卡 |
| [EXT 申请包 C1–C7](2026-10-08-ext-c1-c7-rdm-shared-contracts.md) | RDM 多人共享契约的基座最小扩展申请(EXT-6…EXT-12):身份停用/受管配置/授权适配器/索引发布/分发守卫/Host 工厂/投影过滤 | proposed;需求依据=RDM U0 主记录(D1–D9 裁定,2026-10-08 合入) | 随本提案 PR;每条现状证据为 SELF·源码级(Dev 34a2bbe/backend fe7c245),运行态 NOT_RUN;受理后逐条另立实现任务 |
| [EXT 申请包 G1](2026-10-09-ext-g1-b1-b3-exec-backend.md) | EXT-13…16:回合内共享检索、命令即工具、项目列表共享、执行后端接口 | proposed;需求依据=RDM 方案 V1.1 §3.3/§3.4/§5.3(2026-10-09 批复) | Documents [#52](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Documents/pulls/52) 已合入;EXT-16 已实施(Dev #285,见其任务卡),其余受理评审待定。本行由本次索引维护补充 |
| [子代理分工、模型分级路由与任务沙箱](2026-10-10-agent-delegation-routing-sandbox.md) | 方向主记录:委派契约与角色路由方案(E1–E5 增量)+ 任务工作区沙箱设计(仅记录不实施);修正 overlay≠快照等三处假设 | proposed(2026-10-10);E1、E3、E4(R4+账本)、E5(R3)、S0-1 已完成(E4:Dev #315;S0-1:Backend #135/#136、Dev #318,2026-10-11);E2 暂缓(模型指示)、S0-2 待授权 | 随本提案 PR;现状核查与启动延迟为 SELF(任务卡 evidence);外部来源 URL 全量随任务卡 |
原查询的 19 份原文中,14 份显式标注 owner 为 weichen。双档存储、启动配置面、跨会话读取、文件预览和
N2 实施案这五份未单列 owner,其中预览案仅写“作者:weichen + Agent”。同步新增的 N3 与 Q2/Q3 案页首也未单列 owner。
@@ -0,0 +1,102 @@
# 基座 v0.2.0 发版记录
| 项 | 内容 |
|---|---|
| 状态 | **候选已就绪,tag 待 owner 执行**(高后果操作双钥匙:发布版本由 owner 亲手打 tag 并推送) |
| 发版授权 | owner 2026-10-09 会话:「越快越好,你来执行即可,我已同意」(Q3)。Agent 完成候选组合、验证与本记录;tag 创建与推送按 AGENTS.md 双钥匙规则由 owner 执行 |
| 主任务 | Dev 仓 `feature/base-v020-release`(任务卡即本次发版的主记录) |
| 规范依据 | Agent-System-RDM `docs/materials/12-基座发版核对清单.md`;[扩展面兼容与演进](../proposals/2026-10-01-extension-compat-policy.md) §5 |
## 1. 范围
v0.2.0 是第二段发版:在 v0.1.0 评估基线之上,附 experimental 兼容承诺(实验窗口内仍可破坏性调整,但须改快照并在提案记录)。
相对 v0.1.0 的公开面(`pkg/extension`,SurfaceVersion `2.0.0-experimental` → **`2.5.0-experimental`**):
| 扩展 | 公开面 | 来源 |
|---|---|---|
| EXT-1 | `Handler`、`Host`、`Request`、`Registry.AddCommandHandler` | Backend #66 |
| EXT-5 | `Host.ParseDocument`;内置 xlsx/pptx 解析 | Backend #67、#68 |
| EXT-8 | `ResourceAuthorizer`、`Registry.RegisterResourceAuthorizer`;工件/检索/项目流/向量/hostapi 共享读取 | Backend #105、#107、#109 |
| EXT-9 | `Host.KnowledgePublish`、`Host.KnowledgeDelete`、`Options.DisableUploadIndexing` | Backend #106 |
| EXT-11 | `SetScopedHostFactory`、`NewScopedHost` | Backend #108 |
| EXT-12 | `Options.DisableConvCardMirror` | Backend #107 |
| 执行原语硬关闭 | `Options.DisableExecTools`;CLI `--disable-exec-tools`、配置键 `disableExecTools`、环境变量 `AGENT_SYSTEM_DISABLE_EXEC_TOOLS` | Backend #110 |
同时包含的缺陷修复与行为变化(不改公开面):
- 命令审计按 owner 存储与查询(v0.1.0 的 `GET /api/commands/audit` 跨租户可见;Backend `e0b1c27`,随 #105 合入);
- DOCX 解析保留修订插入、超链接与域结果内的文字;删除/移走的原文与域代码不进正文;含修订时显式告警(Backend #110);
- agent-stack N1–N3、Q1–Q3、I1–I2 阶段的库与接线(见 Backend #69–#103)。
**升级须知(下游)**:
- 多用户共享部署在执行隔离就位前,应设置 `Options.DisableExecTools = true`。只关闭用户设置 `runtime.exec` 不够,因为任何用户都能经 `PUT /api/settings/user` 把它改回 true。
- 下游 go.mod 的 8 条 deps replace 不变(compat-policy §4)。
- 下游兼容验证:RDM PR #113 头 `d49d5c9` 的源码在 replace 指向本次 backend 候选时 `go build ./...` 与 `go test ./...` 通过(SELF,2026-10-09)。
## 2. 候选组合
| 仓 | 提交 | 依据 |
|---|---|---|
| Backend | 由 Dev 合入提交的 gitlink 决定(Backend main `f1c9cc0` 或其后) | Backend #110 合入;CI `ci / vet-and-test (push)` success |
| Core | `a253a40b8e5563119b00c4e71fb61fe0d11d6d6a` | == Dev gitlink == backend `deps/agent-system-core`(同 commit 约束) |
| Frontend | `84b73ab32848e623d0a0ab7ade552784dca8cf3c` | == Dev gitlink == Frontend main |
| UI | `76d6633736a7abb36ac1ecca42fe4e6f120d0558` | == Dev gitlink == UI main |
| Dev | 本任务 PR 合入 main 后的合入提交 | Dev CI(PR HEAD `make test` + `make build`) |
最终 SHA 以 Dev 合入后 `git ls-tree <Dev 合入提交> modules/` 的实际输出为准,执行前按 §4 核对。
**UI 仓自身 CI 现状**:UI 仓 `ci.yml` 跑在 `nightly` 运行器上,自 2026-10-04 起每次都失败于 vitest 启动(`Cannot find native binding`,rolldown 可选依赖),与代码无关。同一 UI 提交在 Dev 组合 CI 中 `npm ci && tsc && vitest` 通过(14 文件 / 103 用例;Dev run 1369),本地 Node 26 复验同样通过。v0.2.0 以 Dev 组合 CI 为 UI 的权威验证;UI 仓 runner 修复另列后续任务。
## 3. tag 消息(五仓一致)
```
SurfaceVersion 2.5.0-experimental. Adds in-proc command handlers, the owner-scoped host handle, document parsing, resource authorization, knowledge publish/revoke, scoped host recovery, the card mirror switch and the exec hard switch.
```
## 4. owner 执行步骤
先在 Dev 主检出确认合入后的组合(只读):
```bash
cd /home/weichen/projects/Agent-System-Dev && git fetch origin && git checkout main && git pull --ff-only
git status --porcelain # 必须为空
DEV=$(git rev-parse origin/main); echo $DEV
git ls-tree $DEV modules/backend modules/core modules/frontend modules/ui
# core 同 commit:以下三行必须相同
git ls-tree $DEV modules/core | awk '{print $3}'
git -C modules/core rev-parse HEAD
git -C modules/backend/deps/agent-system-core rev-parse HEAD
```
然后子仓先、Dev 后,逐仓打 tag(`<SHA>` 用上面 `ls-tree` 打印的该仓 gitlink):
```bash
MSG='SurfaceVersion 2.5.0-experimental. Adds in-proc command handlers, the owner-scoped host handle, document parsing, resource authorization, knowledge publish/revoke, scoped host recovery, the card mirror switch and the exec hard switch.'
for m in backend core frontend ui; do
SHA=$(git ls-tree $DEV modules/$m | awk '{print $3}')
git -C modules/$m fetch origin && git -C modules/$m tag -a v0.2.0 "$SHA" -m "$MSG" && git -C modules/$m push origin v0.2.0
done
git tag -a v0.2.0 "$DEV" -m "$MSG" && git push origin v0.2.0
```
推完核对(五处一致):
```bash
for m in backend core frontend ui; do
echo "$m tag=$(git -C modules/$m rev-parse v0.2.0^{commit}) gitlink=$(git ls-tree v0.2.0 modules/$m | awk '{print $3}')"
done
git ls-remote --tags origin v0.2.0
```
## 5. 发版后(Agent 可执行,tag 在位后启动)
- RDM 升钉到 v0.2.0 五仓 tag,并重定基合入 #113/#114(RDM 计划 G1-1);
- RDM 二进制设置 `DisableExecTools: true`(RDM 计划 G1-7);
- Releases 仓按 `scripts/build-release.sh 0.2.0` 构建制品与 manifest(另立任务,内部发布,不公开)。
## 6. 不做的事
- 不使用 `v0.2.0-experimental` 这类 tag 名;不在实验窗口发布 npm;不改 Go module path。
- RDM 仓 v0.2.0 tag 不在本次范围(RDM 升钉完成后另行评估)。
File diff suppressed because one or more lines are too long
@@ -0,0 +1,31 @@
# 协作存储方案 r1 静态审阅记录
对象:[方案 r1](../proposals/2026-10-11-collaboration-storage-and-distribution.md)。
日期:2026-10-11(Asia/Shanghai)。来源:SELF,Agent 辅助静态审阅。
三个 Agent 分别兼任两个视角:coherence/scope-guardian、feasibility/adversarial、security/product。
同一 Agent 的两个视角不互相独立;没有跨模型审阅、独立测试重放、CI GATE 或人工批准。
使用 ce-doc-review 的检查视角,覆盖与边界以本记录为准,不声称已运行额外外部模型审阅。
## 发现与处置
| 发现 | 原方案后果 | 修订处置 | 复核 |
|---|---|---|---|
| 正式导入与非空目标拒绝冲突 | 预演后可能无法最终导入,或放宽规则混入其他快照 | U4/§6 分开预演与正式空目标;migration_id+快照身份,只有同快照可续传,验证完成后切入口 | 原审阅 Agent 已确认文本闭合 |
| 恢复隔离晚于首期切换 | 现有 ResumeInterruptedChildren 可能自动重派未知副作用任务 | U3 前移持久 recovery hold,启动续跑跳过,U7 放行前验证 | 原审阅 Agent 已确认文本闭合 |
| 命令只绑定旧 epoch | owner 切换后取消或审批意图可能丢失 | KTD5 区分逻辑 run 取消、gate/version 审批与按 epoch 验证的执行结果 | 原审阅 Agent 已确认文本闭合 |
| 父子共享额度可能互等 | 父 run 占满许可并等待不能启动的子 run | §3/U3 区分接纳数与执行许可,等待父 run 让出许可但保留会话所有权 | 原审阅 Agent 已确认文本闭合 |
| U6 先于 outbox 依赖 | 提前声明事务投递机制完成或重复建设 | U6 分追赶修复、outbox、向量三个切片并分别列前置 | 原审阅 Agent 已确认文本闭合 |
| P0 权限检查落到后期 | 首期团队版可能未验证 SSE/检索/工具撤权 | U7 单实例放行前验证完整现有 R6 入口矩阵;新增跨机能力另验 | 原审阅 Agent 已确认文本闭合 |
| 过渡期本地权威未说清 | 尚未迁移的 checkpoint/续跑文件可能被当缓存清除 | KTD2 增加首期与多实例期归属表,联合备份,U5 前禁止跨机接管 | 原审阅 Agent 已确认文本闭合 |
| 只读适配器不足以判断写入/审批 | 可能把共享可读扩大成可写 | KTD13/U3 指定动作授权矩阵、版本化兼容与默认拒绝,不新建成员权威 | 原审阅 Agent 已确认文本闭合 |
| 迁移/备份敏感副本保护未列 | 源库秘密可能经备份旁路暴露 | KTD14 指定访问、加密、密钥分离、保留与恢复验证;正式导出前缺项阻塞 | 原审阅 Agent 已确认文本闭合 |
主编另依据 PostgreSQL 官方 PITR 文档修正备份描述:pg_dump 是逻辑备份,不能作为 WAL 回放基础;PITR 使用物理基础备份加连续 WAL。未改变用户确认的规模与未来分布式方向。
## 剩余决策与验证边界
- 10–100 人、多项目、多会话及未来多服务分布式:用户已确认需求方向。
- 量化容量/恢复目标、正式实例和数据归属、迁移窗口、资源与实施阶段:待人工评审。
- 本记录只说明文档中的九项问题已经改写并由原 Agent 复读,不说明实现缺陷已修复。
- 实施、真实 PG/Redis 测试、压测、故障注入、数据迁移、恢复演练与部署:全部 NOT_RUN。
- 方案正文与调查收据的字节摘要由文档检查命令写入 Dev 任务卡,作为本次候选身份;本文件不手写命令结果。