P6-将 Markdown 文档转换为问答对
概述
本实现利用 Gradio 创建一个用户友好的 Web 界面,允许用户上传 Markdown 文档,通过 DashScope API(使用 qwen-turbo
模型)生成问答对,并在界面上展示结果。流程包括 Markdown 解析、API 调用、问答对生成和结果展示,优化为技术文档(如 TVM、模型优化)生成技术性问答对。
环境准备
依赖库
安装以下 Python 库以支持 Gradio 界面、Markdown 解析和 API 调用:
pip install gradio openai markdown beautifulsoup4
API 配置
使用您提供的 DashScope API 密钥和 base URL:
api_key = "配置你的API"
base_url = "https://dashscope.aliyuncs.com/compatible-mode/v1"
实现代码
以下是完整的 Python 代码,包含 Gradio 界面、Markdown 解析、DashScope API 调用和问答对生成逻辑:
import gradio as gr
import markdown
from bs4 import BeautifulSoup
from openai import OpenAI
import re
import json
import os
# 初始化 DashScope API 客户端
client = OpenAI(
api_key="配置你的API",
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)
# 1. 解析 Markdown 文档
def parse_markdown(file):
"""将上传的 Markdown 文件解析为结构化数据(标题和内容)"""
# 读取文件内容
md_content = file.decode('utf-8') if isinstance(file, bytes) else file.read().decode('utf-8')
# 转换为 HTML
html = markdown.markdown(md_content)
# 解析 HTML,提取标题和内容
soup = BeautifulSoup(html, 'html.parser')
sections = []
for header in soup.find_all(['h1', 'h2', 'h3']):
section_title = header.text.strip()
content = []
# 获取标题后的内容(直到下一个标题)
for sibling in header.find_next_siblings():
if sibling.name in ['h1', 'h2', 'h3']:
break
content.append(sibling.text.strip())
sections.append({
'title': section_title,
'content': ' '.join(content)
})
return sections
# 2. 使用 DashScope API 生成问答对
def generate_qa_pairs(section, model="qwen-turbo"):
"""调用 DashScope API 生成问答对"""
prompt = f"""
你是一个技术文档专家,任务是从以下 Markdown 内容中生成问答对。请根据内容生成 2-3 个简洁、准确的技术性问题,答案需直接引用原文或基于原文总结,确保简明扼要。
**输入内容**:
{section['title']}: {section['content']}
**输出格式**:
- Q: {{问题}}
A: {{答案}}
"""
try:
response = client.chat.completions.create(
model=model,
messages=[
{"role": "system", "content": "You are a technical documentation expert."},
{"role": "user", "content": prompt}
],
max_tokens=500,
temperature=0.7,
top_p=0.9
)
return response.choices[0].message.content.strip()
except Exception as e:
return f"API 调用失败: {e}"
# 3. 清洗生成的问答对
def clean_qa_pairs(raw_output):
"""从模型输出中提取问答对"""
qa_pairs = []
pattern = r"- Q: (.+?)\n\s*A: (.+?)(?=\n- Q:|\n\n|$)"
matches = re.findall(pattern, raw_output, re.DOTALL)
for q, a in matches:
qa_pairs.append({
"question": q.strip(),
"answer": a.strip()
})
return qa_pairs
# 4. 处理上传的 Markdown 文件并生成问答对
def process_markdown(file):
"""主处理函数:解析 Markdown,生成问答对并格式化输出"""
if not file:
return "请上传一个 Markdown 文件!"
try:
# 解析 Markdown
sections = parse_markdown(file)
if not sections:
return "Markdown 文件为空或解析失败!"
all_qa_pairs = []
# 为每个部分生成问答对
for section in sections:
raw_qa = generate_qa_pairs(section)
if raw_qa and "API 调用失败" not in raw_qa:
qa_pairs = clean_qa_pairs(raw_qa)
all_qa_pairs.extend(qa_pairs)
# 格式化输出为 Markdown
output = "## 生成的问答对\n\n"
for qa in all_qa_pairs:
output += f"- **Q**: {qa['question']}\n - **A**: {qa['answer']}\n"
# 保存为 JSON(可选)
output_json = "qa_pairs.json"
with open(output_json, 'w', encoding='utf-8') as f:
json.dump(all_qa_pairs, f, ensure_ascii=False, indent=2)
return output
except Exception as e:
return f"处理失败: {e}"
# 5. Gradio 界面定义
with gr.Blocks(theme=gr.themes.Soft()) as demo:
gr.Markdown("# Markdown 转问答对生成器")
gr.Markdown("上传一个 Markdown 文档,点击生成按钮,查看自动生成的问答对。")
with gr.Row():
file_input = gr.File(label="上传 Markdown 文件", file_types=[".md"])
generate_button = gr.Button("生成问答对")
output_text = gr.Markdown(label="问答对结果")
generate_button.click(
fn=process_markdown,
inputs=file_input,
outputs=output_text
)
# 6. 启动 Gradio 界面
if __name__ == "__main__":
demo.launch()
界面描述
组件
- 标题:显示“Markdown 转问答对生成器”及简要说明。
- 文件上传:支持
.md
文件上传,显示文件名和大小。 - 生成按钮:点击后调用
process_markdown
函数,处理文件并生成问答对。 - 输出区域:以 Markdown 格式显示问答对,格式清晰(如
- **Q**: 问题\n - **A**: 答案
)。 - 主题:使用
gr.themes.Soft()
,提供柔和的视觉风格,适合技术用户。
用户交互
- 用户点击“上传 Markdown 文件”,选择一个
.md
文件(如example.md
)。 - 点击“生成问答对”按钮,触发后台处理。
- 结果显示在输出区域,格式化为 Markdown 列表,同时保存为
qa_pairs.json
。
示例 Markdown 输入
以下是一个示例 Markdown 文件(example.md
),基于之前的“芬太尼简介”:
# 芬太尼简介
## 什么是芬太尼?
芬太尼(Fentanyl)是一种**合成的阿片类药物**,属于强效镇痛剂,广泛用于医疗和非医疗场景。以下是其核心特点:
- **药理特性**:
- 芬太尼的镇痛效力是**吗啡的50-100倍**,是**海洛因的50倍**,2毫克即可致命(相当于几粒盐的重量)。
- 它通过作用于大脑和身体的**μ-阿片受体**,产生镇痛、欣快感和镇静效果。
- **医疗用途**:
- 由医生开具处方,用于**重症疼痛管理**,如**术后疼痛**、**癌症晚期疼痛**或**慢性疼痛**。
- 常用形式包括**透皮贴片**(如 Duragesic)、**含片**(如 Actiq)、**注射液**。
运行步骤
-
安装依赖:
pip install gradio openai markdown beautifulsoup4
-
保存代码:
- 将上述代码保存为
app.py
。
- 将上述代码保存为
-
准备 Markdown 文件:
- 创建
example.md
,内容如上。
- 创建
-
运行脚本:
python app.py
- Gradio 将启动本地 Web 服务器,输出类似:
Running on local URL: http://127.0.0.1:7860
- 打开浏览器,访问该 URL。
- Gradio 将启动本地 Web 服务器,输出类似:
-
使用界面:
- 上传
example.md
。 - 点击“生成问答对”。
- 查看输出区域的问答对。
- 上传
预期输出
界面输出
在 Gradio 界面的“问答对结果”区域显示:
## 生成的问答对
- **Q**: 芬太尼是什么类型的药物?
- **A**: 芬太尼是一种合成的阿片类药物,属于强效镇痛剂。
- **Q**: 芬太尼的镇痛效力如何?
- **A**: 芬太尼的镇痛效力是吗啡的50-100倍,是海洛因的50倍。
- **Q**: 芬太尼在医疗中有哪些用途?
- **A**: 芬太尼用于重症疼痛管理,如术后疼痛、癌症晚期疼痛或慢性疼痛。
JSON 文件
生成 qa_pairs.json
:
[
{
"question": "芬太尼是什么类型的药物?",
"answer": "芬太尼是一种合成的阿片类药物,属于强效镇痛剂。"
},
{
"question": "芬太尼的镇痛效力如何?",
"answer": "芬太尼的镇痛效力是吗啡的50-100倍,是海洛因的50倍。"
},
{
"question": "芬太尼在医疗中有哪些用途?",
"answer": "芬太尼用于重症疼痛管理,如术后疼痛、癌症晚期疼痛或慢性疼痛。"
}
]
代码说明
1. Gradio 界面
- 组件:
gr.File
:支持.md
文件上传,处理二进制或文件对象。gr.Button
:触发问答对生成。gr.Markdown
:显示格式化的问答对,支持 Markdown 渲染。
- 布局:使用
gr.Blocks
和gr.Row
组织界面,简洁直观。 - 主题:
gr.themes.Soft()
提供柔和配色,提升用户体验。
2. Markdown 解析
- 库:
markdown
转换为 HTML,BeautifulSoup
提取标题(h1
,h2
,h3
)和内容。 - 逻辑:按标题组织内容,收集后续段落和列表,确保上下文完整。
- 兼容性:处理上传文件的二进制或文件对象,支持 Gradio 的文件输入。
3. DashScope API 调用
- 客户端:使用
OpenAI
客户端连接 DashScope API,调用qwen-turbo
模型。 - 提示:要求生成 2-3 个技术性问题,答案基于原文,格式为
- Q: {问题}\n A: {答案}
。 - 参数:
max_tokens=500
,temperature=0.7
,top_p=0.9
平衡质量和多样性。 - 错误处理:捕获 API 异常,返回错误信息。
4. 后处理与输出
- 正则表达式:提取问答对,清洗多余空格和换行。
- 格式化:转换为 Markdown 列表,适合 Gradio 显示。
- 保存:生成 JSON 文件,便于后续使用。
优化建议(结合您的背景)
基于您在 AI 开发(TVM、DeepSpeed、模型部署)的经验,以下是定制化建议:
提示优化
- 为 TVM 相关 Markdown 定制提示,生成与算子融合、动态 Shape 或 Relay IR 相关的问题:
生成与 TVM 优化、算子融合或动态 Shape 相关的问题,答案需简洁且基于原文。
- 示例问题:
- Q: TVM 如何实现算子融合?
- Q: 动态 Shape 在 TVM 中如何支持?
模型选择
- 如果需要更高性能,尝试 DashScope 的
qwen-max
模型,提供更强的语义理解。 - 可使用 DeepSpeed 微调 Qwen 模型,加入 TVM 或技术文档的问答样本,提升生成质量。
高并发部署
- 参考您在公文助手项目中的 vLLM 经验,可将 API 调用部署到 GPU 集群,支持批量处理大型 Markdown 文档。
- 使用异步 API 调用:
import asyncio from openai import AsyncOpenAI async_client = AsyncOpenAI( api_key = "配置你的API", base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", ) async def generate_qa_pairs_async(section, model="qwen-turbo"): response = await async_client.chat.completions.create(...) return response.choices[0].message.content.strip()
界面增强
- 添加下载按钮,允许用户下载 JSON 文件:
download_button = gr.File(label="下载 JSON 文件")
- 支持实时预览 Markdown 内容,增加
gr.Textbox
显示上传文件的原始文本。