第 23 讲:附件处理(attachment)——图片如何成为不可变对象

DeepSeek Harness 源码解析 · 第 23 讲

DeepSeek Harness 源码解析系列 第 23 讲:附件处理(attachment)——图片如何成为不可变对象 基于 DeepSeek Harness 源码 · 2026-09-06 💡 本讲一句话: 用户传一张图、模型读一张图,最终都变成 sha256:<64位hex> 的不可变对象——会话日志里只存引用不存字节。本讲拆三个入口(API 上传 / read_image 工具 / LLM 请求回放)如何共用同一套"准入→落盘→校验读回"管道,以及为什么写入要 fsync 到目录级、读取却只做头部探测。 一、服务接缝:AttachmentStore 只有三个动词 packages/attachment 家族分两个包: 接缝包 dsh-attachment (抽象服务 + 类型词汇表)和 本地实现包 dsh-attachment-local (DSH_HOME 下的内容寻址存储)。接缝包的 README 把边界写得很死:「Unsent browser drafts are intentionally outside this capability. Bytes enter durable storage only when a user prompt is submitted or when a provider adapter commits structured model output.」——浏览器里还没发出去的草稿不算附件,字节只在两个时刻进入持久层: 用户提交消息 、 模型产出结构化结果 。 接缝本身只有三个抽象方法( packages/attachment/attachment/src/index.ts ,第 29-60 行): 📄 packages/attachment/attachment/src/index.ts (第 29-60 行) export abstract class AttachmentStore extends Service { // 继承 Cordis Service:注册名固定为 'attachments',消费者经 ctx.attachments / ctx.get('attachments') 拿到 constructor(ctx: Context) { // 构造时绑定宿主上下文——Service 的生命周期跟着 app 走 super(ctx, 'attachments') // 服务键写死在接缝里:所有消费方(api-proxy、tool-fs、llm-pi-ai)都按这个名字取,实现可替换而调用点不动 } /** Deployment-resolved image policy used by authoritative and fast-path validation. */ // JSDoc 声明这是"部署期解析出的图片策略"——权威校验和快速路径共用同一份 limits abstract readonly imageLimits: ImageAttachmentLimits // 只读属性而非方法:limits 是冻结常量,消费方可以同步读取做准入判断(如 read_image 的字节上限) /** Validate one image without persisting it. Batch callers validate every member before saving any member. */ // JSDoc 把批处理契约写进接缝:调用方必须先验证完所有成员、再保存任何一个——防止"半批落盘" abstract validateImage(input: SaveImageAttachment): Promise<void> // 只校验不落盘:API 上传用它做整条消息的预检,任何一张不合格就整体拒绝 /** Validate and durably commit one image before its owning session event is appended. */ // JSDoc 点明时序契约:必须在"拥有它的那条会话事件被追加之前"完成持久提交——引用进日志时对象必须已 durable abstract saveImage(input:…

阅读全文:DeepSeek Harness 源码解析 第 23 讲