视觉隐患识别¶
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,再把结果渲染成对话轮次:
这样做的两个直接结果:
- 智能生成每轮检索不再有网络往返。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/health 的 vision 段确认是否就绪:
{
"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/jpeg、image/png、image/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}
}
citations 与 retrieval_docs 恒为空数组:该模式不检索,所以回答里不会出现 [cite^n]。需要条文依据时用智能生成。
POST /cg-rag/vision/agentic¶
智能生成。在自由生成的字段之上追加:
| 字段 | 类型 | 必填 | 默认 | 说明 |
|---|---|---|---|---|
scope |
string | 否 | full |
检索范围,full / usual / usual_plus_law |
max_search_rounds |
integer/null | 否 | null |
最多检索轮数,范围 1..3;null 用服务配置 |
topk |
integer/null | 否 | null |
每轮检索候选数,范围 1..200;null 用服务配置 |
请求:
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_status为assessable、unsupported_scene、insufficient_visual_evidence或not_assessed;后两者表示图片本身不足以判定,此时应按warnings提示补拍。pipeline_trace用于向用户展示进度,也便于排查某一轮检索是否为空。
隐患等级取值为 无隐患 / 一般隐患 / 疑似重大事故隐患 / 重大事故隐患。定级规则和 2024 版判定标准条款清单由服务端注入 system prompt,调用方无需自行实现。
POST /cg-rag/vision/observe¶
一次结构化观察。请求字段与自由生成相同(images、question),但返回的是校验过的观察结果而不是隐患报告:
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_status 为 failed 时 hazard_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 的片段,channel 为 answer 或 reasoning |
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/health的vision.configured判断能力是否可用,再决定是否给用户开放入口。 - 批量筛查用自由生成,需要出具依据时再对命中项调用智能生成,避免每张图都付多轮检索成本。
- 智能生成显式传
scope和topk,不要依赖默认值。 - 视觉与文本生成共用同一个 generation 容量限制器,批量调用前先确认
capacity.generation余量。 - 图片体积直接决定上传耗时和 token 成本,建议调用方先把长边压到 2048 以内再提交。