Skip to main content
Glama

视觉质检 Agent · vision-qc-agent

一个清单驱动的多模态图片质检 / 洞察 Agent:上传电商商品图,用视觉大模型提取结构化视觉事实, 再用本地规则引擎逐条判定,输出可交付的质检报告(网页 + Markdown + 机读 JSON)。

一句话版本: 把「图片质检」这件靠人眼的事拆成两层:模型只负责看(输出结构化事实), 判罚在一段可审计的规则代码里。这样结论可复现、阈值可调、还能解释为什么不合格。


1. 它解决什么问题

电商主图有明确的硬性规范(白底、无文字、主体占比、最小尺寸…),人工抽检 300 张图要几小时, 而且不同人判得不一样。这个 Agent 把「质检清单」变成可执行配置:

  • 一批图进去 → 每张图一份报告出来,结论是 合格 / 需优化 / 不合格 + 0-100 分;

  • 不合格项按严重度排序,每条都给出「实测值 vs 要求值」和具体改法;

  • 想改标准?改 checklists/*.json 里的一个阈值即可,不用改代码、不用重训、不花一个 token。

Related MCP server: Shelf Auditor MCP

2. 为什么这样设计(关键取舍)

决策

理由

模型只管"看",判罚交给代码

让 LLM 直接输出「合格/不合格」无法解释、无法复现、还会随温度漂移。拆成「结构化事实 + 确定性规则」后,每个结论都能追到具体字段和阈值。

像素级指标由程序测量,不交给模型

分辨率、主体占比、背景白度是可以精确算的。模型只做语义判断(模特姿态、色差风险)。实测值还会作为「既成事实」喂回提示词,避免模型乱估。

一张图只调一次模型

一份清单的全部规则(11–12 条)共用一次视觉调用,规则再怎么加都不增加成本。规则引擎本身是纯函数。

零运行时依赖

只用 node:http + node:zlib,自己写了 PNG 解码、multipart 解析、SSE。npm start 在任何装了 Node 20+ 的机器上都能跑,演示不会因为装包失败翻车。

结果按「图 + 清单内容哈希 + 模型」缓存

同一张图重跑秒出;改了阈值会自然失效,不会拿旧结论骗人。

兜底模式

没有 API Key 时自动用本地像素指标推断事实,并在报告里标注为兜底估计,绝不冒充模型判断。

3. 30 秒跑起来

cd vision-qc-agent
npm start                      # 打开 http://127.0.0.1:4599

不需要 npm install(零依赖)。API Key 解析顺序:

  1. 环境变量 DEEPSEEK_API_KEY

  2. DSH 凭据库 ~/.dsh/.credentials.yaml

  3. 项目内 .env

没有 Key 也能跑:界面会自动进入「演示模式」,用本地像素指标给出兜底结论。

生成样例图(6 张典型问题图,需要本机有 Chrome / Edge):

npm run samples                # → samples/
npm run samples:live           # 顺便用真实模型跑一遍并落盘报告

批量模式(CLI):

node src/cli.mjs samples --save            # 跑整个目录
node src/cli.mjs a.png b.jpg --json out.json
node src/cli.mjs samples --mock            # 强制离线兜底
node src/cli.mjs samples --cost            # 打印成本面板
node src/cli.mjs samples --no-consistency  # 跳过 SKU 组一致性检查
node src/cli.mjs samples --checklist detail-page

人机回环(质检员推翻判定 → 回流成阈值建议):

node src/cli.mjs review list
node src/cli.mjs review overturn --image 03-marketing-text.png --rule R05 \
  --reason business-exception --observed "54%" --note "平铺图主体略小可接受"
node src/cli.mjs review suggest --checklist ecom-main-image

以 MCP 服务器运行(给 Agent 调用):

npm run mcp      # stdio MCP,6 个工具
npm run install:dsh -- --write   # 把工具接进 DSH 的 web profile

测试(67 个离线用例,不联网、不调模型、不需要 Key):

npm test

分四组:core(图像探测 / 规则引擎 / 提示词 / multipart)、extensions(成本、一致性、人机回环、 编排缓存)、regressions(审计发现的缺陷回归,每条对应 docs/fix-log.md 里的编号)、http (真实启动服务,覆盖全部路由、SSE 帧、状态码语义、目录穿越,数据目录指向临时目录)。

另外三个需要环境的验证脚本:

npm run test:mcp    # 手写 JSON-RPC 探针,验证 MCP 握手与工具调用(mock、离线、带断言)
npm run test:dsh    # 用 DSH 自己的 MCP SDK 跑一遍握手(需要本机装过 DSH)
npm run test:ui     # 无头 Chrome 驱动真实页面(上传→运行→推翻→建议)

npm run test:ui 会自己拉起服务并指向临时数据目录,默认演示模式,跑完打印 14/14 项通过 并留下截图 data/ui-e2e.png;它不写项目的 data/feedback.jsonl,可以反复跑。 要跑真实模型:QC_MOCK=0 npm run test:ui(产生费用);要打已经运行的服务: QC_URL=http://127.0.0.1:4599/ npm run test:ui。

4. 工作流

图片字节
  ├─ 1. 测量   本地像素分析:尺寸/画幅/背景白度/主体占比/主体色/明暗离散度  (image/inspect.mjs)
  ├─ 2. 看图   多模态模型一次性输出 43 个结构化事实 + 思考链                (vision/provider.mjs)
  ├─ 3. 判定   清单规则在本地逐条评估:通过 / 不通过 / 未判定                (core/rules.mjs)
  ├─ 4. 复核   叠加质检员的推翻记录(人工判定优先,机器原判保留)            (core/feedback.mjs)
  ├─ 5. 汇总   按严重度加权算分、排序整改建议、给结论                        (core/rules.mjs)
  ├─ 6. 组内   图片≥2 张时做 SKU 一致性:本地 ΔE 实测 + 一次多图语义比对      (core/consistency.mjs)
  ├─ 7. 计价   记录 tokens,按峰谷费率算每张/每批/每月成本                   (core/pricing.mjs)
  └─ 8. 交付   网页实时流 + Markdown 报告 + batch.json 机读结果              (core/pipeline.mjs)

界面上能实时看到每个阶段:排队中 → 像素测量 → 模型分析中 → 已提取事实 → 判定完成 → 一致性比对, 右侧「执行轨迹」还会显示模型的思考摘要。

5. 四份质检清单(这不是配置项,是这套东西的价值所在)

同一套引擎,四份清单,同一个事实在不同清单里阈值不同 —— 这就是「清单可插拔」的实证:

事实

主图

详情页

直播封面

设计稿走查

productCoverage 主体占比

≥ 60%(索引图要一眼看清)

—

≥ 55%(缩略图更小)

≥ 30%(版面本来就要留白)

minSide 最短边

≥ 800px

≥ 750px

≥ 800px

—

textInImage 文字量

∈ {none, minimal}

∈ {none, minimal, moderate}(长图容忍更多信息)

∈ {none, minimal} + 禁止促销价

—

saturationRatio 色彩占比

—

—

≥ 18%(封面要冲击力)

between [12,45](同一事实、反方向约束:品牌色不能太素也不能太花)

blurRisk 模糊

∈ {none, slight}

∈ {none}

—

∈ {none}

backgroundWhiteness 背景白度

≥ 92%

—

—

不使用(设计稿不适用平台白底规范)

主图 12 条规则分四组,每条都写明「为什么」,因为报告要能拿去和运营讲道理:

组

规则

合规底线

背景纯白 ≥92% · 无营销文字 · 无水印/第三方 Logo

质量标准

最短边 ≥800px · 主体占比 ≥60% · 1:1 方图 · 布光自然

质量细节

无线头/污渍/褶皱 · 无模糊

信息完整

色差风险低 · 关键细节可识别 · 背景无杂物

详情页 11 条(用途可辨识 / 信息完整 / 视觉质量)、直播封面 11 条(缩略图可辨识 / 冲击力 / 合规)、 设计稿 11 条(视觉层级 / 留白 / 色彩统一 / 文案对比 / 画面完整)。

加一条新规则只要三行

{
  "id": "R13",
  "group": "质量标准",
  "title": "不得出现牛皮癣式拼图",
  "fact": "collageDetected",
  "op": "==",
  "value": false,
  "severity": "high",
  "suggest": "拆成单图主图;拼图会被平台判为低质。"
}

前提是该 fact 在 src/core/facts.mjs 的事实目录里登记过 —— 提示词、界面下拉、报告列都会自动跟着变, 不存在「改了清单忘了改提示词」的漂移。

6. SKU 组一致性:两个独立信号,故意不合并

同一次上传里只要 ≥2 张图,就自动做一次 SKU 组一致性检查:

  1. 确定性信号(本地、免费、可复现):从每张图的高饱和像素里取主体色 (白底不会被算进去),逐张与首图比 CIE76 色差 ΔE。首图默认为基准色。 ΔE <2 不可感知 / <5 细看可辨 / <10 肉眼可辨 / ≥10 明显不是同一色。

  2. 语义信号(一次多图模型调用):同一件商品?同色?材质细节一致?光影一致?每张图是什么用途?

关键设计:两者冲突时以实测为准,并把冲突单独列出来。 这才是这个功能的价值 —— 模型说「颜色一致」而实测 ΔE=49 的时候,工具的作用就是把这句话顶回去:

## 模型判断与实测结论冲突(需人工确认)
- 模型判定「颜色一致」,但实测主体色差 ΔE=49.4(#306fb2 vs #11756d)落在肉眼可辨区间
  —— 以实测为准,建议人工确认。

另外,实测色差(C05)被定为 high 严重度:它是算出来的,不像模型意见那样可以被「业务上可接受」讲掉。

7. 人机回环:质检员推翻 → 变成规则证据

这是让它从 demo 变成能落地的东西。任何一条 不通过 都能在界面上点「推翻」,选原因、写备注, 记录追加进 data/feedback.jsonl(append-only)。系统从这些记录里推出两件模型给不了的事:

  1. 规则可靠性 —— 每条规则被推翻多少次、推翻率多少。一条被推翻 40% 的规则是规则写得不好, 不是模型不好。

  2. 阈值建议 —— 对数值型规则,被接受的样本值本身就在告诉你阈值该往哪挪:

$ node src/cli.mjs review suggest --checklist ecom-main-image

  R05 主体占比不低于 60%
    推翻 4 次 · 置信度 low
    业务上接受了低至 52% 的样本(中位数 54%)。建议把阈值从 60% 下调到约 54%,
    或改为按商品类目分档。下调属于放宽标准,需质检负责人确认后手工修改
    checklists/ecom-main-image.json。

三条刻意的约束:只建议、不自动改(悄悄按反馈放宽标准,最后会得到一条什么都通过的规则); 设置证据门槛(同一规则至少 2 次,或真实推翻率 ≥30%,否则不给建议,免得训练人忽略它); 枚举型规则给定性建议而不是编一个数字。

推翻后当次报告会重算分数、撤掉对应整改建议,同时保留 machineStatus 供审计。

8. 成本面板:采购和运营一定会问

跑批会记录每次调用的 tokens(输入 / 缓存命中 / 输出 / 推理),按费率表折成钱:

$ node src/cli.mjs samples/01-perfect-white.png samples/04-watermark-logo.png --cost

成本面板
  tokens:输入 4,458(缓存命中 3,328,占 75%)· 输出 2,946(含推理 2,198)
  平均每张:3,702 tokens · $0.001947
  本批合计:$0.003894(2 张图)
  缓存省下:$0.000978(无缓存则需 $0.004873)
  费率:输入 $0.3/M · 缓存 $0.006/M · 输出 $1.2/M
  来源:https://api-docs.deepseek.com/quick_start/pricing(核对于 2026-09-17)
  月度外推:
      1,000 张/月 → $1.95
     10,000 张/月 → $19.47
    100,000 张/月 → $194.71
  排期建议:高峰 $0.002337/张 vs 错峰 $0.001168/张,省 50%

一致性结果也按「图片组」缓存:整批图片全部命中缓存时,成本面板显示 $0,不会再为一致性多付一次调用。

三个容易算错、这里都显式处理的点:缓存命中与未命中是互斥的两个桶(不能拿 prompt_tokens 全按未命中价算)、 推理 token 已含在输出里(不能重复计费)、DeepSeek 按时段计价(高峰为周一至周五 01:00–04:00 与 06:00–10:00 UTC,其余 5 折)。费率表在 src/core/pricing.mjs,每行都带来源与核对日期; OpenAI 那几行在构建环境里访问不到官方定价页,明确标注为未核实而不是装作权威。

9. 接入 DSH:从「人打开的页面」变成「Agent 能调用的能力」

内置一个 MCP stdio 服务器(src/mcp/server.mjs),暴露 6 个工具:

工具

作用

qc_list_checklists

列出四份清单及全部规则与阈值

qc_inspect_images

质检本地图片(路径数组 + 清单 + 可选兜底),返回结论与建议

qc_write_reports

质检并把 Markdown 报告落盘,返回目录路径

qc_recent_reports

列出已缓存结果(同图+同清单+同模型不重复计费)

qc_threshold_advice

依据推翻记录给出阈值调整建议

qc_cost_estimate

估算每张/每批/每月成本与峰谷排期

装进 DSH(已在本机执行过,insert: 包装是必须的,裸写 {id,name,config} 会被当成覆盖不存在的行而静默跳过):

node scripts/install-dsh-integration.mjs            # 只打印,不改文件
node scripts/install-dsh-integration.mjs --write    # 写入 ~/.dsh/profiles/web/cordis.patch.yml

写完后工具会以 mcp__qc__qc_inspect_images 这样的名字出现在 Agent 的工具列表里, DSH 的 web profile 会热加载该补丁。图片路径建议传绝对路径;相对路径按调用方的当前目录解析。 已验证:

$ npm run test:mcp    # 手写 JSON-RPC 探针(带断言,10/10)
✔ initialize 返回协议版本与 serverInfo — 2025-11-25
✔ tools/list 返回 6 个工具
✔ qc_inspect_images(mock)返回两张图的结论与结构化内容 — 结论:2 张完成 · 平均 94/100
✔ 空 paths 返回 isError 而不是协议错误

$ npm run test:dsh    # 用 DSH 自己的 @modelcontextprotocol/sdk 跑握手
✔ initialize 握手成功(DSH 客户端 SDK)        ✔ tools/list 返回 6 个工具
✔ 未声明 outputSchema(避免 DSH 的 structuredContent 强制校验)
✔ 无效路径返回 isError 而不是协议错误          10/10 项通过

outputSchema 那条是踩过的坑:一旦声明,DSH 就会要求每次调用都返回 structuredContent, 否则报 ToolOutputError;不声明反而是最安全的。

10. 项目结构

vision-qc-agent/
├── src/
│   ├── server.mjs              零依赖 HTTP 服务:JSON API + multipart 上传 + SSE 进度 + 静态页面
│   ├── cli.mjs                 batch / review / mcp 三个子命令
│   ├── config.mjs              配置与凭据解析(env → DSH 凭据库 → .env)
│   ├── core/
│   │   ├── facts.mjs           事实目录:清单、提示词、界面共用的词汇表 + 模型输出归一化
│   │   ├── rules.mjs           清单校验 + 规则引擎 + 评分 + Markdown 渲染
│   │   ├── consistency.mjs     多图一致性:CIE76 ΔE 实测 + 语义比对 + 冲突检测
│   │   ├── feedback.mjs        人机回环:推翻日志 + 可靠性统计 + 阈值建议
│   │   ├── pricing.mjs         token 核算 + 峰谷费率 + 月度外推
│   │   ├── pipeline.mjs        编排:并发、缓存、进度事件、落盘
│   │   ├── errors.mjs          带 code 的错误(HTTP 状态映射的唯一来源)
│   │   └── multipart.mjs       手写 multipart 解析
│   ├── image/
│   │   ├── inspect.mjs         PNG 解码 + 像素指标(含主体色)+ 容器尺寸嗅探
│   │   └── synthetic.mjs       纯 Node PNG 编码(测试基准图)
│   ├── mcp/server.mjs          MCP stdio 服务器(6 个工具)
│   └── vision/
│       ├── prompt.mjs          由事实目录生成提示词
│       └── provider.mjs        OpenAI 兼容 / Anthropic 协议 + 多图调用 + JSON 修复重试 + 兜底事实
├── checklists/                 四份清单:主图 / 详情页 / 直播封面 / 设计稿走查
├── public/                     前端(原生 JS,无构建)
├── scripts/                    样例图生成器 + DSH 集成安装器
├── samples/ reports/ data/     样例图 · 落盘报告 · 推翻日志
├── docs/                       研究留档(MCP 实现简报、费率参考)+ 审计报告 / 整改记录 / 架构说明
└── test/                       67 个离线用例 + 3 个环境验证脚本(MCP / DSH / 无头浏览器)

配置项见 .env.example(全部有默认值,不建 .env 也能跑)。

文档索引:

文档

内容

docs/architecture.md

系统架构图、端到端数据流、关键设计决策、错误语义

docs/known-limitations.md

适用边界与后续方向:格式支持、误报来源、一致性与成本口径、部署限制

docs/audit-report.md

一次完整的技术审计:16 项缺陷(含证据/触发条件/影响)、风险分级、整改顺序

docs/fix-log.md

逐项整改记录:改了什么、改前改后差异、验证命令与真实结果

docs/release-checklist.md

发布就绪检查与最终验收结论(含明确的能力边界)

docs/api-pricing-reference-2026-09-17.md

费率表的来源与核对日期

docs/MCP-IMPLEMENTATION-BRIEF.md

MCP 协议依据(研究留档)

11. HTTP API

方法

路径

说明

GET

/api/health

当前视觉线路、凭据来源、可用供应商

GET

/api/checklists

已加载的质检清单(含规则明细)

GET

/api/facts

事实目录(可用来做配置界面)

GET

/api/reports

进程内缓存的质检结果

POST

/api/inspect

multipart(images + checklistId + mock + consistency),返回 SSE 事件流

GET

/api/feedback

推翻记录与按规则聚合的统计

POST

/api/feedback

记录一次推翻(imageName ruleId reason note reviewer context)

GET

/api/adjustments

依据推翻记录生成阈值调整建议

GET

/api/pricing

费率表与外推样例

错误响应统一为 {error, code},状态码由 code 决定(src/core/errors.mjs 是唯一映射处):

状态

code

触发条件

400

BAD_MULTIPART

不是合法的 multipart 请求

400

BAD_JSON / BAD_FIELD

推翻接口的请求体不合法

400

UNKNOWN_CHECKLIST

checklistId 不存在,响应里带 available 列表

413

PAYLOAD_TOO_LARGE

超过 QC_MAX_UPLOAD_BYTES

422

UNSUPPORTED_FORMAT

上传的文件不是 PNG/JPEG/WebP/GIF,响应里带 files 列表

超过 QC_MAX_IMAGES 的部分不会被静默丢弃:batch-start 事件带 dropped 计数,并额外发一条 batch-warning。

SSE 事件类型:batch-start batch-warning image-start image-measured image-thinking image-facts image-report image-done image-cached image-error consistency-start consistency-facts consistency-report consistency-error cost saved batch-end complete fatal。

12. 已验证的真实结果

用 deepseek-v4-flash-vision-exp 跑 samples/ 六张样例图(真实调用,非模拟):

样例

分数

结论

模型抓到的关键问题

01 纯白底

86

不合格

主体占比 44%、色差风险 high(矢量图无材质纹理)

02 浅灰底

88

需优化

色差风险 medium、细节不可辨识

03 促销文字

74

不合格

画面文字量 moderate(合规硬性违规)

04 水印 Logo

75

不合格

检出未授权水印

05 低分辨率杂乱

69

不合格

背景白度 0% + 最短边 520px

06 暗调过曝

73

不合格

背景白度 0% + 色差风险 high

平均 78/100,六图 + 一致性真实调用共 $0.0253(平均 $0.0033/张、5,183 tokens/张、缓存命中 86%),耗时 65.7s。

分数与结论不是单调关系:三条 medium 同时不通过即判不合格(所以 86 分也可能不合格)。 分数衡量的是「离标准多远」,结论衡量的是「能不能上线」。

UI 端到端(无头 Chrome 驱动真实页面,测试自己拉起服务、用临时数据目录):14/14 项通过 —— 上传 → SSE 渲染 → 成本条 → 一致性卡片与色差色块 → 点「推翻」提交 → 复核面板刷新 → 阈值建议面板 → 推翻记录落到隔离目录(两图含一致性约 $0.0049)。


MIT License · 零运行时依赖 · Node ≥ 20.6

Related MCP Connectors

Related MCP Servers