yj060464-commits
dsh-chat-tools
DeepSeek Harness headless 终端伴侣工具链:chat.sh 连续对话 REPL(决策点拍板/工作流实时透传/思考档位切换)+ 会话日志自动总结,零依赖纯 bash+Python
- Stars
- 3
- Language
- Python
- Created
- Aug 13, 2026
- Updated
- Aug 14, 2026
Introduction
dsh-chat-tools
给 dsh(headless 模式)加装「连续对话 + 长期记忆 + 工作流可视化」的零依赖伴侣工具链。纯 bash + Python 标准库,不侵入 dsh 内部,只读写磁盘 transcript。
A zero-dependency companion toolkit for dsh: continuous chat, persistent memory, and live workflow visualization. Pure bash + Python stdlib, reads/writes the on-disk transcript only.
组件
| 文件 | 作用 |
|---|---|
chat.sh | 连续对话 REPL:免 dsh 前缀,分段滚动上下文保持对话连贯;决策点拍板(模型给出多选项时渲染终端菜单,拍板后自动沿该分支继续);退出/Ctrl-C 自动总结存记忆 |
chat_context.py | 分段滚动上下文构建器:段内窗口只增不减(前缀稳定 → 缓存命中率高),超限才整段截断;段起点持久化在 window.state |
chat_choice.py | 决策点解析器:解析/剥离模型回复末尾的 [CHOOSE] 块(纯函数、零依赖),供 chat.sh 渲染拍板菜单 |
chat_workflow_tail.py | 工作流实时透传:增量追读 dsh 会话 transcript(zstd frame 逐帧解码),把工具调用/结果、步骤边界实时打印到终端,零额外 token |
session_log.sh | 一次性会话包装器:包装任意 dsh 命令,会话结束后自动提取关键点写入项目 AGENTS.md「会话日志」 |
session_extract.py | LLM 记忆提取器:解析 transcript → LLM 提炼要点 → 追加到 AGENTS.md;无 key 时回退关键词提取;发送前对密钥/私钥等自动脱敏 |
解决的问题
dsh --profile headless "..." 每次都是全新会话(headless 固定新建随机 session),上下文不连续,还得敲一长串前缀。这套工具让 dsh 拥有:
- 连续对话(省 token) — 每次提问自动带上当前分段窗口内的对话记录(默认上限 8 轮 / 8000 字符,可调):窗口在段内只增不减,相邻两轮请求互为「前缀扩展」,DeepSeek 自动前缀缓存几乎全命中(只 miss 最新一轮增量);仅当超限才一次性截断(丢最旧一半),整窗 miss 从「每轮一次」降到「每段一次」
- 长期记忆 — 会话结束自动把关键点写入项目
AGENTS.md「会话日志」,新会话自动注入(AGENTS.md 分层约定) - 工作流可视化 — 实时看到模型每一步工具调用/结果/耗时,纯透传磁盘上已写好的 transcript,不消耗额外 token
- 中文输入适配 — readline 行编辑:退格按「字符」删,中文不会删成半个字/乱码
- 决策点(多选项拍板) — 任务有多个可行方向时,模型按约定在回复末尾输出
[CHOOSE]块(A/B/C… 选项),chat.sh 渲染成终端可选菜单,你输序号/字母拍板(q=都不选、直接说想法),自动以该选择继续执行分支;新回复仍有块则继续弹菜单(上限CHOICE_MAX_ROUNDS防失控)——headless 下等效于 web 端「多选项让用户拍板」的项目分支体验 - 思考档位实时切换(
!effort) — 运行中临时把模型思考档位切到high/max(写入共享的agent-default-model.reasoningEffort,与 web 端模型选择器同一存储),退出对话自动还原为进入前的档位(仅当期间未被外部改动才还原,避免覆盖 web 端新选择)。注意与!thinking(回复末尾的思考记录附加内容,off/short/full)是两回事:!effort改的是模型实际推理强度(off/high/max,low官方 API 已有但本机 dsh 适配器暂不支持)。
依赖
dsh(必需,底层对话引擎;可用CHAT_DSH_CMD换成任何兼容命令)bash(4.x+)、python3(仅标准库)、zstd(CLI,解码 transcript 用)
Ubuntu/Debian:
sudo apt install zstd
# dsh 按官方方式安装
安装
git clone https://github.com/<你的用户名>/dsh-chat-tools.git
cd dsh-chat-tools
chmod +x chat.sh session_log.sh session_extract.py
用法
连续对话(推荐日常入口)
./chat.sh # 进入连续对话,直接输入问题
./chat.sh --new # 忽略已有上下文,强制开新对话
./chat.sh --help
对话内命令:
| 命令 | 作用 |
|---|---|
exit / quit / q | 结束对话并总结存记忆 |
!save | 立即总结存记忆(不结束对话) |
!clear | 丢弃当前上下文重新开始(旧上下文归档保留) |
!context | 显示当前上下文文件路径 |
!thinking [off|short|full] | 切换回复末尾附加的思考记录模式(off=关闭 short=简短改动摘要 full=完整改动明细;不带参数=查看当前) |
!workflow [on|off] | 工作流实时透传开关 |
!choose [on|off] | 决策点拍板菜单开关(不带参数=查看当前) |
!effort [off|high|max] | 临时切换思考档位(写入 agent-default-model.reasoningEffort,退出对话自动还原为进入前档位;不带参数=查看当前) |
!dsh <命令...> | 临时更换底层 dsh 命令 |
一次性命令包装
./session_log.sh dsh tui # 包装任意 dsh 命令
./session_log.sh --profile headless "..." # 参数原样透传
环境变量
| 变量 | 默认 | 说明 |
|---|---|---|
CHAT_DSH_CMD | dsh --profile headless | 底层命令 |
CHAT_DIR | $DSH_HOME/chat/<工作区> | 上下文目录 |
CHAT_MAX_TURNS | 8 | 分段窗口的轮数上限(0=全部;窗口在段内只增不减,超限才整段截断) |
CHAT_MAX_CHARS | 8000 | 分段窗口的字符数上限(0=不限;超限整段截断) |
CHAT_SHOW_THINKING | full | 思考记录:full 完整改动明细 / 1 简短摘要 / 0 关闭 |
CHAT_SHOW_WORKFLOW | 1 | 工作流实时透传开关 |
CHAT_CHOICE | 1 | 决策点拍板菜单:1 开启(默认)/ 0 关闭([CHOOSE] 块按原文显示;对话内 !choose on|off 可随时切换) |
CHOICE_MAX_ROUNDS | 5 | 连续决策点自动继续的上限轮数(防失控) |
CHAT_WF_CHUNKS | 0 | 1=同时显示推理/文本流式分块(很吵) |
CHAT_WF_MAX_ARGS / CHAT_WF_MAX_RESULT | 200 | 工具参数/结果截断长度 |
SESSION_LOG_BASE_URL / SESSION_LOG_API_KEY / SESSION_LOG_MODEL | 见下 | LLM 端点/密钥/模型,默认取 ANTHROPIC_* / DEEPSEEK_API_KEY,再默认 DeepSeek anthropic 兼容端点 + deepseek-chat |
安全:密钥只走环境变量,不写死在代码里;LLM 提取与日志写入前会对私钥、API Key、token 自动脱敏。所有运行时数据(对话记录、transcript)只落在 $DSH_HOME,仓库本身不产生任何敏感文件。
工作原理(简要)
- 上下文文件:
chat.sh把对话以 dsh transcript 同格式(user/message、assistant/message事件)追加到$DSH_HOME/chat/<工作区>/context.jsonl,每次提问由chat_context.py取当前分段窗口拼入任务(段起点记在window.state,--new/!clear归档时重置)。 - 增量追读:dsh 每次 append 事件批次 = 一个完整 zstd frame + fsync。
chat_workflow_tail.py记住已消费的压缩字节偏移,只喂新字节给zstd -d -c:exit 0 才推进偏移,exit≠0(尾部 frame 未写完)丢弃重试——天然不丢不重,且不做每轮从头解压。 - 会话发现:headless 每次新建随机 session id,tailer 靠「启动前记录的 epoch 时间戳」只挂新会话,并用单向切换防止新旧会话横跳。
- 决策点协议:chat.sh 在附加要求提示词中约定——任务有多个可行方向时,模型在回复末尾输出
[CHOOSE]…[/CHOOSE]块(首行问题描述 + 每行一个A. …选项)。chat.sh 检测到块后:把含块的完整回复存入上下文(模型看得见自己列的选项)→ 用chat_choice.py剥离块、渲染菜单 → 你拍板后把「(决策点回应)用户选择/意见」作为下一轮消息继续执行该分支。CHAT_CHOICE=0时既不注入约定也不拦截,完全退化为旧行为。 - 记忆落盘:会话结束把 context/transcript 交给
session_extract.py,LLM 提炼要点(重试 + 关键词兜底),追加到 AGENTS.md「会话日志」(新的置顶,超 100 行自动删最旧)。 - 思考档位(
!effort):进入对话时读一次settings.yaml的agent-default-model.reasoningEffort(与 web 端模型选择器共享此存储)记为原始档位;!effort写新档位(段不存在则创建、字段不存在则插入、存在则替换);退出(exit/q/Ctrl-C 双路)仅在「当前值仍是本次设置值」时才还原回原始档位——安全网避免覆盖对话期间 web 端的新改动。
兼容性
只依赖 transcript 磁盘格式,不侵入 dsh 内部:换模型、换工作区、甚至换掉 dsh 本身,只要还有 user/message、assistant/message 事件格式的会话文件,这套工具都能接着用。已适配 Linux(Ubuntu)下中文输入法。