第 6 章:Context Selection

本章导读

  • 核心问题:如何从大量候选信息中选择当前最有用的上下文。
  • 关键词:Candidate Recall、Permission Filter、Scoring、Deduplication、Budget Packing
  • 学习产出:为 CI 失败分析设计可解释的上下文选择策略。

1. 本章要解决什么问题

上一章我们拆解了 Context 的组成。

现在问题变成:

候选上下文很多,但模型一次只能看到有限内容,到底该选哪些?

这就是 Context Selection。

在 Agent 系统中,上下文来源可能非常多:

用户任务
对话历史
项目文档
代码文件
Git Diff
测试日志
工具结果
检索文档
长期记忆
环境信息
Policy

如果全部塞进模型,会出现三个问题:

  1. token 成本高;
  2. 延迟变长;
  3. 关键信息被噪声淹没。

因此 Context Selection 的目标不是“找所有相关信息”,而是:

为当前 Agent 的下一步决策,选择最小、最高信号、最可操作的一组上下文。

本章要回答:

  • Context Selection 与 RAG 检索有什么区别;
  • 什么是候选上下文、评分、排序、预算裁剪;
  • 如何综合相关性、时效性、可信度、权限和 token 成本;
  • 为什么不同任务阶段需要不同选择策略;
  • 如何用 Java / Spring Boot 设计 ContextSelector。

2. 对应 Anthropic 文章

本章主要对应:

  • Effective Context Engineering for AI Agents

Anthropic 在文章中强调:

good context engineering means finding the smallest possible set of high-signal tokens

这句话落到工程上,就是 Context Selection。

它不是简单的向量检索,而是一个完整的决策管线。

3. Context Selection 的核心思想

3.1 选择目标不是“相关”,而是“有用”

很多系统把上下文选择简化为相似度检索。

例如:

用户问题 → 向量检索 topK 文档 → 拼接给模型

这在简单问答中可行,但在 Agent 中远远不够。

因为 Agent 需要的不只是语义相关信息,而是对当前决策有用的信息。

例如用户任务是:

分析 CI 为什么失败。

语义相关的信息可能包括:

  • CI 使用说明;
  • 历史 CI 报告;
  • 测试框架文档;
  • 失败日志;
  • 最近提交;
  • 相关测试类;
  • 构建脚本。

但当前第一步最有用的可能只是:

失败摘要
失败测试名
关键错误栈
最近 Git Diff
项目测试命令

所以 Selection 的标准应是:

对下一步行动是否有帮助?

而不只是“语义上像不像”。

3.2 选择是动态的

Context Selection 不是任务开始时做一次就结束。

Agent 每一轮都可能需要重新选择上下文。

例如 Coding Agent 的不同阶段:

阶段 优先上下文
Explore README、AGENTS.md、目录结构、相关文件路径
Plan 用户目标、代码结构、相似实现、约束
Edit 具体代码片段、接口定义、测试样例
Test 测试命令、失败输出、最近修改
Fix 错误栈、失败断言、相关实现
Summarize Git Diff、验证结果、风险列表

这说明 Context Selection 必须感知 Agent Phase。

3.3 选择必须受预算约束

如果没有预算,Selection 会变成无限追加。

Context Budget 至少要考虑:

  • 最大输入 token;
  • 预留输出 token;
  • 每类上下文的预算;
  • 工具定义预算;
  • 最近工具结果预算;
  • Memory 预算。

例如 32K 输入预算可以这样分配:

System / Policy: 2K
User Task / Task State: 3K
Project Knowledge: 4K
Code Context: 12K
Tool Results: 6K
Memory: 2K
Buffer: 3K

预算不是为了省钱而已,更是为了保持注意力质量。

3.4 选择需要可解释

生产环境中,必须能回答:

为什么这段文档进入了上下文?
为什么那段日志被丢弃?
为什么召回了这个历史 Memory?
为什么 Agent 没看到某个关键文件?

所以 Context Selection 应记录选择日志。

每个被选中的 ContextChunk 应包含:

  • 来源;
  • 分数;
  • 选择原因;
  • token 估算;
  • 是否被压缩;
  • 是否因权限过滤;
  • 是否因预算裁剪。

这对调试 Agent 非常重要。

4. Context Selection 管线

一个可生产化的 Selection 管线可以分成六步。

Candidate Recall
  ↓
Permission Filter
  ↓
Scoring
  ↓
Deduplication
  ↓
Budget Packing
  ↓
Rendering Order

4.1 Candidate Recall:候选召回

候选上下文来自多个 Provider:

  • ProjectKnowledgeProvider;
  • CodeSearchProvider;
  • GitDiffProvider;
  • IssueProvider;
  • ToolResultProvider;
  • MemoryProvider;
  • EnvironmentProvider。

召回阶段的目标是“尽量不要漏掉可能有用的信息”。

但召回结果还不能直接进入模型。

4.2 Permission Filter:权限过滤

进入评分前,先做权限过滤。

因为没有权限的信息不应该进入后续流程。

过滤依据包括:

  • 用户权限;
  • 项目权限;
  • 数据敏感度;
  • 工具权限;
  • 当前任务模式;
  • 是否需要脱敏。

注意:权限过滤必须由程序执行,不能依赖模型自觉。

4.3 Scoring:评分

评分可以综合多个因素:

score = relevance * 0.40
      + recency * 0.20
      + authority * 0.15
      + taskPhaseFit * 0.15
      + userPin * 0.10
      - tokenCostPenalty

Relevance:相关性

与用户目标、当前计划、最近工具结果的相关程度。

Recency:时效性

最近产生的信息通常更重要。

例如当前测试失败输出比一周前的历史失败更重要。

Authority:权威性

不同来源可信度不同。

例如:

代码实际实现 > README 描述
测试输出 > Agent 猜测
用户明确要求 > 历史偏好

TaskPhaseFit:阶段匹配

当前阶段需要不同上下文。

Verify 阶段,测试输出优先级高;Plan 阶段,架构文档和相关代码优先级高。

TokenCostPenalty:成本惩罚

同等价值下,短上下文优先。

这符合“小而高信号”的原则。

4.4 Deduplication:去重

上下文经常重复。

例如:

  • README 和 AGENTS.md 都包含测试命令;
  • 工具结果和对话历史都包含错误摘要;
  • Memory 和项目文档都提到同一约定;
  • 多个搜索结果指向同一文件。

去重不是简单按文本相同判断,而是按语义和来源合并。

合并后应保留更权威、更短、更近期的版本。

4.5 Budget Packing:预算装箱

排序后还要放入有限预算。

常见策略:

  • 高优先级必选;
  • 每类上下文有上限;
  • 长内容先压缩;
  • 低分内容丢弃;
  • 保留输出 token;
  • 保留一定 buffer 给后续工具结果。

预算装箱可以类比背包问题:

价值 = 上下文效用
重量 = token 数
背包容量 = context budget

4.6 Rendering Order:渲染顺序

选中上下文后,还要决定顺序。

顺序会影响模型注意力。

推荐顺序:

1. System / Policy
2. User Task
3. Current Task State
4. High-priority Evidence
5. Relevant Project Knowledge
6. Relevant Code / Documents
7. Tool Results
8. Memory
9. Output Requirements

但不同任务可以调整。

例如故障排查时,关键错误证据应该靠前。

5. 静态 Context 与动态 Context

5.1 静态 Context

静态 Context 相对稳定,例如:

  • System Prompt;
  • 项目规范;
  • AGENTS.md;
  • 架构原则;
  • 工具说明;
  • 用户偏好。

静态 Context 适合在任务开始时加载。

但也不应全部加载,而应按任务类型选择。

5.2 动态 Context

动态 Context 随任务变化,例如:

  • 最近工具结果;
  • 当前测试输出;
  • 当前 Git Diff;
  • 当前计划;
  • 最新用户反馈;
  • 当前错误假设。

动态 Context 往往更接近当前决策,因此在很多阶段优先级更高。

5.3 二者的平衡

如果只给静态 Context,Agent 可能不了解最新情况。

如果只给动态 Context,Agent 可能忘记项目规则。

好的选择策略应该让二者平衡。

6. 架构图 / 流程图

Context Selection Pipeline
图 6-1 Context Selection Pipeline 这张图展示从候选上下文召回到权限过滤、评分、去重、预算装箱和渲染的完整管线。 Mermaid 源文件

6.1 Selection 管线

Selection 管线
图 6-2 Selection 管线 Mermaid 源文件

6.2 多因素评分

多因素评分
图 6-3 多因素评分 Mermaid 源文件

6.3 阶段感知选择

阶段感知选择
图 6-4 阶段感知选择 Mermaid 源文件

7. Java / Spring Boot 落地方案

7.1 ContextCandidate

public record ContextCandidate(
    ContextChunk chunk,
    double relevance,
    double recency,
    double authority,
    double phaseFit,
    double tokenCostPenalty,
    List<String> reasons
) {
    public double finalScore() {
        return relevance * 0.40
             + recency * 0.20
             + authority * 0.15
             + phaseFit * 0.15
             - tokenCostPenalty;
    }
}

7.2 ContextScorer

public interface ContextScorer {
    ContextCandidate score(ContextChunk chunk, ContextRequest request);
}

可以组合多个 scorer:

public class CompositeContextScorer implements ContextScorer {
    private final List<ContextScorer> scorers;

    @Override
    public ContextCandidate score(ContextChunk chunk, ContextRequest request) {
        // 合并 relevance、recency、authority、phaseFit 等分数
        return null;
    }
}

7.3 ContextSelector

public interface ContextSelector {
    List<ContextChunk> select(
        List<ContextChunk> candidates,
        ContextRequest request,
        ContextBudget budget
    );
}

一个基础实现:

public class BudgetAwareContextSelector implements ContextSelector {

    private final PermissionFilter permissionFilter;
    private final ContextScorer scorer;
    private final ContextDeduplicator deduplicator;
    private final TokenEstimator tokenEstimator;

    @Override
    public List<ContextChunk> select(
        List<ContextChunk> candidates,
        ContextRequest request,
        ContextBudget budget
    ) {
        List<ContextChunk> allowed = permissionFilter.filter(candidates, request);

        List<ContextCandidate> scored = allowed.stream()
            .map(chunk -> scorer.score(chunk, request))
            .sorted(Comparator.comparing(ContextCandidate::finalScore).reversed())
            .toList();

        List<ContextCandidate> deduped = deduplicator.deduplicate(scored);

        return packWithinBudget(deduped, budget);
    }

    private List<ContextChunk> packWithinBudget(
        List<ContextCandidate> candidates,
        ContextBudget budget
    ) {
        List<ContextChunk> selected = new ArrayList<>();
        int used = 0;

        for (ContextCandidate candidate : candidates) {
            int tokens = tokenEstimator.estimate(candidate.chunk());
            if (used + tokens <= budget.availableInputTokens()) {
                selected.add(candidate.chunk());
                used += tokens;
            }
        }
        return selected;
    }
}

7.4 SelectionTrace

为了可观测性,需要记录选择过程:

public record SelectionTrace(
    String taskId,
    List<SelectedContext> selected,
    List<RejectedContext> rejected,
    int totalCandidates,
    int selectedTokens,
    Instant createdAt
) {}

拒绝原因可能包括:

NO_PERMISSION
LOW_SCORE
DUPLICATE
BUDGET_EXCEEDED
EXPIRED
SENSITIVE

这类 trace 对调试非常有价值。

8. 常见误区

8.1 只靠向量相似度

向量相似度只能回答“语义相近”,不能回答:

  • 是否新鲜;
  • 是否权威;
  • 是否有权限;
  • 是否适合当前阶段;
  • 是否值得 token 成本。

8.2 忽略工具结果

很多系统只检索静态文档,却忽略最新工具结果。

但 Agent 的下一步决策往往最依赖刚刚发生的观察。

8.3 没有预算意识

如果没有预算,系统会不断追加上下文,直到性能下降。

8.4 没有选择日志

当 Agent 答错时,如果不知道它看到了什么、没看到什么,就无法改进系统。

8.5 每个任务使用同一套选择策略

不同任务、不同阶段需要不同上下文。

固定 topK 检索不适合复杂 Agent。

9. 贯穿案例:CI 失败分析 Agent

在 build #4312 中,Context Selection 的目标是从大量候选信息中选择最能支持下一步判断的内容。

候选上下文可能包括:

完整 CI 日志:20000 tokens
关键错误栈:1200 tokens
PR Diff 摘要:4000 tokens
AGENTS.md:1000 tokens
README 全文:8000 tokens
历史类似失败:1500 tokens
无关模块代码:6000 tokens

一个合理选择是:

必选:用户任务、只读 Policy、CI 失败摘要、关键错误栈
高优先级:PR Diff 摘要、AGENTS.md 测试规则
按需加载:完整日志、相关代码文件
可选:历史类似失败
丢弃:无关模块代码、README 全文

选择依据不是简单语义相似度,而是:

  • 是否直接支持当前根因判断;
  • 是否新鲜;
  • 是否来自权威来源;
  • 是否符合当前阶段;
  • token 成本是否合理;
  • 当前用户是否有权限。

因此,CI 失败分析 Agent 的 ContextSelector 应优先选择关键错误、最近变更和项目规则,而不是盲目 topK 检索。

10. 本章小结

本章讨论了 Context Selection。

核心结论:

  1. Context Selection 选择的是“对下一步决策有用”的信息,而不只是语义相关的信息。
  2. Selection 是动态过程,应随 Agent 阶段变化。
  3. 选择策略要综合相关性、时效性、权威性、阶段匹配、权限和 token 成本。
  4. Context Budget 是选择过程的硬约束。
  5. 生产级 Agent 必须记录 SelectionTrace。

一句话总结:

Context Selection 的目标,是在有限注意力预算中,把模型下一步最需要的信息放到最显眼的位置。

11. 实践任务

任务 1:实现 ContextScorer

实现一个基础评分器,输入 ContextChunk,输出:

relevance
recency
authority
phaseFit
tokenCostPenalty
finalScore

任务 2:设计阶段化选择策略

为 Coding Agent 的以下阶段设计上下文优先级:

Explore
Plan
Edit
Test
Fix
Summarize

任务 3:记录 SelectionTrace

设计一张数据库表保存上下文选择日志。

至少包含:

  • task_id;
  • chunk_id;
  • selected;
  • score;
  • reason;
  • token_estimate;
  • created_at。

任务 4:CI 失败分析选择实验

给定一份 CI 日志、Git Diff 和 AGENTS.md,手工选出 10 条最应该进入模型的上下文,并说明理由。

results matching ""

    No results matching ""