CH 29 · 可观测性与上下文管理
本章目标
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

轨迹能用来干什么
- 排障:Agent 做了一件意料之外的事?回轨迹里看它当时收到了什么、为什么做了那个决定。90% 的"AI 抽风"都能在轨迹里找到原因——通常是它读了一个你没注意到的文件,或者工具返回了一个异常值。
- 复盘:任务完成得好或不好,回轨迹里看哪一步是关键转折点、哪一步浪费了 token。下次就能优化提示词或流程。
- 复现实验:做 Agent 研究时,轨迹就是完整的实验记录——同样的输入、同样的工具、同样的模型版本,能复现同样的结果。
- 审计:团队共用时,轨迹能回答"它什么时候改了这个文件、为什么改"——比 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 支持三个操作:
- 恢复(Resume):关掉 dsh 再打开,之前的会话还在,能继续聊——因为日志是持久化的,不是内存态。
- 分叉(Fork):在某条历史消息下方点分叉图标,从这条消息开始开一条新路试试,而不动原会话。比如 Agent 走到第 5 步你觉得方向不对,可以 fork 到第 4 步换个提示词重来,原会话保留。分叉出来的新会话名字会带
(1)、(2)这样的后缀,和原会话区分开。 - 回放(Replay):把一条会话的事件流重新跑一遍,看每一步的输入输出。适合调试插件或复现问题。


性能指标:Token、缓存、上下文
dsh 在界面里实时展示几个关键性能指标,学会看这些,你就能控制成本和速度。
Token 用量
每轮请求结束后,界面底部会显示这一轮的 Token 统计:
- 输入 Token:发给模型的总 token 数(含系统提示、历史对话、工具结果)
- 输出 Token:模型生成的 token 数
- 缓存命中 Token:输入中命中了 DeepSeek 上下文缓存的部分

累计用量在会话统计里能看到——整个会话花了多少 token、花了多少钱。
上下文占用率
输入框右侧有个圆环,显示当前上下文占用百分比。这是 dsh 独有的设计——让你实时看到"还剩多少上下文空间"。

上下文是有限的(不同模型窗口大小不同,一般 128K 起步)。当占用率接近 100% 时,dsh 会自动压缩上下文——把早期的历史总结成一段摘要,替换掉原始多轮对话,保证对话能继续下去,不会因为超出窗口直接报错。
自动压缩是兜底机制。所以更推荐的做法是:在合适的时候自己手动触发压缩——在输入框里输入 /compact 命令,dsh 会立即把当前对话压缩成一份摘要,替换掉原始多轮历史。按你的节奏压缩,关键信息不会丢。
占用率快满时,两个选择:输入 /compact 手动压缩,或直接开新会话。
上下文管理:长会话怎么不崩
Agent 会话有个天然问题:越聊越长,token 越花越多,模型越来越"记不住"前面的事。dsh 给了几个工具来管这件事。
什么时候该开新会话
不是所有任务都该在一个会话里做完。以下情况建议开新会话:
- 任务类型变了:刚才在写代码,现在要做 PPT——开新会话,别让代码上下文污染 PPT 任务
- 工作区变了:换了项目目录——开新会话,dsh 的会话是绑定工作区的
- 上下文占用超过 70%:再聊下去模型会丢早期内容,反应也会变慢——有时候突然感觉模型变蠢了,就是这个原因。开新会话,或先
/compact压缩
成本控制的几个习惯
- 别在一个会话里干所有事——按任务拆会话,每个会话上下文短、缓存命中率高、成本低
- 大文件别反复读——Agent 读一个 1000 行的文件,每次都要花 token。读完一次后让它把关键信息写进一个摘要文件,后面读摘要就行
- 用对模型——简单任务用 flash,复杂推理用 pro,视觉任务用 vision——别什么都用最贵的
- 定期看累计用量——会话统计里能看到总 token 和预估费用,别等到月底才吓一跳
这一章你学到了什么
能自己完成下面几条,就算过关:
- [ ] 知道轨迹是什么、在哪看、轨迹里记录了哪些内容
- [ ] 知道会话日志存在
~/.dsh/sessions/,支持恢复、分叉、回放 - [ ] 能看懂 Token 统计里的输入/输出/缓存命中分别是什么
- [ ] 知道上下文占用率在哪看、快满了该怎么办
- [ ] 知道
/compact命令怎么用,什么时候该手动压缩 - [ ] 能说出至少 3 条成本控制习惯
