一、前言

上一节课我们设计了Agent Loop的骨架,而上下文(Context)就是Agent的“短期记忆”——它包含了用户输入、历史对话、工具执行结果、系统提示等所有信息,是LLM推理的基础。

但实际开发中,我们会遇到三个致命问题:

1. 上下文过长:超过LLM的Token上限,导致模型报错;

2. 关键信息遗忘:上下文过长时,Agent会忘记早期的用户指令或工具结果;

3. 效率低下:上下文越长,LLM推理速度越慢、成本越高。

本节课我们拆解上下文的组成、核心痛点,以及基础的Token控制策略,为下一节课的“三层压缩”打下基础。

二、上下文的核心组成(Claude Code实战解析)

在Claude Code中,上下文由com.claudecode.context.ContextManager管理,其核心组成分为4部分,按优先级排序(越靠前越重要,不会被轻易压缩):

1. 系统提示词(System Prompt)

作用:定义Agent的角色、能力、规则,是LLM推理的“指导方针”。

示例(Claude Code中的系统提示词片段):

你是一个CLI AI编码助手,具备以下能力:
1. 能调用文件操作、Shell、网络搜索等工具,自主完成编码相关任务;
2. 严格遵循用户提供的编码规范,所有代码必须有中文注释;
3. 执行危险操作(如删除文件、执行rm命令)前,必须先获取用户确认;
4. 工具执行失败后,需尝试重试,若多次失败则提示用户。
    

特点:固定不变,优先级最高,不会被压缩或删除。

2. 用户核心指令(User Goal)

作用:用户的最终目标,是Agent循环的核心驱动力。

示例:“帮我在我的Spring Boot项目中,添加一个字符串反转工具类,并编写单元测试,确保符合阿里巴巴Java开发手册”。

特点:贯穿整个循环,优先级仅次于系统提示词,压缩时会优先保留。

3. 工具执行结果(Tool Results)

作用:Agent执行工具后得到的反馈,是后续推理的重要依据。

示例:“读取文件project.txt成功,文件内容:这是一个Spring Boot项目,使用MyBatis做数据持久化”。

特点:数量多、体积大,是上下文膨胀的主要原因,也是压缩的重点对象。

4. 历史对话(History)

作用:用户与Agent的历史交互记录,用于保持对话连贯性。

示例:用户:“帮我检查代码漏洞”;Agent:“已调用安全审查工具,正在分析代码...”。

特点:部分历史对话可被摘要压缩,优先级最低。

三、上下文管理的三大痛点及解决方案

痛点1:上下文过长,超过Token上限

原因:工具执行结果过多(比如多次读取大文件、执行Shell命令返回大量输出)、历史对话积累。

基础解决方案:

1. 截断历史:只保留最近N轮对话和最近M个工具结果(比如最近5轮对话、最近3个工具结果);

2. 长度限制:给上下文设置固定长度上限,超过则触发压缩。

痛点2:关键信息遗忘

原因:截断或压缩时,误删用户核心指令、关键工具结果。

基础解决方案:

1. 优先级排序:系统提示词、用户核心指令永不删除;

2. 关键信息标记:将用户指令中的核心目标、工具结果中的关键数据标记为“不可压缩”。

痛点3:效率低下,推理速度慢、成本高

原因:上下文过长,LLM需要处理大量冗余信息,推理时间和Token消耗增加。

基础解决方案:

1. 实时精简:工具执行结果只保留核心信息(比如读取文件时,只保留与任务相关的片段);

2. 按需加载:不一次性加载所有历史,只加载与当前任务相关的上下文。

四、Claude Code中的Token控制实现(源码解析)

Claude Code使用com.claudecode.core.TokenTracker类,实现Token的实时监控和控制,核心逻辑如下:

1. Token计算方式

TokenTracker通过调用LLM的Token计算接口(如OpenAI的tiktoken、Anthropic的tokenizer),实时计算上下文的Token数量:

public class TokenTracker {
    // Token上限(可配置,默认200000)
    private final int maxTokens;
    // 当前Token数量
    private int currentTokens;
    // Token计算工具
    private final Tokenizer tokenizer;

    // 计算上下文Token数量
    public void updateTokenCount(String context) {
        this.currentTokens = tokenizer.countTokens(context);
    }

    // 检查是否超过阈值(默认93%)
    public boolean isOverThreshold() {
        double threshold = maxTokens * 0.93;
        return currentTokens > threshold;
    }

    // 剩余Token数量
    public int getRemainingTokens() {
        return maxTokens - currentTokens;
    }
}
    
2. Token控制策略

Claude Code的Token控制分为“预防”和“处理”两步:

1. 预防:在构建上下文时,实时计算Token数量,若接近阈值(80%),提前精简上下文;

2. 处理:若超过阈值(93%),触发上下文压缩(下一节课详解)。

五、实操练习:实现简单的上下文管理与Token控制

结合本节课所学,实现一个简单的上下文管理器,包含上下文构建、Token计算、截断逻辑。

import java.util.ArrayList;
import java.util.List;

public class SimpleContextManager {
    // 系统提示词(固定)
    private final String systemPrompt = "你是一个编码助手,只能调用文件工具,严格遵循编码规范。";
    // 用户核心指令
    private String userGoal;
    // 工具结果列表
    private final List<String> toolResults = new ArrayList<>();
    // 历史对话列表
    private final List<String> history = new ArrayList<>();
    // Token上限
    private static final int MAX_TOKENS = 1000;
    // Token计算工具(简化版,实际需调用LLM的Tokenizer)
    private Tokenizer tokenizer = new Tokenizer();

    // 初始化用户目标
    public void setUserGoal(String userGoal) {
        this.userGoal = userGoal;
    }

    // 添加工具结果
    public void addToolResult(String toolResult) {
        toolResults.add(toolResult);
        // 保留最近3个工具结果,防止上下文膨胀
        if (toolResults.size() > 3) {
            toolResults.remove(0);
        }
    }

    // 添加历史对话
    public void addHistory(String message) {
        history.add(message);
        // 保留最近5轮对话
        if (history.size() > 5) {
            history.remove(0);
        }
    }

    // 构建上下文
    public String buildContext() {
        StringBuilder context = new StringBuilder();
        // 1. 系统提示词
        context.append(systemPrompt).append("\n");
        // 2. 用户目标
        context.append("用户目标:").append(userGoal).append("\n");
        // 3. 工具结果
        context.append("工具结果:").append("\n");
        for (String result : toolResults) {
            context.append("- ").append(result).append("\n");
        }
        // 4. 历史对话
        context.append("历史对话:").append("\n");
        for (String msg : history) {
            context.append("- ").append(msg).append("\n");
        }

        // Token检查与截断
        int currentTokens = tokenizer.countTokens(context.toString());
        if (currentTokens > MAX_TOKENS) {
            // 截断历史对话(优先级最低)
            while (history.size() > 0 && tokenizer.countTokens(context.toString()) > MAX_TOKENS) {
                history.remove(0);
                // 重新构建上下文
                context = rebuildContext();
            }
        }

        return context.toString();
    }

    // 重新构建上下文(截断后)
    private StringBuilder rebuildContext() {
        // 逻辑与buildContext一致,略...
        return new StringBuilder();
    }

    // 简化版Token计算(实际需对接LLM)
    static class Tokenizer {
        public int countTokens(String text) {
            // 简化:按空格分割,每个单词算1个Token
            return text.split(" ").length;
        }
    }

    // 测试
    public static void main(String[] args) {
        SimpleContextManager manager = new SimpleContextManager();
        manager.setUserGoal("帮我写一个字符串反转工具类");
        manager.addToolResult("读取文件成功,文件内容:Spring Boot项目结构");
        manager.addToolResult("检查编码规范,符合阿里巴巴Java开发手册");
        manager.addHistory("用户:帮我写工具类;Agent:正在准备相关操作");
        
        String context = manager.buildContext();
        System.out.println("构建的上下文:" + context);
        System.out.println("Token数量:" + new Tokenizer().countTokens(context));
    }
}
    

六、本课重点总结

1. 上下文是Agent的“短期记忆”,核心组成是系统提示词、用户目标、工具结果、历史对话;

2. 上下文管理的核心是“控长、保关键、提效率”,避免Token超限、关键信息遗忘、推理缓慢;

3. 基础Token控制策略:截断冗余信息、优先级排序、实时监控Token数量。

下节课预告

第04课:三层上下文压缩机制(Claude Code核心亮点)——如何在不丢失关键信息的前提下,高效压缩上下文,解决Token超限问题,对应compact包下的三个压缩类源码解析。

Logo

这里是“一人公司”的成长家园。我们提供从产品曝光、技术变现到法律财税的全栈内容,并连接云服务、办公空间等稀缺资源,助你专注创造,无忧运营。

更多推荐