dagent_eval/docs/多模态问答集生成方案.md

20 KiB
Raw Blame History

基于 HTML 知识库的多模态问答集生成方案

知识库特征:

  • 格式Sphinx 生成的 HTML 文档209 个 HTML 文件)
  • 图片1142 张图片PNG/JPG存放在 _images/ 目录
  • 结构:层级目录组织,包含大量配置界面截图、架构图、流程图等

目标: 生成高质量的多模态问答集,充分利用文本和图像信息


一、核心挑战

1.1 当前问题

问题 影响
图像信息丢失 现有 MD 问答集只有文本,配置界面截图、架构图等关键信息缺失
图文关联弱 图片与文本分离,无法生成"如图所示"类问题
问题质量受限 纯文本问题无法覆盖"界面在哪里点击"、"架构图中的模块关系"等场景

1.2 图像类型分析

根据采样分析,知识库中的图像主要分为以下类型:

图像类型 占比估算 示例 问答价值
配置界面截图 ~40% menuconfig 界面、参数配置页面 高价值,可生成操作类问题
架构图/流程图 ~30% 系统架构、数据流向、模块关系 高价值,可生成理解类问题
代码截图 ~15% 代码片段、配置文件示例 中等价值,文本已包含
硬件接口图 ~10% 引脚定义、电路连接 高价值,纯文本难以描述
其他 ~5% Logo、装饰性图片 低价值

二、方案设计

2.1 整体流程

HTML 知识库
    ↓
┌─────────────────────────────────────┐
│  阶段 1HTML → 结构化 Markdown      │
│  - 提取文本内容                      │
│  - 保留图片占位符 [IMAGE: xxx.png]  │
│  - 保留章节层级结构                  │
└─────────────────────────────────────┘
    ↓
┌─────────────────────────────────────┐
│  阶段 2图像分类与描述生成          │
│  - 多模态 LLM 识别图像类型           │
│  - 生成图像描述caption           │
│  - 提取图像中的关键信息              │
└─────────────────────────────────────┘
    ↓
┌─────────────────────────────────────┐
│  阶段 3多模态问答生成              │
│  - 纯文本问题(基于文本内容)        │
│  - 图文结合问题(基于图像+上下文)   │
│  - 图像理解问题(基于图像描述)      │
└─────────────────────────────────────┘
    ↓
┌─────────────────────────────────────┐
│  阶段 4问答集审核与优化            │
│  - 查重(文本 + 图像相似度)         │
│  - 质量评分                          │
│  - 人工审核                          │
└─────────────────────────────────────┘
    ↓
多模态问答集MD + 图像引用)

三、技术实现

3.1 阶段 1HTML → 结构化 Markdown

目标: 将 HTML 转换为保留图像占位符的 Markdown

实现方案:

from bs4 import BeautifulSoup
from pathlib import Path

def html_to_markdown_with_images(html_path: Path, base_path: Path) -> str:
    """
    将 HTML 转换为 Markdown保留图像占位符
    
    返回格式:
    ## 章节标题
    
    文本内容...
    
    ![配置界面](../../_images/image-20220518111319607.png)
    *图Uboot menuconfig 配置界面*
    
    继续文本内容...
    """
    html = html_path.read_text(encoding='utf-8')
    soup = BeautifulSoup(html, 'html.parser')
    
    # 提取主内容区域
    main = soup.find('div', role='main') or soup.find('section')
    
    md_lines = []
    current_section = []
    
    for elem in main.descendants:
        if elem.name in ('h1', 'h2', 'h3', 'h4'):
            # 章节标题
            level = int(elem.name[1])
            title = elem.get_text(strip=True)
            md_lines.append(f"{'#' * level} {title}\n")
        
        elif elem.name == 'p':
            # 段落(可能包含图片)
            if elem.find('img'):
                # 处理图片
                for img in elem.find_all('img'):
                    src = img.get('src', '')
                    alt = img.get('alt', '')
                    # 转换相对路径为绝对路径
                    img_path = (html_path.parent / src).resolve()
                    rel_path = img_path.relative_to(base_path)
                    md_lines.append(f"![{alt}]({rel_path})")
                    # 添加图片说明(从上下文推断)
                    caption = infer_image_caption(elem, img)
                    if caption:
                        md_lines.append(f"*图:{caption}*\n")
            else:
                # 纯文本段落
                text = elem.get_text(strip=True)
                if text:
                    md_lines.append(f"{text}\n")
        
        elif elem.name == 'pre':
            # 代码块
            code = elem.get_text(strip=True)
            md_lines.append(f"```\n{code}\n```\n")
        
        elif elem.name == 'li':
            # 列表项
            text = elem.get_text(strip=True)
            if text:
                md_lines.append(f"- {text}")
    
    return '\n'.join(md_lines)

def infer_image_caption(parent_elem, img_elem) -> str:
    """从图片周围的上下文推断图片说明"""
    # 策略 1查找前一个段落的最后一句
    prev = parent_elem.find_previous_sibling('p')
    if prev:
        text = prev.get_text(strip=True)
        if '如图' in text or '如下图' in text or '界面' in text:
            return text[-50:]  # 取最后 50 字符
    
    # 策略 2使用 alt 属性
    alt = img_elem.get('alt', '')
    if alt and not alt.startswith('image-'):
        return alt
    
    # 策略 3查找后续段落的第一句
    next_elem = parent_elem.find_next_sibling('p')
    if next_elem:
        text = next_elem.get_text(strip=True)
        if text:
            return text[:50]
    
    return ""

输出示例:

## 4.3.2. 配置 Uboot 和 Kernel 选项参数

在嵌入式系统开发中Uboot 和 Kernel 的功能选项配置...

### 使用 xbuild 命令配置

命令执行成功后,系统会启动一个图形化的配置界面。

![menuconfig界面](linux_development/driver_develop_guide/_images/image-20220518111319607.png)
*图Uboot menuconfig 配置界面,可以选择启用或禁用功能*

完成配置后,选择 Exit 退出...

![保存配置](linux_development/driver_develop_guide/_images/image-20220518111506018.png)
*图:保存配置提示界面*

3.2 阶段 2图像分类与描述生成

目标: 使用多模态 LLM 为每张图片生成结构化描述

实现方案:

import base64
from pathlib import Path

async def analyze_image_with_llm(
    image_path: Path,
    context_before: str,
    context_after: str,
    llm_config: dict,
) -> dict:
    """
    使用多模态 LLM 分析图像
    
    返回:
    {
        "type": "config_ui",  # 图像类型
        "description": "Uboot menuconfig 配置界面截图,显示了...",
        "key_elements": ["File System support", "Network support", ...],
        "qa_value": 5,  # 问答价值评分 1-5
        "suggested_questions": [
            "如何进入 Uboot 配置界面?",
            "配置界面中如何保存修改?"
        ]
    }
    """
    # 读取图像并编码为 base64
    img_data = image_path.read_bytes()
    img_b64 = base64.b64encode(img_data).decode()
    
    prompt = f"""你是一个技术文档图像分析专家。请分析以下图像并提供结构化信息。

**图像上下文(前):**
{context_before[-500:]}

**图像上下文(后):**
{context_after[:500]}

请分析图像并返回 JSON 格式:
{{
  "type": "图像类型config_ui/architecture/flowchart/code/hardware/other",
  "description": "详细描述图像内容100-200字",
  "key_elements": ["图像中的关键元素列表"],
  "qa_value": "问答价值评分 1-55=高价值)",
  "suggested_questions": ["基于此图像可以生成的问题示例"]
}}

**图像类型定义:**
- config_ui: 配置界面截图menuconfig、参数设置页面等
- architecture: 架构图、系统框图
- flowchart: 流程图、时序图
- code: 代码截图
- hardware: 硬件接口图、引脚定义
- other: 其他类型

**评分标准:**
- 5分包含关键操作步骤或架构信息必须通过图像才能理解
- 4分补充说明性图像有助于理解但非必需
- 3分代码或配置示例文本已包含但图像更直观
- 2分装饰性图像价值较低
- 1分无实质内容
"""
    
    # 调用多模态 LLMGPT-4V / Claude 3.5 Sonnet
    response = await call_multimodal_llm(
        prompt=prompt,
        image_base64=img_b64,
        config=llm_config,
    )
    
    return json.loads(response)

批量处理策略:

async def batch_analyze_images(
    md_content: str,
    image_paths: list[Path],
    llm_config: dict,
    concurrency: int = 5,
) -> dict[str, dict]:
    """
    批量分析图像,返回 {image_path: analysis_result}
    
    优化策略:
    1. 并发调用concurrency=5
    2. 缓存已分析的图像(基于文件 hash
    3. 低价值图像跳过详细分析(仅记录类型)
    """
    results = {}
    sem = asyncio.Semaphore(concurrency)
    
    async def analyze_one(img_path: Path):
        # 检查缓存
        cache_key = hashlib.md5(img_path.read_bytes()).hexdigest()
        if cache_key in image_cache:
            return image_cache[cache_key]
        
        # 提取上下文
        context_before, context_after = extract_image_context(md_content, img_path)
        
        async with sem:
            result = await analyze_image_with_llm(
                img_path, context_before, context_after, llm_config
            )
            image_cache[cache_key] = result
            return result
    
    tasks = [analyze_one(p) for p in image_paths]
    analyses = await asyncio.gather(*tasks)
    
    return dict(zip(image_paths, analyses))

3.3 阶段 3多模态问答生成

目标: 生成三类问题:纯文本、图文结合、图像理解

3.3.1 纯文本问题生成

复用现有的问题生成逻辑(已实现),基于文本内容生成。

3.3.2 图文结合问题生成

async def generate_image_text_questions(
    section_path: str,
    text_content: str,
    images: list[dict],  # [{path, analysis, context}]
    llm_config: dict,
    n: int = 3,
) -> list[dict]:
    """
    生成图文结合的问题
    
    示例问题类型:
    - "如图所示的配置界面中,如何启用 XXX 功能?"
    - "根据架构图XXX 模块与 YYY 模块的关系是什么?"
    - "图中显示的错误信息是什么原因导致的?"
    """
    # 筛选高价值图像qa_value >= 4
    high_value_images = [img for img in images if img['analysis']['qa_value'] >= 4]
    
    if not high_value_images:
        return []
    
    prompt = f"""你是一个技术文档问答生成专家。基于以下文本和图像信息,生成 {n} 个图文结合的问题。

**章节路径:** {section_path}

**文本内容:**
{text_content[:2000]}

**图像信息:**
"""
    
    for i, img in enumerate(high_value_images[:3]):  # 最多 3 张图
        prompt += f"""
{i+1}{img['path'].name}
- 类型:{img['analysis']['type']}
- 描述:{img['analysis']['description']}
- 关键元素:{', '.join(img['analysis']['key_elements'][:5])}
"""
    
    prompt += """
**要求:**
1. 问题必须同时依赖文本和图像才能回答(不能只看文本或只看图)
2. 问题中明确提及"如图所示"、"图中"、"根据架构图"等
3. 答案需要结合图像中的具体元素(按钮位置、模块名称、流程步骤等)
4. 每个问题附带:
   - 参考答案
   - 关联的图像文件名
   - 答案来源(文本片段 + 图像描述)

输出 JSON 数组:
[
  {
    "question": "如图所示的配置界面中,如何...",
    "answer": "在图中可以看到...",
    "image_ref": "image-20220518111319607.png",
    "source_text": "文本来源片段",
    "source_image_desc": "图像描述片段",
    "quality_score": 0.9
  }
]
"""
    
    response = await call_llm(prompt, llm_config)
    return json.loads(response)

3.3.3 图像理解问题生成

async def generate_image_understanding_questions(
    image_path: Path,
    image_analysis: dict,
    context: str,
    llm_config: dict,
    n: int = 2,
) -> list[dict]:
    """
    生成纯图像理解问题(主要针对架构图、流程图)
    
    示例问题类型:
    - "架构图中有哪些主要模块?"
    - "数据流向是怎样的?"
    - "XXX 模块的输入输出是什么?"
    """
    if image_analysis['type'] not in ('architecture', 'flowchart', 'hardware'):
        return []  # 只对特定类型图像生成
    
    # 读取图像
    img_b64 = base64.b64encode(image_path.read_bytes()).decode()
    
    prompt = f"""基于以下架构图/流程图,生成 {n} 个理解性问题。

**图像类型:** {image_analysis['type']}
**图像描述:** {image_analysis['description']}
**上下文:** {context[:500]}

**要求:**
1. 问题聚焦于图像中的结构、关系、流程
2. 答案必须通过仔细观察图像才能得出
3. 避免过于简单的"有哪些模块"类问题,要问模块间的关系、数据流向等

输出 JSON 数组:
[
  {
    "question": "架构图中 XXX 模块与 YYY 模块通过什么方式通信?",
    "answer": "通过 ZZZ 接口进行通信,数据流向为...",
    "image_ref": "{image_path.name}",
    "quality_score": 0.85
  }
]
"""
    
    response = await call_multimodal_llm(prompt, img_b64, llm_config)
    return json.loads(response)

3.4 阶段 4多模态问答集格式

输出格式: 扩展现有 MD 格式,支持图像引用

## linux_development/driver_develop_guide/uboot_config

## Q1: 如何进入 Uboot 的图形化配置界面?
**A1:** 在 Uboot 目录下执行 `make ARCH=arm menuconfig` 命令,会启动图形化配置界面。

## Q2: 如图所示的配置界面中,如何保存修改后的配置?
**IMAGE:** linux_development/driver_develop_guide/_images/image-20220518111319607.png
**A2:** 在配置界面中选择 Exit 退出,系统会提示是否保存修改,选择 Yes 即可保存配置到 .config 文件中。如图中红框所示,选择 "< Save >" 按钮。

## Q3: 根据架构图BPU 模块与 DDR 之间的数据通路是什么?
**IMAGE:** linux_development/system_architecture/_images/bpu_architecture.png
**A3:** BPU 模块通过 AXI 总线与 DDR 进行数据交互,支持 DMA 方式进行高速数据传输。

---

数据库扩展:

-- 扩展 qa_gen_question 表
ALTER TABLE qa_gen_question ADD COLUMN image_ref TEXT;  -- 关联的图像路径
ALTER TABLE qa_gen_question ADD COLUMN question_type TEXT;  -- text/image_text/image_only

四、实施计划

4.1 Phase 1基础设施1-2 天)

任务 输出
HTML → Markdown 转换器 Python 脚本,支持图像占位符
图像分析 API 封装 调用 GPT-4V/Claude 3.5 Sonnet
数据库扩展 新增 image_ref 字段

4.2 Phase 2图像分析2-3 天)

任务 输出
批量图像分类 1142 张图片的类型标注
高价值图像筛选 筛选出 qa_value >= 4 的图像(约 400-500 张)
图像描述生成 为高价值图像生成详细描述

成本估算:

  • GPT-4V$0.01/image × 1142 = $11.42
  • Claude 3.5 Sonnet$0.008/image × 1142 = $9.14

4.3 Phase 3多模态问答生成3-5 天)

任务 输出
纯文本问题生成 复用现有逻辑
图文结合问题生成 针对 400-500 张高价值图像
图像理解问题生成 针对架构图/流程图(约 100-150 张)
问答集审核 查重、质量评分、人工审核

预期产出:

  • 纯文本问题:~1000 条(与现有方案一致)
  • 图文结合问题:~800 条(每张高价值图像 2 条)
  • 图像理解问题:~200 条(每张架构图 2 条)
  • 总计:~2000 条多模态问答

4.4 Phase 4集成与测试2-3 天)

任务 输出
前端支持图像预览 审核页显示关联图像
导出格式扩展 支持导出带图像引用的 MD
单跳测试适配 支持多模态召回测试

五、技术选型

5.1 多模态 LLM 选择

模型 优势 劣势 推荐场景
GPT-4V 图像理解能力强API 稳定 成本较高($0.01/image 图像分类、架构图理解
Claude 3.5 Sonnet 成本较低($0.008/image中文支持好 图像细节识别略弱 配置界面截图、流程图
Qwen-VL 开源免费,可本地部署 需要 GPU推理速度慢 成本敏感场景

推荐组合:

  • 图像分类Claude 3.5 Sonnet成本低速度快
  • 架构图理解GPT-4V精度高
  • 问答生成Claude 3.5 Sonnet中文生成质量好

5.2 HTML 解析库

  • BeautifulSoup4:简单易用,适合结构化 HTML
  • html2text:快速转换,但图像处理能力弱
  • 推荐BeautifulSoup4 + 自定义逻辑

六、预期效果

6.1 问答集质量提升

维度 现有方案(纯文本) 多模态方案 提升
问题数量 ~1000 条 ~2000 条 +100%
覆盖场景 概念、配置、命令 + 界面操作、架构理解、硬件接口 +3 类
召回准确率 63% 预期 75%+ +12%
用户体验 纯文本问答 图文并茂,更直观 显著提升

6.2 Benchmark 价值

  • 更全面:覆盖文本 + 图像两个模态
  • 更真实:贴近用户实际使用场景(看文档 + 看图)
  • 更有挑战:测试 RAG 系统的多模态召回能力

七、风险与应对

风险 影响 应对措施
图像分析成本高 预算超支 1. 先筛选高价值图像
2. 使用 Claude 3.5 Sonnet 降低成本
3. 缓存分析结果
图像描述不准确 问答质量下降 1. 人工抽查 10% 样本
2. 低置信度图像跳过
3. 提供图像让审核人员验证
多模态召回测试复杂 实施困难 1. Phase 1 先生成问答集
2. Phase 2 再适配召回测试
3. 可先用纯文本测试验证

八、快速启动方案MVP

如果时间紧张,可以先实现 最小可行方案

MVP 范围

  1. 只处理配置界面截图~400 张,占比 40%
  2. 只生成图文结合问题(不做纯图像理解)
  3. 手动筛选 50 张高价值图像 作为 Pilot

MVP 实施3-5 天)

Day 任务
Day 1 HTML → MD 转换 + 手动筛选 50 张图
Day 2 图像分析50 张) + 描述生成
Day 3 图文结合问答生成(~100 条)
Day 4 审核 + 导出
Day 5 单跳测试验证效果

MVP 成本

  • 图像分析50 × $0.008 = $0.4
  • 问答生成100 条 × $0.002 = $0.2
  • 总计:~$0.6

九、下一步行动

  1. 确认方案:是否采用多模态方案?全量还是 MVP
  2. 准备环境
    • 申请 GPT-4V 或 Claude 3.5 Sonnet API key
    • 准备图像存储(本地 or OSS
  3. 开发排期
    • 谁负责 HTML 解析?
    • 谁负责图像分析?
    • 谁负责问答生成?
  4. 预算审批:全量方案约 $20MVP 约 $1

总结: 多模态方案能显著提升问答集质量和覆盖度,建议先用 MVP 验证效果,再决定是否全量实施。