8
1
DCU技术达人
把识图服务交给业务方:海光 DCU 多模态 API 从契约设计到接入排错
2026-09-30 08:55:41
5
## 在海光 DCU 八卡机上把[多模态识图集群](https://zhida.zhihu.com/search?content_id=282246046&content_type=Article&match_order=1&q=%E5%A4%9A%E6%A8%A1%E6%80%81%E8%AF%86%E5%9B%BE%E9%9B%86%E7%BE%A4&zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3OTA5MDI0NTcsInEiOiLlpJrmqKHmgIHor4blm77pm4bnvqQiLCJ6aGlkYV9zb3VyY2UiOiJlbnRpdHkiLCJjb250ZW50X2lkIjoyODIyNDYwNDYsImNvbnRlbnRfdHlwZSI6IkFydGljbGUiLCJtYXRjaF9vcmRlciI6MSwiemRfdG9rZW4iOm51bGx9.ER1pguglY84vP5XYvHJrGV7BLXtgPs67gHMi3JNH-Ms&zhida_source=entity)跑起来只是上半场,下半场是**把它交给业务方接入**——而这半场的胜负,早在第一次联调之前就由「[接口契约](https://zhida.zhihu.com/search?content_id=282246046&content_type=Article&match_order=1&q=%E6%8E%A5%E5%8F%A3%E5%A5%91%E7%BA%A6&zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3OTA5MDI0NTcsInEiOiLmjqXlj6PlpZHnuqYiLCJ6aGlkYV9zb3VyY2UiOiJlbnRpdHkiLCJjb250ZW50X2lkIjoyODIyNDYwNDYsImNvbnRlbnRfdHlwZSI6IkFydGljbGUiLCJtYXRjaF9vcmRlciI6MSwiemRfdG9rZW4iOm51bGx9.nzcxF7tMpYbO9SLXb4lYOTHvSjoeL4kOzCvRTwB2FK8&zhida_source=entity)设计得好不好」决定了。我们的服务是 8 个 [vLLM](https://zhida.zhihu.com/search?content_id=282246046&content_type=Article&match_order=1&q=vLLM&zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3OTA5MDI0NTcsInEiOiJ2TExNIiwiemhpZGFfc291cmNlIjoiZW50aXR5IiwiY29udGVudF9pZCI6MjgyMjQ2MDQ2LCJjb250ZW50X3R5cGUiOiJBcnRpY2xlIiwibWF0Y2hfb3JkZXIiOjEsInpkX3Rva2VuIjpudWxsfQ.IE-TZHGOpZO5EW5GHn_AqZBqUNO37WAG56rp0-Rmk00&zhida_source=entity) 实例(各占一张卡)前置[反向代理](https://zhida.zhihu.com/search?content_id=282246046&content_type=Article&match_order=1&q=%E5%8F%8D%E5%90%91%E4%BB%A3%E7%90%86&zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3OTA5MDI0NTcsInEiOiLlj43lkJHku6PnkIYiLCJ6aGlkYV9zb3VyY2UiOiJlbnRpdHkiLCJjb250ZW50X2lkIjoyODIyNDYwNDYsImNvbnRlbnRfdHlwZSI6IkFydGljbGUiLCJtYXRjaF9vcmRlciI6MSwiemRfdG9rZW4iOm51bGx9.lAwRzHR87vlITBOZ5cnqmY946mk1iRhjfpXABk2iyFQ&zhida_source=entity)统一入口,协议上做 [OpenAI Chat Completions](https://zhida.zhihu.com/search?content_id=282246046&content_type=Article&match_order=1&q=OpenAI+Chat+Completions&zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3OTA5MDI0NTcsInEiOiJPcGVuQUkgQ2hhdCBDb21wbGV0aW9ucyIsInpoaWRhX3NvdXJjZSI6ImVudGl0eSIsImNvbnRlbnRfaWQiOjI4MjI0NjA0NiwiY29udGVudF90eXBlIjoiQXJ0aWNsZSIsIm1hdGNoX29yZGVyIjoxLCJ6ZF90b2tlbiI6bnVsbH0.CNkZPDUavzH7RIlx2SQ2NIp4qRbGMtGS0vPnePAPQSM&zhida_source=entity) 兼容。理论上业务方「随便找个 OpenAI SDK 就能接」,实际上接入期踩到的坑一个接一个:`user` 字段类型、think [推理链](https://zhida.zhihu.com/search?content_id=282246046&content_type=Article&match_order=1&q=%E6%8E%A8%E7%90%86%E9%93%BE&zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3OTA5MDI0NTcsInEiOiLmjqjnkIbpk74iLCJ6aGlkYV9zb3VyY2UiOiJlbnRpdHkiLCJjb250ZW50X2lkIjoyODIyNDYwNDYsImNvbnRlbnRfdHlwZSI6IkFydGljbGUiLCJtYXRjaF9vcmRlciI6MSwiemRfdG9rZW4iOm51bGx9.KGPEhxIvCk6EBPlIf7hw-EQHmFWFiSmdv9M6J4wyOng&zhida_source=entity)的处理、超时设置、错误归因…… 这篇是我们沉淀下来的完整接入指南——不是理想化的 API 文档,而是**每一项契约背后都标注了「不遵守会发生什么」的实战版**。任何团队在海光 DCU(或任何 GPU)上对外提供 LLM 服务,这份契约清单和排错路径都可以直接参考。 ## 服务形态:接入方需要知道的最小集合 | 项 | 值 | | -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 模型 | [MiniCPM-V-4.5](https://zhida.zhihu.com/search?content_id=282246046&content_type=Article&match_order=1&q=MiniCPM-V-4.5&zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3OTA5MDI0NTcsInEiOiJNaW5pQ1BNLVYtNC41IiwiemhpZGFfc291cmNlIjoiZW50aXR5IiwiY29udGVudF9pZCI6MjgyMjQ2MDQ2LCJjb250ZW50X3R5cGUiOiJBcnRpY2xlIiwibWF0Y2hfb3JkZXIiOjEsInpkX3Rva2VuIjpudWxsfQ.jYPdfnMfMfiPpVWR9mDu6U1r2hKSdDS-k6IiBhzt4Ps&zhida_source=entity)(8B 多模态:[Qwen3-8B](https://zhida.zhihu.com/search?content_id=282246046&content_type=Article&match_order=1&q=Qwen3-8B&zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3OTA5MDI0NTcsInEiOiJRd2VuMy04QiIsInpoaWRhX3NvdXJjZSI6ImVudGl0eSIsImNvbnRlbnRfaWQiOjI4MjI0NjA0NiwiY29udGVudF90eXBlIjoiQXJ0aWNsZSIsIm1hdGNoX29yZGVyIjoxLCJ6ZF90b2tlbiI6bnVsbH0.kg9wW9VWQnPYOSR3zpV3vWO-_12v5Wn6eARfdrK20pw&zhida_source=entity) 语言端 + SigLIP2-980 视觉端) | | 入口 | http://<内网IP>:8000(反向代理,8 后端 8002–8009) | | 协议 | OpenAI Chat Completions 兼容,HTTP 无鉴权(内网) | | 总并发 | 32(8 卡 × 每实例 4 序列) | | 上下文 | 32768 tokens | | 客户端超时建议 | ≥120s(think 长输出 + 排队,单请求可到 30s+) | 「超时 ≥120s」这一条经常被忽略:很多 HTTP 客户端默认 30s 超时,而带推理链的详细描述正常就要跑十几秒,接入方会以为服务超时了,实际是自己掐断了连接。 ## 请求契约:五个容易踩坑的字段 标准请求体: ```text { "model": "MiniCPM-V-4.5", "messages": [{"role": "user", "content": [ {"type": "image_url", "image_url": {"url": "data:image/jpeg;base64,/9j/4AAQ..."}}, {"type": "text", "text": "请详细描述这张图片的内容。"} ]}], "max_tokens": 1024, "temperature": 0.3, "stream": false, "user": "ORD-12345", "chat_template_kwargs": {"enable_thinking": false} } ``` 逐字段标注契约和踩坑后果: **①** **`model`** **必须精确匹配** **`"MiniCPM-V-4.5"`**(含大小写、连字符)。写错模型名是最快的 400。 **② 图片用 data URI 内嵌**。content 数组里 `image_url` 的 url 字段推荐 `data:image/jpeg;base64,...` 形式——自包含、无外部依赖。http(s) URL 形式协议上也接受,但**我们的 DCU 服务器是离线内网,拉不到外网图片**,传 URL 等于必败。MIME 按实际图片类型写。单请求最多 9 张图(`limit-mm-per-prompt` 配置)。 **③** **`user`** **必须是字符串**。这是接入期最大的一个坑:业务方把数字 ID 当整数传,vLLM 的 [Pydantic](https://zhida.zhihu.com/search?content_id=282246046&content_type=Article&match_order=1&q=Pydantic&zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3OTA5MDI0NTcsInEiOiJQeWRhbnRpYyIsInpoaWRhX3NvdXJjZSI6ImVudGl0eSIsImNvbnRlbnRfaWQiOjI4MjI0NjA0NiwiY29udGVudF90eXBlIjoiQXJ0aWNsZSIsIm1hdGNoX29yZGVyIjoxLCJ6ZF90b2tlbiI6bnVsbH0.PKYMXt42aG6-nKH_FYgTirRYSK_9dB4WCO7evbA31tE&zhida_source=entity) 校验直接 400——而且如果调用方吞错,会变成「服务方自测全绿、业务方全军覆没」的双盲事故。正确姿势:`"user": str(orid)`。这个字段同时是**追踪锚点**:反向代理把它记进结果日志,任何「我的某张图怎么了」的问题都能拿它 grep。 **④** **`enable_thinking`** **放在** **`chat_template_kwargs`** **里,放顶层无效**。模型默认带 `` 推理链(Qwen3 底座特性),关它的唯一正确姿势: ```text "chat_template_kwargs": {"enable_thinking": false} ``` 放顶层不会报错——这是最阴险的地方:静默不生效,你以为关了,实际每个请求还在付 think 的 token 成本。 **⑤** **`max_tokens`** **建议 ≥512**。think 平均吃 300–400 token,加上正文,384 的预算很容易顶到 `finish_reason=length` 被截断。要留余量给 1024;如果关了 think,384 足够。 `temperature` 建议显式传 0.2–0.4——模型默认 0.6(偏发散),识图类任务压低更稳。 ## think 推理链:三方案权衡 默认响应的 `content` 长这样: ```text 图中有一位男性,坐在桌旁,需要判断…\n\n图片显示一位年长的男性坐在白色的桌子旁… ``` 三种处理姿势,按场景选: | 方案 | 做法 | 适用 | | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ | | 服务端关 | 请求加 chat\_template\_kwargs.enable\_thinking=false | 批量打标、要吞吐(token 少 3–4 倍) | | 客户端剥 | 拿到 content 后[正则](https://zhida.zhihu.com/search?content_id=282246046&content_type=Article&match_order=1&q=%E6%AD%A3%E5%88%99&zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3OTA5MDI0NTcsInEiOiLmraPliJkiLCJ6aGlkYV9zb3VyY2UiOiJlbnRpdHkiLCJjb250ZW50X2lkIjoyODIyNDYwNDYsImNvbnRlbnRfdHlwZSI6IkFydGljbGUiLCJtYXRjaF9vcmRlciI6MSwiemRfdG9rZW4iOm51bGx9.FgfS26nD7wfzlmKx0F5aBEXk2PzArsGtuG-QmK8bSTI&zhida_source=entity)剥 ... | 要保留思考过程的选项 | | 原样保留 | 不处理 | 复杂图表分析,思考过程本身有价值 | `finish_reason` 的语义也随之重要:`stop` 是正常收尾;`length` 是顶到 `max_tokens` 被截——**调用方必须检查这个字段**,被截断的描述直接入库就是[脏数据](https://zhida.zhihu.com/search?content_id=282246046&content_type=Article&match_order=1&q=%E8%84%8F%E6%95%B0%E6%8D%AE&zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3OTA5MDI0NTcsInEiOiLohI_mlbDmja4iLCJ6aGlkYV9zb3VyY2UiOiJlbnRpdHkiLCJjb250ZW50X2lkIjoyODIyNDYwNDYsImNvbnRlbnRfdHlwZSI6IkFydGljbGUiLCJtYXRjaF9vcmRlciI6MSwiemRfdG9rZW4iOm51bGx9.MGR43SbosCTCBwdlSq8moLsU2hgt1_jq_bfKxiWpd1Q&zhida_source=entity)。看到大量 `length`,要么调大 `max_tokens`,要么关 think。 ## 流式:[SSE](https://zhida.zhihu.com/search?content_id=282246046&content_type=Article&match_order=1&q=SSE&zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3OTA5MDI0NTcsInEiOiJTU0UiLCJ6aGlkYV9zb3VyY2UiOiJlbnRpdHkiLCJjb250ZW50X2lkIjoyODIyNDYwNDYsImNvbnRlbnRfdHlwZSI6IkFydGljbGUiLCJtYXRjaF9vcmRlciI6MSwiemRfdG9rZW4iOm51bGx9.WU0XB0qhKOHNPTu8QC12B6Ew-etwtGoEHekjubfLwas&zhida_source=entity) 三行约定 `stream: true` 时返回 `text/event-stream`,约定极简: ```text data: {"choices":[{"delta":{"content":""}}]} data: {"choices":[{"delta":{},"finish_reason":"stop"}],"usage":{...}} data: [DONE] ``` 逐行读 `data:` 前缀的行、解析 JSON 取 `choices[0].delta.content`、遇到 `[DONE]` 收工。注意 usage 只在最后一帧带,要统计 token 就得攒着。 ## 错误码:每一条都对应集群里的具体环节 这张表的价值在于:**错误码不是[抽象协议](https://zhida.zhihu.com/search?content_id=282246046&content_type=Article&match_order=1&q=%E6%8A%BD%E8%B1%A1%E5%8D%8F%E8%AE%AE&zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3OTA5MDI0NTcsInEiOiLmir3osaHljY_orq4iLCJ6aGlkYV9zb3VyY2UiOiJlbnRpdHkiLCJjb250ZW50X2lkIjoyODIyNDYwNDYsImNvbnRlbnRfdHlwZSI6IkFydGljbGUiLCJtYXRjaF9vcmRlciI6MSwiemRfdG9rZW4iOm51bGx9.2FltKifBN4b2z3_wDgmzLR7Osgab41BGABV_q-XL_O4&zhida_source=entity),而是[架构图](https://zhida.zhihu.com/search?content_id=282246046&content_type=Article&match_order=1&q=%E6%9E%B6%E6%9E%84%E5%9B%BE&zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3OTA5MDI0NTcsInEiOiLmnrbmnoTlm74iLCJ6aGlkYV9zb3VyY2UiOiJlbnRpdHkiLCJjb250ZW50X2lkIjoyODIyNDYwNDYsImNvbnRlbnRfdHlwZSI6IkFydGljbGUiLCJtYXRjaF9vcmRlciI6MSwiemRfdG9rZW4iOm51bGx9.HH81TOyqVIn63PBVzkHpWFfBZIWPhIUdGo9xQumdR0Y&zhida_source=entity)的投影**—— | HTTP | 含义 | 真实原因 | 调用方动作 | | ---- | ------------ | ---------------------------------------------- | ------------------------------ | | 400 | 请求格式错 | user 整数 / 模型名错 / 缺 messages / base64 坏 | 看返回体 error.message,别重试 | | 503 | 无健康后端 | 集群重启中 / 全崩 | 稍后重试,查 /\_\_lb\_status | | 504 | 推理超时 | 单请求超 300s(代理超时阈值) | 调小 max\_tokens / 关 think | | 502 | 后端连接异常 | 某实例瞬时崩溃 | 可安全重试(代理已摘除它) | 关键设计:502 之后**重试是安全的**,因为代理在连接失败的那一刻已把该后端摘除,重试必然落到别的卡。400 重试是无意义的——确定性错误重试一万次还是 400。把「可重试」和「不可重试」的错误码在契约里明确分开,能省掉调用方大量无谓重试。 ## 标准客户端:契约的最优载体 文档会被跳读,代码不会。我们把全部契约编译进了一个标准客户端(一两百行 Python,requests 即可运行),所有调用方强制复用: ```text from client_45 import recognize, recognize_stream, check_service, strip_thinking # 探活(比 /v1/models 更真实:返回几个后端健康) ok, hp, ids = check_service() # True, "8/8" # 非流式识图 r = recognize("test.jpg", prompt="图里有几个人?", orid="ORD-123", max_tokens=1024) print(r["answer"]) # 已剥 think 的最终答案 print(r["finish_reason"], r["backend"], r["completion_tokens"]) # 关 think 批量跑(快 3–4 倍) r = recognize("test.jpg", orid="ORD-124", enable_thinking=False) # 流式 for piece in recognize_stream("test.jpg", orid="ORD-125"): print(piece, end="", flush=True) ``` 它内部封装的每一项都对应一个真实踩过的坑:`user` 强制 `str(orid)`、图片 MIME 按文件头推断(不用猜扩展名)、think 剥离、**极端长宽比图自动 padding**(超 3:1 的横条图补白边,防退化)、错误结构化解析。接入方用它的第一天起就是对的——**接口契约的正确位置不是文档,是唯一的标准实现**。 ## 排错三件套:一分钟定位「我的请求怎么了」 接入后不可避免会遇到「这张图失败了」。标准排错路径三步: 1. **[响应头](https://zhida.zhihu.com/search?content_id=282246046&content_type=Article&match_order=1&q=%E5%93%8D%E5%BA%94%E5%A4%B4&zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3OTA5MDI0NTcsInEiOiLlk43lupTlpLQiLCJ6aGlkYV9zb3VyY2UiOiJlbnRpdHkiLCJjb250ZW50X2lkIjoyODIyNDYwNDYsImNvbnRlbnRfdHlwZSI6IkFydGljbGUiLCJtYXRjaF9vcmRlciI6MSwiemRfdG9rZW4iOm51bGx9.em5Nzgmp3zS53Wwakzu0AuAzwL5b9v8kk2DjrrX-KNc&zhida_source=entity)`X-Backend`**:告诉你命中了哪张卡(`127.0.0.1:8005` = 5 号实例)。同一张图反复错且后端固定 → 到那台实例日志里查; 2. **`GET /__lb_status`**:集群全景——`{"healthy": 8, "total": 8, "backends": [...]}`,`active` 是各卡在途请求数。请求失败先看这里:healthy 掉了就是实例死了,active 全满就是过载排队; 3. **代理结果日志按 orid grep**:每个请求一行,含状态码、finish\_reason、token 数、错误正文。拿着 orid 来问的,都能一条 grep 回答。 这三件套是我们「服务可运维」的底线配置——没有它们,「偶发失败」永远是玄学;有了它们,每个失败都是可归因的事件。 ## 小结 接入期的摩擦,八成来自契约里没写清的隐性约定:`user` 的类型、think 的开关位置、超时的量级、错误码的重试语义。在[海光 DCU](https://zhida.zhihu.com/search?content_id=282246046&content_type=Article&match_order=3&q=%E6%B5%B7%E5%85%89+DCU&zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3OTA5MDI0NTcsInEiOiLmtbflhYkgRENVIiwiemhpZGFfc291cmNlIjoiZW50aXR5IiwiY29udGVudF9pZCI6MjgyMjQ2MDQ2LCJjb250ZW50X3R5cGUiOiJBcnRpY2xlIiwibWF0Y2hfb3JkZXIiOjMsInpkX3Rva2VuIjpudWxsfQ.F5lciRHJqiSOdAvbpbgg6hlZJSe19TG7OavsvED3Vag&zhida_source=entity) 上做服务的经验是:**契约要按「调用方会犯的错」来写,而不是按「协议允许什么」来写**——每一个「必须」后面跟上「否则会发生什么」,再把全部约定编译进标准客户端。服务方多花一天做这个,接入期就少吵一周的架。 --- *环境信息:海光 K100\_AI × 8 / MiniCPM-V-4.5(8B 多模态)/ vLLM 0.11 DCU 定制版 + torch 2.5.1 + transformers 4.57.6 / 8 实例 + aiohttp 反代 :8000 / OpenAI 兼容协议。*
DCU识别失败
K100-ai
全部评论(0)
只看楼主
暂无数据
--
楼主
Level.2
0 帖子
楼主热帖
暂无数据