项目地址 huggingface/tau
我顺着这个仓库当前的源码往下读,最后留下的印象很具体。Tau 的核心循环并不长,真正让它像一个可以长期使用的 coding agent 的,是循环周围那些容易被示例代码略过的部分。模型输出要被拆成可以继续处理的消息,工具调用要和结果稳定配对,前端要能看见运行过程,会话要能在下次启动时恢复,历史损坏后要有修复办法,上下文变长后还要继续工作。
这篇文章先把仓库放在一起看,再沿着一次请求进入 tau_agent 的核心循环,最后回到会话、Provider、工具和信任边界。源码阅读不适合从最热闹的 TUI 入口开始。先看协议,再看状态,很多文件会自己找到位置。
先看仓库,Tau 的骨架在哪里
当前本地 checkout 对应 Tau v0.4.2,要求 Python 3.12 及以上,构建包名是 tau-ai,命令行入口在 tau_coding.cli:app。源码采用三个层次。
tau_coding 面向编码工作的应用层
↓
tau_agent Agent、消息、事件、工具和会话原语
↓
tau_ai Provider、流式响应和网络适配
tau_ai 处理模型服务的差异,tau_agent 维护一个相对独立的 Agent 核心,tau_coding 把它接到项目目录、文件系统、shell、session 和终端界面上。这个分层很重要。模型供应商变了,核心循环不必跟着重写;界面变了,工具调用的协议也不用重做。
根目录文件
| 文件或目录 | 作用 |
|---|---|
README.md |
项目定位、安装和基本使用方式 |
AGENTS.md |
仓库级开发约定,也提供项目上下文 |
CONTRIBUTING.md |
贡献、开发和测试说明 |
pyproject.toml |
包信息、依赖、命令行入口和测试配置 |
uv.lock |
锁定 Python 依赖版本 |
TODO.md |
尚未完成的工作 |
LICENSE |
授权信息 |
landing.html |
项目展示页 |
src/ |
三个运行时 Python 包及随包资源 |
tests/ |
Agent、Provider、Session、工具、扩展和 TUI 测试 |
website/ |
Hugo 文档站点,包含 guides、internals 和 reference |
dev-notes/ |
设计记录、架构阶段记录、故障恢复和发布过程 |
examples/ |
扩展示例,包括工具、权限门、prompt section 和侧栏状态 |
plans/602-built-in-local-inference.md |
内置本地推理相关计划 |
scripts/generate_models.py |
生成模型目录数据 |
docs/assets/tau-header.svg |
文档使用的头图资源 |
website/content/internals 里的 architecture.md 和 agent-loop.md 适合先建立整体印象。website/content/guides 解释上下文、Provider、Session、Project Trust、Skills、TUI 等使用层概念,reference 则对应 CLI、配置、RPC、slash command 和工具。dev-notes 更像工程过程的痕迹,里面能看到从核心类型、Provider、Agent Loop 到 Session Tree、Compaction、Extension、TUI 的阶段推进。源码不是突然长成今天这个样子的,这些记录能解释许多接口为什么存在。
tau_agent,可以独立拿出来的 Agent 核心
| 文件 | 主要职责 |
|---|---|
types.py |
JSON 兼容的基础类型、类型别名和序列化边界 |
messages.py |
用户、助手、工具结果、思考内容和工具调用等消息模型 |
provider.py |
Agent 依赖的最小 ModelProvider 协议 |
provider_events.py |
Provider 返回的 assistant 流事件 |
events.py |
Agent、turn、message、tool 和 compaction 生命周期事件 |
tools.py |
AgentTool、工具参数和异步执行协议 |
loop.py |
模型调用、工具执行、消息追加和继续循环 |
harness.py |
对 loop 的运行管理,包含取消、监听、steering 和 follow-up |
tool_history.py |
修复缺失、乱序、重复或孤立的工具结果 |
session/entries.py |
Session entry 的数据结构 |
session/jsonl.py |
JSONL entry 的编码和读取辅助 |
session/memory.py |
从 entry 重建当前可供模型使用的消息状态 |
session/storage.py |
带锁的追加写入、批量写入和临时文件替换 |
session/tree.py |
parent-child 历史树、active leaf 和分支导航 |
__init__.py、py.typed |
包导出和类型分发标记 |
如果只想理解 Agent 怎么运行,先读 messages.py、events.py、tools.py 和 loop.py。harness.py 解释它怎样变成可以被应用层长期控制的对象,session/ 则回答历史如何留下来。
tau_ai,把不同模型服务压到一条协议上
| 文件 | 主要职责 |
|---|---|
provider.py |
Provider 层公共类型和构造辅助 |
stream.py、_provider_events.py、events.py |
把不同服务的流式响应整理成统一事件,并维护 partial assistant message |
content.py |
文本、图片、thinking 等内容块的转换 |
anthropic.py |
Anthropic 消息和 SSE 适配 |
google.py |
Google 模型适配 |
mistral.py |
Mistral 模型适配 |
openai_compatible.py |
OpenAI Chat Completions 和 Responses 兼容接口 |
openai_codex.py |
Codex 运行时相关的 Provider 适配 |
http.py、http_errors.py |
HTTP 请求和错误分类 |
retry.py |
重试和退避策略 |
model_catalog.py、model_limits.py |
模型目录和上下文、能力限制 |
openai_cache.py |
OpenAI 请求缓存相关处理 |
tool_call_ids.py |
跨 Provider 的安全工具调用 ID |
fake.py |
测试使用的假 Provider |
env.py |
环境变量和运行环境辅助 |
__init__.py、py.typed |
包导出和类型分发标记 |
这一层的重点不只是发送 HTTP 请求。Provider 可能用不同的事件名表示文本增量,工具调用参数可能分散在多段响应里,thinking 的签名也可能只对某一个服务有效。stream.py 把这些差异集中处理,Agent core 看到的是统一的 AssistantMessageEvent。
tau_coding,把核心 Agent 放进真实项目
这个包的文件最多。它承担的事情也最接近产品运行时,可以按下面几组看。
| 文件或文件组 | 主要职责 |
|---|---|
session.py、session_manager.py、session_preparation.py |
创建、加载、切换、恢复和准备 CodingSession |
branch_summary.py、session_stats.py、session_usage.py、session_export.py |
分支摘要、统计、用量和 transcript 导出 |
cli.py、commands.py、rpc.py、events.py |
CLI、slash command、RPC 和应用层事件 |
paths.py、credentials.py、shell_config.py |
路径、凭据和 shell 环境 |
version.py、codex_version.py、update_check.py、updater.py |
版本、更新检查和更新流程 |
diagnostics.py、reload.py |
启动诊断和运行时重载 |
context.py、context_window.py |
项目上下文发现和上下文窗口计算 |
resources.py、self_docs.py |
项目资源和 Tau 自身文档 |
skills.py、prompt_templates.py、system_prompt.py |
Skills、prompt 模板和 system prompt 组装 |
project_trust.py |
判断项目资源和扩展是否可以进入运行时 |
thinking.py |
thinking level 和相关配置 |
tools.py、image_processing.py |
read、write、edit、bash 和图片读取 |
provider_catalog.py、provider_config.py、provider_runtime.py |
Provider 目录、配置和运行时实例 |
catalog_loader.py、models_dev.py、models_dev_store.py、codex_model_store.py |
模型目录加载、缓存和 Codex 模型信息 |
local_backends.py |
本地推理后端 |
oauth.py、oauth_types.py、oauth_registry.py、oauth_device.py |
OAuth 的公共类型、注册和设备流程 |
oauth_anthropic.py、oauth_github_copilot.py |
Anthropic 与 GitHub Copilot 登录适配 |
extension_installer.py、built_in_extensions.py |
扩展安装和内置扩展 |
extensions/api.py、extensions/loader.py、extensions/runtime.py |
扩展 API、加载和运行时 |
extensions/providers.py、extensions/provider_registry.py |
扩展 Provider 的注册和查找 |
extensions/builtins/llama_cpp/ |
llama.cpp 的 Hugging Face、路由、服务和状态管理 |
rendering/base.py、rendering/json.py、rendering/plain.py、rendering/transcript.py |
事件的基础、JSON、纯文本和 transcript 渲染 |
ui/app.py、ui/state.py、ui/adapter.py、ui/widgets.py |
Textual 应用、状态、适配器和组件 |
ui/autocomplete.py、ui/config.py、ui/file_drop.py |
补全、界面配置和文件拖放 |
ui/project_trust.py、ui/local_backends.py |
TUI 中的信任确认和本地后端界面 |
ui/terminal_notification.py、ui/terminal_title.py |
终端通知和标题 |
ui/themes/ |
tau-dark.json、tau-light.json 和 high-contrast.json 主题 |
随包数据也有清楚的分工。data/catalog.toml 保存 Provider 目录,data/models-dev-catalog.json 保存模型信息,data/release-notes/releases.json 保存发布记录。data/docs 里有 architecture.md、cli.md、extensions.md、local-inference.md、models.md、security.md、skills.md 和 tui.md,data/examples/extensions 则放着可以随包提供的扩展示例。它们会被运行时当作资源使用,不只是仓库里的说明文字。
测试、文档和开发记录
tests 基本按照运行时边界组织。test_agent_loop.py 和 test_agent_harness.py 盯住核心循环,test_tau_ai.py、test_anthropic_sse.py、test_cross_provider_history.py 处理 Provider 和跨服务历史,test_coding_tools.py、test_coding_session.py、test_context.py、test_project_trust.py 对应应用层,扩展、渲染、RPC、Session、TUI 也各有测试。
website 是面向使用者的文档,dev-notes/design 和 dev-notes/architecture 更接近开发过程中的设计判断。前者告诉你怎么用,后者告诉你为什么会出现这个文件。阅读源码时两者要分开看,否则很容易把计划中的行为误认成已经完成的行为。
一次请求怎样穿过这些文件
从 CLI 或 TUI 输入一条 prompt 后,大致会经过下面这条路径。
cli / TUI
↓
CodingSession.load 或 reload
↓
读取 Session、项目上下文、Skills 和 system prompt
↓
创建 AgentHarness
↓
AgentHarness.prompt
↓
run_agent_loop
↓
ModelProvider.stream_response
↓
模型文本或工具调用
↓
tau_coding.tools 执行 read、edit、bash 等操作
↓
ToolResultMessage 进入下一轮上下文
tau_coding 负责准备环境,tau_agent 负责决定运行过程,tau_ai 负责让 Provider 的响应符合核心协议。三层之间靠消息和事件连接,所以阅读时可以沿着一条请求上下走,不必同时理解整个 TUI。
消息模型,Agent 看到的不是一串纯文本
tau_agent/messages.py 定义了上下文的基本形状。常见消息包括 UserMessage、AssistantMessage、ToolResultMessage,另外还有表示 thinking、工具调用和跨 Provider 内容块的结构。
UserMessage 保存用户输入。AssistantMessage 不是只有一个字符串,它的 content 可以按顺序包含文本、thinking、tool call 等块。ToolResultMessage 通过 tool_call_id 指回具体调用,同时记录工具返回内容和错误状态。
这让一轮对话可以被准确表示为一串结构化事实。
用户消息
↓
助手文本 + tool call
↓
tool result
↓
下一条助手消息
如果把这些内容提前拼成一段文本,后面会丢掉工具调用的边界、ID、错误状态和 Provider 需要的原始结构。Agent 能不能继续工作,常常取决于这些看起来琐碎的字段。
Agent Loop,Tau 真正的发动机
tau_agent/loop.py 做的事情可以压缩成几步。
- 把当前 system、messages、tools 交给 Provider。
- 消费 Provider 的 assistant 流,把增量合并成一条
AssistantMessage。 - 将文本和流事件交给外部消费者。
- 如果 assistant 没有工具调用,当前任务结束。
- 如果有工具调用,按调用顺序执行工具。
- 把工具结果追加到消息列表,再进入下一轮。
用伪代码表示就是这样。
while turn < max_turns:
assistant = await read_provider_stream()
yield assistant_events(assistant)
if not assistant.tool_calls:
break
for call in assistant.tool_calls:
result = await execute_tool(call)
yield tool_events(call, result)
messages.append(result)
这里有几个容易被忽略的细节。
第一,任务是否结束,通常由当前 assistant 是否继续发出 tool call 来决定。Tau 没有一个独立的评估器去检查代码是否真的修好了。模型停止调用工具,循环就会停下来。
第二,AgentTool 中有 execution_mode 这个字段,但当前 run_agent_loop 对一批调用仍然使用顺序执行。接口已经留下了扩展空间,实际行为仍然是一个一个等待结果。这对于依赖前一步输出的工具比较稳妥,对可以并行的读取任务则还没有发挥出并行能力。
第三,工具失败不会直接让整个循环消失。错误会被包装成带有 is_error 状态的 ToolResultMessage,下一轮模型可以看到它。系统把失败留在上下文里,模型才有机会修正参数或换一条路。
第四,max_turns 是一个很实际的边界。模型如果持续调用工具,循环不能无限运行。达到上限时,外层能收到结束状态,但这不代表业务目标已经完成。
第五,Harness 允许用户在运行期间插入 steering,也能把 follow-up 排队到当前任务之后。这个设计解决的是人和 Agent 同时推进的问题。用户不必只能等待模型完全停下,才能补充方向。
事件流,外部怎样看见 Agent 在做什么
Tau 的运行接口主要通过异步生成器暴露事件。它不是一个由后台线程无限生产、前端随时取走的普通事件队列。消费方调用 async for,生产过程才沿着当前请求向前推进。这一点会影响取消、背压和异常处理。
事件大体分为几层。
| 层次 | 代表事件 | 说明 |
|---|---|---|
| Agent | AgentStartEvent、AgentEndEvent |
一次 Agent 运行的开始和结束 |
| Turn | TurnStartEvent、TurnEndEvent |
一次模型请求及其工具处理阶段 |
| Message | MessageStartEvent、MessageUpdateEvent、MessageEndEvent |
一条用户或助手消息的生命周期 |
| Tool | ToolExecutionStartEvent、ToolExecutionUpdateEvent、ToolExecutionEndEvent |
工具执行的开始、更新和结束 |
| Provider | assistant stream 事件 | 文本增量、thinking、tool call、usage 和错误 |
| Context | compaction、retry 等事件 | 上下文压缩与请求重试 |
一次带工具调用的运行,大致会出现这样的顺序。
AgentStart
TurnStart
Assistant MessageStart
Provider text delta
Provider tool-call delta
Assistant MessageEnd
ToolExecutionStart
ToolExecutionUpdate
ToolExecutionEnd
TurnEnd
TurnStart
Assistant MessageStart
Provider text delta
Assistant MessageEnd
TurnEnd
AgentEnd
Provider 事件被包在 Agent 的消息事件里,所以前端可以选择只显示最终消息,也可以显示文本增量、thinking 和工具进度。TUI、print mode、JSON renderer、transcript exporter 不需要各自再实现一份模型调用逻辑,它们只需要消费同一套事件。
事件也构成了持久化和界面之间的接缝。MessageEndEvent 是一个自然的完成边界,监听器可以在这里记录消息、刷新 UI 或生成 transcript。当前实现里,持久化监听器还需要处理事件发送时机和 loop 内存追加顺序之间的差异,会通过 pending write 和后续 reconcile 保证落盘状态跟上运行状态。这个细节说明事件流不是打印日志,它参与了状态管理。
ToolExecutionUpdateEvent 也值得单独说一句。工具协议允许工具提供中间更新,但当前 _run_tool 会先收集回调内容,等 executor 返回后再统一发出。因此这个接口已经表达了进度能力,长任务的真正实时转发还没有完全实现。
Harness 和 CodingSession,各自管什么
AgentHarness 是一个可以脱离 TUI 使用的运行对象。它保存 Provider、model、system、tools、当前消息、取消 token、监听器,以及 steering 和 follow-up 队列。它关心的是 Agent 怎样运行,不关心 session 文件放在哪里。
CodingSession 位于更外层。它把 cwd、Session storage、默认工具、Provider 配置、项目可信状态、AGENTS.md、Skills、prompt、自动压缩、分支、扩展和前端事件放到同一个 coding 环境里。
启动时,session.py 会读取 JSONL entries,重建 SessionState,处理项目可信状态,加载资源和扩展,解析 Provider 与 model,创建工具,组装 system prompt,最后用恢复出的消息创建 Harness。session_preparation.py 还提供候选 session 的准备与 adopt 流程,让项目切换和信任确认不必一开始就修改权威历史。
这两层的边界很干净。Harness 可以被别的前端复用,CodingSession 则把它绑定到一个具体项目。
默认工具,模型怎样碰到真实文件
tau_coding/tools.py 先定义 ToolDefinition,再通过 to_agent_tool 转换为核心层的 AgentTool。这里保留了工具描述、schema、prompt snippet 和 guidelines,工具执行细节没有直接塞进 loop。
read 按 cwd 解析路径,支持 offset 和 limit,对大文件限制行数和字节数,也可以在模型具备能力时返回图片内容。write 使用 UTF-8 写入、自动创建父目录,并用异步锁避免进程内的并发覆盖。
edit 是一个精确文本替换工具。它要求 oldText 非空且唯一匹配,多个替换不能互相重叠,所有检查完成后才写文件。它还会处理换行格式、UTF-8 BOM、diff 和 unified patch,并对同一路径加锁。模型改文件的风险,更多来自修改范围过大。把范围写成可以验证的条件,系统才有机会拒绝模糊修改。
bash 在 session cwd 中启动子进程,支持超时和取消,合并标准输出与错误输出,并限制返回给模型的行数和字节数。超过限制的完整输出会保存到临时文件,结果里带有退出码、耗时、截断状态和完整输出路径。
核心 loop 提供 before_tool_call 和 after_tool_call 这样的控制点,宿主可以用它们做确认、拦截或结果修正。钩子提供了能力,是否默认确认每一次 shell 调用仍由外层应用决定。
Provider,真正难的是把流整理好
tau_agent/provider.py 中的 ModelProvider 协议很小。核心只需要知道如何传入 model、system、messages、tools、取消信号和 session id,并收到 AssistantMessageEvent 流。
真正复杂的部分在 tau_ai/stream.py。它要把响应开始、文本 delta、thinking delta、tool call、停止原因、usage、诊断信息、错误和 Provider metadata 合并到一个可用的 partial assistant message 中。最终 content 的顺序依据实际流过的 block,而不是简单相信 Provider 最后一条完整 message。
OpenAI 兼容适配还要处理 SSE、HTTP 错误、网络错误、重试、取消、session affinity、prompt cache、图片、Chat Completions、Responses API 以及 output tool call。把这些都塞进 loop,核心会很快变成某一家 SDK 的包装层。现在它们被留在 Provider 边界里。
跨 Provider 的 tool call ID 也是一个小而真实的问题。不同服务对字符和长度有不同要求。tool_call_ids.py 会保留已经合法的 ID,对不合法的 ID 做确定性 hash,避免两个不同的原始 ID 在简单替换后撞在一起。
thinking metadata 也不能随便搬运。某个 Provider 产生的签名可能只适用于它自己的下一次请求。模型切换时,需要一起考虑消息格式、工具 ID、thinking block、历史转换和上下文限制,model 字符串本身只是其中一项。
Session,磁盘上保存的是一棵历史树
tau_agent/session/entries.py 中的 entry 通常带有 id、parent_id、timestamp 和 type。消息、模型切换、thinking level、compaction、branch summary、label、session info 和扩展数据都可以成为 entry。
所以 JSONL 文件表面上是一行一行的记录,内存里却是一棵树。SessionState.from_entries 会找到当前 active leaf,沿着 parent_id 回放 root 到 leaf 的路径,恢复 model、Provider、thinking level 和 compaction 状态,最后得到本次请求真正使用的上下文。
session/tree.py 负责树导航。branch_to_entry 可以把当前状态移动到历史中的某个分支点,原有记录仍然保留。需要时,Tau 会追加 BranchSummaryEntry,把被放弃的那段路径压缩成回来以后可以继续理解的上下文。
session/storage.py 让 JSONL 不至于停留在“打开文件追加一行”的水平。每个 session 有独立锁,写入会 flush 和 fsync,批量写入会经过同目录临时文件和 replace,Windows 与 POSIX 分别使用对应的锁机制。它没有把 Session 包装成数据库,却认真处理了进程中途退出这种普通情况。
工具历史修复放在 tool_history.py。如果进程刚好停在 assistant 发出 tool call、工具结果还没写入的位置,下次请求可能会遇到悬空调用。修复逻辑会补一个中断错误,处理乱序和重复结果,丢弃没有对应 call 的孤立结果,并优先保留真实结果。修复信息也会作为 tau.session-history-repair custom entry 落盘,不会每次启动都只在内存里临时擦一遍。
Compaction,历史完整和上下文可用要同时成立
上下文变长后,Tau 不会直接删除旧 JSONL。它会在 active context 上选择保留边界,请 Provider 生成摘要,再追加 CompactionEntry。entry 记录 parent、summary、first_kept_entry_id、压缩前 token 数,以及摘要请求的 usage 和 Provider metadata。
first_kept_entry_id 让压缩边界对应真实的 session entry。这样 Session replay 时仍然知道摘要覆盖到哪里,不会因为只对消息数组做切片而破坏分支关系。
如果 Provider 返回 context overflow,CodingSession 会记录错误,追加 compaction,发出自动重试事件,再用缩短后的上下文继续请求。磁盘上保留完整历史,模型当前请求只拿需要的部分。两种需求没有被混成一件事。
System prompt、Skills 和项目可信状态
system_prompt.py 会把 Agent 基础行为、工具描述、prompt guidelines、Tau 文档位置、AGENTS.md、Skills 索引、扩展 prompt sections、日期和 cwd 组装到 system prompt 中。
project_trust.py 决定项目目录里的指令、Skills 和扩展能不能进入运行时。项目文件可能改变模型的行为,所以“能不能读取”与“能不能执行”之外,还有一层“能不能成为模型上下文”的问题。
skills.py 采用 Agent Skills 的目录约定。system prompt 通常只放名称、描述和位置,任务匹配后再展开完整 SKILL.md。这样不会把所有技能全文都塞进每一次请求。
有一个边界需要记住。当前 CodingSession.load 或 reload 时会组装 system prompt,run_agent_loop 在同一次运行里复用这份字符串。运行期间修改 AGENTS.md,不代表下一次 Provider 请求立即读取了新内容,通常需要经过 reload 或其他明确的重建边界。
我认为 Tau 重要的地方
Loop 让模型调用变成可观察的过程
最小 Agent 可以写成“模型、工具、结果、模型”。Tau 往里面加的不是装饰,它把文本增量、工具调用、取消、失败、重试、上下文压缩和任务结束都变成了可消费的事件。前端能看到发生了什么,持久化也有明确的接缝,调试时不必只盯着最后一句回答。
事件流把核心和界面分开
TUI、print mode、JSON 输出和 transcript 导出消费同一套事件。UI 可以换,核心 loop 仍然保持原来的消息和工具协议。对于一个会不断增加展示方式的项目,这个边界能省掉很多重复实现。
Session 保存的是过程,不只是一条答案
历史树、分支、摘要、模型切换和工具结果都被记录下来。下次启动时,Tau 能重建“现在为什么处在这里”,这比保存一份最终 Markdown 多出了状态的连续性。
工具边界决定了 Agent 的实际能力
模型本身只会产生意图,read、edit、bash 才让意图作用到项目。路径解析、唯一匹配、输出截断、取消、锁和错误状态这些工程约束,决定了 Agent 的能力能不能被控制。
信任边界进入了运行时
AGENTS.md、Skills 和扩展都可能影响模型,所以项目资源的加载需要经过 trust 判断。权限不只存在于 shell 确认框里,system prompt 的组成同样属于运行时边界。
当前源码的边界
读到这里,Tau 已经是一个结构完整的 coding agent,但它仍然有几处清晰的限制。
- 没有独立的任务评估器,模型停止调用工具不等于目标已经验证完成。
- 当前工具调用按顺序执行,
execution_mode还没有转化成真正的并行调度。 ToolExecutionUpdateEvent的接口存在,但核心执行路径会缓存 update,长任务的实时进度还不完整。- system prompt 在 session 的明确重建边界上刷新,不会每一轮自动扫描项目文件。
- permission hook 是宿主提供的控制点,不是默认覆盖所有应用的安全策略。
- Provider 适配和历史修复可以让消息继续流动,却不能替模型验证代码逻辑。
这些限制并不削弱 Tau 的学习价值。它们反而把 Agent 框架里还没有解决的部分摆在了明面上。
做过的定向验证
当前环境是 Tau v0.4.2、Python 3.12.10 和 Windows。我针对本次阅读的核心链路运行了以下测试。
tests/test_agent_loop.py
tests/test_agent_harness.py
tests/test_tool_history.py
tests/test_session.py
tests/test_tau_ai.py
结果是 159 passed in 3.70s。这次是核心测试的定向结果,不代表整个仓库的测试套件都在这一轮被执行。
推荐的阅读顺序
可以按下面的顺序接手这套代码。
src/tau_agent/types.py和messages.pysrc/tau_agent/provider_events.py、events.py和tools.pysrc/tau_agent/loop.pysrc/tau_agent/harness.py和tool_history.pysrc/tau_agent/session/entries.py、memory.py、storage.py、tree.pysrc/tau_ai/stream.py和openai_compatible.pysrc/tau_coding/tools.py、system_prompt.py、project_trust.pysrc/tau_coding/session.py、session_preparation.py和session_manager.pysrc/tau_coding/cli.py、rendering 和 TUI
先把消息和事件读懂,再进入 Provider、Session 和 UI,代码里的很多决定就不会显得突然。
结语
Tau 的 loop 可以用几行伪代码说明,真正的工程量藏在它需要维持的关系里。助手消息里的 tool call 要有对应结果,Provider 的增量要还原成稳定内容,事件要让外部看见过程,Session 要让过程能够恢复,压缩要缩短上下文却不能抹掉历史,项目指令要在可信边界内生效。
我现在更愿意把 Tau 看成一套围绕 Agent loop 展开的协议,而不只是一个模型调用器。它把模型、工具、消息、事件和 session 拆开,让每一层都有可以检查的责任,也把取消、损坏、超限和模型差异这些不太好看的问题留在代码里。
这件事没有一个漂亮的终点。下一次启动时,系统只要还能知道自己上次停在哪里,就已经帮了很大的忙。
评论