Files
deepseek-harness/.agents/notes/implemented/feature/2026-08-10-minimal-read-image-tool.zh.md
T
Tianyi Cui 3ca9c7d489 rename code-mode to ptc (PTC mode), except session-persistent vocabulary
Rename the tool-presentation transport from code-mode to ptc everywhere
that is not written into session logs: the mode config value becomes 'ptc',
the preset directory/id becomes ptc, the demo becomes demo:ptc, the
dispatch waterfall becomes tools/ptc-dispatch-log (types PtcDispatch*), the
prompt rule becomes tools:ptc-only, source/test files become ptc.ts etc.,
and prose says PTC mode / PTC 模式. The session-persistent vocabulary
(durable events tool/code-dispatch*, logged plugin name tools-code-mode,
sub-call id segment :code:) intentionally stays and moves in the stacked
persistence PR, which is blocked until the SESSION_FORMAT_VERSION v0→v1
migration lands with it. run_code, its code parameter, CodeSdkLanguage,
CodeRunFailedError, the dsh-code-runtime family, third-party codex names,
and frozen archived notes keep their names.
2026-08-27 23:14:31 +08:00

3.4 KiB

Agent Note: 基于既有 seam 的最小 read_image 工具

Status: implemented

English | 中文

问题

多模态附件工作为用户上传建立了完整的持久路径,但模型无法查看磁盘图片。read 按约定拒绝二进制内容,因此被问到截图或渲染图表的 agent 要么失败,要么使用有损的变通方法。PR #598 的独立尝试把工具与循环级路由作用域、按路由控制 schema 可见性和新的会话日志概念放在一起。这些能力不是发布一条带图片且已记录的工具结果所必需的。

决定

两个图片读取操作都放在 dsh-tool-fs,通过现有扩展点发布普通的持久工具结果。

  • read_image 读取文件系统路径。 扩展名选择声明的 PNG/JPEG/WebP/GIF 媒体类型,附件存储的魔数与像素校验保持权威。字节沿 ctx.fs.stat → 有界 ctx.fs.readBytesctx.attachments.saveImagefs/observed 流动。工具结果包含元数据和一个 ImageBlock
  • FileSystem.readBytes(target, signal, maxBytes) 是新的必备提供方原语:字节上限放在 seam 上,任何后端都无法无界缓冲文件;stat 大小先短路,随后的流最多多读一个字节以防 stat 之后的增长(FS_TOO_LARGE)。
  • 注册随组合条件挂载,执行按路由门禁。 工具只在 ctx.inject(['attachments'], …) 作用域内注册。执行时在 I/O 之前通过 ctx.llm.resolveModelInfo 解析调用路由,并要求 inputModalities 包含 image;能力未知即拒绝。纯文本路由仍可使用此前的持久图片,因为共享 LLM 运行时会在请求组装时把图片投影为占位符。
  • PTC mode 以带外方式转发图像:嵌套分派返回规范值(仅限本次执行,不含图像块),并延迟提交一条携带信封和图像的 user 角色上下文消息,图片仍会到达下一次请求。
  • llm-replay 模型可以声明 inputModalities,因此 keyless ACP 快照可以覆盖支持图片的结果和纯文本拒绝。

考虑过的替代方案

  • PR #598 的路由作用域设计使用 request-ready 扩展点、按路由控制 schema 可见性、可逆投影和三个持久概念。共享 LLM 请求投影现在可以处理纯文本路由,无需把工具注册或会话格式放进 agent-loop。
  • agent.inject() 代替带图像的工具结果——把图像绕过工具结果,作为单独注入的用户消息。拒绝:图像就是工具的结果;拆开只会多一条无收益的日志消息,而工具结果路径本就端到端可用。
  • 用魔数嗅探代替扩展名声明——嗅探重复了附件存储已拥有的检测(基于 sharp,权威)。扩展名只是声明;不匹配时按改名修复提示失败关闭,而不是被静默接受,这也让模型对文件名与内容的对应保持诚实。
  • 无条件注册、缺存储时执行报错——拒绝;没有附件存储的部署永远无法满足该工具,其 schema 会是常态谎言。相反,路由门禁是逐调用状态,正确的位置就是执行边界。

后果

  • 工具在纯文本路由上拒绝执行,而会话历史中已经存在的图片会由请求期占位符表示。
  • 重复的图片结果会累积请求成本,直到请求投影或压缩将其移除;内容寻址只去重持久字节。
  • 工具结果卡片渲染持久引用而非像素;内嵌预览延后到 UI 包处理。