跳到主要内容
路径文档

反馈

一句话版:反馈族有两个刻意分离的契约——/feedback 命令在会话日志里追加一条只读的 feedback/record 事件(log-only,不进模型),而消息反馈(ctx.messageFeedback)是绑在单条 assistant 消息上的可编辑评分/备注侧车,存 storage 域、可 list/put/delete两者都不进入模型对话

反馈让"人"把意见留给系统,但不污染模型上下文:命令反馈是会话日志里不可变的一条备注,消息反馈是宿主持有的、可改可删的逐消息评分。

一、两个契约

角色ctx key
command-feedback与触发方式无关的 feedback/record 事件 + 面向人的 /feedback 生产者
message-feedback绑定生命周期的逐消息评分/备注侧车 + Host messageFeedback.list/put/delete Remote 契约messageFeedback

命令反馈是 log-only:永不进模型上下文或派生历史。挂载 dsh-session-telemetry-otel 时,它观察 feedback/record 来释放待发的遥测前缀,或警告"遥测关闭时反馈只留在本地";捕获本身与这一策略无关。

消息反馈不是 Session 事件或投影:它待在 storage 域的侧车里,不触发遥测交接。Host Remote 契约随服务发布,客户端 Remote 聚合与 UI 消费者另行持有。

二、命令反馈(command-feedback)

包导出 recordFeedback(session, text),追加一条 log-only 的 feedback/record 事件。它的插件经 ctx.commands 注册一个全局命令,因此每个组合的命令适配器都能发现它;发布的 Web 客户端无需一个模型轮次即可执行。

它做什么、不做什么

  • recordFeedback与命令无关的写入路径:拒绝空(归一化后)文本,追加 feedback/record { text }。别的 UI、hook 或宿主集成可以不构造斜杠命令直接调用它
  • /feedback 处理器用这个生产者,且不启动任何模型工作
  • 可选的 dsh-session-telemetry-otel 消费者观察该事件,不改动其捕获契约
  • 反馈文本只出现在唯一一份持久载荷 feedback/record 里。dsh-commands 仍追加通用的 command/run/command/done 事件对,但此定义设 recordInput: false,command/run 省略 args,command/done 只带结果。三个事件都是 log-only,不出现在有序 surface、deriveMessages() 或模型请求里
  • 追加会启动持久化的常规 eager drain,但生产者不强制 session/flush:所以确认只意味着反馈进了日志,不代表已落盘
  • 事件是权威来源而非命令记录:反馈可能经 /feedback 之外的触发到达;把载荷留在 command/run 之外,避免两份记录带同一段文本

三、/feedback 命令契约

输入结果
/feedback <text>追加 feedback/record,确认信息带 Feedback recorded for session {sessionId}User: {userId},以及会话共享披露
/feedback直接返回用法错误;纯空白输入视作空
  • 首尾空白被丢弃,除此之外不解析:无截断、大小写折叠或控制词
  • 看起来像别的命令的文本(如 /feedback /plan felt slow)就是反馈内容
  • 重复命令各自产生自己的事件;不替换、不合并

四、会话共享披露

确认信息会点名接收会话 id,并报告该会话如何共享,来自挂载的 telemetry 服务(经插件上下文 ctx.get('telemetry'),不是声明的注入)。披露是后端 TelemetrySharingStatus 决定的一句话:

披露状态确认句子
fullSession sharing is enabled.
feedback-onlySession sharing is feedback-gated; recording feedback releases the session prefix for sharing.
disabledSession sharing is disabled.
无服务Session sharing is not configured.

披露只陈述部署当前的共享策略,从不承诺送达或保留。full/feedback-only 下,记录交给后端的非阻塞入队,SDK 负责批处理、重试与丢失策略,所以句子不声称任何内容已到达收集器。披露不追加事件,也不进模型 surface。

五、消息反馈侧车(message-feedback)

宿主持有的、针对一条已定稿 assistant 消息的可编辑反馈。插件注册 ctx.messageFeedback,在 storage 域为每个 Session 持久化一条绑定生命周期的侧车行,并发布 Host 的 messageFeedback.list / messageFeedback.put / messageFeedback.delete 一元 Remote 契约。它独立于不可变的 Session 级 feedback/record 事件,不产生遥测交接。

配置

key含义
maxNoteBytes必填的正安全整数:单条可选备注的最大 UTF-8 字节长度

备注必须含至少一个非空白字符,但接受的文本原样存储、不 trim。省略 note 表示"期望值没有备注",所以版本匹配的实质性 put 会清掉已有备注。备注校验先于 Session 查找,因此对缺失的 Session 也可能返回 note-blank / note-too-large,而不会碰持久化。

- id: message-feedback
name: '@deepseek-ai/dsh-message-feedback'
config:
maxNoteBytes: 8192

服务注入 storageDomainsessionPersistencesessions。持久域 message_feedback,每个 SessionId 一行 sessions 表记录。

六、数据、生命周期与持久性

MessageFeedbackItemmessageIdrating: 'positive' | 'negative'、可选 note、一个只用于等值比较的不透明 version,以及宿主分配的 createdAt/updatedAt Unix 毫秒时间戳。实质性更新保留 createdAt、替换 version、并保证 updatedAt 不倒退。list 返回按首次创建顺序的新鲜不可变快照;更新某项保留其位置,删除后再重建则追加为新项。

每行携带被检视的 Session 头部身份 {createdAt, cwd}。不匹配视作缺失:list 返回空 items,delete 返回缺失后置条件,put 可能用绑定当前身份的替换掉陈旧行。这挡住了 SessionId 被复用时头部身份不同的情况;fork 用不同的 Session 身份,不继承反馈行。

put 只接受非空、append 来源assistant/message;替换来源的消息、空的 usage-only assistant 记录、非 assistant 记录都返回 target-not-found

初验后,put 在写侧车前建立持久性屏障:匹配的活 Session 经规范的 ctx.sessions.flush 检查点提交,然后活路径与冷路径都从 sequence zero 经 SessionPersistence.readFrom 物理读。缺失 flush 参与者、身份变化、目标消失或物理读失败都会阻止侧车提交——所以持久的反馈绝不会先于持久的目标消息

七、Compare-and-set 与幂等

  • ifVersion: null 只请求创建;对已存在项的每个请求都要求其精确当前版本,包括"期望值已相同"的 no-op
  • 检查按消息而非按 Session:改一项不会与另一项冲突
  • 每次实质性创建/更新分配一个新的不透明 UUID token,防止陈旧写跨越 ABA 值循环
  • 版本匹配的 no-op 返回已存项,版本与时间戳不变;丢失成功响应后,用旧 token 重试会收到 version-conflict.current,调用方无需额外读即可比对权威项
  • delete 在项已缺失时忽略 ifVersion,成功后始终返回稳定的 { absent: true }
  • 每个 Session 的 promise 队列把检视、持久性校验、侧车读、比较、整行写串行化;storage 域本身没有跨进程条件写
  • 插件 dispose 关闭变异准入、排干每个 Session 队列里已接收的操作,然后才关闭 storage 域;dispose 开始后提交的变异作为生命周期失败拒绝

八、Service / Host Remote 契约

GatewayService@Remote 发布同一组 MessageFeedbackService 方法,Host 端点名 messageFeedback.list / messageFeedback.put / messageFeedback.delete。每个方法返回判别业务联合:{ ok: true, value }{ ok: false, error };操作级存储、损坏或缺失持久性监听器的失败会拒绝,而不是被误标为业务错误。

方法请求成功 value拒绝 error.code
list{ sessionId }{ items }session-not-found
put{ sessionId, messageId, rating, note?, ifVersion }已提交的 MessageFeedbackItemsession-not-foundtarget-not-foundversion-conflictnote-blanknote-too-large
delete{ sessionId, messageId, ifVersion }{ absent: true }session-not-foundversion-conflict

MessageFeedbackVersionConflict 返回权威的 current 项(无项时为 null),让调用方无需第二次 list 即可协调当前 rating/note/version。MessageFeedbackNoteTooLarge 同时返回 maxBytesactualBytes。客户端 Remote 聚合暂未挂载生成的客户端贡献;宿主调用方无需该客户端装配即可使用 service/Remote 契约。

九、挂载状态

默认挂载默认配置
command-feedbackbase 默认挂载(无条件,无配置)
message-feedbackweb-app 默认挂载maxNoteBytes: 8192

/feedback 只在 Web 客户端暴露(经命令适配器);headless、ACP 自动化、JSON-RPC 不提供命令适配器,故不可用。消息反馈的 Host Remote 契约已随服务发布,但客户端 Remote 聚合与 UI 消费者另行持有、暂缺。

十、对模型的可见性

命令反馈:模型什么都看不到——斜杠输入、feedback/record、确认信息都不进模型请求(都无 surfaceOp,不进有序 surface / deriveMessages() / 系统提示)。录制反馈不改变该轮剩余请求。Token 效应为零;KV cache 独立,不影响前缀复用。

消息反馈ctx.messageFeedback 不注册工具、提示段、模型上下文或 Session 事件;反馈待在宿主侧车,除非另有消费者显式暴露。Token 效应为零;KV cache 独立。

十一、已知限制

  • 命令反馈:无检索/管理 surface、无结构化字段(一条自由文本)、无 amend/withdraw(日志 append-only)、无持久化屏障(确认≠落盘)、全新会话上无可见确认行、只在 Web 暴露
  • 消息反馈:客户端聚合与 UI 暂缺;CAS 是单进程的(多宿主进程仍可能丢更新);无持久 Session 删除级联;detach/catalog 收编窗口内可能收到 session-not-found;头部身份不是内容指纹;无鉴权调用者边界;item 数量与单 Session 行字节总量未设上限

验证

# 命令反馈已默认挂载(base)
dsh web --dump-config | grep -iE "command-feedback"
# 消息反馈已默认挂载(web-app)
dsh web --dump-config | grep -iE "message-feedback"
# 会话日志里看 feedback/record 事件
zstdcat ~/.dsh/sessions/*/*/session.jsonl.zstd | grep -E '"feedback/record"' | tail

下一步