装闭 RenoPit 源码解析(08):多模态AI调用、重试与文本降级

Prompt 构建完成后,RenoPit 还要面对不同模型、不同兼容接口以及图片能力差异。开源仓库 fthux/RenoPit 将这些差异集中在 llm_service.py,业务层只调用 analyze_design() 等统一函数。

一、统一创建 OpenAI 兼容客户端

_get_openai_client() 使用三个环境配置创建 AsyncOpenAI:API Key、Base URL 和 180 秒请求超时。只要服务实现 OpenAI Chat Completions 兼容协议,RenoPit 就可以通过同一客户端调用。

设计分析、合同分析、增项预测和交叉核查最终都进入两个底层函数:

  • _call_llm():发送图片和文本;
  • _call_llm_text():只发送系统 Prompt 与用户文本。

这种划分让多模态能力判断集中在设计分析路径,文档类任务始终使用纯文本调用。

二、图片如何进入多模态消息

_call_llm() 遍历 Base64 图片,识别可能存在的 Data URL 前缀,默认 MIME 类型为 image/jpeg,然后构造 OpenAI 的 image_url 内容:

python 复制代码
content_parts.append({
    "type": "image_url",
    "image_url": {
        "url": f"data:{mime_type};base64,{img_data}",
        "detail": "high",
    },
})

全部图片之后再追加一个 text 内容块。最终请求包含一条 system 消息和一条 user 消息,其中 user 的 content 是图片块与文本块组成的数组。

模型名称完全来自 LLM_MODEL_NAME,最大输出设置为 65536 Token,温度为 0.3,让结构化结果保持相对稳定。

三、联网工具只对特定端点启用

ENABLE_WEB_SEARCH 为真时,代码还会检查 Base URL 是否属于 OpenAI API。只有满足条件才附加 web_search_preview 工具。

Gemini、DeepSeek、Ollama 等兼容端点不会收到该参数,因为它们可能能处理 Chat Completions,却不支持同样的工具定义。此时联网要求仍保留在系统 Prompt 中,API 请求本身保持普通聊天格式。

四、重试封装如何工作

多模态和纯文本各有一个指数退避封装。当前常量为:

python 复制代码
MAX_RETRIES = 2
BASE_DELAY = 2
TIMEOUT_SECONDS = 180

每次调用都由 asyncio.wait_for() 限制在 180 秒内。空字符串也会被视为失败。第一次失败后等待两秒再进行第二次尝试;两次都失败时抛出带模型名称和最后错误信息的 RuntimeError

多模态重试对 ModelNotMultimodalError 做了特殊处理:这种错误不会继续重复请求,而是立即交给上层决定是否走文本降级。

五、如何识别模型不支持图片

部分纯文本模型收到 image_url 后会返回包含 unknown variant image_url 的 400 错误。_call_llm() 捕获异常并把它转换成 ModelNotMultimodalError

analyze_design() 先判断是否有图片和文本,再决定执行路线:
flowchart TD Aanalyze_design --> B{是否有图片} B -->|是| C多模态调用 C -->|成功| D返回 LLM 文本 C -->|不支持图片| E{是否还有文本} C -->|其他调用失败| E B -->|否| E E -->|有| F纯文本调用 E -->|无| G抛出 RuntimeError F --> D

如果用户同时上传了图片和合同,即使当前模型不支持视觉输入,系统仍能使用提取出的合同文本和用户说明完成分析。只有"存在图片、模型不支持图片、又没有任何文本"时才无法降级。

六、同步 Celery 任务如何调用异步函数

LLM 服务全部使用 async def,但 Celery 的 run_analysis_task 和分析引擎是同步函数。run_analysis_sync() 通常通过 asyncio.run() 执行 analyze_design();如果当前线程已经存在运行中的事件循环,则使用线程池创建新的异步执行环境。

文档分析、增项预测和交叉核查也采用同样的同步包装。因此异步网络客户端被限制在服务层,Celery 编排代码仍然保持同步调用形式。

七、LLM 服务调用链小结

RenoPit 的 LLM 层完成四件事:根据配置创建兼容客户端,把图片和文本组装成请求,为超时及空响应提供重试,并在模型缺少视觉能力时切换到纯文本。上层分析引擎拿到的始终是一段待解析的模型输出文本。

相关实现可以在 fthux/RenoPitllm_service.py 中逐行对照。下一篇将分析这段原始文本如何经过 AnalysisEngine、JSON 修复和数据库事务,最终成为可查询的结构化装修闭坑结果。

相关推荐
用户69371750013842 小时前
深夜炸场!DeepSeek 没发新模型,却重构了整个 Agent 生态
前端·后端·github
Raas1002 小时前
MAIGateway,魔芋企业级AI网关的安全基建化设计
大数据·人工智能·网关·网络安全·api网关·mai gateway·魔芋
冬奇Lab2 小时前
开源项目第186期:Open Ontologies — Rust 实现的 AI 原生本体工程 MCP 服务器
人工智能·开源·资讯
tachibana22 小时前
文件上传分布式限流如何做?
人工智能·ai·大模型·llm·prompt
武子康2 小时前
实现 GPT-Live-like:两条路线、一个控制面和六阶段验收
人工智能·chatgpt·agent
郑州光合科技余经理2 小时前
餐饮预定系统架构拆解:订单链路、权限组织与私有化源码交付
java·开发语言·前端·数据库·人工智能·系统架构·php
Wang's Blog2 小时前
AI Agent白手起家71: LangGraph 云平台与本地开发实战
人工智能
vipjx12 小时前
2026最新实测:PanDownload复活版百度网盘解析工具,突破限速跑满带宽
开源
九硕智慧建筑一体化厂家2 小时前
打破系统孤岛!IBMS 一体化平台,构建智慧楼宇全域管控体系
运维·网络·人工智能·笔记·智慧城市