三、第三阶段

3.1 S09 记忆

      上下文满了要压缩,压缩就有损。需要一层不参与压缩、跨会话保留的存储。 memory保存的是“以后还可能有价值、但当前代码里不容易直接重新看出来”的信息。

       3.1.1 四类记忆

1 user用户 用户偏好
2 feedback反馈 用户明确纠正过的
3 project项目 不容易从代码直接重新看出来的项目约定或背景
4 reference参照 外部资源引用

       3.1.2 用户请求开始时(循环外),选择相关记忆加载select_relevant_memories

                prompt = ( "Given the recent conversation and the memory catalog below, " "select the indices of memories that are clearly relevant. " "Return ONLY a JSON array of integers, e.g. [0, 3]. " "If none are relevant, return [].\n\n" f"Recent conversation:\n{recent}\n\n" f"Memory catalog:\n{catalog}" )

               {recent}为最近3条会话。{catalog}为存在的所有记忆文件。

       3.1.3 本轮请求结束,记忆提取保存extract_memories

                 prompt = ( "Extract user preferences, constraints, or project facts from this dialogue.\n" "Return a JSON array. Each item: {name, type, description, body}.\n" "- name: short kebab-case identifier (e.g. 'user-preference-tabs')\n" "- type: one of 'user' (user preference), 'feedback' (guidance), " "'project' (project fact), 'reference' (external pointer)\n" "- description: one-line summary for index lookup\n" "- body: full detail in markdown\n" "If nothing new or already covered by existing memories, return [].\n\n" f"Existing memories:\n{existing_desc}\n\n" f"Dialogue:\n{dialogue[:4000]}" )

                 {dialogue}为最近10条对话,返回格式 [ {name, type, description, body} ]。

       3.1.4 记忆提取保存后,>=10个文件时整理记忆consolidate_memories

                prompt = ( "Consolidate the following memory files. Rules:\n" "1. Merge duplicates into one\n" "2. Remove outdated/contradicted memories\n" "3. Keep the total under 30 memories\n" "4. Preserve important user preferences above all\n" "Return a JSON array. Each item: {name, type, description, body}.\n\n" f"{catalog[:16000]}" )

               去重、移除过期记忆、不超30个、保留重要的用户偏好。

       3.1.5 CC源码

1

记忆选择:LLM 选,不是 embedding

"根据名称和描述选出真正有用的记忆(最多 5 个)。不确定就不要选。"
2

提取时机:stop hook,不是 autoCompact 后

提取通过 forked agent 执行:受限权限、skipTranscript: truemaxTurns: 5。还有重叠保护:如果主 Agent 已经写入了记忆文件,跳过提取。
3

记忆文件格式

Markdown + YAML frontmatter
4

Dream:四层门控

  1. 时间门控:距上次合并 ≥ 24 小时
  2. 扫描节流:避免频繁扫描文件系统
  3. 会话门控:自上次合并以来修改了 ≥ 5 个会话 transcript
  4. 锁门控:没有其他进程正在合并(.consolidate-lock 文件)
5

User Memory vs Session Memory

User Memory Session Memory
持久性 跨会话 单会话
存储 memory/ 下多个 .md 文件 session-memory/<id>/memory.md
加载到 system prompt compact 摘要
用途 跨会话的知识积累 跨 compact 的上下文连续性

6

真实实现比教学版复杂的地方

  • Feature flags:记忆相关功能有多层 feature gate 控制
  • Team memory:团队共享记忆,loadMemoryPrompt() 有专门路径(教学版未涉及)
  • KAIROS:时机感知的记忆提取策略,loadMemoryPrompt() 中 daily-log 模式
  • Prompt cache:记忆注入需要考虑 prompt cache 的 TTL,避免每次都重写 system prompt 的大段内容
  • 文件锁:多进程并发时的锁机制
  • Memory prefetch:异步预取,不阻塞主流程

       3.1.6 测试prompt

       a. I prefer using tabs for indentation, not spaces. Remember that.
       b. Create a Python file called test.py(观察 Agent 是否用了 tab)
       c. What did I tell you about my preferences?(观察 Agent 是否记得)
       d. I also prefer single quotes over double quotes for strings.

3.2 S10 系统提示词

       系统提示词不应该硬编码,应该是运行时根据真实状态按需拼接,缓存结果避免重复组装

       3.2.1 四个Section,两种加载策略

Section 加载策略 内容 依据
identity 始终 角色:你是谁、怎么做事 始终存在
tools 始终 可用工具列表 enabled_tools
workspace 始终 工作目录 始终存在
memory 按需 相关记忆内容 .memory/MEMORY.md 是否存在

          PROMPT_SECTIONS = { "identity": "You are a coding agent. Act, don't explain.",

"tools": "Available tools: bash, read_file, write_file.",

"workspace": f"Working directory: {目录}",

"memory": "Relevant memories are injected below when available.", }

       3.2.2 循环:每轮循环开头拼提示词。context 变了就重新组装,没变就返回缓存。

    # 拼接系统提示词
    system = get_system_prompt(context)
    while True:
        response = client.messages.create(
            model=MODEL, system=system, messages=messages,
            tools=TOOLS, max_tokens=8000)
        # ... 工具执行 ...
        context = update_context(context, messages)
        system = get_system_prompt(context)
  get_system_prompt:命中缓存则直接返回,否则重新拼接提示词。

      context 反映当前运行态的真实状态:

{
      "enabled_tools": list(TOOL_HANDLERS.keys()),
      "workspace": str(WORKDIR),
      "memories": memories,
}

       3.2.3 CC源码

1

system prompt 有多少 section

静态 section(始终加载)、动态 section(按状态加载)、mcp_instructions 是唯一的易失性 section
2

组装函数

返回 string[](每个元素是一个 section),由 SYSTEM_PROMPT_DYNAMIC_BOUNDARY 分隔静态和动态部分
3

cache scope

三层缓存:

1.lodash memoize

2.section 注册缓存

3.API 级缓存

4

getUserContext vs getSystemContext

getSystemContext getUserContext
内容 gitStatus、cacheBreaker CLAUDE.md 内容、currentDate
注入方式 追加到 system prompt 数组 前置为 <system-reminder> 用户消息
何时跳过 自定义 system prompt 时 始终运行

5

模式如何改变 prompt

1. CLAUDE_CODE_SIMPLE:整个 prompt 只有 2 行
2. Proactive/KAIROS:用紧凑版 prompt 替换所有标准 section
3. Coordinator:用协调器专用 prompt 完全替换
4. Agent 模式:Agent 定义的 prompt 替换或追加到默认 prompt
6

总大小

标准交互模式下 system prompt 核心约 20-30KB 文本。CLAUDE_CODE_SIMPLE 约 150 字符。

       3.2.4 测试prompt

         a. Read the file README.md(观察始终加载的三个 section)
         b. Create a file called .memory/MEMORY.md with content "- [test](test.md) — test memory"(写入记忆索引)
         c. Read the file code.py(观察 memory section 是否出现)

3.3 S11 错误恢复

       错误不是终点,是重试的起点。重试须控制预算(次数)。

       3.3.1 三种最常见的恢复模式

模式 触发 恢复动作
输出截断 max_tokens 升级 8K→64K(1次) / 续写提示(3次)
上下文超限 prompt_too_long reactive compact(最近5条消息) → 重试(1次)
临时故障 429 / 529 指数退避(延时) + 抖动(10次),连续 529(3次) 可切换备用模型

       3.3.2 循环:外层 try/except 捕获 API 异常(prompt_too_long 等),with_retry 处理瞬态错误(429/529),stop_reason 检查处理截断。

    system = get_system_prompt(context)
    state = RecoveryState() # 状态记录
    max_tokens = DEFAULT_MAX_TOKENS

    while True:
        # ── LLM call: with_retry处理临时故障,如429/529 ──
        try:
            response = with_retry(
                lambda mt=max_tokens, mdl=state.current_model:
                    client.messages.create(
                        model=mdl, system=system, messages=messages,
                        tools=TOOLS, max_tokens=mt),
                state)
        except Exception as e: #处理api异常
            # Path 2: prompt_too_long -> reactive compact (once)
            if is_prompt_too_long_error(e):
                if not state.has_attempted_reactive_compact:
                    messages[:] = reactive_compact(messages)
                    state.has_attempted_reactive_compact = True
                    continue
                messages.append({"role": "assistant", "content": [
                    {"type": "text",
                     "text": "[Error] Context too large, cannot continue."}]})
                return

            # Unrecoverable
            name = type(e).__name__
            messages.append({"role": "assistant", "content": [
                {"type": "text", "text": f"[Error] {name}: {str(e)[:200]}"}]})
            return

        # ── Path 1: LLM返回的错误,如max_tokens ──
        if response.stop_reason == "max_tokens":
            # First escalation: don't append truncated output, retry same request
            if not state.has_escalated:
                max_tokens = ESCALATED_MAX_TOKENS
                state.has_escalated = True
                continue
            # 64K still truncated: save truncated output + continuation prompt
            messages.append({"role": "assistant", "content": response.content})
            if state.recovery_count < MAX_RECOVERY_RETRIES:
                messages.append({"role": "user", "content": CONTINUATION_PROMPT})
                state.recovery_count += 1
                continue
            return

        # Normal completion: append assistant response
        messages.append({"role": "assistant", "content": response.content})

        if response.stop_reason != "tool_use":
            return

        # ... Tool execution ...

       3.3.3 CC源码

1

十几种 reason/transition(不只是 3 条)

max_turns等
2

指数退避的精确公式

delay = min(500 × 2^(attempt-1), 32000) + random(0~25%)
3

CONTINUATION 提示原文

Output token limit hit. Resume directly — no apology, no recap of what
you were doing. Pick up mid-thought if that is where the cut happened.
Break remaining work into smaller pieces.
4

流式错误处理

可恢复的错误(413、max_tokens、media error)在 streaming 期间被暂扣不展示,等 streaming 结束后才判断是否需要恢复。
5

529 → Fallback Model 切换

连续 3 次 529 过载错误后(MAX_529_RETRIES = 3),CC 自动切换到 fallback model。切换时清除所有 pending 消息和 tool 结果。
6

Diminishing Returns 检测

当连续 3 次 continuation 且 token 增量 < 500 时,系统判断"继续也没有实质性产出",停止 continuation

       3.3.4 测试prompt

        a. 让 Agent 生成一段很长的代码,观察截断后是否自动续写
        b. 连续读取大量文件撑大上下文,观察 reactive compact
        c. 如果遇到 429/529,观察指数退避的日志输出

四、第四阶段

4.1 S12 任务系统

      任务系统比 todo 多出来的核心能力,是“依赖关系”和“持久化”。

       4.1.1 TodoWrite vs Task System

TodoWrite Task System
定位 当前任务的执行清单 可恢复的任务系统
存储 进程内 / 会话状态 .tasks/{id}.json
依赖 blockedBy / blocks 依赖图
生命周期 当前会话 / 当前任务 跨会话保留
分工 不负责任务认领 owner / claim
状态 pending / in_progress / completed pending / in_progress / completed
粒度 Agent 自己的步骤 可被认领、追踪、解锁的任务

       4.1.2 五个任务工具

create_task 创建任务
claim_task 认领任务
complete_task 完成与解锁
get_task 查看任务完整细节
list_tasks 列出所有任务状

       4.1.3 CC源码

1

TaskRecord 的完整字段

id: 递增整数 ID

subject: 简短标题

description: 自由格式描述

activeForm: 进行时态

owner: 分配的 agent ID

status: 生命周期

blocks:此任务阻塞的任务 ID

blockedBy: 阻塞此任务的任务 ID

metadata: 任意扩展键值对

2

不是 TodoWrite 的升级,是两个独立系统

Task System 和 TodoWrite 同时存在。交互式会话默认启用 Task(V2),非交互式/SDK 默认用 TodoWrite。
3

并发认领的锁机制

claimTask用双重锁防竞争:

a. 任务文件锁

b. 列表级锁

4

高水位标防 ID 重用

.highwatermark 文件记录曾分配过的最高任务 ID。即使任务被删除,ID 也不会被重用。
5

四个 Task 工具

TaskCreateTaskGetTaskUpdateTaskList

       4.1.4 测试prompt

        a. Create tasks: setup database schema, create API endpoints (depends on schema), write tests (depends on endpoints), write docs (depends on schema)
        b. List all tasks and their statuses
        c. Claim the first unblocked task and complete it
        d. List tasks again — which ones are now unblocked?

4.2 S13 后台任务

       “慢操作”扔到后台线程,Agent 继续跑主循环,后台完成后把通知注入到对话里。 

      4.2.1 循环:工具执行分两条路,通知和结果合并为一条 user 消息。

        results = []
        for block in response.content:
            if block.type != "tool_use":
                continue

            if should_run_background(block.name, block.input):
                bg_id = start_background_task(block)
                results.append({"type": "tool_result",
                                "tool_use_id": block.id,
                                "content": f"[Background task {bg_id} started] "
                                           f"Command: {block.input.get('command', '')}. "
                                           f"Result will be available when complete."})
            else:
                output = execute_tool(block)
                results.append({"type": "tool_result",
                                "tool_use_id": block.id,
                                "content": output})

        # 通知和工具结果合入同一条user消息
        user_content = []
        bg_notifications = collect_background_results()
        if bg_notifications:
            for notif in bg_notifications:
                user_content.append({"type": "text", "text": notif})
        user_content.extend(results)
        messages.append({"role": "user", "content": user_content})

      4.2.2 CC源码

1

pendingToolUseSummary:Haiku 后台生成

每批工具执行完后,启动一个 Haiku side-query 生成工具使用摘要。
2

线程模型:没有真正的线程

运行在 Node.js/Bun 单线程事件循环中。"后台"只是 "不 await"。
3

七种后台任务类型

local_bashlocal_agentremote_agentin_process_teammatelocal_workflowmonitor_mcpdream。每种有自己的注册、生命周期和通知机制。
4

通知注入:命令队列

后台任务完成后通过 enqueueTaskNotificatio或 enqueuePendingNotification入队到共享命令队列
5

停滞看门狗

后台 bash 任务有一个看门狗,防止后台任务卡在无人响应的交互式对话框。
6

并发限制

前台工具调用:CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY(默认 10 个并发安全工具)。

      4.2.3 测试prompt

        a. Run pip list in the background and find all Python files in this directory 
        b. Run npm install (use run_in_background) and while waiting, read package.json
        c. Create a task to setup the project, then run pip list in the background

4.3 S14 定时任务

       4.3.1 Cron 调度四层模型

Scheduler daemon 线程,每秒轮询,判断时间到了没有 调度器(cron_scheduler_loop)跑在独立的 daemon 线程里,不依赖 agent_loop 是否在执行。单个 job 异常不会杀掉整个线程
Queue cron_queue,调度线程写入已触发任务 -
Queue Processor 发现队列非空且 Agent 空闲,启动一轮 agent_loop queue_processor_loop
Consumer agent_loop 从队列消费,注入到 messages -

       4.3.2 CC源码

1

三个 Cron 工具

CronCreateCronDeleteCronList
2

存储:

.claude/scheduled_tasks.json

Durable 任务写磁盘;session-only 任务存于 STATE.sessionCronTasks 内存数组。.scheduled_tasks.lock 文件防止同项目的多个 session 重复触发。
3

调度器:1 秒轮询

cronScheduler.ts 每秒检查一次
4

Cron 表达式:标准 5 字段

分钟 小时 日 月 星期
5

抖动(防惊群效应)

  • 重复性任务:触发延迟最多可达期间的 10%(上限 15 分钟),基于任务 ID 的确定性哈希
  • 一次性任务:当触发时间落在 :00 或 :30 时,最多提前 90 秒触发
  • 抖动配置可通过 GrowthBook 实时调整,60 秒刷新一次
6

自动过期

重复性任务 7 天后自动过期(可配置,上限 30 天)。
7

作业数上限

MAX_JOBS = 50
8

触发注入

触发后通过 enqueuePendingNotification() 以 priority: 'later' 入队命令队列。
9

Queue Processor:自动交付

在无 query、无阻塞 UI、队列非空时自动触发处理

       4.3.3 测试prompt

        a. Schedule a task to print the current date every 2 minutes
        b. List all cron jobs
        c. Create a one-shot reminder in 1 minute to check the build status
        d. Cancel the recurring job and verify with list_crons

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐