Repository navigation
Closed
Labels
bug功能异常、崩溃、数据错误(agent 诊断 → 人工确认)功能异常、崩溃、数据错误(agent 诊断 → 人工确认)
Description
环境
- dsh-mneme 0.7.32(npm 安装,DSH profile
web) - DSH 0.1.5-rc.2,Windows 11
- 相关配置全默认:
autoInject: true、hotMemoryEnabled: true
现象
某个会话从某一轮起,每条新消息都在 turn/start 后立刻失败:
malformed prompt variable reference "{{.Server.Version}}" in context "memory" (variable names match /^[a-z][a-z0-9_]*$/)
而且是粘性的:该会话此后每条消息都以同一错误失败,无法恢复。
同一会话的时间线(本地时间):
| 时刻 | 事件 |
|---|---|
| 14:31:04 | turn 41 start,prompt 组装成功 |
| 14:31:08 | 模型产出回复,其 reasoning 里写了 docker version --format "{{.Server.Version}}",随后发起工具调用 |
| 14:32:05 | 工具执行后第二次组装 prompt → 抛错,turn 41 整轮作废 |
| 14:39:36 | turn 42(用户发「再检查一下」)→ 18 ms 后同样抛错 |
根因
- DSH 核心
interpolate()(packages/core/system-prompt)会扫描 section/context 文本里所有{{...}},变量名不匹配/^[a-z][a-z0-9_]*$/即 throw —— 这是核心的设计行为,不是本插件能改的。 memorycontext 由 mneme 注册(src/inject.js),内容 = hot memory(本会话最近 N 轮问答原文)+ 长期记忆条目。- hot memory 的
response取 content 里所有带.text的部分,因此 assistant 的 reasoning 文本也会被注入(extractRounds→textOf)。 - 三处注入出口(
renderHotContext()、render(candidates)、renderUserSettings())都是原样透传,没有任何花括号转义。
于是:模型只要在思考里写下一句 docker --format "{{.Server.Version}}"(或任何 Go template / Vue / Handlebars / wiki 标记片段),下一轮组装 prompt 时这段文本就进了 memory context → 校验 throw。又因为 hot memory 每次从会话日志重建,那条消息会一直留在最近 N 轮窗口里,于是会话永久卡死。
关键:这个问题 v0.7.4 修过,0.7.32 的代码里却没有了
dsh-mneme/CHANGELOG.md(v0.7.4,issue #40)与 README 版本表都写着:
修复:注入边界 run-based 花括号转义(连续 2+ 个花括号之间插
\:{{a}}→{\{a\}\}),奇数连续括号(如{{{a}}})也不残留字面{{,interpolate()不再扫描到;覆盖 memory 块 / 短期上下文 / 用户设置三处注入出口。新增配置escapePromptVariables(默认开,关闭时原样透传)新增 6 个回归测试(inject 5 + config 1,含
{{{a}}}奇数括号回归),全套 782 通过
但在 0.7.32 上实测:
- 包内
lib/inject.js与src/inject.js哈希一致,三处注入出口都没有转义代码; - 全包 grep
escapePromptVariables:0 命中 ——lib/config.js里没有这个 schema 项,Web 设置面板也没有该项; - GitHub
main分支的dsh-mneme/src/inject.js(即最新代码)同样没有转义,三处出口仍是原样返回; - GitHub 全仓代码搜索
escapePromptVariables:只命中dsh-mneme/README.md与dsh-mneme/CHANGELOG.md,没有任何.js文件。
即:文档说修了,代码里没有。疑似该修复在某次重构/同步中丢失(类似 #65 那种 src/lib 脱节,但这次 lib 与 src 都没有)。
影响
- 触发面比 # [Bug] 记忆内容含
{{hl|}}等模板语法时整轮失败:malformed prompt variable reference "{{hl|}}" in context "memory"#40 时更大:不需要用户写任何东西。agent 自己的思考/回复里出现{{...}}(docker--format、Go template、Vue/Handlebars、正则与日志原文……)就会踩到; - 一旦踩到,该会话永久不可用(除非最近 N 轮被新消息顶出去,而每条新消息本身也会失败),只能弃用。
建议
- 恢复 v0.7.4 的 run-based 转义,并确认
escapePromptVariables(默认开)真的回到config.jsschema、三处注入出口与 Web 设置面板; - 回归测试补一条 hot memory 路径:构造一个 assistant 事件、其 content 含
{{.Server.Version}},断言注入文本里不残留字面{{—— v0.7.4 的 5 个 inject 测试显然没覆盖到 reasoning 这条来源; - 顺带可议:hot memory 是否应只取 assistant 的
text片段、排除reasoning。reasoning 里出现模板语法/正则/日志原文的概率远高于正文。
最小复现
- 任意会话里让模型在思考或回复中出现
{{.Server.Version}}(例如让它执行docker version --format "{{.Server.Version}}"); - 该轮工具执行后即失败;
- 此后该会话每条新消息都以同一错误失败。
需要的话我可以提供会话 jsonl 片段与注入文本,便于加回归测试。
Metadata
Metadata
Assignees
Labels
bug功能异常、崩溃、数据错误(agent 诊断 → 人工确认)功能异常、崩溃、数据错误(agent 诊断 → 人工确认)
Activity
fix(issue#162): 恢复注入边界花括号转义(v0.7.4 修复在 v0.7.11 误删)+ hot memory 跳过 rea…
modusensus commentedon Sep 13, 2026
已修复,随 v0.8.0 上线(npm 0.8.0 / Release,PR #165)。
修复三层:
escapePromptVars,v0.7.11 重构时被整块误删——与你的根因第 1 点完全一致,谢谢抓到这个历史回归):三处注入出口(memory 块 / 短期上下文 / 用户设置)重新包裹,{{a}}→{\{a\}\},奇数连括号也不残留字面{{;textOf只取type==="text"与字符串,assistant 思考里的 docker--format "{{.Server.Version}}"从源头不再进注入文本——比 v0.7.4 只做转义更彻底;escapePromptVariables配置(默认 true,关闭时原样透传)。你的时间线分析(turn 41 二次组装抛错 → turn 42 18ms 即失败)与「粘性卡死」的机制描述完全准确——hot memory 每轮从会话日志重建,触发文本一直留在窗口内。升级到 0.8.0 后该会话即可恢复(转义后那些文本安全通过 interpolate)。
再次感谢这份报告的质量:环境、版本、时间线、根因链、连 v0.7.4 历史修复都对上了——正是我们的 Issue 报告要求 希望看到的范本。
fix(inject): emoji 代理对截断与会话时间注入恢复(issues slow-stack#334 slow-stack#333)