dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
把 agent 自报的错误收进分类经验库,并每轮回注系统提示词(每条草稿都先经用户确认或修改);同时在对话标题栏提供两个面板,用于编辑跨对话全局提示词与本次对话提示词。
本项目由 DeepSeek Harness 的 AI 智能体独立开发。
对话标题栏里的两个小面板:编辑提示词、维护错误经验库。
English | 中文
| 面板 | 作用 |
|---|---|
| 提示词 | 编辑跨对话的全局提示词,以及本次对话的提示词(新对话会延续)。面板顶部显示哪一半当前正在生效。 |
| 错误库 | 按分类归纳反复犯的错误:每条一个简短类型 + 一句教训,归入三档分类之一。最近 10 条(条数可自己设)会被注入系统提示词,让后续对话以极低的 token 成本吸取教训。agent 按格式汇报「我犯了个错误:错误经过:… 教训:…」时会被自动捕获成待确认草稿。 |
第二个面板的意义:一个反复犯同类错误的 agent,会在自己的上下文里收到一份简短的、按分类归好的提醒,成本远低于重新踩一遍坑再发现。
点面板以外的空白处会自动关闭(按 Esc 也可以)。
错误经验库 —— 按分类折叠,每条带下拉框可改类;顶部显示注入条数,底部是自动捕获的待确认草稿:

提示词 —— 顶部自检状态与「临时暂停注入」,中间是全局与本次对话提示词,底部是实际注入文本的预览(可拖拽调高,也可隐藏;三个框都能竖向拖动):

本插件目前只发布在 GitHub(未发布到 npm)。
完整的安装步骤、验证方法与常见问题见 INSTALL.md。下面是最短路径。
设置 → 插件 → 安装,在输入框里填 GitHub 短地址:
github:1015677515/dsh-prompt-ErrorNotes-desk
也可以填完整网址:
https://github.com/1015677515/dsh-prompt-ErrorNotes-desk
安装界面提示「输入插件的包名、GitHub 仓库地址或本地目录路径」,上面两种都是它接受的写法。
不需要自己写 commit SHA。 填不带版本后缀的短地址即可:市场会先把默认分支的 HEAD 解析成 40 位 commit SHA,再把 github:owner/repo#<sha> 这种已固定的形式交给 pnpm。这样 lockfile 里记录的是可追溯的提交,安装后版本号能正常显示。
⚠️ 反过来要注意:不要自己写
#HEAD,也不要让安装源停留在会移动的分支上。源码里写明,HEAD 形态的 tarball「installs perfectly and then reports no version forever」——能装上,但版本栏会一直是空的,之后也检测不到更新。
已经克隆到本地时,直接填目录路径(安装界面会提示「本地目录」):
A:\大肥鱼工作区\dsh-prompt-ErrorNotes-desk
开发时这样最方便:local 安装默认以目录链接(Windows 上是 Junction)接入,改完源码刷新页面即可,不必重装。
# cordis.patch.yml of your bundle
- insert:
- id: prompt-desk
name: dsh-prompt-ErrorNotes-desk
装好后需要重启 DSH:浏览器半边会热重载,但宿主半边(负责往系统提示词注入)要重启才会加载新的模块代次。
| 依赖 | 要求 |
|---|---|
| DSH | `>=0.2.0-rc.1 |
| Node | ^22.19.0 || >=24.0.0 |
| HTTP 服务 | 必需。宿主半边注册 GET|POST /prompt-desk/state,浏览器半边靠它读写。没有 HTTP 服务的 profile 会加载插件但保持不活动,而不是报错 |
无第三方运行时依赖;浏览器半边只用页面已提供的 react,没有构建步骤。
全局提示词就是 DSH 自己那个 $DSH_HOME/AGENTS.md —— 也就是官方 dsh-agent-instructions 插件本来就会读的同一个文件。本插件不另存副本:两个都自称「全局提示词」的文件必然会漂移,而且只有一个真正会送达模型。
| 数据 | 位置 |
|---|---|
| 全局提示词 | $DSH_HOME/AGENTS.md |
| 本次对话提示词、错误库、面板开关 | $DSH_HOME/prompt-desk/state.json |
写入是原子的(先写 .tmp 再改名),崩溃不会截断你的提示词。浏览器半边完全不碰文件系统——所有读写都在宿主半边,所以不会写到 $DSH_HOME 之外。
仓库根目录有一份 AGENTS.example.md,是插件作者自己正在用的全局提示词原文,公开出来纯粹当写法参考:你可以看看别人怎么分节组织、怎么把"先自检再交付""删除前报范围"这类要求写成一条能照着执行的话,然后照自己的习惯改。
它不会覆盖你的提示词,装插件也不会。 说清楚以免误会:
AGENTS.md。代码里唯一会写这个文件的地方是你在面板里点「保存」的那一刻,写的是你自己输入框里的内容,不是这个样例。AGENTS.example.md,不是 AGENTS.md,也没有放在会被自动加载的位置。DSH 的项目指令加载器只认 AGENTS.md / CLAUDE.md / AGENTS.local.md / CLAUDE.local.md 这几个精确文件名,AGENTS.example.md 不在名单里——哪怕你把这个仓库当工作区打开,它也不会被当成指令读进去。想用的话自己动手,最稳的方式是先备份再合并:
# DSH_HOME 没设置时插件用的是 ~/.dsh,这里跟着它走
$dsh = if ($env:DSH_HOME) { $env:DSH_HOME } else { Join-Path $HOME '.dsh' }
Copy-Item "$dsh\AGENTS.md" "$dsh\AGENTS.md.bak"
然后打开 $DSH_HOME/AGENTS.md(也就是插件面板里的"全局提示词"框),挑你需要的那几条粘进去。直接整份覆盖也可以,但那会丢掉你原有的规则——你的提示词应该长成你自己的样子,别人的只能当参考。
本次对话提示词、以及最近 10 条错误经验,通过 ctx.systemPrompt.section() 注入,顺序值 10200(DEPLOYMENT_PERSONA_SUFFIX),即排在工具文档之后。
text 传的是函数,所以改完下一轮对话就生效——不用重启,也不用重新注册。detail 解释了事情经过,type 是这次的事故标题——两者描述的都是某一个瞬间,只有碰上长得像的下一次任务才有用;而教训是一条能迁移的规则。detail 同时是 token 最贵的一项,所以两者都只留在面板里。在错误库面板里直接填(最方便):面板里有一个「注入条数上限」输入框,填好点「保存」即可,下一轮对话生效。
| 字段 | 默认 | 含义 |
|---|---|---|
injectLimit | 10 | 注入系统提示词的错误经验条数上限。0 = 一条都不注入(会话提示词不受影响);最大 200,填更大也会被夹到 200 |
优先级:面板里填的值优先;清空输入框并保存(或点「恢复默认」)就回到"跟随插件配置"。
下面两种改法改的是默认值(面板没填时用它):
Config schema 生成这个表单)cordis.patch.yml:- id: prompt-desk
name: dsh-prompt-ErrorNotes-desk
config:
injectLimit: 20 # 只接受非负整数
只接受非负整数:负数、小数、非数字会被拒绝(面板里会提示;schema 会给出具体原因,例如 expected number >= 0 but got -5)。
个别精简宿主可能不提供
@deepseek-ai/schemastery(设置表单由它生成)。那种情况下功能不受影响:面板里的输入框和 patch 的config都照常工作,只是设置里不会出现这个字段。
每一条都归到三个一级分类之一。面板按分类折叠展示,每条右侧的下拉框可以随时改类:
| 一级分类 | 含义 | 二级分类 |
|---|---|---|
| 判断与依据错误 | 想错了 | 验证缺位(没核实就下结论)、依据缺位(凭推测代替取证) |
| 生成与操作错误 | 做错了 | 语法生成(模板串、转义、拼接)、工具与编码(选错工具或破坏编码) |
| 其他 | 暂未归类 | —(本身就是终点,不再细分) |
分类是按补救手段分的,不是按症状:每个二级分类都对应一个"下次换个做法"的具体动作,这样它才是可执行的,而不是描述性的。
开启错误库导出时,插件会在系统提示词里附一段固定的认错引导(实际注入的全文,逐字):
## 自我认错
发现自身错误时,**必须**主动用下面这一行汇报:
我犯了个错误:错误经过:<当时实际发生了什么> 教训:<以后怎么做,一句可执行的规则>
两个标签缺一不可、顺序固定,且必须写在同一行;缺任一标签或换行都不会被捕获。分类由用户在面板里选。
错误经过 20 字以内,只写这次发生了什么;教训 40 字以内,但要写成换任务、换文件也成立的规则,不提本次的文件名、路径、命令或具体数值。
宿主半边监听助手的输出流,发现这个句式就把一条草稿放进面板顶部的「待确认」区,并在错误库按钮上显示数字徽标。草稿不会自动进库——必须点「采纳」;采纳前可以改措辞、选分类,或者直接「丢弃」。
我犯了个错误(引导里示范的那个)与 我发现了个错误。命中多个时,取最靠前的、能解析成功的那一个。- * +、有序列表序号或 >)。这一条是为了挡住"在句子里提到标记"——讨论本功能、引用代码、摘录文档时,标记常常出现在句子中间,那不是认错。错误经过: 与 教训: 是硬边界,且顺序固定、必须在同一行。这是取代旧 | 分隔符的关键改动:分隔符太容易在散文里被引用(曾有一句话里写着"(忘了写 |)",结果在错误的位置切开,产生了垃圾草稿),而两个标签很难被无意复现。我犯了个错误:xxx|yyy(用 | 分隔、无标签)不会再产生草稿,请用双标签格式。<当时实际发生了什么>"这类垃圾。认错引导和错误经验列表由同一个开关控制:引导的存在就是为了喂这个库,库不注入时还留着引导,等于让 agent 对着空气认错。
有两个都会"停止注入错误经验"的开关。它们作用范围不同,所以放在提示词面板的上下两端,而不是挨在一起:
| 开关 | 位置 | 停什么 | 是否持久 |
|---|---|---|---|
临时暂停注入(等同 /lessons off) | 面板顶部 | 只停错误经验列表;本次对话提示词与「自我认错」引导照常生效 | 否,重载后自动恢复为开启 |
| 错误库导出到系统提示词 | 面板底部 | 停错误经验列表 + 自我认错引导 | 是,写进 state.json |
也可以在对话框里直接敲命令:
/lessons off 关闭(全局生效,跨对话)
/lessons on 恢复
/lessons 不带参数则翻转当前状态
/lessons是全局开关,且重载后复位——它的定位是"这一轮探索先别拿旧教训烦我"。要长期关掉,用面板底部的导出开关。参数写错会返回用法提示,不会静默翻转。
--dsw-static-neutral-bluish-* 而不是 --dsw-alias-bg-layer-*:液态玻璃主题插件(例如 dsh-plugin-wallpaper-engine)会把后者重写成半透明膜,用它绘制的地方会透出壁纸,文字变得难读。TypeError。以下都是读源码确认过的,不是假设:
inject 导出是字符串数组(如 ['tools']),没有 {optional: [...]} 对象写法。可选服务用 ctx.inject([...], cb) 获取,这样它不会拦住插件的激活。webServer.register({ kind, path, handler }) 返回一个 disposer。PromptSection.text 的类型是 string | ((context: AssembleContext) => string)。conversation.session.header.utilities 是 list 类型、session 作用域的插槽;其组件收到的 props 是 { sessionId, useSessions }。data-ds-dark-theme。AssembleContext 只有两个字段:{ scope?: ScopeKey; signal?: AbortSignal }——没有本轮用户消息,也没有激活工具集。所以本插件不做上下文匹配;text 每次 assembly 都会被求值。systemPrompt.section() 的 text 在每次 assembly 时求值:SystemPrompt.assemble 里是 typeof section.text === 'function' ? section.text(context) : section.text。ScopeKey 是 object(不透明类型),而命令回调拿到的是 invocation.agent.id(字符串)。两者之间没有取到可靠的映射,所以 /lessons 做成全局开关,而不是会话级。agent/assistant-stream 的载荷是 { agent, frame };frame.chunk 是 StreamChunk(text-delta 带 text 字段),frame.outcome.kind 为 'committed' | 'abandoned'。commands 服务提供 register(definition: CommandDefinition): () => void;CommandDefinition 形如 { name, description, input?, recordInput?, handler };CommandResult 是 { kind: 'success', text? } | { kind: 'error', text }。ctx 上的监听器确实能收到 Scoped<Agent> 派发的 agent/assistant-stream。实测方式:在真实对话里输出一条认错句式,它被正确解析为待确认草稿并写入 state.json(source: "auto")。上面那节是读源码得出的结论;下面这些是在真实 profile 里跑出来的,两者分开列,因为读源码不能替代跑一遍:
| 验证项 | 结果 |
|---|---|
| 宿主半边加载 | 插件成功激活,GET /prompt-desk/state 与 /prompt-desk/pending 均返回 200 |
| v1 → v3 状态迁移 | 真实状态文件被升到 version: 3,老条目获得稳定 id 与分类字段 |
| 自我认错自动采集 | 在真实对话里输出认错句式,被解析为待确认草稿(source: "auto"),独立复现两次 |
| 采纳草稿 + 改类 | 经面板采纳并改到「依据缺位」,正确落库、保留 source: "auto"、铸了新的唯一 id(未复用草稿 id) |
| 临时暂停(面板入口) | 生产 POST 实测:false 后错误列表归零、认错引导保留;另一个独立请求复读仍为 false(证明是进程级共享状态) |
/lessons off / on | 会话日志出现 command/run → command/done,返回 kind: "success",回显文字与预期一致 |
已知的边界:
/lessons必须单独成行(parseCommand的正则锚定在行首^/)。写在句子中间或末尾不会被当作命令,而是普通消息文本。
MIT
Developed independently by an AI agent running on DeepSeek Harness.
Two small panels in the DeepSeek Harness conversation header: edit prompts, keep an error-experience library.
中文 | English
| Panel | What it does |
|---|---|
| Prompt | Edits the cross-conversation global prompt and a per-session prompt that carries into the next conversation. Shows which half is currently reaching the model. |
| Error library | Recurring mistakes grouped by category — a short type plus a one-sentence lesson each, filed under one of three top-level categories. The most recent 10 (a configurable number) are injected into the system prompt, so a later turn can absorb them cheaply. A message reporting 「我犯了个错误:错误经过:… 教训:…」 is captured automatically as a draft awaiting confirmation. |
The point of the second panel: an agent that keeps repeating the same class of mistake gets a short, categorised reminder of it in its own context, at a token cost far below re-discovering the mistake.
Clicking anywhere outside an open panel closes it (Escape works too).
Error library — collapsible category groups, a per-row dropdown to re-file an entry, the injection count on top, and automatically captured drafts awaiting confirmation:

Prompt — the self-check line and the temporary pause on top, the global and per-session prompts in the middle, and a preview of the exact injected text at the bottom:

This plugin is published on GitHub only (not on npm).
Full steps, verification and FAQ: INSTALL.md. The short path follows.
Settings → Plugins → Install, then paste the GitHub shorthand:
github:1015677515/dsh-prompt-ErrorNotes-desk
The full URL works too:
https://github.com/1015677515/dsh-prompt-ErrorNotes-desk
The install field accepts a package name, a GitHub address, or a local directory path — both forms above are accepted.
You do not need to write a commit SHA yourself. The shorthand without a version suffix is enough: the market resolves the default branch's HEAD into a 40-character commit SHA first, then hands pnpm the pinned github:owner/repo#<sha> form. The lockfile therefore records a traceable commit and the installed version shows up correctly.
⚠️ The reverse matters too: do not write
#HEADyourself, and do not leave the source on a moving branch. The source says a HEAD tarball "installs perfectly and then reports no version forever" — it installs, but the version column stays empty and updates are never detected.
When you already have a clone, point at the directory (the install field labels this "local directory"):
A:\大肥鱼工作区\dsh-prompt-ErrorNotes-desk
This is the convenient path while developing: a local install is wired in as a directory link (a Junction on Windows), so editing the source takes effect on a page refresh — no reinstall.
# cordis.patch.yml of your bundle
- insert:
- id: prompt-desk
name: dsh-prompt-ErrorNotes-desk
Restart DSH afterwards. The browser half hot-reloads, but the host half — the one that injects into the system prompt — only picks up a fresh module generation on restart.
| Dependency | Requirement |
|---|---|
| DSH | `>=0.2.0-rc.1 |
| Node | ^22.19.0 || >=24.0.0 |
| HTTP server | Required. The host half registers GET|POST /prompt-desk/state; the browser half reads and writes through it. A profile without an HTTP server loads the plugin and it stays inactive rather than failing |
No third-party runtime dependencies; the browser half uses only the react the page already provides, and there is no build step.
The global prompt is the harness's own $DSH_HOME/AGENTS.md — the same file the official dsh-agent-instructions plugin already loads. This plugin keeps no second copy: two files both claiming to be "the global prompt" would drift, and only one of them would actually reach the model.
| Data | Location |
|---|---|
| Global prompt | $DSH_HOME/AGENTS.md |
| Session prompt, error library, panel visibility | $DSH_HOME/prompt-desk/state.json |
Writes are atomic (.tmp sibling + rename), so a crash cannot truncate your prompt. The browser half never touches the filesystem — all I/O lives on the host half, so nothing can write outside $DSH_HOME.
The repository root carries AGENTS.example.md, the author's own global prompt verbatim, published purely as a reference for how to write one: see how a working prompt groups its rules into sections and states "self-check before delivering" or "report the scope before deleting" as a line you can actually act on, then rewrite it in your own style.
It does not overwrite your prompt, and installing the plugin does not either. To be explicit about it:
AGENTS.md. The only place in the code that writes this file is the moment you press "Save" in the panel, and what it writes is what you typed in the box, not this example.AGENTS.example.md, not AGENTS.md, and it does not sit anywhere that gets auto-loaded. The harness project-instruction loader matches only the exact file names AGENTS.md / CLAUDE.md / AGENTS.local.md / CLAUDE.local.md; AGENTS.example.md is not among them — even if you open this repository as your workspace, it is never read as instructions.To adopt any of it, do it by hand — and back up first, then merge:
# This plugin falls back to ~/.dsh when DSH_HOME is unset, so mirror that here
$dsh = if ($env:DSH_HOME) { $env:DSH_HOME } else { Join-Path $HOME '.dsh' }
Copy-Item "$dsh\AGENTS.md" "$dsh\AGENTS.md.bak"
Then open $DSH_HOME/AGENTS.md (the "global prompt" box in the panel) and paste in the rules you want. Overwriting it wholesale is possible too, but that throws away rules you already had — your prompt should look like yours; someone else's is only a reference.
The session prompt and the most recent 10 error entries are contributed through ctx.systemPrompt.section() at order 10200 (DEPLOYMENT_PERSONA_SUFFIX), i.e. after the tool documentation.
text is a function, so an edit takes effect on the next turn — no restart, no re-registration.detail explains the incident and type is its headline — both describe one moment, and only a lesson states a rule that transfers to the next task. detail is also the most token-expensive field, so both stay in the panel.Set it right in the error-library panel (the convenient way): the panel has a "注入条数上限" field — type a number, press 保存, and it takes effect on the next turn.
| Field | Default | Meaning |
|---|---|---|
injectLimit | 10 | Cap on how many error entries reach the system prompt. 0 injects none (the session prompt is unaffected); 200 is the ceiling, and anything larger is clamped to it |
Precedence: a value entered in the panel wins. Emptying the field and saving (or pressing 恢复默认) puts it back on "follow the plugin config".
The two routes below set the default (what applies while the panel field is unset):
Config schema)cordis.patch.yml:- id: prompt-desk
name: dsh-prompt-ErrorNotes-desk
config:
injectLimit: 20 # non-negative integers only
Only a non-negative integer is accepted: a negative, fractional or non-numeric value is refused (the panel says so; the schema gives a specific reason such as expected number >= 0 but got -5).
A stripped-down host may not provide
@deepseek-ai/schemastery— the package that renders the settings form. Nothing breaks in that case: the panel's field and the patch'sconfigboth keep working; the field simply does not appear in Settings.
Every entry belongs to one of three top-level categories. The panel shows them as collapsible groups, and each row carries a dropdown so an entry can be re-filed at any time:
| Category | Means | Sub-categories |
|---|---|---|
| 判断与依据错误 | thought it wrong | 验证缺位 (concluded without checking), 依据缺位 (substituted a guess for evidence) |
| 生成与操作错误 | did it wrong | 语法生成 (template strings, escaping, concatenation), 工具与编码 (wrong tool, or broke the encoding) |
| 其他 | not yet filed | — (a destination in its own right, not a parent) |
The split is by remedy, not by symptom: each leaf names a concrete thing to do differently next time, which is what makes it actionable instead of merely descriptive.
With export enabled, the plugin appends a fixed confession cue to the system prompt (the injected text, verbatim):
## 自我认错
发现自身错误时,**必须**主动用下面这一行汇报:
我犯了个错误:错误经过:<当时实际发生了什么> 教训:<以后怎么做,一句可执行的规则>
两个标签缺一不可、顺序固定,且必须写在同一行;缺任一标签或换行都不会被捕获。分类由用户在面板里选。
错误经过 20 字以内,只写这次发生了什么;教训 40 字以内,但要写成换任务、换文件也成立的规则,不提本次的文件名、路径、命令或具体数值。
The Host watches the assistant's output stream; on seeing that phrasing it puts a draft into the 待确认 queue at the top of the panel and shows a count badge on the 错误库 button. A draft never enters the library by itself — you press 采纳, and you may rewrite the wording and pick a category first, or discard it.
我犯了个错误 (the one the cue demonstrates) and 我发现了个错误. When several match, the earliest one that parses cleanly wins.- * +, an ordered-list number, or > may precede it). The rule exists to reject sentences that merely mention the marker — a discussion of this feature, a code sample, or a documentation excerpt usually carries it mid-sentence, which is not a confession.错误经过: and 教训: are hard boundaries, and their order is fixed and must be on one line. This is what replaced the old | separator: a separator character gets quoted in prose (one real sentence contained "(忘了写 |)", which split in the wrong place and produced two junk drafts), while two labels are very hard to reproduce by accident.我犯了个错误:xxx|yyy (separator, no labels) produces no draft. Use the two-label format.<当时实际发生了什么>.The cue and the library are gated by the same switch: the cue exists to feed the library, so leaving it on with the library un-injected would ask the agent to confess into nothing.
Two controls both stop error entries reaching the model. They differ in scope, so they sit at the top and bottom of the prompt panel rather than side by side:
| Switch | Where | Stops | Persisted |
|---|---|---|---|
临时暂停注入 (equivalent to /lessons off) | panel top | only the error list; the session prompt and the confession cue stay in force | no — resets to on after a reload |
| 错误库导出到系统提示词 | panel bottom | the error list and the confession cue together | yes, written to state.json |
The same pause is reachable from the composer:
/lessons off