第 3 卷
20readImage:95 字节,image/png,8x8
日志里只有引用,字节一个都没进去。④ 那条 user/message 的 content 是一个 ImageBlock,装着 attachmentId、尺寸、字节数这些元数据,图片字节留在盘上,日志
里只是指向它的引用。
前面说的准入,就是 ctx.attachments.imageLimits 这条线。跑 npm run start:ch12:admission,看它怎么运作:
复制代码 1--- ① 准入线在哪 ---
2maxImageBytes:5242880 3maxImagesPerMessage:20
4maxMessageImageBytes:104857600 5maxImagePixels:40000000
6mediaTypes:["image/png","image/jpeg","image/webp","image/gif"] 7
8--- ② 校验不落盘 --- 9抛错:Unsupported or malformed image data.(code=INVALID_IMAGE)
10存储根被建出来了吗:false← 校验只是看看,一个字节都不写 11
12--- ③ 一批图里有一张坏掉 --- 13抛错:Unsupported or malformed image data.(code=INVALID_IMAGE)
14存储根里已落盘的对象数:0← 先全量校验、再按序提交;坏在校验阶段就一个都不落 15
16--- ④ 收紧限制,不影响旧历史 --- 17新限制下再存同一张:Image exceeds the configured byte limit.(code=IMAGE_TOO_LARGE)
18但读回收紧之前那张:95 字节,没问题← 准入只在写入时生效
准入线看完了。存到外面之后,模型怎么看见它?图片这条路是“发请求时才解析”,它在日志里的形态是一个内容块(ImageBlock):
复制代码
真正把图片的字节读回来,发生在发请求给模型的时候:提供方的适配器会拿着引用去 readImage(),把字节转成 base64 放进请求。
所以它和文本那条路正好相反:文本是模型自己再发一次工具调用去取,图片则是 dsh 在发请求时自动解析、模型不用动手。根源就在“能不能被模型自己检索”:文本是纯文本,给它一
条路径,模型用 grep 就能自己翻;图片是字节,模型没法 grep 一张图。
至于这些图片对象的存放、归属、授权和回收,和溢出存储很不一样:
溢出存储附件
存放位置会话目录下(默认在进程级临时目录)全局的 <DSH_HOME>/a
随会话——fork 出去的子会话继承已有定位符,新产归属不随会话——全局一份,谁生的归子会话
回收很可能随会话清理一起失效目前不自动删,没有垃圾回
靠盘上权限——只有所有者能读(文件 600、目录授权授权是会话级的——只有引 700),不按会话授权
本讲小结
这一讲划下最后一条边界:工具的大段输出、用户拖进来的图片,都不该整份进上下文,dsh 给它们各开了一条外置通道。
溢出存储(工具输出)只管纯文本:超阈值就整份存走,上下文里只留头尾预览和一句"去哪儿读全文"。
方。
到这里,核心篇:能力、工具与会话就收口了。下一讲进入编排篇:从 Agent 循环到多 Agent 协作。
注:本讲 GitHub 链接
思考题
动手实操:打开 attachment-demo.ts,把第 30 行那个 name(现在是绝对路径 /Users/alice/secret/pixel.png)换成 '../../etc/passwd.png',重跑 npm
run start:ch12:attachment。预期 ① 那行变成 name:passwd.png——路径(含 ../)被剥掉,只剩最后一段。
第二步探测:这个 demo 没有 try/catch,而 code 挂在错误对象上、Node 默认不打印它。在 ① 之前插一段 try/catch,调 ctx.attachments.saveImage({ data:
bytes, mediaType: 'image/bmp' })(声明成 bmp,实际是 PNG 字节),catch 里打出 (error as Error & { code?: string }).code,预期
IMAGE_TYPE_MISMATCH。
想一下:为什么报的是类型不符而不是不支持?提示:它先按字节识别出这张图实际的格式,再跟声明的比。另一个小提示:'image/bmp' 不在 ImageMediaType 联合里,tsx 跑得
通、npm run typecheck 会报错,想两全就写成 'image/bmp' as never。
小改动:把 admission-demo.ts 第 57 行的 maxImageBytes: 1 换成 maxImagePixels: 1,重跑 npm run start:ch12:admission。预期 ④ 变成
code=IMAGE_TOO_MANY_PIXELS——8×8 共 64 像素,也超了 1。顺带回看 ③(它走默认准入线,不受第 57 行影响):那批里明明有一张是好的,为什么一张都没落盘?