装闭 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 修复和数据库事务,最终成为可查询的结构化装修闭坑结果。

相关推荐
回眸&啤酒鸭1 天前
【回眸】Minicart 电商购物车核心功能落地指南
人工智能
一隅论数智1 天前
给AI一张“业务概念地图“:本体如何从哲学走向企业智能
大数据·人工智能·经验分享·笔记·学习·学习方法·政务
不悔哥1 天前
开源OV-Watch:怎么做一个智能手表
单片机·开源·嵌入式
AI的探索之旅1 天前
97 个 OpenCV 实例(三十):双目立体,从标定到点云
人工智能·opencv·计算机视觉
AlbertZein1 天前
Step-5-Preview 上手实测:3D 游戏、金融分析、网页设计一次跑完
人工智能·aigc
家和801 天前
Carla仿真系列:4_在 Carla 的 Tesla 上装 4 路摄像头,并创建网格为环视拼接做准备
开源
LaughingZhu1 天前
Product Hunt 每日热榜 | 2026-09-19
人工智能·深度学习·神经网络·搜索引擎·百度
美狐美颜SDK开放平台1 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
wukangjupingbb1 天前
智能网联汽车安全能力框架
人工智能