第 10 章:Prompt、上下文与会话连续性
10.1 Multica 的上下文工程不是拼一个长字符串
Multica 同时维护两类输入:
- 本轮 Prompt:告诉 Agent “这次为什么被叫醒、先处理哪条消息、最终回复到哪里”;
- 持久 Brief:告诉 Agent “你是谁、在哪个工作区、能用哪些命令、有哪些仓库与能力、必须遵守什么交付约束”。
两者的事实源分别在:
- server/internal/daemon/prompt.go 的
BuildPrompt; - runtime_config.go 与 runtime_config_sections.go。
这项拆分很重要。每次评论都重复完整工作区说明,会浪费上下文;只发一句“请处理新评论”,Fresh Session 又不知道 Issue、仓库和回复协议。
10.2 Claim 响应是上下文快照
Daemon 不在本地临时查询几十个 API。Server 在 Claim 时组装 Task:
- Task、Issue、触发 Comment 与 Thread;
- Agent 身份、说明、模型、参数和 Runtime Config;
- Workspace Context、请求用户、任务发起人;
- Project、Repositories、Project Resources;
- Skills、Skill Refs、禁用的本地 Skills;
- Connected Apps 与合并后的 MCP Config;
- 上次 Provider Session ID 与 Workdir;
- Chat、Quick Create、Autopilot、Squad 的专用字段。
它不是长期缓存,而是这一次运行的能力与归因快照。即使运行中管理员修改 Agent,已 Claim 的任务仍按自己的快照执行,结果才可解释。
10.3 五类入口,五种 Prompt
BuildPrompt 先按字段判别任务种类:
| 判别信号 | 任务类型 | Prompt 的首要目标 |
|---|---|---|
ChatSessionID 非空 | Chat | 读取会话、回答当前消息 |
QuickCreatePrompt 非空 | Quick Create | 恰好创建一个 Issue |
AutopilotRunID 非空 | Autopilot run-only | 执行自动化并写 Run 结果 |
TriggerCommentID 非空 | Comment | 处理新评论并回复正确线程 |
| 其余 | Assignment | 完成被分配的 Issue |
这比一个通用模板加可选段落更安全,因为每类入口的交付动作不同:Chat 要回 Chat,Comment 要回 Thread,Quick Create 要创建 Issue,run-only 不能假装有 Issue。
10.4 Assignment Prompt
Assignment 是冷启动基线:
- 明确 Agent 已被分配到哪个 Issue;
- 要求用任务内 CLI 读取完整 Issue;
- 提醒检查仓库并按需 Checkout;
- 要求持续更新进度;
- 最终以 Comment 或状态变化完成交付。
Issue 正文没有全部塞进短 Prompt。详细上下文写入执行目录,使 Provider 的原生上下文发现机制也能读到。
10.5 Comment Prompt 与“新消息指针”
评论触发最难,因为它可能是:
- 一个新线程根评论;
- 某个旧线程的回复;
- Agent 上次运行以后出现的若干新评论;
- 多条排队评论被合并到一个 Task;
- 多个不同线程的评论被合并。
reply_instructions.go 区分三种读取提示:
BuildNewCommentsHint:已知时间锚点和新评论数量;BuildResumedCommentsHint:会话已续接,只需定向读最新内容;BuildColdCommentsHint:无法续接,要求重新建立上下文。
Prompt 还直接嵌入 CoalescedComments 的作者、时间、线程与内容。这样 Agent 不会误以为所有合并评论都属于同一 Thread。
10.6 回复线程是交付协议
对普通评论,Agent 最终应回复触发线程;对多线程合并,Prompt 会给出多个 ThreadReplyTarget。
这样做不是 UI 偏好,而是并发正确性:
- 回复错到 Issue 根会丢失讨论上下文;
- 只回复最新一条会让较早的合并评论永远悬空;
- 用普通文本作为最终输出,Server 无法确认结果是否真的写入业务对象。
BuildCommentReplyInstructions 会按 Provider 选择详细或瘦身版本,但“用 Multica CLI 完成交付”的不变量不变。
10.7 Chat Prompt
Chat 又分 Web、Slack、Feishu 等 Channel:
- Web Chat 可以携带已上传的附件元数据;
- Slack 需要区分频道消息与 Thread Reply;
- Feishu 与 Slack 的可用历史命令、Markdown 能力不完全相同;
- 外部 Channel 默认要求文本式回复,不能假定 Web 富交互组件存在。
prompt.go 会告诉 Agent 先读取哪个会话范围,并将附件写成 ID、文件名、Content Type。Agent 通过 multica attachment download <id> 下载,不接触会过期的 CDN 签名 URL。
ChatIntro 是特殊分支:它要求 Agent 主动自我介绍,不伪造一条不存在的用户问题。
10.8 Quick Create 的 Exact-once 约束
Quick Create 把自然语言转成结构化 Issue。Prompt 明确:
- 创建且只创建一个 Issue;
- 尊重用户显式选择的 Priority、Due Date;
- 绑定上传附件;
- 若从子 Issue 入口发起,使用给定 Parent;
- 创建成功后返回新 Issue,而不是继续执行它。
“只创建一个”防止模型一边重试一边产生重复对象。真正的幂等还依赖 Server Task 状态与 CLI 请求,但 Prompt 是 Agent 行为层的第一道边界。
10.9 Autopilot run-only 没有虚构 Issue
run-only 任务携带:
- Autopilot 标题与说明;
- Source:manual、schedule、webhook 或 API;
- 可选 Trigger Payload;
- 持久化 Run ID。
它的输出目标是 Autopilot Run,不是 Issue Comment。与 create_issue 模式相比,run-only 省去中间业务对象,适合巡检、同步、汇总等动作。
Prompt 不能引用空 Issue ID,否则 Agent 可能调用错误命令,或把自动化结果写到无关 Issue。
10.10 Squad Leader 的 Prompt 是协调器契约
Leader Task 不等同于普通执行任务:
- Leader 先理解目标,再把工作委派给 Squad Members;
- 它可以观察子任务和回复;
- 不应抢 Worker 的具体实现;
- 没有可执行动作时用结构化
no_action活动收尾; - Agent-to-Agent 确认信息可静默处理,避免每次 ACK 都触发新任务。
这套特殊说明来自 runtime_config_sections.go 与 Squad 任务字段,而不是 Provider 自己理解“团队”。
10.11 长 Brief 写到哪里
InjectRuntimeConfig 按 Provider 选择原生入口,例如:
- 支持
CLAUDE.md语义的 Provider 写对应说明文件; - Codex/Pi 等采用
AGENTS.md或 Provider 可发现的等价位置; - Multica 自己还写任务上下文 Marker、Project Resource JSON 和 Sidecar Manifest。
文件位置由 runtime_config.go 统一决定,不能在 Adapter 里各自随意写。Marker Block 让 Multica 只更新自己管理的区段,尽量保留仓库原有说明。
10.12 Brief 的层次
runtime_config_sections.go 按稳定顺序生成:
- Multica 管理标记与后台任务安全;
- Agent Identity;
- Requesting User;
- Task Initiator;
- Workspace Context;
- Connected Apps;
- 可用命令;
- Repositories 与 Checkout Preflight;
- Project Context 和 Resources;
- Session Continuity Notice;
- 当前任务类型的 Workflow;
- Skills、Mentions、Attachments;
- 输出与交付不变量。
稳定顺序便于测试,也让模型先建立身份与权限,再读任务操作细节。
10.13 Requesting User 与 Task Initiator 不相同
Claim 中有两组身份:
- Requesting User:Runtime Owner,决定本机凭据和个人上下文;
- Task Initiator:这次 Comment 或 Chat 的真实发起者,只用于归因和语义。
Initiator 不是 Credential。某位同事在共享工作区 @Agent,并不会让 Agent 自动获得那位同事的第三方应用授权;运行仍使用 Owner 范围的能力。
这是多用户共享 Agent 最容易误解的信任边界。
10.14 三层 Session 必须分开
| 名称 | 持久对象 | 作用 |
|---|---|---|
| Task | Multica 数据库 Task | 一次执行尝试和状态机 |
| Provider Session | Claude/Codex 等 Session ID | 模型与 Agent CLI 的原生上下文 |
| Chat Session | Multica Chat Session | 用户可见的长期会话 |
一次 Chat Session 可以产生多个 Task;多个 Task 可以续接同一个 Provider Session;重试也可能因为中毒而创建新 Provider Session。三者不能用一个 ID 代替。
10.15 Session 与 Workdir 必须成对恢复
Provider Session 往往引用:
- 之前看到的文件;
- 相对路径;
- Provider Home 中的 Transcript/Rollout;
- 上一轮生成但未提交的改动。
因此 gateResumeToReusedWorkdir 规定:只有成功复用先前 Workdir,才能继续使用 PriorSessionID。
否则模型记得旧文件状态,进程却站在一个全新目录,最危险的不是报错,而是基于错误假设继续修改。
10.16 Codex 还有 Rollout 存在性门
Codex 的 Session ID 只有在任务隔离的 Codex Home 中还能找到对应 Rollout 才可 Resume。
gateCodexResumeToRolloutPresence 会在目录复用成功以后做第二次检查。不存在就同时清掉:
- Backend 的 Prior Session;
- Brief 中“已续接”的声明。
系统宁愿显式告诉 Agent 连续性丢失,也不假装 Resume 成功。
10.17 Provider 拒绝 Resume 时如何降级
运行前检查只能发现本地缺失;Provider 仍可能在握手时拒绝 Session。
正确降级需要 Adapter 返回 ResumeRejected=true:
- 终止当前失败尝试;
- 在同一安全 Workdir 启动 Fresh Session;
- 注入 Session Continuity Notice;
- 不把网络故障、限流或认证失败误判为 Session 丢失。
第 9 章已经说明:只有“Session 明确不存在或不兼容”的正面证据才允许 Fresh Retry。
10.18 中毒 Session
server/internal/daemon/poisoned.go 识别一组不适合继续 Resume 的失败:
- Iteration Limit;
- Agent Fallback Message;
- API Invalid Request;
- Codex Semantic Inactivity;
- Context Overflow;
- 原始 400 Invalid Request 形态。
这些错误往往会在同一上下文中稳定重现。下次任务保留 Issue/Comment 历史,但舍弃 Provider Session,避免“恢复”变成无限失败循环。
10.19 Context 文件的生命周期
- 写任务 Marker;
- 序列化 Project Resources;
- 物化托管 Skills;
- 修复 Skill Frontmatter;
- 渲染 Issue、Quick Create、Autopilot Context;
- 把创建过的 Sidecar 记入 Manifest。
清理时按 Manifest 删除 Multica 管理文件,不遍历猜测用户文件。对复用 Workdir,这一点尤其关键。
10.20 为什么不把所有历史塞进 Prompt
Multica 把上下文压缩主要委托给 Provider:
- Provider Session 管理模型级历史;
- Issue/Chat 历史由 CLI 按需读取;
- Brief 放稳定规则;
- 本轮 Prompt 只放增量触发;
- Project Resource 作为独立文件暴露。
优点是减少重复 Token,也避免 Claim Payload 随 Issue 历史无限增长。代价是 Provider Resume、任务内 CLI 和文件注入必须同时可靠。
10.21 扩展一种任务类型时的检查清单
增加新入口不能只改 Prompt:
- 在数据库和 Claim Wire 中增加明确判别信号;
- 在
BuildPrompt定义本轮目标; - 在 Brief 定义命令、交付位置和禁区;
- 说明是否允许 Resume/Workdir Reuse;
- 定义取消和终态写到哪里;
- 检查任务令牌是否有最小权限;
- 为 Fresh、Warm、Coalesced、Retry 写测试;
- 确认 GC 能识别这种环境归属。
10.22 本章结论
Multica 的上下文连续性由四个对齐关系维持:
- Prompt 对齐当前触发;
- Brief 对齐长期身份与工作流;
- Provider Session 对齐模型历史;
- Workdir 对齐文件世界。
任何一个只“看起来续上了”,而另一个已经变化,都会制造比显式冷启动更难发现的错误。