在 Claude Code / Codex / Pi 里用 DeepSeek,怎么让它看见图?

14 阅读7分钟

在 Claude Code / Codex / Pi 里用 DeepSeek,怎么让它看见图?

DeepSeek 写代码很香,接进 Claude Code、Codex、Pi 也越来越常见。但有一件事会反复踩坑:官方 API 是纯文本的。你把报错截图、UI 稿、接口文档拍照丢进去,模型要么直接说看不见,要么根据文件名瞎编。

我后来做了个很小的 skill:glm-vision。思路不复杂——看图的事交给智谱 GLM,推理和写代码还是 DeepSeek。仓库在这里:voidman2017/glm-vision

这篇文章把问题和做法讲清楚:为什么官方看不了图、skill 的架构怎么拆、怎么装、边界在哪。

问题不在 agent,在模型接口

Claude、GPT、Gemini 本身就能吃 image 块。DeepSeek V4 Flash / Pro 的托管 API 文档里,输入类型是 text。coding agent 不管多聪明,发出去的请求里只要带上图片,上游就会拒,或者在进模型之前把图丢掉。

所以会出现一种很割裂的体验:

  • 用 Claude 时,@screenshot.png 就能对界面评头论足
  • 切到 DeepSeek 后,同一张图变成「我无法查看图片」

社区里另一条路是视觉代理:本地拦请求,把图转成文字再转发给 DeepSeek。那条路能做到「粘贴即看」,但要改 base_url、常驻端口,还可能和你已经在跑的 Claude 反代叠在一起。我想先要一个更轻的东西:

  • 不改 DeepSeek 的鉴权和线路
  • 不装额外 Python 依赖
  • Claude Code、Codex、Pi、OpenCode 都能用
  • 智谱 Flash 视觉模型免费,先把日常截图跑通

于是就只剩 skill + 一条命令。

它到底做了什么

agent 看见图片路径时,不要自己「看」,而是跑:

python3 scripts/see.py shot.png -q "这是什么界面?读出报错和关键按钮。"

see.py 把本地图编成 data:image/...;base64,...,打到智谱的 OpenAI 兼容接口 /chat/completions,把 stdout 里的描述交回 DeepSeek。DeepSeek 再决定怎么改代码、怎么回你。

整条链路可以画成:

flowchart TD
    A[&#34;你 @ 了一张图&#34;] --> B[&#34;SKILL.md 约束 agent<br/>先跑 see.py,禁止脑补像素&#34;]
    B --> C[see.py]
    C --> C1[&#34;读取 ~/.config/glm-vision/env&#34;]
    C1 --> C2[&#34;同一模型重试<br/>429 / 1305 / 5xx&#34;]
    C2 --> C3[&#34;再换队列里的下一个模型&#34;]
    C3 --> D[GLM 输出文字描述]
    D --> E[&#34;DeepSeek 继续写代码 / 解释报错&#34;]

有两个角色,不要混:

flowchart LR
    subgraph eye[眼睛]
        G[智谱 GLM-4.6V-Flash 等]
    end
    subgraph brain[大脑]
        D[DeepSeek]
    end
    G -->|文字描述| D
    D -->|改代码 / 解释报错| U[你]
角色谁来当干什么
眼睛智谱 GLM-4.6V-Flash 等读图、OCR、说界面上有什么
大脑DeepSeek根据文字描述推理、改代码、给方案

这不是原生多模态。DeepSeek 拿到的是「别人看过之后的笔记」,不是视觉 token。好处是主模型不用换;代价是描述漏了的细节,后面补不回来。所以 -q 要把当前任务写进去,而不是笼统的「描述这张图」。

架构上就三块

仓库很小,刻意保持三块分离。

flowchart TB
    subgraph skill[给 agent]
        S[SKILL.md]
    end
    subgraph cli[真正发请求]
        P[scripts/see.py]
    end
    subgraph cfg[不进 git]
        E[&#34;~/.config/glm-vision/env&#34;]
    end
    S -->|约束:先跑脚本,禁止脑补| P
    E -->|API Key / 模型队列 / 重试| P
    P -->|image_url + 描述| G[智谱 /chat/completions]

一次完整调用的时序:

sequenceDiagram
    actor User as 你
    participant Agent as DeepSeek Agent
    participant Skill as SKILL.md
    participant See as see.py
    participant GLM as 智谱 GLM

    User->>Agent: @error.png 这是什么报错?
    Agent->>Skill: 匹配识图场景
    Skill-->>Agent: 必须先跑 see.py
    Agent->>See: see.py error.png -q 用户原话
    See->>See: 读 env,转 data URL
    loop 同一模型重试
        See->>GLM: chat/completions + image_url
        alt 429 / 1305 / 5xx 且还有次数
            GLM-->>See: 限流
        else 成功
            GLM-->>See: 文字描述
        end
    end
    alt 仍失败且队列未空
        See->>GLM: 换下一个模型再试
        GLM-->>See: 文字描述
    end
    See-->>Agent: stdout 描述
    Agent-->>User: 基于描述解释并改代码

1. SKILL.md:给 agent 看的说明书

它不负责发 HTTP。它只规定:

  • 什么时候必须调用脚本(@ 图、路径是 png/jpg、用户说识图/看截图)
  • 命令怎么写,-q 怎么带上用户原话
  • 脚本已经会重试和降级,禁止再包一层 sleep && retry
  • stdout 当事实,stderr 里的 info: / warn: 只说明用了哪个模型
  • 没有文件路径就请用户先保存,不要假装看见了剪贴板

人看的安装说明放在 README.md,不塞进 skill 包,避免每次识图都把装机步骤灌进上下文。

2. scripts/see.py:真正干活的 CLI

只有 Python 标准库:urllibbase64argparse。不需要 pip install openai

它做几件具体的事:

  • 把本地 PNG / JPEG / GIF / WebP 转成 data URL(上限 5MB)
  • 按 OpenAI 的 image_url 格式发给 VISION_BASE_URL/chat/completions
  • VISION_LANG 给视觉模型加一句「请用中文/英文回答」,和系统环境变量 LANG 分开,免得 macOS 的 en_US.UTF-8 把配置冲掉
  • 同一模型先重试,再换下一个

默认队列:

flowchart LR
    A[glm-4.6v-flash] --> B[glm-4.1v-thinking-flash] --> C[glm-4v-flash]

glm-4.6v-flash 官方标免费,也支持本地 base64,所以放队首。老的 glm-4v-flash 有过「不支持 base64」的记录,只能吃公网 URL,所以放队尾;一旦遇到的是格式错误而不是限流,脚本不会继续降级,避免把同一个坏请求打遍所有模型。

重试策略也很直白:

flowchart TD
    S[&#34;对当前模型发起请求&#34;] --> R{成功?}
    R -->|是| OK[输出描述]
    R -->|否| T{可重试?<br/>429 / 1305 / 5xx}
    T -->|是且未达次数| W[&#34;等待 2s / 4s / 8s&#34;] --> S
    T -->|是但次数用尽| N{还有下一个模型?}
    T -->|否 格式/鉴权等| X[停止降级并报错]
    N -->|是| M[换下一个模型] --> S
    N -->|否| X

每个模型最多 1 + VISION_RETRIES 次;等待按 VISION_RETRY_DELAY 翻倍。可重试:HTTP 429 / 500 / 502 / 503 / 504,以及智谱 1302、1305。

免费 Flash 高峰期很容易 429。如果失败一次就换模型,主模型几乎用不上;如果只死磕一个模型,又会卡死整轮对话。所以是「先礼貌地再问两遍,再换人」。

3. ~/.config/glm-vision/env:密钥单独放

Key 不进 git。agent 和脚本读同一份配置:

VISION_API_KEY=你的智谱key
VISION_BASE_URL=https://open.bigmodel.cn/api/paas/v4
VISION_MODELS=glm-4.6v-flash,glm-4.1v-thinking-flash,glm-4v-flash
VISION_RETRIES=2
VISION_RETRY_DELAY=2
VISION_LANG=zh

查找顺序:

flowchart TD
    A{&#34;$VISION_ENV_FILE 有值且文件存在?&#34;} -->|是| U[用该文件]
    A -->|否| B{&#34;~/.config/glm-vision/env 存在?&#34;}
    B -->|是| U2[用这份 env]
    B -->|否| C{&#34;~/.config/agent-vision-toolkit/env 存在?&#34;}
    C -->|是| U3[兼容 toolkit 的 env]
    C -->|否| E[缺少 VISION_API_KEY]

进程里的环境变量优先于文件。

怎么用

最佳推荐:AI时代自然是魔法打败魔法。直接提供仓库地址给AI,让它帮忙执行安装。当然也可以执行手动安装

安装

先克隆,再链到你正在用的 agent(不必四个都装):

git clone https://github.com/voidman2017/glm-vision.git
cd glm-vision

ln -sfn "$(pwd)" ~/.claude/skills/glm-vision
ln -sfn "$(pwd)" ~/.codex/skills/glm-vision
ln -sfn "$(pwd)" ~/.pi/agent/skills/glm-vision

对应目录:

AgentSkills 目录
Claude Code~/.claude/skills/glm-vision
Codex~/.codex/skills/glm-vision
Pi~/.pi/agent/skills/glm-vision
OpenCode~/.config/opencode/skills/glm-vision

Windows 可以用目录 Junction。不想软链就 cp -R。装完重启 agent,很多 CLI 只在启动时扫 skill。

然后配 Key:

mkdir -p ~/.config/glm-vision
cp assets/env.example ~/.config/glm-vision/env
chmod 600 ~/.config/glm-vision/env
# 填上 VISION_API_KEY,国内使用建议 VISION_LANG=zh

智谱开放平台实名后就能调 Flash 视觉模型,不必先充值。

先在终端验一下

python3 scripts/see.py --help
python3 scripts/see.py ~/Desktop/error.png -q "读出报错全文和底部按钮"

成功时 stderr 类似 info: using model glm-4.6v-flash,stdout 才是给 DeepSeek 看的描述。

常用参数:

# OCR,按阅读顺序抄文字
python3 scripts/see.py dialog.png --ocr

# 对比两张图
python3 scripts/see.py before.png after.png -q "布局和文案有什么差异?"

# 这次指定队列,或加大重试
python3 scripts/see.py shot.png --model glm-4.1v-thinking-flash
python3 scripts/see.py shot.png --retries 3 --retry-delay 2

在 agent 里怎么用

切到 DeepSeek,然后:

@error.png 这个报错是什么意思?相关代码在哪?

skill 生效时,模型会先跑 see.py,再基于描述回答。如果你只是把图粘进输入框、没有任何路径,skill 帮不上——像素根本没落到磁盘。先保存,再 @

和「视觉代理」怎么选

glm-vision本地视觉代理(如 agent-vision-toolkit)
粘贴即看否,要有路径能做
要不要改 DeepSeek 的 base_url不用通常要改,还可能叠一层本地端口
安装量skill + 一份 env代理进程、开机自启、和现有反代协调
适合日常 @ 截图、OCR、对比 UI三端无缝粘图、内置 view_image

我自己日常用 skill 就够了。已经有 Codex / Claude 反代、又特别依赖粘贴的人,再上代理更合适。两套可以共用同一份 VISION_* 配置。

几个踩过的坑

免费模型会限流。 智谱 1305「当前访问量过大」很常见。所以脚本默认同一模型试 3 次,再换 glm-4.1v-thinking-flash。三个 Flash 一起挤的时候,把付费 glm-4.6v 加到 VISION_MODELS 队尾当保底即可。

老 Flash 不一定吃 base64。 coding agent 里的图几乎都是本地文件,没有公网 URL。队首一定要用支持 data URL 的模型。

这是有损压缩。 GLM 漏读的一行小字,DeepSeek 无法「再看一眼」,除非你让脚本带着更具体的 -q 再跑一次。把用户原话传进去,比「请详细描述」有用得多。

skill 不是魔法。 它改变的是 agent 的行为约束,不是模型的输入模态。没有路径,就没有图。

小结

DeepSeek 官方暂时不提供识图 API。与其等接口,不如把「看」和「想」拆开:GLM 看图,DeepSeek 写代码。glm-vision 把这件事收成一个可安装的 skill,一条不依赖第三方包的命令,加上重试和降级,让 Claude Code / Codex / Pi 里的 DeepSeek 至少能认真对待一张截图。

仓库:github.com/voidman2017…
MIT,欢迎star,提 issue 和 PR。