跳转至

视觉隐患识别

CG_RAG 自带施工现场图片的安全隐患识别能力,通过 HTTP 和 MCP 对外提供。调用方只要能发出 base64 图片,就能拿到结构化的隐患报告,不需要自己接 VLM、维护判定标准或实现 agent 循环。

三种模式:

模式 接口 是否检索法规 回答是否带引用 典型耗时
自由生成 /cg-rag/vision/answer 单次模型调用
智能生成 /cg-rag/vision/agentic 是,模型自行决定检索问题 是,[cite^n] 多轮观察 + 检索
观察 /cg-rag/vision/observe 单次模型调用

自由生成适合快速筛查和高并发场景;智能生成适合需要条文依据、可审计结论的场景;观察返回校验过的隐患标签与展示文本,供调用方把图片转成检索问题后自行编排检索。

与 Question App 的关系

仓库内所有 VLM 调用都发生在 CG_RAG。 Question App 不再自己组装多模态请求,而是通过本页的 HTTP 接口调用 CG_RAG,再把结果渲染成对话轮次:

Question App ──HTTP──> CG_RAG vision ──进程内──> 检索

这样做的两个直接结果:

  • 智能生成每轮检索不再有网络往返。agent 与检索同进程,共享同一份 retrieve cache、profile fingerprint 与容量限制器。
  • 判定标准、prompt、引用校验只有一份实现。Question App 与外部调用方拿到的是同一套行为,不会因为两处实现漂移而给出不同结论。

调用方只需要访问 CG_RAG,不需要再部署一层编排。

前置配置

视觉能力需要一个 多模态 上游,与文本生成的 CG_RAG_GENERATION_* 相互独立:

CG_RAG_VISION_ENDPOINT=https://<vlm-host>/v1/chat/completions
CG_RAG_VISION_MODEL=<vlm-model>
CG_RAG_VISION_API_KEY=<key>      # 留空时回退到 CG_RAG_API_KEY

未配置时检索和文本生成完全不受影响,只有 /cg-rag/vision/* 返回 vision_not_configured。完整变量见配置

/cg-rag/healthvision 段确认是否就绪:

{
  "vision": {
    "configured": true,
    "endpoint_configured": true,
    "endpoint_host": "<VISION_ENDPOINT_HOST>",
    "model": "<vlm-model>",
    "enable_thinking": false,
    "streaming": true,
    "max_images": 4,
    "max_image_bytes": 8388608,
    "max_tokens": 4096,
    "agentic_max_search_rounds": 3,
    "agentic_retrieval_topk": 12
  }
}

图片输入

两个接口的 images 字段结构相同:

字段 类型 必填 默认 说明
data string base64 数据,或 data:<mime>;base64,<...> 形式的 data URL
mime_type string image/jpeg image/jpegimage/pngimage/webp 之一

约定:

  • 单次请求最多 max_images 张(默认 4),单张解码后最多 max_image_bytes 字节(默认 8 MiB)。
  • data 是 data URL 时,以 data URL 自带的媒体类型为准mime_type 字段被忽略。这样调用方直接粘贴浏览器产生的 data URL 不会造成类型错标。
  • 不接受 HEIC。iPhone 原图需由调用方先转成 JPEG/PNG/WebP —— CG_RAG 收到的是已解码的字节,而上游 VLM 都不接受 HEIC 上线。
  • 图片只在内存中流转,不落盘。

多图时服务会在每张图前注入 图N: 标记,模型按图分块输出(## 图1## 图2 …)。单图不注入该标记。

POST /cg-rag/vision/answer

自由生成。请求字段:

字段 类型 必填 默认 说明
images array 至少一张,见上文
question string 请检测图中的安全隐患 观察重点;非隐患类问题会被直接回答,不套用固定格式
enable_thinking boolean/null null 单次覆盖服务级 CG_RAG_VISION_ENABLE_THINKING

请求:

curl --noproxy '*' -sS \
  -X POST http://127.0.0.1:8864/cg-rag/vision/answer \
  -H 'Content-Type: application/json' \
  -H 'X-Request-ID: req-doc-vision' \
  -d "{\"images\":[{\"data\":\"$(base64 -w0 site.jpg)\",\"mime_type\":\"image/jpeg\"}]}"

示例输出,已删减:

{
  "ok": true,
  "request_id": "req-doc-vision",
  "mode": "free_form",
  "question": "请检测图中的安全隐患",
  "answer": "隐患等级:一般隐患\n\n隐患描述:\n1. 脚手架基础区域存在明显积水,未采取有效排水措施。\n\n整改措施和建议:\n1. 立即排除积水并设置排水设施。",
  "reasoning": "",
  "image_count": 1,
  "model": "<vlm-model>",
  "thinking_enabled": false,
  "citations": [],
  "retrieval_docs": [],
  "usage": {"prompt_tokens": 1180, "completion_tokens": 96, "total_tokens": 1276},
  "runtime_stats": {"total_time_ms": 6421.3}
}

citationsretrieval_docs 恒为空数组:该模式不检索,所以回答里不会出现 [cite^n]。需要条文依据时用智能生成。

POST /cg-rag/vision/agentic

智能生成。在自由生成的字段之上追加:

字段 类型 必填 默认 说明
scope string full 检索范围,full / usual / usual_plus_law
max_search_rounds integer/null null 最多检索轮数,范围 1..3null 用服务配置
topk integer/null null 每轮检索候选数,范围 1..200null 用服务配置

请求:

curl --noproxy '*' -sS \
  -X POST http://127.0.0.1:8864/cg-rag/vision/agentic \
  -H 'Content-Type: application/json' \
  -d "{\"images\":[{\"data\":\"$(base64 -w0 site.jpg)\"}],\"scope\":\"full\",\"topk\":12}"

示例输出,已删减:

{
  "ok": true,
  "request_id": "req_xxx",
  "mode": "agentic",
  "question": "请检测图中的安全隐患",
  "answer": "## 隐患等级\n重大事故隐患\n\n## 隐患描述\n1. 脚手架立杆底部浸泡在积水中。\n\n## 隐患判定依据\n1. 《房屋市政工程生产安全重大事故隐患判定标准(2024版)》第七条第一款:脚手架工程基础承载力和变形不满足设计要求。\n2. 《施工脚手架通用规范》(GB 55023-2022)地基应坚实、平整,不应有积水 [cite^2]。\n\n## 整改措施及建议\n1. 立即排水并复核地基承载力。",
  "reasoning_stages": [
    {"label": "OBSERVATION", "content": "图中脚手架立杆底部处于积水中。"},
    {"label": "SEARCH_PLAN", "content": "检索脚手架地基基础排水与承载力要求。"},
    {"label": "EVIDENCE_REVIEW", "content": "检索到的条文支持地基不应积水。"},
    {"label": "FINAL_REASONING", "content": "按 2024 版判定标准第七条第一款定级。"}
  ],
  "citations": [2],
  "retrieval_docs": [
    {
      "law_name": "GB55023-2022施工脚手架通用规范",
      "article_number": "2.0.1",
      "contents": "脚手架性能应符合下列规定……"
    }
  ],
  "pipeline_trace": [
    {"name": "图片接收", "status": "done", "summary": "已接收并校验 1 张图片。"},
    {"name": "第 1 轮法规检索", "status": "done", "summary": "执行 1 条检索问题,累计保留 12 条证据。"},
    {"name": "证据化结论生成", "status": "done", "summary": "已生成最终回答,使用 1 条服务器引用。"}
  ],
  "scene_status": "assessable",
  "image_count": 1,
  "model": "<vlm-model>",
  "scope": "full",
  "thinking_enabled": false,
  "warnings": [],
  "usage": {"total_tokens": 8241},
  "runtime_stats": {"total_time_ms": 41250.8}
}

字段说明:

  • citations 是回答中实际使用的服务器证据序号,与 retrieval_docs 的下标一一对应(从 1 开始)。
  • reasoning_stages 是可公开的审计依据,不是模型的原始 thinking。
  • scene_statusassessableunsupported_sceneinsufficient_visual_evidencenot_assessed;后两者表示图片本身不足以判定,此时应按 warnings 提示补拍。
  • pipeline_trace 用于向用户展示进度,也便于排查某一轮检索是否为空。

隐患等级取值为 无隐患 / 一般隐患 / 疑似重大事故隐患 / 重大事故隐患。定级规则和 2024 版判定标准条款清单由服务端注入 system prompt,调用方无需自行实现。

POST /cg-rag/vision/observe

一次结构化观察。请求字段与自由生成相同(imagesquestion),但返回的是校验过的观察结果而不是隐患报告:

curl --noproxy '*' -sS \
  -X POST http://127.0.0.1:8864/cg-rag/vision/observe \
  -H 'Content-Type: application/json' \
  -d "{\"images\":[{\"data\":\"$(base64 -w0 site.jpg)\"}]}"

示例输出:

{
  "ok": true,
  "mode": "observe",
  "text": "图片描述:图中可见施工区域地面存在积水。\n\n确定隐患描述:场地积水问题\n\n整改建议:\n1. 及时排除积水。",
  "hazard_label": "场地积水问题",
  "is_non_scene": false,
  "parse_status": "succeeded",
  "parse_error": "",
  "profile_id": "construction-hazard-inspection-v1",
  "system_prompt_sha256": "<PROFILE_PROMPT_SHA256>",
  "image_count": 1,
  "model": "<vlm-model>",
  "usage": {"total_tokens": 812},
  "elapsed_ms": 5120.4
}

hazard_label 取自服务端的 62 项标签词表,模型无法自造。parse_statusfailedhazard_label 为空、parse_error 说明原因,而 text 仍是可安全展示的原始观察——这是一个正常返回而不是错误:调用方据此决定降级展示还是中止,不应把它当成调用失败。观察没有流式变体。

流式接口

/cg-rag/vision/answer/stream/cg-rag/vision/agentic/stream 与各自的非流式版本请求字段完全相同,响应类型为 text/event-stream

curl --noproxy '*' -N \
  -X POST http://127.0.0.1:8864/cg-rag/vision/agentic/stream \
  -H 'Content-Type: application/json' \
  -d "{\"images\":[{\"data\":\"$(base64 -w0 site.jpg)\"}]}"

事件序列示例,已删减:

event: progress
data: {"stage":"vision","label":"正在结合法规识别图片隐患","status":"running"}

event: trace
data: {"name":"第 1 轮法规检索","status":"done","summary":"执行 1 条检索问题,累计保留 12 条证据。"}

event: agent
data: {"event_id":"vision.final","kind":"final","status":"running","label":"正在生成最终回答"}

event: token
data: {"channel":"answer","text":"## 隐患等级\n重大事故隐患\n"}

event: final
data: {"ok":true,"mode":"agentic","answer":"...","citations":[2]}

事件说明:

事件 出现于 含义
progress 两种模式 阶段进度
token 两种模式 可追加到 UI 的片段,channelanswerreasoning
trace 智能生成 流水线节点:图片接收、各轮检索、结论生成
agent 智能生成 agent 事件(观察、检索、重试、最终回答)的状态流转
answer_reset 智能生成 作废此前已发送的全部 answer 文本,随后重发
error 两种模式 业务或流式中断错误
final 两种模式 与非流式响应结构一致的完整结果

answer_reset 必须处理。智能生成会先校验最终回答是否满足公开协议与引用契约,不满足则重试;重试成功后服务重发正确的答案,客户端需要先清空已渲染的 answer 缓冲再追加后续 token。忽略该事件会导致页面上出现两份拼接在一起的回答。

其余语义与 /cg-rag/rag/stream 一致,详见 SSE、取消与错误。以 final 作为最终状态来源;final 的 payload 与非流式响应结构相同。

MCP 工具

同样的两种模式也作为 MCP 工具暴露,见 MCP 工具

  • cg_vision_answer(images, question, enable_thinking)
  • cg_vision_agentic(images, question, scope, max_search_rounds, topk, enable_thinking)

MCP 返回完整 JSON envelope,不提供 token 流;需要动态吐字时使用上面的 HTTP 流式接口。

错误

错误码 HTTP 状态 含义
vision_not_configured 503 未设置 CG_RAG_VISION_ENDPOINT / CG_RAG_VISION_MODEL
invalid_image 400 图片数量超限、体积超限、base64 非法或媒体类型不受支持
invalid_request 400 请求体字段类型或范围不合法
vision_upstream_error 502 调用 VLM 失败
vision_response_invalid 502 VLM 未返回可用内容
capacity.exceeded 429 生成容量等待超时

示例:

{
  "request_id": "req_xxx",
  "error": {
    "code": "invalid_image",
    "message": "第 1 张图片为 12582912 字节,超过上限 8388608 字节。",
    "details": {}
  }
}

流式接口的响应头在生成开始前就已发出,因此上述失败在流中表现为 event: error,HTTP 状态仍为 200

集成建议

  • 先用 /cg-rag/healthvision.configured 判断能力是否可用,再决定是否给用户开放入口。
  • 批量筛查用自由生成,需要出具依据时再对命中项调用智能生成,避免每张图都付多轮检索成本。
  • 智能生成显式传 scopetopk,不要依赖默认值。
  • 视觉与文本生成共用同一个 generation 容量限制器,批量调用前先确认 capacity.generation 余量。
  • 图片体积直接决定上传耗时和 token 成本,建议调用方先把长边压到 2048 以内再提交。