# 基于 HTML 知识库的多模态问答集生成方案
**知识库特征:**
- 格式:Sphinx 生成的 HTML 文档(209 个 HTML 文件)
- 图片:1142 张图片(PNG/JPG),存放在 `_images/` 目录
- 结构:层级目录组织,包含大量配置界面截图、架构图、流程图等
**目标:** 生成高质量的多模态问答集,充分利用文本和图像信息
---
## 一、核心挑战
### 1.1 当前问题
| 问题 | 影响 |
|------|------|
| **图像信息丢失** | 现有 MD 问答集只有文本,配置界面截图、架构图等关键信息缺失 |
| **图文关联弱** | 图片与文本分离,无法生成"如图所示"类问题 |
| **问题质量受限** | 纯文本问题无法覆盖"界面在哪里点击"、"架构图中的模块关系"等场景 |
### 1.2 图像类型分析
根据采样分析,知识库中的图像主要分为以下类型:
| 图像类型 | 占比估算 | 示例 | 问答价值 |
|---------|---------|------|---------|
| **配置界面截图** | ~40% | menuconfig 界面、参数配置页面 | ⭐⭐⭐⭐⭐ 高价值,可生成操作类问题 |
| **架构图/流程图** | ~30% | 系统架构、数据流向、模块关系 | ⭐⭐⭐⭐⭐ 高价值,可生成理解类问题 |
| **代码截图** | ~15% | 代码片段、配置文件示例 | ⭐⭐⭐ 中等价值,文本已包含 |
| **硬件接口图** | ~10% | 引脚定义、电路连接 | ⭐⭐⭐⭐ 高价值,纯文本难以描述 |
| **其他** | ~5% | Logo、装饰性图片 | ⭐ 低价值 |
---
## 二、方案设计
### 2.1 整体流程
```
HTML 知识库
↓
┌─────────────────────────────────────┐
│ 阶段 1:HTML → 结构化 Markdown │
│ - 提取文本内容 │
│ - 保留图片占位符 [IMAGE: xxx.png] │
│ - 保留章节层级结构 │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ 阶段 2:图像分类与描述生成 │
│ - 多模态 LLM 识别图像类型 │
│ - 生成图像描述(caption) │
│ - 提取图像中的关键信息 │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ 阶段 3:多模态问答生成 │
│ - 纯文本问题(基于文本内容) │
│ - 图文结合问题(基于图像+上下文) │
│ - 图像理解问题(基于图像描述) │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ 阶段 4:问答集审核与优化 │
│ - 查重(文本 + 图像相似度) │
│ - 质量评分 │
│ - 人工审核 │
└─────────────────────────────────────┘
↓
多模态问答集(MD + 图像引用)
```
---
## 三、技术实现
### 3.1 阶段 1:HTML → 结构化 Markdown
**目标:** 将 HTML 转换为保留图像占位符的 Markdown
**实现方案:**
```python
from bs4 import BeautifulSoup
from pathlib import Path
def html_to_markdown_with_images(html_path: Path, base_path: Path) -> str:
"""
将 HTML 转换为 Markdown,保留图像占位符
返回格式:
## 章节标题
文本内容...

*图: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"")
# 添加图片说明(从上下文推断)
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 ""
```
**输出示例:**
```markdown
## 4.3.2. 配置 Uboot 和 Kernel 选项参数
在嵌入式系统开发中,Uboot 和 Kernel 的功能选项配置...
### 使用 xbuild 命令配置
命令执行成功后,系统会启动一个图形化的配置界面。

*图:Uboot menuconfig 配置界面,可以选择启用或禁用功能*
完成配置后,选择 Exit 退出...

*图:保存配置提示界面*
```
---
### 3.2 阶段 2:图像分类与描述生成
**目标:** 使用多模态 LLM 为每张图片生成结构化描述
**实现方案:**
```python
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-5(5=高价值)",
"suggested_questions": ["基于此图像可以生成的问题示例"]
}}
**图像类型定义:**
- config_ui: 配置界面截图(menuconfig、参数设置页面等)
- architecture: 架构图、系统框图
- flowchart: 流程图、时序图
- code: 代码截图
- hardware: 硬件接口图、引脚定义
- other: 其他类型
**评分标准:**
- 5分:包含关键操作步骤或架构信息,必须通过图像才能理解
- 4分:补充说明性图像,有助于理解但非必需
- 3分:代码或配置示例,文本已包含但图像更直观
- 2分:装饰性图像,价值较低
- 1分:无实质内容
"""
# 调用多模态 LLM(GPT-4V / Claude 3.5 Sonnet)
response = await call_multimodal_llm(
prompt=prompt,
image_base64=img_b64,
config=llm_config,
)
return json.loads(response)
```
**批量处理策略:**
```python
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 图文结合问题生成**
```python
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 图像理解问题生成**
```python
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 格式,支持图像引用
```markdown
## 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 方式进行高速数据传输。
---
```
**数据库扩展:**
```sql
-- 扩展 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. **预算审批**:全量方案约 $20,MVP 约 $1
---
**总结:** 多模态方案能显著提升问答集质量和覆盖度,建议先用 MVP 验证效果,再决定是否全量实施。