5 min read
Day 08:Context Builder:AI Coding Agent 上下文构建器

上一篇:Transcript Logging:Agent 执行日志与可观测性
下一篇:Context Explorer:调试 Prompt 与 Token 使用
English version: Context Builder for AI Coding Agents

文章介绍

Day 07 我们把 agent run 的关键事件写成了 JSONL transcript。现在已经能回答“运行时发生了什么”。

Day 08 要解决另一个更靠前的问题:模型到底看到了什么?

coding agent 的效果很大程度取决于上下文组装。上下文不是把所有字符串拼起来,而是有选择地组织这些材料:

  • system prompt
  • user request
  • history summary
  • tool definitions
  • project snippets

今天实现一个最小 Context Builder,把这些材料变成结构化 ContextPart,同时生成真正发给模型的 messages。

Day 08 context builder flow

今天要解决什么

今天完成四个交付:

  1. 新增 context/builder.ts
  2. 让 builder 组装 system prompt、用户请求、历史摘要、工具定义和项目片段。
  3. ContextPart 增加 source,让 context report 能显示来源。
  4. agent/run.ts 使用 builder,而不是手写 prompt 和 context parts。

Demo 命令:

npm run dev -- --context-report "build context for this repo"

输出里会出现类似结构:

System prompt          system        ~1,023 tokens (context-builder)
User request           conversation  ~7 tokens (argv)
History summary        conversation  ~11 tokens (context-builder)
Tool definitions       tools         ~391 tokens (tool-registry)
Project snippets       files         ~492 tokens (README.md, package.json)
Conversation           conversation  ~2,394 tokens (agent-loop)
Agent steps            tool-results  ~1,701 tokens (agent-loop)

它在 Cursor/Codex 里对应哪一层

今天实现的是 context assembly layer

在 Cursor、Codex、Claude Code 这类 coding agent 中,模型请求前通常会经过一个上下文构建阶段:

User task
  -> system instructions
  -> project rules
  -> selected files
  -> tool schemas
  -> conversation summary
  -> recent messages
  -> model request

这层的质量直接影响 agent 行为:

  • 上下文太少,模型会猜。
  • 上下文太多,关键信息会被稀释。
  • 工具定义不清楚,模型会调用错工具。
  • 文件片段选错,模型会围绕错误模块推理。
  • 历史摘要不可靠,模型会带着错误状态继续。

Day 08 的实现还很小,但它把“上下文”从散落字符串升级成了可检查的数据结构。

设计思路

1. Context Builder 返回两类结果

新增文件:apps/mini-harness/src/context/builder.ts

builder 返回:

export type BuiltRunContext = {
  messages: ChatMessage[];
  contextParts: ContextPart[];
  systemPrompt: string;
};

messages 是真正发给 model provider 的输入。

contextParts 是给 context report、transcript、debug UI 使用的结构化视图。

这两个结果来自同一套构建逻辑,避免“模型实际看到的内容”和“报告展示的内容”不一致。

2. ContextPart 增加 source

Day 08 给共享类型加了一个可选字段:

export type ContextPart = {
  label: string;
  kind: ContextKind;
  content: string;
  source?: string;
};

kind 说明材料类别,source 说明材料来源。例如:

Tool definitions -> kind: tools, source: tool-registry
Project snippets -> kind: files, source: README.md, package.json
User request -> kind: conversation, source: argv

这样 Day 09 做 Context Explorer 时,不只是显示 token 数,还能解释“这些 token 是从哪里来的”。

3. 先用保守的项目片段选择

今天不做复杂检索,也不做 embedding。Context Builder 默认读取两个稳定文件:

const DEFAULT_SNIPPET_FILES = ["README.md", "package.json"];

它们通常能提供项目目标、脚本、workspace 结构和包信息。每个文件最多读 4KB:

const MAX_SNIPPET_BYTES = 4_000;

路径仍然经过 workspace guard,避免 context builder 读取工作区外的文件。

4. agent loop 不再手写 prompt

Day 07 的 run.ts 里直接拼 system prompt、tool definitions 和 context parts。

Day 08 改成:

const builtContext = await buildRunContext({
  task: input.task,
  tools: input.tools,
  workspaceRoot: input.workspaceRoot,
});

const messages = [...builtContext.messages];

返回 summary 时再追加运行后的动态部分:

  • full conversation
  • agent steps

这样静态输入由 Context Builder 负责,运行时过程由 Agent Loop 负责。

实现步骤

1. 新增 context builder

文件:apps/mini-harness/src/context/builder.ts

核心入口:

export async function buildRunContext(input: BuildRunContextInput): Promise<BuiltRunContext>

输入包括:

task
tools
workspaceRoot
historySummary
selectedFiles

当前默认 history summary 是:

No prior conversation summary for this run.

后续如果实现长期会话或 transcript replay,可以把历史摘要接进这里。

2. 组装 system prompt

system prompt 包含:

  • agent 身份
  • Day08 行为边界
  • tool call 文本协议
  • history summary
  • available tools
  • selected project snippets

这里刻意把 tool definitions 和 project snippets 放进 system prompt,是为了维持当前最小 ChatMessage 类型。真实 provider adapter 可以进一步映射成模型原生 tool schema 或 tool role。

3. 生成 context parts

builder 输出五个基础 parts:

System prompt
User request
History summary
Tool definitions
Project snippets

agent loop 运行结束后再补两个动态 parts:

Conversation
Agent steps

这让 context report 能区分“模型请求前的输入”和“运行后的观察结果”。

4. 更新 context report

文件:apps/mini-harness/src/context/context-report.ts

原来只显示 label 和 token 数:

System prompt          ~484 tokens

现在显示 label、kind、token 和 source:

System prompt          system        ~1,023 tokens (context-builder)
Project snippets       files         ~492 tokens (README.md, package.json)

Demo

See Day 08 demo.

核心命令:

npm run dev -- --context-report "build context for this repo"

输出摘要:

Context Explorer

System prompt          system        ~1,023 tokens (context-builder)
User request           conversation  ~7 tokens (argv)
History summary        conversation  ~11 tokens (context-builder)
Tool definitions       tools         ~391 tokens (tool-registry)
Project snippets       files         ~492 tokens (README.md, package.json)
Conversation           conversation  ~2,394 tokens (agent-loop)
Agent steps            tool-results  ~1,701 tokens (agent-loop)
Total                                ~6,019 tokens

具体 token 数会随文章、工具和文件内容变化,但应该能看到 builder 产出的五个基础 context parts。

当前系统能力变化

到 Day 08,mini harness 的模型输入已经不再是临时拼接字符串:

  • 上下文有 kind
  • 上下文有 source
  • system prompt、user request、tools、history、files 分开记录。
  • agent loop 只消费 builder 产物。
  • context report 可以显示每部分 token 估算。

这为 Day 09 的 Context Explorer 打好了基础。

遇到的问题

1. 文件片段选择仍然很粗糙

今天只是默认读取 README.mdpackage.json。这对 demo 足够,但离真实 coding agent 还差很远。

真实系统需要根据任务选择文件,例如:

  • 用户提到某个路径时优先读取该文件。
  • 搜索相关符号后加入匹配文件。
  • 通过 git diff 加入当前改动。
  • 根据 token budget 做排序和截断。

2. system prompt 变大了

把 tool definitions 和 project snippets 放进 system prompt 简单直接,但会让 system prompt token 增长。

后续可以把工具定义放到 provider 原生 tool schema,把文件片段作为单独 message 或 provider-specific context block。

3. history summary 还没有真实来源

当前 history summary 是占位文本。等后续支持 transcript replay 或长会话时,才会有真正的 history summarizer。

明天做什么

Day 09 会实现 Context Explorer:把上下文来源、token 估算、截断状态和调试信息做成更明确的命令行报告。

Day 08 负责“构建上下文”,Day 09 负责“解释上下文”。

Comments

  • Loading comments…

Comments are posted immediately and emailed to the site owner. No account needed.