Skip to main content

Phase 3 — 统一装配(Assembly)设计草案 + 决策清单

Date: 2026-07-08 Status: 两个 blocking 决策已由用户拍板(2026-07-08) → 见 §2 定案。下一步补字段级设计(需先核实当前 /chat NestJS→agent 契约),再写实施计划。

✅ 定案摘要(用户 2026-07-08):

  • D-2 placement = NestJS 组装 → 传结构化 prompt 给 agent 执行(与 E2 共存)。assembler 建在 NestJS(确定性组装:层序/优先级/token 预算/红线永不截断),产出结构化 prompt/context → 序列化跨服务传给 agent → agent 仍用 Hermes 生成 draft(agent 从「自己拼 prompt」变成「执行 NestJS 组装好的 prompt」)。依据:亲验 E2 phase-4.5 设计(主 checkout 的 2026-07-01-adr034-phase-4.5-design.md)——E2 phase-4.5 只迁 Expert consultation 的 chatStream,明确把主 draft pipeline runAgentPipeline 列为 non-goal/follow-up,主 draft 短期仍在 Hermes。故 assembler 建 NestJS 与 E2 共存、为将来 E2 迁主 pipeline 铺路。需定义 NestJS assembler → agent 的新 prompt 契约。
  • D-1 KB 契约 = 结构化 kb_context 升为主通道。与 D-2 自洽:KB 在 NestJS 侧,assembler 直接消费结构化 chunk(带 doc_id/chunk_id/isGolden/source)组装进 prompt,R5.1 强制引用 + P3.4 chunk-id trace 直接可实现。
  • 翻转的改动面:Phase 3 从「agent 侧就地增强」翻转为「NestJS 建 assembler + 新 NestJS→agent prompt 契约 + agent 改成执行组装好的 prompt」。比 master plan 原设想大,需字段级设计。 基线: 已亲验 agent 侧装配现状 @ 490517fb(见下"核实基线")。 Ancestry: master plan docs/superpowers/plans/2026-07-07-specialist-value-output-master-plan.md §四 Phase 3(P3.1-P3.5)+ §五 blocking 决策(N3/D3 placement)· R5.1-R5.5 · NFR3(分层预算 + 红线/身份永不截断)· ADR-034 E2(LlmService 迁移轨道)。

为什么这份文档存在:P2.4/P2.5 都有字段级设计可依,直接 subagent-driven 执行成功。Phase 3 只有 master plan 的任务级描述,亲验后发现落地要先回答几个跨 agent(Python)/NestJS(TS)的契约问题和一个战略 placement 决策——这些是 P2.4 keyword-service 返工的放大版风险(在错误契约上写完跨两服务的代码)。所以先出设计 + 停在决策点,而非盲目执行。


1. 核实基线(agent 侧装配现状,亲验)

维度现状Phase 3 缺口
prompt 组装纯字符串拼接:prompts_loader.py:507 拼 system_prompt(security→clarify→attachment→web→soul→persona-header→role body),chat_helpers.py:430 再两级 \n\n join [system, directives, kb, web, request-ctx, history, inbound, docs, response_contract, clarity, user_msg]P3.1:数据驱动层序 + 优先级表 + 统一 token 预算 + 红线/身份永不截断——基本新建
字符预算3 个独立字符上限常量(DIRECTIVE_CHAR_LIMIT=1500/KB_CONTEXT_CHAR_LIMIT=4000/WEB_CONTEXT_CHAR_LIMIT=6000),各自截断P3.1:分层 token(非字符)预算,层间可按预算跳过,红线/身份豁免截断
Soul 消费两通道:per-request agent_context.specialist_system_prompt(NestJS resolveSpecialistPersona 下发)+ boot-time runtime-config 落 /opt/data/SOUL.mdP1 已就绪,Phase 3 复用
persona heuristic_FULL_TEMPLATE_SIGNALS(prompts_loader.py:400)仍在;P1.6 加了 header_only 软开关(默认 legacy),未物理删除与 Phase 3 弱相关(ADR-033 过渡态)
skill 消费零 runtime consumer——只有 _fetch_runtime_config_if_missing 落盘 RUNTIME_DATA_ROOT/skills/*,无读取/trigger/注入P3.2:全新建 skill 读取 + trigger 评估 + merge/tie-break + abstain
检索级联主要在 NestJS:pinGoldenFirst(P2.4,一次检索内置顶)+ maybeInjectCorrectionFewShot(flag-gated)+ hybrid,三段分散。agent 只在 kb_context_provided=false 走 keyword fallbackP3.3:三段收敛成按预算逐级跳过的编排,改动面主要 NestJS
KB 注入形态NestJS 把 KB 作为 history 的 user-role 轮注入(<kb_context> 包裹,非结构化);agent 结构化 format_kb_context/kb_context 字段只在 keyword fallback 生效契约冲突,见 D-1
traceP0.6 minimal:draft metadata.assembly {promptVersion, templateKeySent, templateUsed, personaOverrideApplied, kbContextProvided, activeDirectivesCount} + agent audit。promptVersion = sha256(SOUL + role_template)P3.4:扩 version_id(soul/skill)+ KB chunk ids + 逐层预算 + 独立表 + 90 天保留
红线检查不存在——skill.redLines 字段无 runtime 消费;现有 post-draft 只有 path scrubber/echo-guard/PII(安全非业务红线)P3.5:全新建,agent 出 draft 后检查 + NestJS flag 落 Expert workspace

2. 两个 BLOCKING 决策(需用户/架构拍板)

D-1(契约级,必须先定):KB 怎么从 NestJS 传到 agent 侧 assembler

问题:P3.1 的「KB 强制引用」(R5.1)和 P3.4 的「KB chunk-id trace」要求 agent 侧 assembler 拿到带 id 的结构化 KB chunk。但现状 KB 主要作为 NestJS 注入的 history user-role 轮(非结构化文本,<kb_context> 包裹),agent 结构化 kb_context 字段只在 kb_context_provided=false 的 keyword fallback 生效。assembler 拿不到可引用/可追踪的 chunk。

选项做法
D-1-A(倾向):结构化 kb_context 字段升为主通道NestJS 把已检索(golden+hybrid+correction)的 chunk 以结构化数组(带 doc_id/chunk_id/isGolden/source/content)放进 /chatkb_context 字段传 agent;agent assembler 消费它做强制引用 + chunk-id trace;保留 history-轮作为兼容/展示assembler 拿到可引用可追踪 chunk,R5.1+P3.4 直接可实现;golden 置顶信息(isGolden)透传改 /chat 契约 + NestJS 注入路径(P2.4 刚改过这块);两种注入形态并存需明确哪个是真相
D-1-B:assembler 留 NestJS 侧把 assembler 放 NestJS(KB 本就在这),agent 只收最终 promptKB 不用跨服务传与 D-2(placement)强绑定,且 prompt 最终拼接、soul、persona heuristic 全在 agent——assembler 放 NestJS 要把这些也搬,改动面巨大;违反 master plan D3「短期 agent 侧」

推荐 D-1-A,但依赖 D-2 的 placement 结论(若 D-2 决定 assembler 长期迁 NestJS,则 D-1 的答案不同)。

D-2(战略级,master plan §五 P0.3 前应定案):assembler 的 placement

问题:master plan D3 建议 assembler「短期 agent 侧 + runtime 中立契约」。但 ADR-034 E2 phase-4.5 的方向是把 draft 生成路径从 Python/Hermes 迁到 NestJS 的 LlmService。若 E2 推进,agent 侧 assembler 面临整体搬迁。

选项含义影响
D-2-A:agent 侧就地增强(master plan D3 原意)P3.1 把 prompts_loader.py 演进为确定性 assembler改动面收敛(prompt 拼接本就在 agent),风险最低;但若 E2 推进需搬迁——用「runtime 中立契约」(assembler 逻辑与 Hermes CLI 解耦、输入输出是纯数据结构)缓解
D-2-B:直接建在 NestJS(押注 E2 方向)assembler 建在 NestJS,agent 变薄与 E2 对齐免搬迁;但要把 soul/persona/prompt 拼接从 agent 搬 NestJS,和 Hermes runtime 的关系要重新定义——大动作,且 E2 phase-4.5 本身状态未明

推荐 D-2-A + 中立契约(除非用户知道 E2 phase-4.5 即将推进)。这是 master plan 明确标注需 Franky+Eng 定案的 blocking 决策,不是 agent 可自决的


3. 其余待定(设计级,评审时定,非 blocking)

  • D-3 skill 输入通道:shared/org-agnostic agent(cutover 拓扑)无 HUMANWORK_ORG_SLUG→跳过 runtime-config fetch→拿不到 /opt/data/skills/*。P3.2 的 skill 输入必须兼容 per-request agent_context 下发(不能假设磁盘)。建议:skill 走 per-request(和 soul 的 specialist_system_prompt 同款),磁盘只作 per-org agent 的缓存。
  • D-4 trace 落点:P3.4 继续扩 metadata.assembly 字段 vs 建独立表 + 90 天保留(P0.6 行注明后者)。chunk-id/version_id 数据量小,倾向先扩 metadata.assembly,独立表留到有查询需求。
  • D-5 红线检查位置:P3.5 在 agent 出 draft 后检查(能拿到完整 draft + skill.redLines)vs NestJS persist 前。倾向 agent 侧检查(靠近 draft 生成 + skill 上下文)+ NestJS 侧落 flag 到 Expert workspace(违规入 queue)。
  • token 预算裁剪 + 永不截断:Hermes 单 -q prompt 上做 token 级预算 + 红线/身份豁免截断无先例;prompt 结构改动要重跑 echo-guard/CoT-leak 回归(agent/CLAUDE.md:62-65)。

4. P3.1-P3.5 依赖顺序 + 改动面(核实后)

P3.1 assembler [AG]        ← 根;改 prompts_loader.py + chat_helpers.py(纯 agent Python);依赖 D-1+D-2
├─ P3.2 trigger 引擎 [AG] ← 依赖 P3.1;新建 skill 读取+trigger+merge/tie-break+abstain(纯 agent,依赖 D-3)
└─ P3.3 检索级联 [AG/BE] ← 与 P3.2 可并行;主要改 NestJS(收敛 golden/hybrid/correction 成编排);agent 收契约
P3.4 full trace [AG/BE] ← 依赖 P3.1+P3.3 产出 version_id/chunk_id;agent 扩 AuditMetadata + NestJS 扩 metadata.assembly(依赖 D-4)
P3.5 红线 post-draft [AG/BE] ← 相对独立可较早;agent 检查 + NestJS flag 落 Expert workspace(依赖 D-5)

5. 下一步

  1. 用户评审 D-1 + D-2(blocking)。D-2 若涉及 E2 phase-4.5 战略,可能需 Franky+Eng。
  2. D-1/D-2 定案后,据此把本草案补成字段级设计(KB 契约的确切 schema、assembler 的层序/预算数据结构、trigger schema)。
  3. 再写 P3.1 实施计划,subagent-driven 执行。
  4. D-3/D-4/D-5 可在写实施计划时随各任务定。

6. 字段级设计(D-1/D-2 定案后,基于 /chat 契约亲验 @ 490517fb)

6.1 契约现状(亲验)

  • 出站 wire AgentV1ChatPayload(api/src/common/agent.client.ts:69-86):{conversation_id, run_id?, org_id, role?, specialist:{id?,name,system_prompt,prompt_template_key?}, message_history[], latest_message, agent_context?, inbound_artifacts?, kb_context_provided?, active_directives[]}无独立 kb_context 字段 —— KB 走 message_history 的 user-role 轮(conversations.service.ts:3025 history.unshift({role:"user", content: formatKbContext(...)}),含 pinGoldenFirst 排序 @3014)。
  • agent 入站 ChatRequest(agent/models/chat.py:21-54)→ _to_legacy_request(agent/routers/chat.py:47-75)把 specialist.system_prompt 塞进 agent_context["specialist_system_prompt"](persona override 读取键)。
  • agent 组装 build_hermes_prompt_with_meta(chat_helpers.py:430-459)两级拼接 → Hermes -q 单字符串(hermes_client.py:340 ["chat","-q",prompt])。硬约束:最终必须坍缩成一个 prompt 字符串。
  • NestJS 已确定性拥有(D8):persona 解析(resolveSpecialistPersona:545)、memory(appendSpecialistMemoryContext:513)、KB 渲染+golden 排序(formatKbContext:4078)、correction few-shot(maybeInjectCorrectionFewShot:452)、directives(resolveActiveDirectives:430)、persona-mode(resolvePersonaOverrideMode:5411)、web/image 门控。
  • agent 侧真正独占(结构上难前移):soul(磁盘 bind-mount /opt/data/SOUL.md)、role template(镜像磁盘 prompts/<role>.txt)、inbound-files 真实路径(workspace 物化在 R2 fetch 后才生成)、extracted-docs(R2+MarkItDown)、Hermes CLI 拼接、security/clarification/web-grounding/response-contract 常量(可搬但也可留)。

6.2 契约翻转设计(NestJS assembler + agent 执行器)

核心现实:agent 无法变成"纯执行器" —— inbound-files 真实路径/extracted-docs/soul 磁盘是运行时状态,NestJS 调用前不知道。所以采用双层结构:

NestJS AssemblerService.assemble(...) → {
assembled_context: string, // 预渲染的确定性主体(NestJS 已有/易搬的一切)
layer_manifest: Layer[], // 层序 + 优先级 + token 预算 + never_truncate 标记(trace + agent 尾块锚点)
}
↓ additive 进 agent_context.assembled_prompt(不动 /chat 顶层必填字段)
agent(执行器路径,见 assembled_prompt 时):
final_prompt = assembled_context + <按 manifest 锚点插运行时尾块:inbound-files 路径 / extracted-docs / web_context>
→ hermes -q final_prompt

assembled_context 包含(NestJS 侧确定性组装,层序即优先级 R5.1): ① 红线(SECURITY_PREFIX,never_truncate)→ ② crisis(若有)→ ③ active_directives → ④ skill(P3.2 trigger 命中的 instructions)→ ⑤ soul persona → ⑥ role 模板骨架 → ⑦ KB(结构化 chunk 渲染,带 [doc:{doc_id} chunk:{chunk_id}] 强制引用锚点,golden 置顶)→ correction few-shot → response-contract。

Layer 结构(TS + Python 双侧镜像):

interface Layer {
id: "red_lines" | "crisis" | "directives" | "skill" | "soul" | "role" | "kb" | "correction" | "response_contract" | "runtime_tail";
priority: number; // 越小越先、越不可截断
neverTruncate: boolean; // 红线/身份=true,NFR3 永不截断
tokenBudget: number | null;// 该层预算(null=不限);超预算在 assembler 侧截断,agent 不再截
charCount: number; // 实际字符数(trace)
sourceRefs?: string[]; // KB chunk ids / skill ids / soul version_id(P3.4 trace)
}

6.3 token 预算 + 永不截断(NFR3)

  • 预算在 assembler 侧执行(NestJS),agent 不再持有 3 个字符上限常量(DIRECTIVE/KB/WEB_CONTEXT_CHAR_LIMIT 废弃)。
  • 层序即截断顺序:预算不足时从优先级最低层(response-contract → correction → kb …)反向裁剪;neverTruncate:true 层(红线、soul 身份)永不进入裁剪候选
  • 总预算 = 模型 context window − 运行时尾块预留(inbound/extracted/web 的保守上限)− 响应预留。运行时尾块字符数 NestJS 未知,故预留固定余量,agent 侧尾块超预留时由 agent 截断尾块(不碰主体)。

6.4 迁移策略(不断主 pipeline)

  • additive wire:新增 agent_context.assembled_prompt({assembled_context, layer_manifest})+ agent_context.prompt_assembler_version。缺键 = 老路径(agent 自组装,现状逐字节不变)。复用 persona_override_mode 的 additive 先例(agent.client.ts:58-66,缺键=legacy)。不动 /chat 顶层必填字段(agent/CLAUDE.md 硬约束)。
  • flag-gated:prompt_assembler_enabled 默认 OFF per-Specialist。OFF=现状(agent 自组装);ON=NestJS 送 assembled_prompt、agent 走执行器路径。回滚=关 flag,零 wire 变更。
  • KB 双注入陷阱:ON 时 KB 进 assembled_context(结构化),必须同时停止 history-轮 KB 注入(conversations.service.ts:3025),否则双份 KB。flag 分支要互斥。
  • soul 双源陷阱:ON 时 soul 由 NestJS resolveSpecialistPersona 渲染进 assembled_context,agent 侧 load_org_soul 磁盘读必须跳过(否则 persona 叠加)。执行器路径不读磁盘 soul。
  • echo-guard 回归:prompt 形态变 → 重跑 agent/evals/ 的 prompt-echo/CoT-leak 回归(agent/CLAUDE.md #4012/#3977/#4021)。新旧形态并存期两套假设并存。

6.5 P3.1-P3.5 落地映射(定案后)

任务改动面依赖
P3.1 AssemblerService(NestJS):assemble() 产出 {assembled_context, layer_manifest},层序/优先级/token 预算/永不截断;additive wire + flag;agent 执行器路径(见 assembled_prompt 则用之,拼运行时尾块)NestJS 新建 AssemblerService + agent chat_helpers 加执行器分支 + wire additiveD-1/D-2 ✅
P3.2 trigger 引擎(NestJS,因 skill 消费移到 assembler 侧):读 published skill assets(config-assets)→ trigger 评估 → merge/tie-break → 命中 skill 的 instructions 进 assembled_context 层④;no-match → 角色模板 + abstainNestJS(skill 在 config-assets,assembler 在 NestJS,就近);abstain 姿态需 agent 侧 response 契约配合P3.1
P3.3 检索级联:把 pinGoldenFirst+maybeInjectCorrectionFewShot+hybrid 收敛成 assembler 内的按预算逐级编排(golden→hybrid→correction,逐级可跳过)NestJS(都已在 conversations.service,收敛进 assembler)P3.1
P3.4 full trace:layer_manifestsourceRefs(KB chunk ids、skill ids+versions、soul version_id)+ 逐层 charCount/budget → 扩 metadata.assembly(D-4 决定是否独立表)NestJS(trace 落点)+ agent(尾块 charCount 回传)P3.1+P3.3
P3.5 红线 post-draft 检查:agent 出 draft 后 / NestJS persist 前,用 skill.redLines + soul boundaries 检查 draft,违规 flag 落 Expert workspace queueagent 检查 or NestJS persist 前(D-5)+ NestJS flag相对独立,可较早

6.6 未决(写实施计划时定,非 blocking)

  • D-3 skill 输入通道:skill 现由 config-assets(NestJS)持有,assembler 在 NestJS 就近读 published skill —— 不再有"agent 从磁盘读 skill"问题(契约翻转后 skill 消费在 NestJS,不下发到 agent 磁盘)。shared/org-agnostic agent 的 runtime-config 缺失不影响(skill 不再走 agent 磁盘)。
  • D-4 trace 落点:扩 metadata.assembly vs 独立表 + 90 天。倾向先扩(数据量小)。
  • D-5 红线检查位置:agent 出 draft 后(能拿完整 draft)vs NestJS persist 前。倾向 NestJS persist 前(skill.redLines 在 config-assets、flag 落 queue 也在 NestJS,就近)+ agent 只回传 draft。
  • role template 迁移:role 模板骨架(prompts/<role>.txt)是否搬 NestJS。倾向留 agent,assembler 的 assembled_context 不含 role 骨架、由 agent 执行器在锚点插入(role 模板是 agent 镜像资产,搬迁成本高且低价值)—— 即 role 骨架也算"运行时尾块"之一。这一点写实施计划前需确认:role 留 agent 意味着 assembler 不是"全主体预渲染",层⑥(role)也是 agent 尾块锚点。

7. 建议:Phase 3 作为独立设计+实施循环

Phase 3 是 program 最大的一块(契约翻转 + 碰生产 /chat 主 pipeline + 跨 NestJS/agent + flag-gated 迁移)。建议用户评审本字段级设计(§6)后再开始 P3.1 实施——理由同 P2.4(碰生产主路径的大改动,设计需人 review 才动跨两服务代码)。评审确认点:①双层结构(assembled_context + layer_manifest + agent 尾块)是否认可;②role 模板留 agent(§6.6);③flag-gated 迁移 + soul/KB 双源互斥策略;④P3.2 trigger/P3.5 红线放 NestJS。

本文档决策已拍板(§2)。字段级设计(§6)+ §7 的 4 个推荐点(双层结构、role 留 agent、flag-gated + 双源互斥、trigger/红线放 NestJS)由用户授权全自动推进采纳(2026-07-08,用户未叫停设计评审、重申全自动)——按 §6/§7 写 P3.1 实施计划并 subagent-driven 执行。安全网:prompt_assembler_enabled 默认 OFF = 生产零行为变化;实施中如再遇同级架构分叉再停回用户。