Compare commits
9
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
238f90e92a | ||
|
|
d3c9b4554e | ||
|
|
176db44c4b | ||
|
|
69360de5bb | ||
|
|
9995552152 | ||
|
|
4e57196a8e | ||
|
|
4ce8155161 | ||
|
|
09d968cb2f | ||
|
|
9505c7eadc |
@@ -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` |
|
||||
@@ -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) |
|
||||
@@ -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` |
|
||||
@@ -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/` |
|
||||
@@ -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 推翻,以本文为准)。
|
||||
@@ -74,6 +74,8 @@ 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;受理后逐条另立实现任务 |
|
||||
|
||||
原查询的 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 升钉完成后另行评估)。
|
||||
Reference in New Issue
Block a user