Claude Code中长会话和 Agent 分工时提示词怎么变

4 阅读7分钟

一次对话开始容易,撑过 100 轮才是考验。context 超限、Agent fork、MCP 注入——这篇文章讲清楚提示词系统如何应对这些复杂场景。


先建立一个直觉

普通提示词系统像一张纸:越写越长,直到写不下。

Claude Code 的做法是三种策略组合:

策略一:微压缩(microcompact)
  → 悄悄把旧的工具结果缩短,用户无感知

策略二:主动摘要(compact)
  → 把整段历史总结成一段话,重新开始

策略三:折叠(context collapse)
  → 隐藏部分内容,需要时可以展开

还有两个扩展场景:

  • Agent fork:子 Agent 不是"主 prompt 加几句话",而是独立的提示词域
  • MCP 注入:外部服务既带来工具,也带来自己的 prompt 指令

长会话的敌人:context 窗口

每轮对话,消息历史都在增长:用户消息 + AI 回复 + 工具调用结果,三者叠加。工具结果是大头——读一个文件可能 200 行,跑一条命令可能几十行输出。30 轮对话后,总 token 量轻松超过 60k;80 轮后,接近大多数模型的上限。

不处理会发生三件事:

  1. 413 错误:context 超限,API 直接拒绝
  2. 行为漂移:context 太长,早期的规则和背景被"稀释"
  3. 成本爆炸:每轮都要把所有历史发给 API

Claude Code 用三道防线依次应对。

三道防线的执行顺序:

顺序防线触发条件对用户可见性
每轮无条件microcompact无,静默运行不可见
token 超阈值时compact自动触发或手动 /compact/compact 时可见
compact 后仍超限context collapsecompact 之后的最后兜底不可见

了解顺序之后,下面逐节展开每道防线的实现细节。


第一道防线:microcompact——静默清理旧工具结果

microCompact.ts 在每轮循环开始前运行,用户完全感知不到。

它做什么?

把老的工具调用结果替换成占位符:

原始内容:
[Read 工具返回了 500 行代码]

microcompact 后:
[Old tool result content cleared]

为什么 tool result 是最值得压缩的?

工具结果通常是大头:读一个文件可能 200 行,跑一条命令可能几十行输出。但这些结果的"有效期"很短——AI 读完文件、处理完结果之后,原始内容就不再需要了,只有 AI 的分析和决策才是真正需要保留的。

哪些工具的结果会被压缩?

// microCompact.ts:41-50
const COMPACTABLE_TOOLS = new Set([
  FILE_READ_TOOL_NAME,    // Read
  ...SHELL_TOOL_NAMES,    // Bash
  GREP_TOOL_NAME,         // Grep
  GLOB_TOOL_NAME,         // Glob
  WEB_SEARCH_TOOL_NAME,   // WebSearch
  WEB_FETCH_TOOL_NAME,    // WebFetch
  FILE_EDIT_TOOL_NAME,    // Edit
  FILE_WRITE_TOOL_NAME,   // Write
])

几乎所有会产生大量输出的工具都在列表里。

microcompact 还管缓存

microCompact.ts 维护一套 cacheEdits 状态——记录哪些消息被压缩了、对应的缓存块在哪里。这样 API 层可以精确地更新缓存,而不是每次都重新发送整段历史。


第二道防线:compact——主动摘要整段历史

当 microcompact 不够用,或者用户手动执行 /compact,就会触发 compact。

compact 的核心是一次特殊的模型调用

它让模型读完整段对话历史,然后生成一份结构化摘要。但这次调用有一个关键限制:

// compact/prompt.ts:19-26
CRITICAL: Respond with TEXT ONLY. Do NOT call any tools.
- Do NOT use Read, Bash, Grep, Glob, Edit, Write, or ANY other tool.
- You already have all the context you need in the conversation above.
- Tool calls will be REJECTED and will waste your only turn — you will fail the task.
- Your entire response must be plain text: an <analysis> block followed by a <summary> block.

为什么要专门禁止工具调用?

因为 compact 本身就是一次模型调用,而这个模型在看到对话历史时,可能会"顺手"想去读文件、跑命令。但 compact 的目的只是摘要,不是继续执行任务。一旦它调用了工具,这次 compact 就失败了(只有一次机会)。

结尾还有一个 trailer 再次强调:

REMINDER: Do NOT call any tools. Respond with plain text only.
Tool calls will be rejected and you will fail the task.

头尾都写,确保模型不会"忘记"。

摘要的格式

模型的回复被要求包含两个 XML 块:

<analysis>
[草稿便签:模型用来整理思路,不会出现在最终摘要里]
</analysis>

<summary>
[真正的摘要内容,会替换掉整段历史]
</summary>

formatCompactSummary() 会剥掉 <analysis> 块,只保留 <summary> 的内容。

三种摘要模式

模式触发场景特点
BASE_COMPACT_PROMPT完整对话摘要9 个结构化部分
PARTIAL_COMPACT_PROMPT只摘要最近的消息(from)保留早期历史
PARTIAL_COMPACT_UP_TO_PROMPT摘要到某个位置(up_to)为继续工作准备上下文

第三道防线:context collapse——主循环的最后兜底

compact 是"重写历史",collapse 是"折叠历史"。

query.ts 的主循环在每轮开始时调用 applyCollapsesIfNeeded()

flowchart TD
    A["每轮循环开始"] --> B{"compact 已执行,\ntoken 仍接近上限?"}
    B -- 否 --> C["正常继续"]
    B -- 是 --> D["applyCollapsesIfNeeded()"]
    D --> E["找到可以折叠的消息段\n通常是工具调用的中间过程"]
    E --> F["标记为 collapsed\n不发给 API,但本地保留"]
    F --> G["继续本轮循环\n用更少的 token"]

collapse 和 compact 的本质区别:

  • compact:历史被摘要替换,原始内容消失
  • collapse:历史被折叠隐藏,本地还在,只是不发给 API

collapse 更轻量,是"最后一道防线"而不是主要手段。

三道防线的触发顺序:microcompact 在每轮静默运行(无需触发条件);compact 在 token 消耗超过阈值时自动触发,或用户手动 /compact;collapse 在 compact 之后仍然接近上限时作为最后手段。


Agent fork:每个子 Agent 都有独立的提示词域

当任务需要分工(比如"一个 Agent 探索代码库,另一个 Agent 写代码"),Claude Code 会 fork 出子 Agent。

子 Agent 不是在主 prompt 上追加几句话。它是一个完整的独立实例:

flowchart TD
    subgraph 主 Agent
        A["完整 system prompt\n身份 + 规范 + 工具协议"]
        B["完整 userContext\nCLAUDE.md + rules"]
        C["完整工具集"]
        D["完整 MCP 连接"]
    end

    subgraph Explore Agent(子 Agent)
        E["裁剪后的 system prompt\n去掉 CLAUDE.md\n去掉 git 状态"]
        F["❌ 不继承 userContext\n不需要提交规范"]
        G["受限工具集\n只读工具"]
        H["独立 memory"]
    end

    subgraph Plan Agent(子 Agent)
        I["裁剪后的 system prompt\n去掉 CLAUDE.md\n去掉 git 状态"]
        J["❌ 不继承 userContext"]
        K["受限工具集"]
        L["独立 memory"]
    end

    主 Agent -->|fork| Explore Agent
    主 Agent -->|fork| Plan Agent

为什么 Explore Agent 要去掉 CLAUDE.md 和 git 状态?

Explore Agent 的任务是快速只读探索代码库。它不需要知道提交规范(不会提交代码),不需要 git 状态(会自己跑 git status)。去掉这些内容节省 token,每天数千万次调用,效果很可观。

子 Agent 的配置来自哪里?

loadAgentsDir.ts.claude/agents/ 目录加载 agent 定义,每个 agent 可以独立配置:

system prompt / getSystemPrompt
tools / disallowedTools
skills
mcpServers
hooks
memory
initialPrompt
permissionMode
maxTurns

这让子 Agent 不只是"功能受限的主 Agent",而是可以针对特定任务深度定制的独立实例。


MCP 的双重身份:工具 + 提示词来源

MCP(Model Context Protocol)server 在提示词系统里扮演两个角色,很多人只知道第一个:

flowchart LR
    A["MCP Server"] --> B["工具层\ntool schema\n进入 API tools 列表"]
    A --> C["指令层\ninstructions 字段\n通过 getMcpInstructionsSection()\n注入 system prompt 动态段"]

工具层是大家熟悉的:MCP server 暴露工具,模型可以调用。

指令层是容易被忽略的:MCP server 可以带 instructions 字段,这些指令会通过 getMcpInstructionsSection() 注入到 system prompt 的动态段里。

这意味着连接一个 MCP server,不只是给模型增加了新工具,还给模型增加了新的行为约束。比如一个数据库 MCP server 可以在 instructions 里写"查询前必须先确认操作范围"。

为什么 MCP instructions 放动态段而不是 userContext?

因为它是会话级的外部服务配置,不是用户的长期规则。每次会话连接的 MCP server 可能不同,放动态段可以精确控制缓存边界(不同 MCP 配置不共享缓存)。


slash command 的三条路

/command 看起来像本地命令,但它对提示词的影响可以很深:

flowchart TD
    A["/command 输入"] --> B{"command 类型"}

    B --> C["本地执行\n比如 /help、/status"]
    C --> D["结果直接展示给用户\n不进入 API 调用"]

    B --> E["prompt command\n比如 /compact、skill 命令"]
    E --> F{"context 策略"}
    F --> G["inline\n把 skill 内容展开\n注入为当前会话的 prompt block"]
    F --> H["fork\n启动子 Agent\n独立上下文执行\n结果回灌主会话"]

    B --> I["带 model / allowedTools / hooks 的命令"]
    I --> J["在 fork 的子 Agent 里执行\n独立 prompt 域 + 受限工具集"]

command 的类型定义(types/command.ts):

type: 'prompt'
context?: 'inline' | 'fork'
agent?: string        // 指定用哪个 agent 定义
allowedTools?         // 限制可用工具
model?                // 覆盖模型
hooks?                // 这个命令专属的 hooks
effort?               // 思考深度

本质上,slash command 是一个提示词注入和执行策略分发器,不只是快捷方式。


长会话 + 多 Agent 的全景

flowchart TD
    A["主会话\n完整 prompt 域"] -->|每轮静默运行| E["microcompact\n清理旧 tool result\n用户无感知"]

    A --> B{"context 接近上限?"}
    B -- 否 --> C["正常执行"]
    B -- 是 --> F["compact\n摘要整段历史\n重新开始"]
    B -- 是 --> G["context collapse\n折叠中间过程\n最后兜底"]

    A --> H{"/command 或 AgentTool"}
    H --> I["fork 子 Agent"]
    I --> J["独立 prompt 域\n裁剪 userContext\n独立工具集\n独立 MCP 连接"]
    J --> K["子 Agent 完成任务"]
    K --> L["结果回灌主会话\n作为 tool result 注入"]

    A --> M["MCP Server 连接"]
    M --> N["工具层:tool schema → API tools"]
    M --> O["指令层:instructions → system prompt 动态段"]

小结

长会话和多 Agent 场景下,提示词系统的核心挑战是在有限的 context 窗口里,把最有价值的信息保留下来

机制解决什么问题对用户的可见性
microcompact旧 tool result 占空间完全不可见
compact整段历史太长/compact 时可见
context collapse接近上限时的最后兜底不可见
Agent fork任务分工,避免主会话污染Agent 工具调用时可见
MCP instructions外部服务的行为约束不可见(注入 system prompt)

这套机制让 Claude Code 能够处理真实世界里的长任务——不是靠"更大的 context 窗口",而是靠主动治理 context 的内容和结构