跳到主要内容

第 10 章:Prompt、上下文与会话连续性

10.1 Multica 的上下文工程不是拼一个长字符串

Multica 同时维护两类输入:

  • 本轮 Prompt:告诉 Agent “这次为什么被叫醒、先处理哪条消息、最终回复到哪里”;
  • 持久 Brief:告诉 Agent “你是谁、在哪个工作区、能用哪些命令、有哪些仓库与能力、必须遵守什么交付约束”。

两者的事实源分别在:

这项拆分很重要。每次评论都重复完整工作区说明,会浪费上下文;只发一句“请处理新评论”,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 是冷启动基线:

  1. 明确 Agent 已被分配到哪个 Issue;
  2. 要求用任务内 CLI 读取完整 Issue;
  3. 提醒检查仓库并按需 Checkout;
  4. 要求持续更新进度;
  5. 最终以 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 按稳定顺序生成:

  1. Multica 管理标记与后台任务安全;
  2. Agent Identity;
  3. Requesting User;
  4. Task Initiator;
  5. Workspace Context;
  6. Connected Apps;
  7. 可用命令;
  8. Repositories 与 Checkout Preflight;
  9. Project Context 和 Resources;
  10. Session Continuity Notice;
  11. 当前任务类型的 Workflow;
  12. Skills、Mentions、Attachments;
  13. 输出与交付不变量。

稳定顺序便于测试,也让模型先建立身份与权限,再读任务操作细节。

10.13 Requesting User 与 Task Initiator 不相同

Claim 中有两组身份:

  • Requesting User:Runtime Owner,决定本机凭据和个人上下文;
  • Task Initiator:这次 Comment 或 Chat 的真实发起者,只用于归因和语义。

Initiator 不是 Credential。某位同事在共享工作区 @Agent,并不会让 Agent 自动获得那位同事的第三方应用授权;运行仍使用 Owner 范围的能力。

这是多用户共享 Agent 最容易误解的信任边界。

10.14 三层 Session 必须分开

名称持久对象作用
TaskMultica 数据库 Task一次执行尝试和状态机
Provider SessionClaude/Codex 等 Session ID模型与 Agent CLI 的原生上下文
Chat SessionMultica 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

  1. 终止当前失败尝试;
  2. 在同一安全 Workdir 启动 Fresh Session;
  3. 注入 Session Continuity Notice;
  4. 不把网络故障、限流或认证失败误判为 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 文件的生命周期

execenv/context.go 负责:

  • 写任务 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:

  1. 在数据库和 Claim Wire 中增加明确判别信号;
  2. BuildPrompt 定义本轮目标;
  3. 在 Brief 定义命令、交付位置和禁区;
  4. 说明是否允许 Resume/Workdir Reuse;
  5. 定义取消和终态写到哪里;
  6. 检查任务令牌是否有最小权限;
  7. 为 Fresh、Warm、Coalesced、Retry 写测试;
  8. 确认 GC 能识别这种环境归属。

10.22 本章结论

Multica 的上下文连续性由四个对齐关系维持:

  • Prompt 对齐当前触发;
  • Brief 对齐长期身份与工作流;
  • Provider Session 对齐模型历史;
  • Workdir 对齐文件世界。

任何一个只“看起来续上了”,而另一个已经变化,都会制造比显式冷启动更难发现的错误。