Back ay4's blog · blog

实习笔记06:Tau Context 管理机制解析

1,839 words 5 min read #Agent开发

实习笔记 06:Tau Context 管理机制解析

最近开始阅读 Tau 的源码,希望通过一个真实的 Coding Agent 项目,理解 Agent 内部的工作机制。

相比直接学习各种 Agent Framework,我更希望从一个完整实现中弄清楚:

  • Agent 如何维护上下文?
  • Token 如何计算?
  • 为什么需要 Context Compression?
  • 压缩后的历史如何继续支持后续任务?

这次主要阅读的是 Tau 中负责 Context 管理的部分。

为什么需要关注 Context?

LLM 本身并没有无限记忆。一次请求发送给模型的内容通常包括:

System Prompt
+
Conversation Messages
+
Tool Definitions
+
Tool Results

随着 Agent 不断执行任务,对话会越来越长,工具调用越来越多,文件内容也会不断加入上下文,最终可能超过模型的 Context Window。

因此,Agent 至少需要具备两个能力:

  1. 估算当前 Context 的大小;
  2. 在接近限制时压缩历史信息。

Tau 的 Context 管理主要围绕这两个方向展开。

Tau 如何估算 Context?

Tau 对 Context 的计算主要分为两种情况。

Provider 返回可靠的 usage 信息时

如果模型 Provider 返回了可靠的 usage 信息,例如:

  • Input Tokens
  • Output Tokens
  • Total Tokens
  • Cache Tokens

Tau 会优先使用 Provider 提供的数据,因为它来自模型的真实计算结果,比基于字符数的估算更准确。

此时,Tau 的计算策略可以概括为:

Provider 已知 Token
+
后续新增消息估算
+
后续新增工具估算

例如,Provider 返回已经使用了 5000 tokens,之后新增的用户消息约为 200 tokens,新增工具定义约为 100 tokens,那么最终估算结果就是:

5000 + 200 + 100 = 5300 tokens

这样可以避免重复计算已经有可靠数据的历史上下文。

Provider 没有返回可靠的 usage 信息时

如果没有可用的 Provider Token 信息,Tau 会使用自己的估算方式:

System Prompt Token
+
Message Token
+
Tool Token
=
Estimated Context Tokens

Text Token 估算

Tau 使用下面的方式进行粗略估算:

字符数量 / CHARS_PER_TOKEN

默认情况下:

4 个字符 ≈ 1 Token

这并不是真实的模型 tokenizer,而是一种低成本的近似计算,用于快速判断当前 Context 是否接近限制。

Message Token 估算

一条消息不仅可能包含正文,还可能包含 Thinking 内容、Tool Call 和 Tool 参数。因此,消息的 Token 估算大致包括:

Message Token
=
消息固定开销
+
正文 Token
+
Thinking Token
+
Tool Call Token

Tool Token 估算

工具定义本身也会占用 Context。模型需要知道工具名称、工具作用以及输入参数格式,因此工具 Token 的估算主要包括:

Tool Token
=
工具名称
+
工具描述
+
Input Schema

Tau 如何压缩上下文?

当 Context 接近限制时,Tau 会压缩部分历史。整个过程主要分为两个阶段。

第一步:判断是否存在旧摘要

Tau 首先判断当前 Context 中是否已经存在之前生成的摘要,以此决定本次是新建摘要还是更新摘要。

第一次压缩时:

历史消息
↓
生成新 Summary

后续再次压缩时:

旧 Summary
+
新增消息
↓
更新 Summary

旧摘要的存在意味着上下文此前已经被压缩过。此时不能忽略旧摘要,否则更早的任务信息就会丢失。

第二步:构造摘要 Prompt

Tau 不会直接把原始消息对象交给模型,而是先将消息序列化,再构造专门用于摘要的 Prompt:

Agent Messages
↓
serialize_messages_for_compaction()
↓
转换成模型可读的结构化文本
↓
build_compaction_summary_prompt()
↓
生成完整的 Summary Prompt

最终的 Prompt 大致包含以下内容:

<conversation>
用户任务……
助手操作……
工具结果……
</conversation>
<previous-summary>
旧摘要内容……
</previous-summary>
Summary Rules

其中 <previous-summary> 只会在旧摘要存在时加入。

阅读这部分源码时,最容易混淆的是几个名字相近的函数。它们分别处在不同层次:

summarize_messages_for_compaction()

作用:将消息按照固定格式整理成简化文本。

Message
↓
提取角色
↓
提取正文
↓
添加 Tool 信息
↓
限制长度
↓
拼接字符串

它属于程序级摘要,不会调用模型。

serialize_messages_for_compaction()

作用:将消息对象转换成结构化文本。

例如:

UserMessage
↓
<message role="user">
内容
</message>

它主要负责:

  • 保留消息角色;
  • 保留工具调用信息;
  • 保留工具错误状态。

build_compaction_summary_prompt()

作用:构建最终发送给模型的摘要 Prompt。

新消息
+
旧摘要(如果存在)
+
摘要规则
+
用户额外要求
↓
完整 Prompt

需要特别注意:这个函数只负责构造 Prompt,并不负责:

  • 调用模型;
  • 保存摘要;
  • 替换历史消息。

把这些步骤串联起来,Tau Context 管理的完整流程如下:

Agent 运行
↓
产生 Messages
↓
估算 Context Token
↓
判断是否接近限制
↓
触发 Compression
↓
判断是否存在旧 Summary
↓
构造 Summary Prompt
↓
调用 LLM 生成 Summary
↓
保存 Summary
↓
后续 Context 使用 Summary 代替部分历史

我对 Context 管理的理解

1. Context 不只是聊天记录

在 Agent 中:

Context
=
System Prompt
+
Messages
+
Tools
+
Tool Results
+
历史摘要

Context 是模型在当前请求中能够看到的全部信息。

2. Compression 不等于删除历史

过去我对上下文压缩的理解更接近“删除旧消息”,但实际过程是:

旧消息
↓
总结成 Summary
↓
Summary 代替旧消息进入 Context

完整历史仍然可以保存,只是当前模型看到的是压缩后的版本。

3. Agent 的核心不只是调用模型

一个真正的 Agent 系统还需要解决:

  • 如何管理 Context;
  • 如何调用工具;
  • 如何保存状态;
  • 如何恢复任务。

LLM 只是其中负责推理的一部分。

下一步:进入 Agent Loop

完成 Context 模块后,下一步准备进入 Agent Loop。计划中的学习顺序是:

Context Management
↓
Agent Loop
↓
Message System
↓
Tool System
↓
Session Management

下一阶段需要重点回答这些问题:

  • 用户输入后,模型在哪里被调用?
  • 模型如何决定调用工具?
  • Tool Call 如何执行?
  • Tool Result 如何返回模型?
  • 什么条件下结束循环?

也就是理解下面这条完整链路:

User
↓
LLM
↓
Tool Call
↓
Tool Execution
↓
Tool Result
↓
LLM
↓
Final Answer

这是理解 Agent 工作机制最核心的一步。

通过阅读 Tau 的 Context 源码,我第一次从实现层面理解了:一个 Coding Agent 并不是简单地“调用大模型,再加几个工具”,而是需要围绕模型构建一套 Context 管理、状态保存、工具执行和任务恢复机制。

Context 管理只是 Agent 系统中的一个模块。它决定了模型能够看到什么、历史信息如何被保留,以及长任务如何在有限的 Context Window 中继续执行。

下一篇计划继续阅读 Tau 的 Agent Loop,理解 Agent 如何完成一次完整的任务执行:

《Tau 源码学习笔记 02:从 Agent Loop 理解 Coding Agent 如何调用工具》

现在已经知道了“模型看到了什么”,下一步就是研究:模型为什么会行动,以及谁驱动它行动。

Comments

Quiet notes for this article.