dsh-auto-review

58Stars1Forks1Issues0Watchers

在 DSH 审批环节插入第二个只读 AI 模型审阅请求,让危险工具在执行前先经过独立判断,失败默认拒绝,全程留审计日志。

Language
TypeScript
License
Apache-2.0
Branch
main
ai-safetyapprovalauto-reviewcordisdeepseekdeepseek-harnessdshdsh-plugin

Install

$ dsh plugin --profile web add github:PerryLink/dsh-auto-review

Run the command above in your terminal to install this plugin via the dsh CLI. You can switch Profile in the top-right corner. New to dsh? Read the beginner tutorial

一句话定位

在 DeepSeek Harness 的审批链上挂一个第二模型,让沙箱外的工具调用先由一个只读子 agent 读上下文并返回允许/拒绝;失败默认拒绝,全程记录会话日志。配套风险等级策略、拒绝熔断器和 Web 可视面板。

核心能力

  • 第二模型审批:在 approval/request answerer 链上注册一个钩子,命中 ai 策略的工具交给只读子 agent 裁决,其它请求一律走 next() 放行给原有人类审批链。
  • 结构化裁决:审查子 agent 通过结构化输出返回 { decision, reason, riskLevel },只有 read/glob/grep 这三个只读工具,不会真正执行被审查的动作。
  • 失败默认拒绝:审查超时、崩溃、子 agent 拉不起、verdict 结构不对都走 fallbackPolicy(默认 rejected),不会被悄悄放行;可改为 delegate(交给人类)或 allow-once(一次性放行,文档明确标注为危险)。
  • 风险等级 + 熔断器:允许裁决附带的 riskLevel 超过 maxAutoAllow 时可配置 delegate 或 deny;同一回合连续拒绝或滑窗拒绝过多时熔断,触发 delegate / reject / abort-turn 三种动作之一。
  • deny 原因回流到模型:被拒的工具结果里会注入 [auto-review] / [auto-review-fallback] / [auto-review-never] 标记 + 拒绝理由 + 防绕过提示,模型能看到为什么被拒、为什么不能换个姿势重试。
  • 完整审计 + Web 面板autoReview/stateautoReview/verdictautoReview/rejectionautoReview/circuitautoReview/override 五类事件写入会话日志;Web profile 多一个会话头 AI Review 按钮,展示开关、预算、累计统计、最近裁决和一次性批准。

技术实现

  • 语言: TypeScript(ESM,type: module
  • 关键依赖: @deepseek-ai/cordis(插件宿主框架)、@deepseek-ai/schemastery(配置 schema + 默认值)、@deepseek-ai/dsh-session + @deepseek-ai/dsh-session-projection(会话日志 + Web 面板数据通道)、@deepseek-ai/dsh-subagent(审查子 agent 编排)、zod(projection wire schema 校验)
  • 架构模式: 走 Cordis 函数插件契约(name + inject + apply),通过 ctx.on('approval/request', ...)ctx.on('tools/post-execute', ...)ctx.commands.register(...) 三个 effect 注册副作用;通过 ctx.inject(['sessionProjections']) 在 host 支持时挂载 autoReview 投影单元供 Web 面板读取
  • 入口文件: src/index.ts(插件导出),src/runtime.ts(审批回答器 + 命令),src/review.ts(审查子 agent 编排),src/config.ts(Schemastery schema + resolveConfig

适用场景

你希望把 DSH 跑在无人值守环境,但又不放心让主模型独断"写文件"或"跑命令"这类有副作用的操作;装上这个插件后,主模型要做的每一步沙箱外动作会先被第二个模型读上下文、给出裁决和理由,命中失败兜底会保守拒绝,整个过程在会话日志里可审计、可回放。

前置依赖与兼容性

依赖最低版本说明
DeepSeek Harness0.1.0-rc.7所有 dsh-* peerDependencies 精确锁定为 0.1.0-rc.7dshWorkshop.compatibility.dshVersions 也只列该版本
Node.js^22.19.0 || >=24.0.0package.json#engines.node 声明
Cordis^4.0.1peerDependencies
Schemastery^3.18.0peerDependencies
平台跨平台host 半段纯 JS,不依赖原生模块;Web review panel 仅在 web profile 下注册
原生模块os/cpu 限制,无 node-gyp/node-pty 等依赖

安装方式

dsh plugin --profile web add github:PerryLink/dsh-auto-review

配置项

配置类型说明默认值
enableByDefaultboolean新会话是否默认开启 auto-review;/auto-review on|off 写覆盖事件true
toolsPolicy.defaultai | human | never工具表里没列的工具走哪条策略human(交给人类)
toolsPolicy.overridesobject具体工具 → 策略映射,例如 { bash: ai, write: ai }{}
riskRulesarray命中请求 reason/toolName/参数的正则 + 策略(先于工具表匹配)[]
reviewerProviderstring审查子 agent 用的 subagent providerfork(进程内 fork)
reviewerModelstring审查用的模型 ID;不填继承当前会话模型继承
reviewerTimeoutMsnumber审查子 agent 超时毫秒数;超时走 fallbackPolicy60000
reviewerToolsstring[]审查子 agent 允许使用的工具(白名单,必须非空)["read","glob","grep"]
fallbackPolicyrejected | delegate | allow-once审查失败(超时/崩溃/结构错误)时怎么落rejected(保守拒绝)
maxReviewsPerTurnnumber每个回合最多真实 AI 裁决数;超过后请求交给人类10
maxFailuresPerTurnnumber每个回合最多审查失败次数;超过后请求交给人类10
reasonMaxCharsnumber审查 reason / 请求 reason / 参数预览的最大字符数2000
reviewerGuidancestring追加到审查 prompt 的可选建议(advisory,不是硬约束)
reviewerPolicyTextstring像 Codex 那样注入到审查 prompt 的 Markdown 策略模板无(参考 fixtures/config/policy-template.md
denyGuidancestring每次注入拒绝原因时追加的防绕过提示默认英文 + 默认中文
contextBudget.turnsnumber给审查子 agent 看多少回合已展示过的会话内容;0 表示关闭0
contextBudget.maxCharsnumber上下文段总字符上限4000
riskPolicy.maxAutoAllowlow | medium | high允许裁决的 riskLevel 上限;超过则 delegate 或 denyhigh
riskPolicy.onHighRiskdelegate | deny超过 maxAutoAllow 时落哪种delegate
circuitBreaker.consecutiveDeniesnumber连续多少次拒绝触发熔断3
circuitBreaker.windowDeniesnumber滑窗内多少次拒绝触发熔断6
circuitBreaker.windowSizenumber滑窗大小(按最近 N 个裁决数)10
circuitBreaker.actiondelegate | reject | abort-turn熔断后同一回合后续请求怎么处理delegate
overrideTtlMsnumber/auto-review approve 一次性授权的有效毫秒数300000(5 分钟)
languageen | zh/auto-review 命令输出语言en
allowUnmarkedAuditboolean是否在 host 不识别 ignorable 标记时仍写审计事件(危险:可能让更严格 harness 无法恢复会话)false

常见问题

Q: 默认会自动审查哪些工具?

A: 只审查 bashwrite,其余工具交给人类审批。edit(原地修改)刻意没列入默认 AI 审查列表——如果你接受原地修改也走自动审查,把 edit: ai 加到 toolsPolicy.overrides 即可。

Q: 审查子 agent 自己也能发起工具调用吗?会不会再次触发审查?

A: 不会。审查子 agent 走的是 fork provider 的子会话,插件按 session id 识别审查子会话并直接走 next() 跳过审查;同时审查只允许 read/glob/grep 三个只读工具,它既不能写、不能跑 shell、也不能调用别的子 agent(maxDepth 限定)。

Q: 审查挂掉了怎么办?

A: 走 fallbackPolicy,默认 rejected——也就是保守拒绝,并把审查失败原因回灌给主模型。如果你想让人类兜底,把 fallbackPolicy 设为 delegate;非要把失败当成功,设为 allow-once(README 明确标注这是无人值守部署才会选的危险选项,grant 是无条件的)。

Q: 我能临时关闭吗?

A: 可以。在会话里跑 /auto-review off 即可,关闭状态写进会话日志,下次回放仍然有效。/auto-review status 看当前状态、当回合预算、累计统计、熔断情况;/auto-review approve [n] 给最近第 n 条拒绝一次性放行。

Q: 它会写入会话日志吗?我能事后审计吗?

A: 会。一条审批请求对应的 approval/askedautoReview/verdict(或 autoReview/rejection)→ approval/decided 链条都能从 session log 复原。早期的 harness 版本(0.1.0-rc.1 ~ rc.7)会丢掉 ignorable 标记,所以插件会在第一次写入前做能力探测,如果探测不通过就自动降级为内存审计并一次性提醒,必要时用 dsh-permission-rules 仓库里的 scripts/repair-session-logs.mjs 修复已经被污染的历史日志。

Q: 风险等级策略和拒绝熔断器有什么区别?

A: 风险等级策略作用于单次裁决:审查返回 allowriskLevel 超过 maxAutoAllow,该次允许会被升级成 delegate 或 deny;熔断器作用于回合累计:连续 3 次拒绝、或最近 10 次裁决里出现 6 次拒绝,触发后整回合后续请求按预设动作(delegate/reject/abort-turn)落。两者互不冲突。

Q: 数据存哪里?会联网吗?

A: 不写本地文件、不额外发网络请求。审计数据全在内存 + session log(受 MEMORY_DENIES_CAP=200 / MEMORY_OVERRIDES_CAP=50 等有界缓冲约束)。reviewerModel 不填则继承当前会话模型;要换成别的 provider,等于把已展示的会话内容呈现给另一个 provider,自己拿捏一下。

Q: 怎么卸载?

A: dsh plugin --profile web remove dsh-auto-review,或从 profile patch 里删 auto-review 那一行;manifest 标的是 transactional 卸载,不会动 profile 之外的配置。

上手难度

进阶 — 需要理解 DSH 的审批链、risk rule 正则、会话日志 audit 事件这三条核心概念,且要在 cordis.yml 里调风险策略和熔断阈值才能发挥全部作用;只装默认配置直接用也是可以的。

已知问题与限制

  • 早期 harness 兼容性:0.1.0-rc.1 ~ rc.7 的 @deepseek-ai/dsh-session 不识别 ignorable 标记,会导致 autoReview/* 事件落地后让更严格 harness 无法 resume 该会话。插件默认会预检并降级为内存审计(功能照常,但无审计日志),需要持久审计可设 allowUnmarkedAudit: true(危险)或用 dsh-permission-rules 的 repair 脚本修历史日志(CHANGELOG.md:9-17 / src/audit.ts:36-52)。
  • 审查模型需要可用的 LLM 路由:审查默认继承主会话模型路由;路由不通时每个审查都走 fallbackPolicy——绝对不会静默放行(README.md:232-241)。
  • reviewerTools 必须非空:空白名单会让审查子 agent 无工具可用,加载时直接报错;且名单里的工具名必须是 profile 已注册的全局工具,否则审查子 agent 启动时失败。
  • /auto-review approve 授权的是"下一次同工具审查"而不是历史那一次调用:如果下一次同工具的调用参数、上下文已经不一样,授权照样会被消费,审查仍按当时上下文判定(README.md:236-237)。
  • never 是单向锁:命中 never 策略直接拒绝,不会进入人类链——这是个 lockdown 旋钮,不是日常选项(README.md:230)。
  • git 渠道安装需要 pnpm.allowBuilds: { esbuild: true }:pnpm 11 忽略 package.json#pnpm 字段,必须在 pnpm-workspace.yaml 里写 allowBuilds(repo 自带)。typescript + tsdown 在常规 dependencies 里以保证 git 隔离 prepare 环境能装上。
  • invariant 配套需要 invariants 服务:只在 headless/ACP 这类 agent-spine composition 下能用;普通 web profile 没有该服务,配套 patch 在仓库里默认注释掉。