README.zh.md

August 19, 2026 · View on GitHub

dsh-agent-driver-writehere — 把 WriteHERE 做成 DeepSeek Harness 的 Agent Driver

dsh-agent-driver-writehere

English · 中文

GitHub stars Last commit dsh-plugin WriteHERE driver MIT arXiv 2503.08275 TypeScript DSH web profile

一个 DeepSeek Harness Agent Driver:把 WriteHERE 的长文循环做成第二套推理循环,而不是往默认 ReAct 上再挂一批写作工具。
GetInfo → Update → 决定 → 按类型执行 · tools: [] · task 卡另开 standard 工人

是什么 · 案例 · 安装 · 快速上手 · 一拍在做什么 · 自定义 · 扩展 · 不适合做什么 · 环境要求 · 致谢

是什么

默认 ReAct 上跑长文,很容易摊平。模型要么先列大纲再整篇倒出,要么一路调工具,直到对话记录本身变成稿子。中途改目标、「这一段还缺事实」、以及检索 / 推理 / 成稿这三类工作,都没有一等公民的位置。

WriteHERE 把写作看成异构递归规划:先更新当前节点,再决定立刻执行,或拆成不同类型的子节点。本包装的是同一套循环,调度权在宿主 TypeScript,不在模型的 function calling。

默认 ReAct 会话本驱动
构造器ReactLoopAgentWriteHereAgent
主编工具原生 function callingtools: []
规划 / 检索 / 成稿一份越积越长的对话分类型卡片:write / think / task
检索同一会话继续调工具可续跑的 standard 工人
草稿模型在对话里打出来的字叶子 write 追加 article.md
Web 界面只有对话可选独立窗口 拆卡树

拆卡树窗口示意图:从「什么是 ReAct?」根节点长出 write / think / task 卡

Web 界面示意图。卡片颜色与真实窗口一致(write / think / task / needs-update)。不是某次私人会话的实拍。

所以它是 Agent DriverAgentLoop.prepare 可以选的另一种构造器),不是默认循环上的 article_* 工具包。

案例

ReAct Loop ReAct Loop 是用本驱动写技术文的微信公众号。微信搜这个名字即可关注。


一期成稿题目是「什么是ReAct?」。主编会话是 WriteHereAgenttools: []。检索和之后的排版是 task 卡,交给普通 standard 工人。叶子 write 追加进 article.md发到公众号不是本包的事——宿主调度器只负责规划、更新和成稿。

循环之所以还能给别人用:调度拍的协议(GetInfo → Update → 决定 → 执行)里不写微信。栏目差异放在 preset 人设、方法论 skill,以及 task 卡上的调度单。

安装

需要本机已有能跑的 DeepSeek Harnessdsh 在 PATH 上,以及 pnpm)。官方加载器就是 dsh plugin add:它在 $DSH_HOME/profiles/<名字> 里执行 pnpm add,因为本包声明了 dsh.bundle,所以会多叠一层配置。

一行(推荐)

dsh plugin --profile web add github:Player-YN/dsh-agent-driver-writehere
dsh --profile web

这一条会把本仓库装进 web profile、登记 bundle,并拉下 zod。插件第一次加载时,只有 ~/.dsh/.agent-presets/article-editor 还不存在 才会拷出厂 preset,并注册 WriteHere driver、把该 preset 绑上去。标准模式 / PTC / 极简 / 创造仍走默认 React。然后:新会话 → 选手动 技术博客博主article-editor)。这个会话会构造 WriteHereAgent,不会掉回 React。

钉死某个 commit,避免 main 一推安装物就变:

dsh plugin --profile web add github:Player-YN/dsh-agent-driver-writehere#<sha>

先克隆再装

git clone https://github.com/Player-YN/dsh-agent-driver-writehere.git
cd dsh-agent-driver-writehere
dsh plugin --profile web add .

Windows 在检出目录里也可以(会立刻拷一份 preset):

.\install.ps1

确认、更新、卸载

dsh --profile web --dump-config   # 应出现 "# == dsh-agent-driver-writehere"
dsh plugin --profile web update github:Player-YN/dsh-agent-driver-writehere
dsh plugin --profile web remove dsh-agent-driver-writehere

尚未发布到 npm。若某个 profile 已经从其他层装过本驱动,不要再加一次 bundle。

install-remote.sh / install-remote.ps1 只包装官方的 add,再拷 preset。不是第二套加载器。只对你读过的脚本做管道执行。

快速上手

请用 Web。

  1. 启动 profile:dsh --profile web(或 dsh web)。
  2. 新会话,选 技术博客博主article-editor)。只给这一路绑 WriteHere。
  3. 发一个题目,不要发一条 shell 命令。
  4. 点侧栏页脚打开 拆卡树。独立可拖动窗口、无限画布:拖卡片拉开或聚拢,父子连线跟着走。默认从上往下展开。
  5. 叶子 write 追加进 article.mdtask 交给 standard 工人,等回报。

主编不会开终端。需要跑命令、读仓库、调外部 API,一律写成 task 卡。

若宿主会把 --preset 写进 session.header.agentPreset,headless 可以这样开:

dsh --profile headless --preset article-editor "为什么必须写回"

遇到 task 卡,这一枪进程会 park。要等工人回来,请用 Web。

一拍在做什么

调度器一拍:GetInfo、Update、决定、执行

  1. 用户题目成为根节点(若后续不是新题目,则继续同一棵树)。
  2. 宿主构造 WriteHereAgent,不是 ReactLoopAgent
  3. GetInfo — 当前节点、祖先、依赖、本稿;规划拍还会带结构图。
  4. Update — 模型只回 {"goal":"..."},且只改 这一张 卡。
  5. 决定{"atomic":true} 表示现在执行,或 {"atomic":false,"children":[…]} 表示拆卡。
  6. 执行write 写读者正文;think 写备忘;taskpreset: 'standard' 调用 startContinuable

子节点完成后,父卡可能进入 needs-update,下一拍会先再 Update,再执行。

模型允许回什么

允许的回复
Update{"goal":"..."} — 只改本节点;不要带子卡;不要改父节点
决定{"atomic":true}{"atomic":false,"children":[{"type":"task"|"think"|"write","goal":"...","atomic":true}]}
write 执行读者会读到的段落。不要回 JSON,不要写成「写手须知」
think 执行推理备忘,不是成稿

调度器还会执行这些约束:

  • write 父节点若拆卡,孩子里至少要有一张 write
  • thinktask 默认原子;再拆必须在该孩子上写 atomic: false
  • 可选的 length 只给 write 孩子当篇幅预算。
  • 不要在决定 JSON 后面粘正文。

自定义

三层,从最便宜到「你在改驱动」。

改哪里要不要重新构建
主编人设~/.dsh/.agent-presets/article-editor/agent.cordis.ymlpersona / config.text不用。重启 dsh web
方法论~/.dsh/.agent-presets/article-editor/skills/<名字>/SKILL.md不用。下一拍规划会重读目录。
每一拍的指令、工人人设、检索分类packages/writehere/src/prompts.ts要:node scripts/build.mjs,再装一次或对着这份检出重启。
把另一个 preset id 绑到 WriteHerepackages/writehere/src/index.ts 里的 bindPreset(...)要。目前只绑了 article-editorxieka
算法 / 节点类型 / tools: []调度器 + 树引擎要,并尽量保持 Algorithm 1 兼容。

第一次加载 不会覆盖 已经存在的 article-editor 目录。请改 ~/.dsh/.agent-presets/ 里那份。只有想恢复出厂 preset 时才删那个目录。

哪些 prompt 能改

Prompt文件作用
主编人设(模型以为自己是谁)preset 的 agent.cordis.yml真正活着的人设。日常改这里。
Update 拍UPDATE_INSTRUCTION必须仍是「只回 JSON {"goal":"..."},只改本节点」。
决定拍(write 父节点)DECIDE_WRITE_INSTRUCTION必须仍是 JSON atomic / children
决定拍(think / task)DECIDE_ATOM_INSTRUCTION同一套 JSON;默认原子。
write / think 执行EXECUTE_WRITE_INSTRUCTIONEXECUTE_THINK_INSTRUCTION只收散文。
父节点合成COMPOSE_WRITE_INSTRUCTION子卡齐了之后只收散文。
检索工人RETRIEVAL_PERSONARETRIEVAL_PROMPT_PREFIXisRetrievalGoal(goal) 为真时传给 startContinuable
其他 task 工人LAB_PERSONA同一套派工,非检索任务。
GetInfo 外壳GET_INFO_OPEN / GET_INFO_CLOSE快照两侧的标签。只改标签、不改 completeText,旧快照会漏到表面上。

GetInfo 的 JSON 形状(节点、祖先、依赖、本稿、规划拍的结构图)由 packages/article-tree/src/getinfo.ts 生成。把它当协议,不要当文案改。

DSH_JSON_SCHEMA=1 会把 Update / 决定从 { type: "json_object" } 换成 { type: "json_schema" }。适配器没写明支持就不要开——DeepSeek 的 chat-completions 对 json_schema 仍会 400。

扩展

这套循环的设计是 小主编 + 普通 ReAct 工人。多出来的能力放在工人或 preset 上,不要做成主编身上的 article_* 函数。

Skills(现在就能加,不用改驱动)

方法论 skill 不是 function calling 工具。编辑 preset 下每个 SKILL.md 会在规划拍拼进 <article-methodology>…</article-methodology>packages/writehere/src/skills.ts)。

~/.dsh/.agent-presets/article-editor/skills/
  my-house-style/
    SKILL.md

出厂例子:presets/article-editor/skills/teach-for-transfer/column-runtime-control/。第一次拷贝之后它们在用户 roster 里;新 skill 加在旁边即可。

不要指望在主编 preset 上放一个会开终端、调 API 的 skill,模型就会去调。主编请求里是 tools: []

工人 skill 就是 standard preset 已经会加载的那些(用户 / 工作区 / 系统 skill 目录)。task 卡继承那个世界。给工人加能力:按 DSH 常规给 standard 装 skill 包,或在工作区放 SKILL.md

工具(工人可以,主编不行)

工具
主编(WriteHereAgent没有。不要在这里加 article_decompose / article_write / bash
task 工人(preset: 'standard'standard 有什么就是什么:终端、搜索、你用 dsh plugin add 装的其他工具。

要给专栏加一种新能力(抓网页、调 API、排版):

  1. 把那个工具做成普通 DSH 插件,装在 web / standard 一侧。
  2. 在人设或方法论 SKILL.md 里写清:何时拆一张 task,goal 写成给工人的调度单。
  3. 调度器已经会 startContinuable({ preset: 'standard', persona })。不用给主编再注册函数。

prompts.ts 里的 isRetrievalGoal 只决定 检索人设还是实验人设,不选工具。长得像发稿的 goal 会走实验人设,不走检索。

换 preset,或换驱动

  • 同一套 WriteHERE 循环,另一栏。article-editor 拷到 ~/.dsh/.agent-presets/<id>/,改人设和 skills,再在 apply() 里加一行 ctx.agentDrivers.bindPreset('<id>', 'writehere')(或写一个只做 bindPreset 的小宿主插件)。不绑的话,会话仍是 ReactLoopAgent
  • 另一种构造器。 ctx.agentDrivers.register(id, Ctor) 是公开注册表。对 article-editor 再绑一次会抛错。卸掉本 bundle,writehere 的绑定一起卸掉。
  • 不要靠给主编挂工具带来做「扩展」。那会把循环摊回默认 ReAct。

除非你改算法,否则不要动这些

  • 拍序:GetInfo → Update → 决定 → 按类型执行
  • 节点类型:write / think / task(论文里的 search
  • 依赖齐了进入 needs-update
  • 叶子 write 追加 article.md;父节点合成不会另写一篇稿
  • 步子由宿主走;模型不能靠 function calling 自己点下一张卡

能做什么

  • 层次化文章树:write(读者正文)、think(推理备忘)、task(检索或实验,论文里的 search
  • 先 Update 当前选中节点 的 goal,再决定或执行
  • 依赖刚完成时进入 needs-update
  • 叶子 write 增量写入工作区草稿
  • Web 独立窗口 拆卡树ui-article-tree,可选)
  • 附带 preset article-editor(界面名:技术博客博主)

不适合做什么

出现下面任一需求,请不要用本包:

  • 要写代码、运维、开终端的主编。编辑模型没有工具;干活的是普通 standard 会话。
  • 要一份与 principia-ai/WriteHERE 逐文件对应的 Python 移植。这是按算法重写的 TypeScript 实现。
  • 只想在默认循环上多几个 article_decompose / article_write 之类的 ReAct 函数。
  • 希望 headless 一枪把检索跑完。task 卡会 park,工人不会在同一次 dsh 进程里结束。完整交互入口是 Web。

环境要求

  • 已安装 DeepSeek Harnessdsh 命令
  • 活跑需要 profile 里已配置好的模型密钥

在官方 DSH 上,本插件会包一层 AgentLoop.prepare:绑了的 preset 构造 WriteHereAgent;没绑的仍走 ReactLoopAgent。宿主自己已经查 ctx.agentDrivers 时(见 patches/agent-loop-prepare.snippet.ts)不会再包一层。

dsh plugin --profile <名字> add 会把参数转发给 $DSH_HOME/profiles/<名字> 里的 pnpm。这是官方插件安装路径,见打包并安装插件

如果新建的 技术博客博主 会话仍像会调工具的编程助手,说明这层包装没挂上。请带上 dsh --profile web --dump-config 和会话日志开 issue。

工作原理

本仓库是 DSH bundlepackage.json 声明 "dsh": { "bundle": { "patch": "./cordis.patch.yml" } }。装进 profile 后会多一层配置,插入三个插件:

  • agent-drivers — 宿主平面的构造器注册表(ctx.agentDrivers
  • writehere — 注册 WriteHereAgent,绑定 preset article-editor;宿主自己还不会查 driver 时,再包一层 stock AgentLoop.prepare
  • ui-article-tree — Web 拆卡树窗口;在 headless 上为空操作

registerbindPreset 必须在宿主上下文、任何会话创建之前执行。它们不能放进 preset:preset 在 new Agent 之后才 mount。

每次调模型都会重新生成一封 GetInfo(<article-get-info>…</article-get-info>)。方法论 skill 和短栏记忆在这封 JSON 外面。Update / 决定拍走 JSON 响应格式;think / write / 合成拍只收散文。completeText 在模型可见表面上只保留最新一封 GetInfo。

和论文、Python 原版的差别

算法和节点类型跟 WriteHERE §5 / Algorithm 1 以及 principia-ai/WriteHERE。下面几处是有意不同,不是漏移植:

论文 / Python本包
主编一侧Python 引擎里的写作工具tools: [],由宿主调度
检索类型名searchtask
GetInfo共享的规划上下文Update、决定、执行各自刷新一封
检索 / 实验Python lab 进程DSH standard 会话,经 startContinuable
代码参考 Python 实现新的 TypeScript 实现

仓库结构

docs/                      README 封面、拆卡树示意图、一拍流程图
packages/agent-drivers     ctx.agentDrivers 注册表
packages/article-tree      树、GetInfo、草稿辅助
packages/writehere         WriteHereAgent 与 Algorithm 1 调度器
packages/ui-article-tree   Web 拆卡树窗口
presets/article-editor     人设与 skills(无工具)
cordis.patch.yml           `dsh plugin add` 叠上去的那一层

改循环请先看 CONTRIBUTING.md。主编一侧保持没有模型可见工具。

致谢与引用

署名说明见 NOTICE。机器可读引用见 CITATION.cff

@misc{xiong2025heterogeneousrecursiveplanning,
  title={Beyond Outlining: Heterogeneous Recursive Planning for Adaptive Long-form Writing with Language Models},
  author={Ruibin Xiong and Yimeng Chen and Dmitrii Khizbullin and Mingchen Zhuge and J{\"u}rgen Schmidhuber},
  year={2025},
  eprint={2503.08275},
  archivePrefix={arXiv},
  primaryClass={cs.AI},
  url={https://arxiv.org/abs/2503.08275}
}

许可证

MIT。算法属于论文作者。这份 TypeScript 实现是新写的,不是逐文件移植。

发现

DeepSeek Harness 用 GitHub topic dsh-plugin 收录社区插件。本仓库已打上:

dsh-plugin · dsh · deepseek-harness · writehere · agent-driver · long-form-writing · cordis

awesome-dsh-plugin 和市场类索引都靠这个 topic 发现新插件。被收录不等于安全审计——安装前请先读本 README 和源码。