第 1 周 · Tether 源码课

从一次运行认识 Agent Harness

预计 60–75 分钟 先修:会基础 C#、会在终端运行命令

学完你能做到

  • 区分 LLM、chatbot 与 agent harness
  • 无 API key 跑通 Tether CLI 的一次完整 turn
  • 从输出中找到 session、event、status 与 teardown 证据
  • 用一条主链描述输入如何变成输出

课程进度

  1. 第 1 周
  2. 第 2 周
  3. 第 3 周
  4. 第 4 周
  5. 第 5 周
  6. 第 6 周
  7. 第 7 周
  8. 第 8 周
  9. 第 9 周
  10. 第 10 周
  11. 第 11 周
  12. 第 12 周
  13. 第 13 周
  14. 第 14 周
  15. 第 15 周
  16. 第 16 周

一句话先懂

LLM 负责“想出下一段内容”,agent harness 负责“让一次工作可靠地发生”。

Tether 把模型接到会话、工具、权限、持久化和生命周期上;没有这层 harness,模型只是在输入文本后继续生成文本。

今天不拆内部零件。目标只有两个:亲手跑一次,然后能指着输出说出“系统刚才做过什么”。

先看大图

你的输入 --send CLI 读取参数 插件树 Agent 组织 turn / step 追加事件 ReplayLlm 读预录回复 Session 保留事件事实 输出
本周只记主链:CLI 接收输入,Agent 组织工作,Session 留下事实,ReplayLlm 提供可重复的模型回复。

一个类比:餐厅不是厨师

把 LLM 想成厨师:它根据订单做出一道菜。agent harness 更像整家餐厅:

餐厅里的角色Tether 中的角色
顾客写下需求用户消息
前台接单CLI
店长安排这一单的步骤Agent loop
厨师产出菜品LLM 生成内容
工具间执行切配、清洗Tools
订单流水记录发生过什么Session events
门禁与操作规范Approval、Sandbox、Invariants

类比边界:餐厅类比只帮助理解角色分工。它不能推出 Tether 的并发、异常、取消或持久化语义;这些都必须以源码和测试为准。

术语落地

术语现在先这样理解本周入口
LLM接收上下文并流式生成内容的能力ReplayLlm 用预录内容模拟它
Agent把一次用户请求组织成一个或多个 stepIAgent.SendAsync
Harness把模型与软件能力可靠连接起来的运行系统Tether 整体
TurnAgent 处理一批输入直到本轮停止turn/start 到 turn/end
Step一次模型请求及其可能的工具处理step/start 到 step/end
Session以 append-only event 记录已经发生的事实输出中的 events 与 transcript

先不要背类名。能在图和输出里找到它们,比会默写定义重要。

源码放大镜

入口是 apps/Tether.Cli/Program.cs。先看目的:它只负责读取进程参数、启动已经组合好的插件树、运行 CLI host,最后可靠回收。

// 生产组合挂真实 llm-multi;只有显式 --replies 才把发行 replay patch 追加为最后一层,
// 把 llm/default-model 换成脚本回放客户端。
ReplayLlm? llm = null;
if (replies is not null)
{
    llm = new ReplayLlm();
    llm.Load(replies);
    patches.Add(BundleCatalogs.ReplayPatchPath);
}

// …
boot = await AppBoot.StartAsync(cliCatalog, bootOptions, cts.Token);
result = await boot.Context.Require<ICliHost>().RunAsync(cts.Token);

这几行完成三件事:

  1. ReplayLlm 从 JSON 文件读取预录回复,所以第一次运行不需要 API key; 发行组合本身始终挂真实 provider 行,--replies 只会把 bundles/replay.patch.yaml 追加为最后一个 patch 层,把 llm-multi/agent-default-model 换成脚本回放。
  2. AppBoot.StartAsync 根据 composition 挂载插件树。
  3. CLI 从 Context 里取得 ICliHost,再把你的输入交给 Agent。

注意这里没有 new Agent(...)、new SqliteStore(...) 的长清单。具体实现由组合配置选择;第 7 周会追踪这条装配链。

动手实验:第一次完整 turn

实验目标:不用真实模型跑完一次 turn,并从结果中找到“开始、生成、结束、回收”的证据。

1. 预测

运行前,在纸上或临时文本里写下:

  • 预录文件只有 hello-world,最终文本会是什么?
  • 一个没有工具调用的 turn 至少会留下哪些 start/end 事件?
  • 命令结束时 Agent 应该是 Running 还是 Idle?

2. 运行

macOS / Linux:

course_home=$(mktemp -d)
dotnet run --project apps/Tether.Cli -- \
  --home "$course_home" \
  --replies apps/Tether.Cli/replies/text.json \
  --send "Summarize this repository"

PowerShell:

$courseHome = Join-Path ([IO.Path]::GetTempPath()) ("tether-course-" + [guid]::NewGuid())
dotnet run --project apps/Tether.Cli -- `
  --home $courseHome `
  --replies apps/Tether.Cli/replies/text.json `
  --send "Summarize this repository"

第一次会先编译项目,耐心等到输出两行 JSON。第一行是面向终端的文本结果;第二行是便于测试和诊断的完整 snapshot。

3. 观察

输出很长,先只找这些片段:

{"op":"text","text":"hello-world"}
{
  "output": "ok",
  "error": null,
  "status": "Idle",
  "events": [
    { "type": "turn/start" },
    { "type": "step/start" },
    { "type": "user/message" },
    { "type": "assistant/chunk" },
    { "type": "assistant/message" },
    { "type": "step/end" },
    { "type": "turn/end" }
  ],
  "teardown": { "complete": true }
}

实际 events 还会按顺序出现 permission/preset、agent/inbox/spliced、system/message、 request/header、request/start 等事件(不同 seq 填充其间),本节略去具体 seq。 不要把上面的节选误认为完整日志。

4. 解释

  • hello-world 来自 apps/Tether.Cli/replies/text.json,不是模型理解了你的英文问题。
  • status: Idle 说明 Agent 已处理完 inbox,不再运行。
  • turn/start、step/start 与对应的 end 形成“括号”,让失败、取消和恢复有可检查边界。
  • teardown.complete: true 说明进程退出前插件树完成回收;能给出答案不等于能安全退出。

检查理解

1. 如果把 --send 文本换成别的内容,为什么回复仍然可能是 hello-world?

查看答案

因为本实验使用 ReplayLlm。它按预录文件返回内容,不根据提示词推理。这样测试就不依赖网络、密钥、模型更新或随机性。

2. assistant/message 已经出现,为什么还需要 step/end 和 turn/end?

查看答案

消息内容只说明模型产出过什么;end 事件说明对应工作边界已经正常闭合。工具调用、失败或取消时,内容事件与生命周期事件承担不同职责。

3. Tether 与 LLM 是同一个东西吗?

查看答案

不是。LLM 是 Tether 可替换的一项能力;Tether 是组织 LLM、工具、会话、权限与生命周期的 harness。今天甚至用 ReplayLlm 替代了真实模型。

本周带走

  • 先把 Tether 看成“让一次 agent 工作可靠发生的系统”,而不是聊天界面。
  • 一次成功运行至少有输入、turn/step 生命周期、模型内容、session 事实和 teardown 证据。
  • ReplayLlm 让第一次运行完全可重复;后续每周都能沿同一份输出向内追踪。

下一周我们不再看运行结果,而是把六十多个项目压缩成一张可阅读的仓库地图。完整运行入口可随时回看快速开始。

在 GitHub 上编辑此页