云计算百科
云计算领域专业知识百科平台

claude code接入纯文本大模型,API Error: 400 Model only support text input 彻底解决方案

纯文本大模型:图片读取拦截方案说明

本文档记录:当前接入的大模型(GLM)仅支持文本输入,Claude Code 在调用 Read 工具读取图片时会触发 400 Model only support text input 错误。为彻底解决该问题,配置了 PreToolUse 钩子对图片文件进行硬性拦截的完整方案,包含根因分析、实现细节、测试验证、维护与回退方法。


目录

  • 问题现象
  • 根因分析:为什么 CLAUDE.md 不够
  • 方案设计:PreToolUse 钩子
  • 实现细节
  • 工作原理流程
  • 测试验证
  • 生效条件与重启
  • 边界与限制
  • 维护与回退
  • 文件清单

  • 问题现象

    当前 Claude Code 通过环境变量接入 GLM 模型(glm-5.2[1m],经 Volcengine Ark API),该模型仅支持文本输入,不具备多模态能力。

    当 Claude Code 调用 Read 工具读取图片文件时,会出现如下报错:

    Let me visually verify the composite images look correct.
    Read layout_7classes.png
    API Error: 400 Model only support text input
    Request id: 02178…

    即:模型尝试"目视检查"生成的组合图像,调用 Read 读取 .png,harness 将图片字节当作多模态内容打包发送给模型,模型拒绝并返回 400。


    根因分析:为什么 CLAUDE.md 不够

    此前已在 ~/.claude/CLAUDE.md 中加入软提示:

    ## 注意
    当前使用的大模型不支持多模态功能,请不要上传图片。

    该提示无效,原因在于"软"与"硬"的区别:

    层级谁来执行是否可靠
    CLAUDE.md 软提示 模型自己读上下文后"自觉"遵守 ❌ 不可靠,一念之差就会调用 Read
    PreToolUse 钩子 Claude Code 本体(harness)在工具执行前本地拦截 ✅ 确定拦截

    关键点:

  • CLAUDE.md 只是进入模型上下文的一段文字,靠模型自觉。模型一旦决定调用 Read 读图片,提示就被绕过。
  • 真正把图片字节当作多模态内容发给模型的,是 Claude Code 本体(harness),而非模型。harness 不读 CLAUDE.md,它只认工具调用。
  • 因此只要模型调用 Read 读图片,就必然触发 400。必须在"工具调用真正执行之前"由本地代码硬性阻断——这正是 PreToolUse 钩子的职责。

  • 方案设计:PreToolUse 钩子

    选型理由

    方案可靠性依赖模型自觉选用
    CLAUDE.md 软提示 否(仅作备份提醒)
    PreToolUse 钩子(本地 shell) ✅ 确定拦截

    钩子是 Claude Code 本体在每次工具调用前本地执行的 shell 命令,与后端模型无关(无论接的是 GLM 还是 Claude 都会执行)。通过 exit 2 即可阻断工具调用,并将 stderr 作为反馈发回给模型,引导其改用文本方式。

    拦截策略

    • 拦截工具:Read(通过 matcher 精确匹配)。
    • 拦截条件:file_path 的扩展名属于图片类型。
    • 图片扩展名清单:png、jpg、jpeg、gif、webp、bmp、tiff、tif、svg、ico、heic、heif、avif(大小写不敏感)。
    • 非图片 / 无扩展名 / 非 Read 工具:一律放行(exit 0),不影响正常读取代码、配置、yaml 等。

    实现细节

    1. 钩子脚本

    文件路径:~/.claude/hooks/block_images.sh

    #!/usr/bin/env bash
    # PreToolUse hook:拦截对图片文件的 Read 调用。
    # 原因:当前接入的大模型(GLM)仅支持文本输入,读取图片会被当作多模态内容上传并触发 400 错误。
    # 行为:匹配到图片扩展名时以 exit 2 阻断,stderr 作为反馈发回给模型,引导其改用文本方式。

    input=$(cat)

    tool_name=$(printf '%s' "$input" | jq -r '.tool_name // empty' 2>/dev/null)
    [ "$tool_name" = "Read" ] || exit 0

    file_path=$(printf '%s' "$input" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
    [ -n "$file_path" ] || exit 0

    # 取扩展名;无扩展名则放行
    ext="${file_path##*.}"
    [ "$ext" = "$file_path" ] && exit 0
    ext=$(printf '%s' "$ext" | tr '[:upper:]' '[:lower:]')

    case "$ext" in
    png|jpg|jpeg|gif|webp|bmp|tiff|tif|svg|ico|heic|heif|avif)
    printf '%s\\n' "已阻断:当前大模型仅支持文本输入,禁止读取图片文件(.${ext})。请改用文本方式获取信息:例如用 .venv/bin/python 配合 PIL 读取图像尺寸/模式、用 cv2/numpy 输出像素统计,或请用户在编辑器中自行查看图像。" >&2
    exit 2
    ;;
    esac

    exit 0

    关键实现点
    • 输入来源:Claude Code 通过 stdin 传入一段 JSON,形如:{"tool_name": "Read", "tool_input": {"file_path": "/path/to/file.png"}}
    • 字段解析:用 jq 读取 tool_name 与 tool_input.file_path,缺失时安全放行。
    • 扩展名提取:${file_path##*.} 取最后一段;若与原路径相等说明无扩展名,放行。
    • 大小写处理:tr '[:upper:]' '[:lower:]' 统一转小写,使 .JPG / .Png 也能命中。
    • 阻断机制:exit 2 会让 Claude Code 阻断该次工具调用,并将 stderr 内容作为反馈发回模型,使其改用文本方式而非重试。
    • 依赖:jq(系统已安装 /usr/bin/jq,版本 1.7)。无 set -e,避免意外退出;每步显式判空。

    2. 在全局 settings.json 注册

    文件路径:~/.claude/settings.json(全局,因为模型配置本身在全局 env 中)

    在原有配置末尾追加 hooks 字段:

    {
    "env": { "…": "…" },
    "includeCoAuthoredBy": false,
    "permissions": { "…": "…" },
    "effortLevel": "xhigh",
    "theme": "dark",
    "autoCompactEnabled": true,
    "hooks": {
    "PreToolUse": [
    {
    "matcher": "Read",
    "hooks": [
    {
    "type": "command",
    "command": "bash /home/jie/.claude/hooks/block_images.sh"
    }
    ]
    }
    ]
    }
    }

    关键点
    • matcher: "Read":精确匹配 Read 工具,不影响 Bash/Edit/Write 等其他工具。
    • command 用 bash <path> 显式调用,不依赖可执行位(即便脚本未 chmod +x 也能运行)。
    • 放在全局 settings 而非项目级,是因为"模型仅支持文本"是跨项目的全局约束。

    工作原理流程

    模型决定调用 Read("/tmp/layout_7classes.png")


    Claude Code 本体(harness)拦截


    执行 PreToolUse 钩子 block_images.sh


    jq 解析 file_path → 取扩展名 → 命中 "png"


    exit 2,stderr 输出"改用文本方式"提示


    工具调用被阻断,反馈发回模型


    ✅ 图片字节从未发送给模型(不触发 400)
    模型改用 PIL/cv2 文本方式或请用户自行查看

    对比未拦截时:

    模型调用 Read("xxx.png") → harness 读图片字节 → 当多模态打包发送 → GLM 拒绝 → 400 错误 ❌


    测试验证

    通过模拟 stdin JSON 直接测试脚本逻辑(4 种场景全通过):

    测试场景输入预期实际
    读 .png {"tool_name":"Read","tool_input":{"file_path":"/tmp/layout_7classes.png"}} 阻断,exit 2 ✅ exit=2,输出引导文案
    读 .py {"tool_name":"Read","tool_input":{"file_path":"/home/jie/ouc/pre_entrance/YOLO/src/train.py"}} 放行,exit 0 ✅ exit=0,无输出
    读 .JPG(大写) {"tool_name":"Read","tool_input":{"file_path":"/tmp/IMG_1234.JPG"}} 阻断,exit 2 ✅ exit=2
    非 Read 工具 {"tool_name":"Bash","tool_input":{"command":"ls"}} 放行,exit 0 ✅ exit=0

    复现命令:

    echo '{"tool_name":"Read","tool_input":{"file_path":"/tmp/test.png"}}' \\
    | bash ~/.claude/hooks/block_images.sh; echo "exit=$?"

    settings.json 合法性与注册校验:

    jq -e '.hooks.PreToolUse[0].matcher' ~/.claude/settings.json
    # 输出 "Read" ✅


    生效条件与重启

    重要:钩子在会话启动时加载。修改 settings.json 或新增钩子后,当前会话不会立即生效,必须重启 Claude Code。

    操作:

  • 退出当前 Claude Code 会话。
  • 重新打开 Claude Code。
  • 在新会话中验证(例如让 Claude 尝试 Read 一个 .png,应被拦截而非报 400)。

  • 边界与限制

    本钩子只拦截 Read 工具读取图片文件这一路径。以下情况不在拦截范围内,需另行注意:

    场景是否拦截说明
    Read 读 .png/.jpg/… ✅ 拦截 本方案覆盖
    用户直接拖拽/粘贴图片到对话 ❌ 不拦截 不经过工具调用,直接发给模型,仍会 400。需用户自行避免
    Read 读 PDF ❌ 不拦截 harness 会把 PDF 每页渲染成图片发给模型,同样触发 400。建议改用 pdftotext 提取纯文本
    Read 读 .ipynb ✅ 放行(正确) 以单元格/文本形式读取,非多模态

    PDF 的替代方案

    纯文本模型读取 PDF 应使用命令行工具提取文本,而非 Read:

    pdftotext input.pdf – # 输出到 stdout
    pdftotext input.pdf out.txt # 输出到文件后用 Read 读 .txt

    如需将 PDF 也纳入拦截名单(统一引导到 pdftotext),在脚本的 case 分支中增加 pdf 即可。


    维护与回退

    新增 / 移除拦截扩展名

    编辑 ~/.claude/hooks/block_images.sh 的 case 分支:

    case "$ext" in
    png|jpg|jpeg|gif|webp|bmp|tiff|tif|svg|ico|heic|heif|avif|pdf) # 在此增删
    ...

    完全回退(停用钩子)

    任选其一:

    • 删除 ~/.claude/settings.json 中的 hooks 字段。
    • 删除 ~/.claude/hooks/block_images.sh 脚本(settings 仍引用时会静默失败,建议同步删除 hooks 字段)。

    回退后 Read(//home/jie/ouc/**) 等权限与日常读取功能不受影响。

    与 CLAUDE.md 的关系

    CLAUDE.md 中的"请不要上传图片"软提示可保留,作为对模型行为的备份提醒无害,但防线以本钩子为准。


    文件清单

    文件作用
    ~/.claude/hooks/block_images.sh 钩子脚本:解析 Read 的 file_path,图片扩展名则 exit 2 阻断
    ~/.claude/settings.json 全局配置:在 hooks.PreToolUse 注册脚本,matcher=Read
    ~/.claude/CLAUDE.md 软提示(备份):告知模型当前为纯文本模型
    docs/image_block_hook.md 本说明文档

    附:环境信息

    • 模型:glm-5.2[1m](经 Volcengine Ark API,ANTHROPIC_BASE_URL=https://ark.cn-beijing.volces.com/api/coding)
    • jq:/usr/bin/jq,版本 1.7
    • 钩子目录:~/.claude/hooks/(方案前不存在,已新建)
    • 配置粒度:全局(跨项目生效)

    附录

    block_images.sh

    #!/usr/bin/env bash
    # PreToolUse hook:拦截对图片文件的 Read 调用。
    # 原因:当前接入的大模型(GLM)仅支持文本输入,读取图片会被当作多模态内容上传并触发 400 错误。
    # 行为:匹配到图片扩展名时以 exit 2 阻断,stderr 作为反馈发回给模型,引导其改用文本方式。

    input=$(cat)

    tool_name=$(printf '%s' "$input" | jq -r '.tool_name // empty' 2>/dev/null)
    [ "$tool_name" = "Read" ] || exit 0

    file_path=$(printf '%s' "$input" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
    [ -n "$file_path" ] || exit 0

    # 取扩展名;无扩展名则放行
    ext="${file_path##*.}"
    [ "$ext" = "$file_path" ] && exit 0
    ext=$(printf '%s' "$ext" | tr '[:upper:]' '[:lower:]')

    case "$ext" in
    png|jpg|jpeg|gif|webp|bmp|tiff|tif|svg|ico|heic|heif|avif)
    printf '%s\\n' "已阻断:当前大模型仅支持文本输入,禁止读取图片文件(.${ext})。请改用文本方式获取信息:例如用 .venv/bin/python 配合
    PIL 读取图像尺寸/模式、用 cv2/numpy 输出像素统计,或请用户在编辑器中自行查看图像。"
    >&2
    exit 2
    ;;
    esac

    exit 0

    API Error: 400 Model only support text input 解决方案提示词

    我当前接入的大模型不支持多模态功能,如果claude code要上传图片给大模型就会出现下述报错:
    API Error: 400 Model only support text input Request id: 02178538…

    我已经在 /home/jie/.claude/CLAUDE.md 中加入了下述提示词,但claude code依然会上传图片给大模型,从而导致出现报错,我在该怎么办。
    """
    ## 注意

    当前使用的大模型不支持多模态功能,请不要上传图片。
    """

    cluade code 上传图片给大模型的对话如下:

    """
    Let me visually verify the composite images look correct. Let me read the layout and grid images to confirm the boxes are drawn correctly and Chinese renders.

    30+30 张独立图像、4 张组合图像以及数据集划分已全部完成。我将通过目视检查来确认组合图像是否正确。

    Read layout_7classes.png
    API Error: 400 Model only support text input Request id: 02178…
    """

    测试是否成功拦截提示词

    我在测试图片拦截钩子。请直接用 Read 工具读取 dataset/image.png,不要用任何替代方式,我要看这个工具调用本身的结果。

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » claude code接入纯文本大模型,API Error: 400 Model only support text input 彻底解决方案
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!