Skip to content

CH 29 · 可观测性与上下文管理

全文字数3172 字预估耗时约 20 分钟前置CH 04、CH 16难度理解为主

本章目标

Agent 跑起来之后,你迟早会遇到这些问题:它刚才为什么做了那个决定?这一轮花了多少 token?长会话越来越慢、越来越贵怎么办?

dsh 的做法是把 Agent 的每一步都摊开给你看——它看到了什么、想了什么、做了什么、花了多少,全部可追溯。前面 CH 04 讲过轨迹界面的基础,CH 16 提过缓存命中率,这一章把可观测性和性能讲透:轨迹怎么看、会话日志存在哪、Token 和缓存指标怎么读、上下文怎么管。学会这些,你就能从"能用"进阶到"用得省、用得稳"。

轨迹(Trajectory):Agent 的完整流水账

什么是轨迹

普通 Agent 工具给你看的是"聊天记录"——你说了什么、它回了什么。但中间发生了什么?它读了哪个文件、调了什么工具、工具返回了什么、为什么决定调用这个工具而不是那个——这些在聊天视图里是看不到的。

轨迹(Trajectory)就是解决这个问题的:它把 Agent 的完整执行过程按时间线记下来,是"模型视角的流水账",不是"用户视角的聊天记录"。

记录的内容包括:

类别记什么
系统提示每次请求发给模型的完整 system prompt
用户输入你发的消息、注入的上下文
模型请求发给模型的完整内容、模型返回的完整内容
工具调用调了哪个工具、参数是什么、返回结果是什么
子 Agent 调度起了几个子 Agent、各自干了什么、结果怎么汇总的
审批交互哪些操作请求了审批、你允许还是拒绝了

这些全部写入一份仅追加的会话日志——只增不改,保证记录的完整性和可审计性。

在哪看轨迹

Web UI 的会话界面里,顶部有个 轨迹(Trajectory) 页签,点进去就是。

轨迹界面:时间线 + 事件列表 + 详情面板

界面长得像浏览器开发者工具的网络面板:

  • 顶部时间轴:按真实起止时刻从左往右画,每一轮请求占一段
  • 左侧事件列表:一行一条记录,按轮次分组,每条标了类型(LLM / TOOL / SUBAGENT / APPROVAL)
  • 右侧详情面板:点某条记录,展开看完整内容——工具调用的 Schema / Payload / Result,模型请求的完整 prompt 和 response

轨迹详情:选中一条 TOOL 记录,右侧展开 Schema / Payload / Result

轨迹能用来干什么

  1. 排障:Agent 做了一件意料之外的事?回轨迹里看它当时收到了什么、为什么做了那个决定。90% 的"AI 抽风"都能在轨迹里找到原因——通常是它读了一个你没注意到的文件,或者工具返回了一个异常值。
  2. 复盘:任务完成得好或不好,回轨迹里看哪一步是关键转折点、哪一步浪费了 token。下次就能优化提示词或流程。
  3. 复现实验:做 Agent 研究时,轨迹就是完整的实验记录——同样的输入、同样的工具、同样的模型版本,能复现同样的结果。
  4. 审计:团队共用时,轨迹能回答"它什么时候改了这个文件、为什么改"——比 git log 更细,因为它连"为什么改"都记了。

会话日志:存在哪、怎么用

轨迹数据最终落在本地文件里,不是只存在内存里。

存储位置

所有会话存在 ~/.dsh/sessions/ 目录下(Windows 上是 C:\Users\<你的用户名>\.dsh\sessions\),按工作区分目录。

工作区目录名是路径的转义形式——用 -- 包裹,路径分隔符换成 -,特殊字符用 URL 编码。比如工作区 E:\software-workspace\DeepSeek harness demo 对应的目录名是 --E-software-workspace-DeepSeek~0020harness~0020demo--

每个会话是一个子目录,命名格式是 session-<uuid>(Web UI 创建的)或纯 <uuid>(headless 跑的),里面有一个 session.jsonl.zstd 文件(zstd 压缩的 JSONL,每行一条事件)。

~/.dsh/sessions/
├── --E-software-workspace-DeepSeek~0020harness~0020demo--/
│   ├── session-05da13b1-c7bf-4f42-843b-.../
│   │   └── session.jsonl.zstd
│   ├── session-06451c43-35fb-4338-bc87-.../
│   │   └── session.jsonl.zstd
│   └── 37e884fa-7c73-40fa-81fc-.../          ← headless 跑的会话
│       └── session.jsonl.zstd
└── --E-software-workspace-doubaowork-DeepSeekHarnessGuide--/
    └── session-f417b4dd-3c8f-4098-85.../
        └── session.jsonl.zstd

会话的三个能力

基于这份日志,dsh 支持三个操作:

  1. 恢复(Resume):关掉 dsh 再打开,之前的会话还在,能继续聊——因为日志是持久化的,不是内存态。
  2. 分叉(Fork):在某条历史消息下方点分叉图标,从这条消息开始开一条新路试试,而不动原会话。比如 Agent 走到第 5 步你觉得方向不对,可以 fork 到第 4 步换个提示词重来,原会话保留。分叉出来的新会话名字会带 (1)(2) 这样的后缀,和原会话区分开。
  3. 回放(Replay):把一条会话的事件流重新跑一遍,看每一步的输入输出。适合调试插件或复现问题。

分叉入口:消息下方的分叉图标 + 会话列表的更多操作菜单

分叉结果:新会话名字带 (1) 后缀,独立运行

性能指标:Token、缓存、上下文

dsh 在界面里实时展示几个关键性能指标,学会看这些,你就能控制成本和速度。

Token 用量

每轮请求结束后,界面底部会显示这一轮的 Token 统计:

  • 输入 Token:发给模型的总 token 数(含系统提示、历史对话、工具结果)
  • 输出 Token:模型生成的 token 数
  • 缓存命中 Token:输入中命中了 DeepSeek 上下文缓存的部分

对话底部的 Token 统计栏:鼠标悬浮显示完整信息(轮次/步数/耗时/缓存命中率/输入输出 token)

累计用量在会话统计里能看到——整个会话花了多少 token、花了多少钱。

上下文占用率

输入框右侧有个圆环,显示当前上下文占用百分比。这是 dsh 独有的设计——让你实时看到"还剩多少上下文空间"。

上下文占用圆环:输入框右侧显示百分比

上下文是有限的(不同模型窗口大小不同,一般 128K 起步)。当占用率接近 100% 时,dsh 会自动压缩上下文——把早期的历史总结成一段摘要,替换掉原始多轮对话,保证对话能继续下去,不会因为超出窗口直接报错。

自动压缩是兜底机制。所以更推荐的做法是:在合适的时候自己手动触发压缩——在输入框里输入 /compact 命令,dsh 会立即把当前对话压缩成一份摘要,替换掉原始多轮历史。按你的节奏压缩,关键信息不会丢。

占用率快满时,两个选择:输入 /compact 手动压缩,或直接开新会话。

上下文管理:长会话怎么不崩

Agent 会话有个天然问题:越聊越长,token 越花越多,模型越来越"记不住"前面的事。dsh 给了几个工具来管这件事。

什么时候该开新会话

不是所有任务都该在一个会话里做完。以下情况建议开新会话:

  • 任务类型变了:刚才在写代码,现在要做 PPT——开新会话,别让代码上下文污染 PPT 任务
  • 工作区变了:换了项目目录——开新会话,dsh 的会话是绑定工作区的
  • 上下文占用超过 70%:再聊下去模型会丢早期内容,反应也会变慢——有时候突然感觉模型变蠢了,就是这个原因。开新会话,或先 /compact 压缩

成本控制的几个习惯

  1. 别在一个会话里干所有事——按任务拆会话,每个会话上下文短、缓存命中率高、成本低
  2. 大文件别反复读——Agent 读一个 1000 行的文件,每次都要花 token。读完一次后让它把关键信息写进一个摘要文件,后面读摘要就行
  3. 用对模型——简单任务用 flash,复杂推理用 pro,视觉任务用 vision——别什么都用最贵的
  4. 定期看累计用量——会话统计里能看到总 token 和预估费用,别等到月底才吓一跳

这一章你学到了什么

能自己完成下面几条,就算过关:

  • [ ] 知道轨迹是什么、在哪看、轨迹里记录了哪些内容
  • [ ] 知道会话日志存在 ~/.dsh/sessions/,支持恢复、分叉、回放
  • [ ] 能看懂 Token 统计里的输入/输出/缓存命中分别是什么
  • [ ] 知道上下文占用率在哪看、快满了该怎么办
  • [ ] 知道 /compact 命令怎么用,什么时候该手动压缩
  • [ ] 能说出至少 3 条成本控制习惯

Open Source · MIT · Community Driven