Back to home

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.pyLLM 记忆提取器:解析 transcript → LLM 提炼要点 → 追加到 AGENTS.md;无 key 时回退关键词提取;发送前对密钥/私钥等自动脱敏

解决的问题

dsh --profile headless "..." 每次都是全新会话(headless 固定新建随机 session),上下文不连续,还得敲一长串前缀。这套工具让 dsh 拥有:

  1. 连续对话(省 token) — 每次提问自动带上当前分段窗口内的对话记录(默认上限 8 轮 / 8000 字符,可调):窗口在段内只增不减,相邻两轮请求互为「前缀扩展」,DeepSeek 自动前缀缓存几乎全命中(只 miss 最新一轮增量);仅当超限才一次性截断(丢最旧一半),整窗 miss 从「每轮一次」降到「每段一次」
  2. 长期记忆 — 会话结束自动把关键点写入项目 AGENTS.md「会话日志」,新会话自动注入(AGENTS.md 分层约定)
  3. 工作流可视化 — 实时看到模型每一步工具调用/结果/耗时,纯透传磁盘上已写好的 transcript,不消耗额外 token
  4. 中文输入适配 — readline 行编辑:退格按「字符」删,中文不会删成半个字/乱码
  5. 决策点(多选项拍板) — 任务有多个可行方向时,模型按约定在回复末尾输出 [CHOOSE] 块(A/B/C… 选项),chat.sh 渲染成终端可选菜单,你输序号/字母拍板(q=都不选、直接说想法),自动以该选择继续执行分支;新回复仍有块则继续弹菜单(上限 CHOICE_MAX_ROUNDS 防失控)——headless 下等效于 web 端「多选项让用户拍板」的项目分支体验
  6. 思考档位实时切换(!effort — 运行中临时把模型思考档位切到 high/max(写入共享的 agent-default-model.reasoningEffort,与 web 端模型选择器同一存储),退出对话自动还原为进入前的档位(仅当期间未被外部改动才还原,避免覆盖 web 端新选择)。注意与 !thinking(回复末尾的思考记录附加内容,off/short/full)是两回事:!effort 改的是模型实际推理强度off/high/maxlow 官方 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_CMDdsh --profile headless底层命令
CHAT_DIR$DSH_HOME/chat/<工作区>上下文目录
CHAT_MAX_TURNS8分段窗口的轮数上限(0=全部;窗口在段内只增不减,超限才整段截断)
CHAT_MAX_CHARS8000分段窗口的字符数上限(0=不限;超限整段截断)
CHAT_SHOW_THINKINGfull思考记录:full 完整改动明细 / 1 简短摘要 / 0 关闭
CHAT_SHOW_WORKFLOW1工作流实时透传开关
CHAT_CHOICE1决策点拍板菜单:1 开启(默认)/ 0 关闭([CHOOSE] 块按原文显示;对话内 !choose on|off 可随时切换)
CHOICE_MAX_ROUNDS5连续决策点自动继续的上限轮数(防失控)
CHAT_WF_CHUNKS01=同时显示推理/文本流式分块(很吵)
CHAT_WF_MAX_ARGS / CHAT_WF_MAX_RESULT200工具参数/结果截断长度
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/messageassistant/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.yamlagent-default-model.reasoningEffort(与 web 端模型选择器共享此存储)记为原始档位;!effort 写新档位(段不存在则创建、字段不存在则插入、存在则替换);退出(exit/q/Ctrl-C 双路)仅在「当前值仍是本次设置值」时才还原回原始档位——安全网避免覆盖对话期间 web 端的新改动。

兼容性

只依赖 transcript 磁盘格式,不侵入 dsh 内部:换模型、换工作区、甚至换掉 dsh 本身,只要还有 user/messageassistant/message 事件格式的会话文件,这套工具都能接着用。已适配 Linux(Ubuntu)下中文输入法。

License

MIT