DeepSeek Harness学习手册
0/33
阅读路径 / 阶段 6
Chapter 06 · Runtime & Safety

运行与生产

工具流水线、沙箱与审批、会话日志、防御性编程与事故复盘。

5 节 · 约 2.5 小时 · 前置:阶段 5每节含目标 / 前置 / 要点 / 任务 / 自检
本阶段进度 0 / 5
6.1

工具执行流水线与权限门禁

约 45 分钟运行时 原文 ↗ 仍然成立
审计依据 ·

流水线顺序与决策类型来自教程;钩子 API 以当前 dsh-tools 类型为准。 查看本节审计记录

学习目标

说出一次工具调用的完整环节,并用 pre-execute / guard 做权限门禁。

前置知识

完成 3.5 与 4.3。

核心要点
  1. 顺序:tools/pre-execute → 单调守卫 → tools/execute → tools/post-execute → finalizeContent → tools/result。
  2. pre-execute 返回 allow / deny / ask;参数不可改写,因为历史、审计、UI 与执行必须一致。
  3. guard 只能返回拒绝字符串或 undefined,没有 allow 分支;所以顺序无法把拒绝变回允许。
  4. execute 管超时/重试/指标;post-execute 管结果呈现;result 只观测冻结后的最终结果。
  5. 选型记忆:能不能做 → pre;必须拒绝 → guard;怎么做 → execute;怎么呈现 → post。
动手任务

写 permission-gate:命中 fs_write 或参数包含 .env 时 deny,否则 next()。验证被拒绝的调用返回可读错误,而工具主体没有执行。

自检
guard 为什么没有 allow?
它是不变式的最终防线,只允许缩减权限;如果能有 allow,后注册的监听器就能推翻前面的拒绝。
post-execute 替换内容能当保密手段吗?
不能。内容替换是展示策略,程序化值仍在;要隐藏必须替换规范值或阻止结果。
进度保存在本机浏览器
6.2

沙箱与审批:失败关闭

约 45 分钟安全 原文 ↗ 仍然成立
审计依据 ·

沙箱模式、enforcement 与审批结果来自教程;平台差异以官方安全文档为准。 查看本节审计记录

学习目标

解释 sandbox 与 approval 的分工,并验证「无后端/无应答者 = 拒绝」。

前置知识

完成 6.1。

核心要点
  1. ctx.sandbox.confine(argv, policy) 包装确切 argv;模式:read-only、workspace-write、danger-full-access。
  2. 只有前两种进沙箱;受限执行必然到达 confine,静默无隔离透传永远不合法。
  3. 后端报告 enforcement: full / partial;要求绝对边界时,partial 应视为不够。
  4. 无可用后端抛 SANDBOX_UNAVAILABLE,属于失败关闭,不是普通任务失败。
  5. approval 结果:allowed-once / rejected / cancelled / unavailable;缺应答者、异常、不合规都算 unavailable,一律拒绝;策略 ask / never。
动手任务

在 read-only 下跑一条写入命令,记录拒绝路径;切到 workspace-write 再试。然后模拟审批无应答者,确认结果是拒绝而不是放行。

自检
partial enforcement 怎么处理?
要求绝对边界时按「不够」处理,拒绝执行或向上暴露差异;不能把部分管控当成完整边界。
审批超时或异常会放行吗?
不会。缺失、异常、不合规都产生 unavailable,调用方对 rejected / cancelled / unavailable 一律拒绝。
进度保存在本机浏览器
6.3

会话日志与轮次生命周期

约 45 分钟可观测 原文 ↗ 仍然成立
审计依据 ·

会话事件与投影规则来自教程;事件字段以当前 SessionEvent 类型为准。 查看本节审计记录

学习目标

解释「模型可见即已记录」,并从 JSONL 还原一次轮次。

前置知识

完成 5.3。

核心要点
  1. Session 是仅追加的 SessionEvent 日志,seq 单调递增;模型历史从日志派生,不单独存储。
  2. deriveMessages() 投影:user/assistant/tool/result 进入历史;assistant/chunk 与 turn/step 跳过。
  3. 事件三域:会话事件(持久事实)、Agent 事件(实时控制)、能力事件(seam 策略)。
  4. turn 包含零个或多个 step;step = 一次模型请求 + 它调用的工具。
  5. 空 assistant 消息不进历史,但保留 usage 事件;新增模型可见输入必须新增会话事件。
动手任务

解析一段 JSONL:列出 turn/start、step/start、tool/call、tool/result 与最终 assistant/message;再用 deriveMessages() 的规则解释哪些事件不会进历史。

自检
为什么历史不单独存?
单独存储会出现两份真源,回放/分叉/检索容易不一致;从同一份事件日志派生,才能保证可重建。
turn/start 与 agent/pre-step 有什么区别?
turn/start 是持久化会话事件,记录轮次边界;agent/pre-step 是实时 Agent 事件,决定模型看到什么,可以改写或拒绝输入。
进度保存在本机浏览器
6.4

防御性编程:五个边界模式

约 40 分钟工程 原文 ↗ 仍然成立
审计依据 ·

五个模式与示例来自教程;原文标题拼写为「DeeSeek」,本站更正为 DeepSeek。 查看本节审计记录

学习目标

应用五个防御模式,避免一个边界情况拖垮整个 Agent。

前置知识

完成 3.3 与 6.1。

核心要点
  1. 正交结果:timedOut、signal、exitCode 独立上报;进程可能超时后捕获 SIGTERM 并以 0 退出。
  2. dispose 要完全停稳:先移除监听,再发终止信号,等待退出,超时升级 SIGKILL。
  3. 凭据擦除:启动子进程前移除匹配 *KEY*、*SECRET*、*TOKEN*、*PASSWORD* 的环境变量。
  4. 临时文件放 0700 私有目录,随机文件名,用 wx / 0600 独占创建。
  5. 回调隔离:一个监听器抛错不能影响主流程或其它监听器;错误要单独捕获与上报。
动手任务

给一个子进程封装加三个独立字段(timedOut / signal / exitCode),写三条断言:超时但 exitCode=0、被信号终止、正常退出。再故意让一个回调抛错,确认主流程继续。

自检
为什么 exitCode=0 仍可能超时?
进程可能捕获了终止信号并以 0 退出;只看退出码会把「被强杀但掩盖了」误判为成功。
为什么清理要等待退出?
只发信号就返回会留下孤儿进程;dispose 返回时必须保证进程已退出或被 SIGKILL。
进度保存在本机浏览器
6.5

事故复盘与工程文化

约 35 分钟工程 原文 ↗ 仍然成立
审计依据 ·

四问、四个案例与测试分层来自教程;原文标题拼写为「DeeSeek」,本站更正。 查看本节审计记录

学习目标

用「四问」写一份复盘,并说明为什么 100% 覆盖率仍会漏掉集成 bug。

前置知识

完成 6.4;有项目经验。

核心要点
  1. 四问:什么坏了、机制是什么、为什么每道安全网都没拦住、新增了什么防护。
  2. 只写同时满足「隐蔽、系统性、重新发现代价高」的 bug;不是所有 bug 都值得复盘。
  3. 四个真实案例:default export 丢 inject、!!js 配置误用、验收了替代服务器、Landlock 误分类。
  4. 测试分层:单元、覆盖率、真实 API e2e、快照;行覆盖率证明执行过,不证明行为正确。
  5. 验证外部世界,不信任 Agent 自述;文档纪律是「一个事实一个家」。
动手任务

选一个你遇到的 bug,按四问写 300 字复盘;至少补一条可执行防护(测试、规则或类型约束),并说明它能拦住哪一类复发。

自检
为什么 100% 覆盖率仍漏 bug?
覆盖率只证明行被执行;如果测试都走 mock 或手动挂载,真实入口、真实 Loader、真实浏览器路径可能从未被覆盖。
什么 bug 不值得写复盘?
一次性的笔误、机制显而易见、且没有系统性逃逸原因的;写了也不会改变流程。
进度保存在本机浏览器