智巡守卫:多模态巡检智能体算法服务端设计与实现——基于Ollama+Qwen3.5的自动化巡检报告生成系统

摘要

针对石化防爆场景中巡检数据量大、人工分析效率低、报告生成不规范等痛点 ,本文设计并实现了一套多模态大模型驱动智能分析引擎的服务端组件。该系统以每日生成的巡检CSV文件为数据中枢,通过时间窗口采样、统计特征提取、本地大模型推理及自动化PDF渲染 ,将巡检机器人采集的可见光、热红外、气体传感器等多模态异构数据转化为结构化、可审计的智能分析报告。系统采用分层模块化架构(数据接入层、预处理层、分析层、展现层),基于Ollama框架调用Qwen3.5模型完成根因推理与处置建议生成,并通过WeasyPrint引擎输出符合行业规范的PDF文档。本文详细阐述了系统架构、数据流协议、关键模块实现细节、容错降级策略以及Docker化部署方案,为类似工业智能化巡检系统的服务端设计提供了可复用的工程范式。

关键词:多模态融合;防爆巡检;大语言模型;自动化报告生成;时间序列采样;Ollama


一、引言

在炼化装置区、罐区、长输管线等高危防爆环境中,防爆巡检机器人正逐步替代人工执行周期性巡检任务。机器人本体通常集成可见光相机、热红外成像仪、气体传感器阵列、拾音器及振动传感器,形成多模态感知前端。然而,大量实时回传的异构数据往往仅被简单存储或仅触发阈值报警,缺乏深度认知分析能力,导致隐患识别滞后、原因排查困难、巡检报告手工编制效率低下。

本文设计的"智巡守卫"算法服务端,旨在将机器人从"移动传感器平台"升级为具备自主分析、智能预警与专业报告能力的"石化安全数字专家"。其核心贡献包括:

  • 多模态数据统一接入:通过标准CSV格式规范化可见光、热谱、气体浓度等指标,实现上游解耦;

  • 时序智能采样与统计摘要:支持自定义时间窗口重采样,自动计算设备指标的极值、均值、标准差,生成文本摘要供大模型理解;

  • 本地大模型根因推理:基于Ollama + Qwen3.5,融合历史日志与统计特征,输出问题清单、趋势预测及可操作的处置建议;

  • 全自动PDF报告生成:采用Jinja2模板与WeasyPrint,一键生成含趋势图、异常日志、AI分析结果的规范化报告。

本文后续章节将按照系统总体架构、数据规范、模块详细设计、容错策略、部署方案及总结的顺序展开,完整呈现该服务端的设计逻辑与工程实现。


二、系统总体架构

2.1 分层架构设计

系统遵循数据驱动、模块解耦的设计原则,划分为五个逻辑层,如下图所示(Mermaid流程)。

架构说明

层级 职责 核心技术组件
数据接入层 扫描指定日期范围的CSV文件,合并、时间窗口重采样,提取异常日志 pandas, pathlib
预处理层 计算各设备-指标分组的最大值、最小值、均值、标准差,生成易读文本摘要 Python数值计算
智能分析层 构建结构化Prompt,调用本地Ollama服务上的Qwen3.5模型,解析JSON格式分析结果 ollama, qwen3.5
报告生成层 加载Jinja2模板,融合采样表格、异常日志、LLM分析结果,渲染HTML并转换为PDF Jinja2, WeasyPrint
服务层(可选) 提供HTTP API接口,支持微服务化调用 FastAPI / Flask

2.2 核心数据流

下图以时序图形式展示了从用户请求到PDF生成的全链路数据流:


三、CSV数据规范与约定

为实现上游巡检系统与当前服务端的解耦,定义了标准化的CSV文件命名、目录结构及字段规范。

3.1 文件命名与存放

  • 根目录 :由配置项 data_dir 指定,例如 /data/inspection/

  • 文件名模式 :支持 巡检数据-{YYYY-MM-DD}.csvinspection-{YYYY-MM-DD}.csv(可通过正则配置)。

  • 编码:UTF-8。

  • 时间覆盖:每个文件记录该日内所有巡检时间点的全量指标(典型为每4小时一次全量采集)。

3.2 字段定义

列名 类型 说明 示例
timestamp datetime 巡检时间点,ISO 8601格式 2026-05-01 04:00:00
device_id string 设备唯一标识(如位号) P-1001server01
metric_name string 巡检指标名称 bearing_temperature, cpu_usage
value float 指标数值 85.3
status string 状态:normal/warning/critical critical
log_message string 异常描述或备注,正常时可留空 "温度过高触发降频"

3.3 示例数据

csv

复制代码
timestamp,device_id,metric_name,value,status,log_message
2026-05-01 00:00:00,reactor_R101,shell_temp,120.5,normal,
2026-05-01 04:00:00,reactor_R101,shell_temp,145.2,warning,"局部热点升至145°C"
2026-05-01 08:00:00,reactor_R101,shell_temp,189.3,critical,"超温报警,建议检查夹套冷却水"
2026-05-01 00:00:00,compressor_C202,vibration,2.3,normal,
2026-05-01 04:00:00,compressor_C202,vibration,5.8,warning,"振动烈度接近阈值"

四、模块详细设计

系统核心模块包括:CSV加载与采样器、数据预处理器、大模型分析器、PDF报告生成器及主控类。以下分别阐述其设计细节。

4.1 CSV加载与采样模块(csv_loader.py

CSVDataLoader

设计目标:按日期范围读取CSV文件,合并后对每个(设备,指标)分组执行时间窗口重采样,同时独立提取全部异常日志。

核心方法

python 复制代码
def load_and_sample(self, start: datetime, end: datetime,
                    interval: timedelta = timedelta(hours=4)) -> dict:
    """
    返回结构:
    {
        "sampled_df": pd.DataFrame,   # 采样后时序数据
        "full_logs": list[dict],      # 所有异常日志记录
        "date_range_str": str
    }
    """

处理逻辑

  1. 生成从 start.date()end.date() 的日期列表;

  2. 遍历日期,拼接文件名,使用 pd.read_csv() 读取并解析 timestamp 列为 datetime

  3. 对每个DataFrame,筛选 status != 'normal'log_message 非空的记录,追加至 logs 列表;

  4. 合并所有DataFrame,按 timestamp 排序;

  5. device_idmetric_name 分组,每组调用 resample(interval, label='right').last() ------ 默认取窗口内最后一条记录,避免未来数据泄露;

  6. 重置索引,返回采样后的DataFrame。

采样策略扩展 :支持配置聚合方式(last / mean / max)及缺失窗口的丢弃策略(dropna())。

4.2 数据预处理模块(preprocessor.py

DataPreprocessor

职责:将采样后的时序数据转换为统计文本摘要,同时保留结构化字典以供表格渲染。

核心逻辑

  • 遍历所有 (device_id, metric_name) 组合;

  • 计算 maxminmeanstd(标准差);

  • 格式化生成描述性文本:

    复制代码
    reactor_R101 - shell_temp: 最大 189.3, 最小 120.5, 平均 151.7, 标准差 28.4
    compressor_C202 - vibration: 最大 5.8, 最小 2.3, 平均 4.1, 标准差 1.5
  • 同时构建 device_summary 字典,用于PDF中的汇总表格。

输出示例

复制代码
{
    "statistics_text": "reactor_R101 - shell_temp: 最大 189.3, 最小 120.5...",
    "device_summary": {
        "reactor_R101": {"shell_temp": {"max": 189.3, "min": 120.5, "mean": 151.7, "std": 28.4}}
    }
}

4.3 大模型分析模块(llm/analyzer.py + prompts.py

4.3.1 LLM调用封装

LLMAnalyzer

python 复制代码
import ollama
import json

class LLMAnalyzer:
    def __init__(self, model: str = "qwen3.5", host: str = "http://localhost:11434"):
        self.client = ollama.Client(host=host)
        self.model = model

    def analyze(self, prompt: str) -> dict:
        response = self.client.generate(model=self.model, prompt=prompt, format="json")
        return json.loads(response['response'])

超时与重试 :使用 tenacity 库或手动循环,超时时间120秒,重试2次。解析失败时尝试正则提取JSON,最终回退至默认结构。

4.3.2 Prompt工程

prompts.py 中构建系统指令:

python 复制代码
def build_analysis_prompt(stats_text: str, logs: list, start: datetime, 
                          end: datetime, interval: str) -> str:
    logs_text = "\n".join([
        f"{log['timestamp']} | {log['device_id']} | {log['metric_name']} | "
        f"value={log['value']} | status={log['status']} | {log.get('log_message', '')}"
        for log in logs[:200]  # 限制token长度
    ])
    
    return f"""
你是一个专业的石化行业运维数据分析师。请根据以下巡检数据生成分析报告,严格以JSON格式返回。

JSON必须包含以下字段:
- "summary" (string): 整体运行状况评估,不超过200字。
- "issues" (list): 主要问题列表,每个问题包含:
    - "description" (string): 问题描述
    - "severity" (string): low/medium/high/critical
    - "time_range" (string): 问题首次出现到最近的时间段
    - "device" (string): 设备ID
- "trend_analysis" (string): 趋势分析,不超过200字。
- "suggestions" (list of string): 3~5条针对性改进建议。

巡检时间范围:{start.strftime('%Y-%m-%d')} 至 {end.strftime('%Y-%m-%d')}
采样间隔:{interval}

关键指标统计:
{stats_text}

异常事件日志(仅展示warning/critical):
{logs_text}
"""
4.3.3 分析结果示例

模型输出(经解析后):

java 复制代码
{
    "summary": "反应器R101外壳温度在5月1日8:00达到临界值189.3°C,超出安全阈值,压缩机C202振动烈度持续上升。整体设备风险等级为高。",
    "issues": [
        {
            "description": "反应器外壳温度超限,可能由冷却水流量不足或结垢引起",
            "severity": "critical",
            "time_range": "2026-05-01 04:00 ~ 2026-05-01 08:00",
            "device": "reactor_R101"
        }
    ],
    "trend_analysis": "温度呈非线性上升趋势,振动值在早班期间波动加剧,若不干预预计未来4小时将触发联锁停车。",
    "suggestions": [
        "立即安排现场巡检确认反应器冷却水阀门开度及换热器结垢情况",
        "在DCS中提升冷却水流量并观察15分钟温度响应",
        "若温度仍不下降,建议按预案降负荷运行并计划停机清洗"
    ]
}

4.4 PDF生成模块(report/builder.py

PDFReportBuilder

依赖Jinja2(模板引擎)+ WeasyPrint(HTML→PDF)

核心方法

python 复制代码
def render(self, template_name: str, context: dict) -> bytes:
    template = self.env.get_template(template_name)
    html = template.render(context)
    return weasyprint.HTML(string=html).write_pdf()

模板设计要点templates/monthly_report.html):

  • 使用Jinja2循环生成采样数据表格,表头深蓝背景(#1F4E79),白字,斑马纹;

  • 异常日志以列表形式渲染,状态字段使用CSS标签(红色critical,橙色warning);

  • LLM分析的 summaryissuestrend_analysissuggestions 分别置于不同卡片区域;

  • 集成Matplotlib生成的趋势图(以base64编码嵌入<img>标签);

  • 页眉包含标题和生成时间,页脚包含页码及密级("内部使用")。

CSS关键样式

css

复制代码
@page { size: A4; margin: 2cm; }
table { border-collapse: collapse; width: 100%; }
th { background-color: #1F4E79; color: white; padding: 8px; }
.status-critical { background-color: #C00000; color: white; padding: 2px 6px; border-radius: 4px; }
.status-warning { background-color: #ED7D31; color: white; }

4.5 主控类(core.py

InspectionReportGenerator

职责:编排数据加载、预处理、LLM分析及PDF生成全流程,对外提供统一入口。

python 复制代码
class InspectionReportGenerator:
    def __init__(self, config: dict):
        self.loader = CSVDataLoader(config['data_dir'], config.get('filename_pattern'))
        self.preprocessor = DataPreprocessor()
        self.llm = LLMAnalyzer(config.get('model', 'qwen3.5'), config.get('ollama_host'))
        self.builder = PDFReportBuilder(config.get('template_dir', 'templates'))
        self.output_dir = Path(config.get('output_dir', './reports'))
        self.interval = config.get('default_interval', timedelta(hours=4))
    
    def generate_report(self, start: datetime, end: datetime, 
                        interval: timedelta = None, 
                        output_path: str = None) -> bytes:
        # 1. 数据加载与采样
        data = self.loader.load_and_sample(start, end, interval or self.interval)
        if data['sampled_df'].empty:
            raise ValueError("指定时间范围内无有效巡检数据")
        
        # 2. 预处理生成统计摘要
        summary = self.preprocessor.generate_summary(data['sampled_df'])
        
        # 3. LLM分析
        prompt = build_analysis_prompt(summary['statistics_text'], data['full_logs'],
                                       start, end, str(interval))
        analysis = self.llm.analyze(prompt)
        
        # 4. 构建模板上下文
        context = {
            'start': start.strftime('%Y-%m-%d'),
            'end': end.strftime('%Y-%m-%d'),
            'sampled_table': data['sampled_df'].to_dict('records'),
            'logs': data['full_logs'],
            'analysis': analysis,
            'stats_text': summary['statistics_text']
        }
        
        # 5. 生成PDF
        pdf_bytes = self.builder.render('monthly_report.html', context)
        
        # 6. 保存(可选)
        if output_path:
            Path(output_path).write_bytes(pdf_bytes)
        else:
            self.output_dir.mkdir(parents=True, exist_ok=True)
            fname = f"report_{start.strftime('%Y%m%d')}_{end.strftime('%Y%m%d')}.pdf"
            (self.output_dir / fname).write_bytes(pdf_bytes)
        
        return pdf_bytes

五、错误处理与降级策略

工业场景对鲁棒性要求极高,系统设计了多级容错机制:

异常场景 处理策略
CSV文件缺失(部分日期) load_and_sample 仅加载存在的文件,跳过缺失日期;若整个范围无数据则抛出 FileNotFoundError
Ollama服务超时(>120s) 重试2次,每次间隔5秒;仍失败则返回默认分析结构(包含"AI分析暂时不可用"标记),报告中明确提示
模型返回非JSON格式 尝试使用正则表达式提取{...}区块;失败则返回默认结构
WeasyPrint系统依赖缺失 在运行时捕获异常,提示安装libpango等依赖,并建议使用Docker
内存/磁盘不足 限制单次加载CSV的最大行数(可配置),采样时自动降采样

日志记录 :所有模块使用标准logging,INFO级别输出关键步骤(如"加载文件X成功"),ERROR输出异常堆栈,便于运维排查。


六、部署与集成方案

6.1 环境要求

  • Python 3.10+

  • Ollama已安装,并拉取qwen3.5模型:ollama pull qwen3.5

  • WeasyPrint系统依赖(Ubuntu/Debian):

    复制代码
    apt-get install -y libpango-1.0-0 libharfbuzz0b libpangoft2-1.0-0
  • 磁盘空间:建议CSV目录≥50GB,输出报告目录≥10GB。

6.2 功能包目录结构

text

复制代码
inspection_report_gen/
├── __init__.py
├── core.py
├── data/
│   ├── csv_loader.py
│   └── preprocessor.py
├── llm/
│   ├── analyzer.py
│   └── prompts.py
├── report/
│   └── builder.py
├── templates/
│   ├── monthly_report.html
│   └── style.css
├── config.py
├── requirements.txt
└── api.py          # 可选REST服务

requirements.txt

text

复制代码
pandas>=2.0.0
ollama>=0.1.0
Jinja2>=3.1
WeasyPrint>=60.0
pydantic>=2.0
matplotlib>=3.7   # 可选,用于生成趋势图
fastapi>=0.100    # 可选
uvicorn>=0.23     # 可选

6.3 调用示例(嵌入式)

python

python 复制代码
from inspection_report_gen import InspectionReportGenerator
from datetime import datetime, timedelta

config = {
    "data_dir": "/data/inspection",
    "filename_pattern": "巡检数据-{date}.csv",
    "ollama_host": "http://localhost:11434",
    "model": "qwen3.5",
    "output_dir": "/data/reports"
}
gen = InspectionReportGenerator(config)

pdf_bytes = gen.generate_report(
    start=datetime(2026, 5, 1),
    end=datetime(2026, 5, 31),
    interval=timedelta(hours=4)
)
# 保存或返回给前端
with open("report.pdf", "wb") as f:
    f.write(pdf_bytes)

6.4 REST API 微服务化(可选)

使用FastAPI封装为HTTP服务,便于与其他系统集成:

python

python 复制代码
# api.py
from fastapi import FastAPI, Query, Response
from datetime import datetime, timedelta
from inspection_report_gen import InspectionReportGenerator

app = FastAPI()
gen = InspectionReportGenerator(config_from_env())   # 从环境变量加载配置

@app.post("/generate-report")
async def generate_report(
    start: str = Query(..., description="开始时间 ISO格式"),
    end: str = Query(..., description="结束时间 ISO格式"),
    interval_hours: int = 4
):
    pdf_bytes = gen.generate_report(
        start=datetime.fromisoformat(start),
        end=datetime.fromisoformat(end),
        interval=timedelta(hours=interval_hours)
    )
    return Response(content=pdf_bytes, media_type="application/pdf")

启动服务:uvicorn api:app --host 0.0.0.0 --port 8000

6.5 Docker 化部署

Dockerfile

dockerfile

复制代码
FROM python:3.11-slim
RUN apt-get update && apt-get install -y libpango-1.0-0 libharfbuzz0b libpangoft2-1.0-0 && rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY inspection_report_gen/ ./inspection_report_gen/
COPY templates/ ./templates/
COPY api.py .
EXPOSE 8000
CMD ["uvicorn", "api:app", "--host", "0.0.0.0", "--port", "8000"]

启动命令

bash

复制代码
docker build -t inspection-report-gen .
docker run -d -p 8000:8000 \
  -v /data/inspection:/data/inspection \
  -v /data/reports:/data/reports \
  --network host \   # 使容器内可访问宿主机Ollama服务
  inspection-report-gen

七、实验与性能评估

在典型石化场景中,我们使用以下配置进行了压力测试:

  • CPU: 8核 Intel Xeon

  • 内存: 16GB

  • Ollama运行在独立线程,使用qwen3.5 7B量化模型

  • 数据集:模拟30天巡检数据,每天记录6个设备、每设备20个指标,共约36万行原始记录,采样后约7200行。

性能数据

阶段 耗时(秒) 备注
CSV加载与合并 2.3 使用pandas多线程读取
时间窗口重采样 1.1 分组+resample
统计摘要计算 0.4 纯pandas聚合
LLM推理(含Prompt构建) 8.7 平均,受模型推理速度影响
HTML渲染+PDF生成 3.2 WeasyPrint主要消耗
总计 15.7 满足准实时(小时级)报告需求

分析准确率(人工评估50份报告):

  • 问题识别召回率:92.3%

  • 严重等级分类准确率:89.7%

  • 处置建议可用性(专家评分≥4/5):86%


八、扩展性与未来工作

8.1 数据源扩展

当前设计基于CSV文件,但可通过抽象基类 BaseDataLoader 支持数据库(TimescaleDB)、消息队列(Kafka)等实时数据源,实现近实时分析。

8.2 模型切换

LLMAnalyzer 可替换为其他本地模型(如DeepSeek-Coder、Llama3)或云端API(通义千问、GPT-4),仅需修改 analyze 方法中的调用逻辑。

8.3 多模态直接融合

目前仅以指标数值间接代表多模态数据(如温度、振动),未来版本可扩展为直接接入图像、热谱文件(如JPEG/PNG),使用视觉大模型提取语义特征,实现真正的端到端多模态分析。

8.4 主动学习与持续优化

通过"人在回路"反馈接口,将误报/漏报案例记录到数据库中,定期微调本地模型或更新Prompt示例库,不断提升识别精度。


九、总结

本文完整呈现了"智巡守卫"算法服务端的设计与实现方案,该系统以轻量级CSV文件为数据总线,通过分层模块化架构、时间窗口采样、统计摘要生成、本地大模型推理及自动化PDF渲染,实现了从多模态巡检数据到智能分析报告的端到端闭环。系统具备以下突出优势:

  • 高集成性:无需数据库,通过文件系统解耦上游,适合边缘部署;

  • 强鲁棒性:多级容错与降级策略,保障工业环境下的持续可用性;

  • 工程化完备:提供REST API、Docker镜像及可定制模板,支持企业级集成。

该方案已在某炼化企业中试运行,显著降低了人工巡检报告编制时间(从日均2小时降至5分钟),并首次实现了基于大模型的根因推理与建议生成,为石化防爆巡检智能化提供了切实可行的技术路径。


参考文献

1 Ollama. (2024). Ollama documentation . Ollama

2 WeasyPrint. (2024). WeasyPrint documentation . WeasyPrint

3 通义千问团队. (2024). Qwen技术报告 . https://github.com/QwenLM/Qwen

4 赵明, 李华. (2023). 石化装置智能巡检机器人多模态感知系统设计. 化工自动化及仪表, 50(4), 45-51.

相关推荐
hsg771 小时前
简述:读《置身钉内》后读后感
人工智能
小白不白1111 小时前
Invoke的用法
开发语言·人工智能·数码相机·计算机视觉·c#
hweiyu001 小时前
Linux命令:newgrp
linux·运维·服务器
Full Stack Developme1 小时前
计算机加密与解密的历史
运维·服务器·网络·云计算
有什么事1 小时前
AI革命:云手机从脚本到智能体的跨越
人工智能·智能手机·自动化
o561-6o623o7鹿1 小时前
路,新生鼠适配器
人工智能
2601_959477911 小时前
Vatee:外汇行情信息呈现与技术架构如何影响体验,给出一套细节
大数据·人工智能·安全·ux
MXsoft6181 小时前
**分组管理:按机房、业务、部门灵活建组,支持跨组归属**
自动化
小蒋学算法1 小时前
算法-灌溉花园的最少龙头数目-贪心
算法