【AI 测试】墨刀需求文档自动化转换结构化md文档
·
AI 辅助测试之墨刀需求文档自动化处理
产品经理给了一个墨刀 URL,如何快速转化为可检索的结构化文档,并自动生成测试用例?
一、背景与痛点
在日常测试工作中,我们经常遇到这样的场景:
产品经理: "这是新的需求文档,墨刀链接:https://modao.cc/app/xxx"
测试工程师: 打开链接一看,是几十个页面的墨刀原型...
问题来了:
- 墨刀链接无法直接被 AI 读取 - AI 无法直接访问内网/私密的墨刀链接
- 页面太多 - 一个需求可能有几十个页面需要人工翻阅
- 信息分散 - 每个页面的文字、标注散落在各处
- 难以复用 - 无法被 AI 知识库检索,也无法直接生成测试用例
项目地址:
https://github.com/kkapro/modao-to-markdown
二、解决方案:两阶段处理流程
我们通过两阶段处理来解决这个问题:
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ 墨刀 URL │ → │ save_modao.py │ → │ 原始截图 + MD │
│ (产品经理提供) │ │ (页面采集) │ │ (内容原始但 │
└─────────────────┘ └─────────────────┘ │ 不够结构化) │
└────────┬────────┘
↓
┌─────────────────┐ ┌─────────────────┐ ┌────────┬────────┐
│ 测试用例 │ ← │ AI 生成 │ ← │ image_to_md.py │
│ (最终产物) │ │ (结构化处理) │ │ (AI 识别) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
阶段一:页面采集(save_modao.py)
- 自动打开墨刀 URL
- 遍历所有画布和页面
- 截图 + 提取原始文字
- 输出:原始截图 + 非结构化 MD
阶段二:AI 结构化(image_to_md.py)
- 读取截图
- 调用视觉大模型理解图片内容
- 输出:结构化 Markdown
三、阶段一:墨刀页面采集
3.1 脚本功能
save_modao.py 核心能力:
- 自动登录 - 通过 Playwright 控制浏览器,无需手动操作
- 智能遍历 - 自动识别墨刀项目中的画布和页面
- 滚动截图 - 长页面自动滚动,分段截图后无缝拼接
- 内容提取 - 提取页面可见文字,生成 Markdown
3.2 核心技术实现
1. 浏览器自动化
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(headless=False) # 可视化模式
page = browser.new_page()
page.goto(url) # 打开墨刀项目
2. 画布和页面识别
def find_canvas_list(page):
"""查找左侧画布列表"""
lis = page.query_selector_all("li")
for li in lis:
text = li.inner_text().strip()
# 过滤无关项(总览、演示、废纸篓等)
if should_skip_item(text, CANVAS_SKIP_KEYWORDS):
continue
canvas_items.append(first_line)
def get_page_list_in_canvas(page):
"""获取当前画布下的页面列表"""
elements = page.query_selector_all(".canvas-sortable-list > ul > li")
for el in elements:
page_name = el.inner_text().strip()
data_cid = el.get_attribute("data-cid")
3. 长页面滚动截图
墨刀页面经常很长,一个屏幕截不完,需要分段截图后拼接:
def take_screenshot(page, screenshot_path, data_cid):
# 1. 获取画布元素和位置
canvas_el = page.query_selector(f'.tree-node.rResCanvas[data-cid="{data_cid}"]')
box = canvas_el.bounding_box()
# 2. 判断是否需要滚动
visible_height = (VIEWPORT_HEIGHT - HEADER_OFFSET) * current_scale
content_height = el_height * current_scale
needs_scrolling = (content_height - visible_height) > 100
# 3. 分段截图
images = []
for scroll_pos in range(0, el_height, SCROLL_STEP):
# 滚动到指定位置
page.evaluate(f"zoom.style.transform = 'translate(x, {scroll_pos}px) scale(s)'")
page.screenshot(path=temp_path)
cropped = crop_canvas_region(img, box)
images.append(cropped)
# 4. 智能拼接(特征匹配 + 平滑融合)
result = stitch_images(images)
result.save(screenshot_path)
4. 图像拼接算法
def find_optimal_overlap(prev_img, curr_img):
"""使用多区域特征匹配找最佳重叠区域"""
# 多区域分析:中心、左侧、右侧
regions = [(0.2, 0.8), (0.1, 0.3), (0.7, 0.9)]
# 计算相似度(SSIM + 边缘检测)
score = calculate_overlap_score(prev_region, curr_region, regions)
# 置信度判断
if score > 0.9: # 高置信
return best_overlap
elif score > 0.7: # 低置信,保守处理
return int(best_overlap * 0.9)
else: # 无效重叠,直接拼接
return 0
def apply_blend(result, prev_img, curr_img, overlap_height):
"""在重叠区域应用平滑融合,消除接缝"""
for y in range(overlap_height):
weight = 1.0 - (y / overlap_height) # 渐变权重
blended = prev_pixel * weight + curr_pixel * (1 - weight)
result.putpixel((x, result_y), blended)
5. 内容提取与清洗
def get_page_content(page):
"""提取有效内容,过滤 UI 噪声"""
ui_tags = [
"头像", "用户昵称", "私聊", "聊天", "消息", "发送",
"设置", "保存", "删除", "添加", "关闭", "确定",
"关注", "粉丝", "礼物", "排行榜", "等级",
# ... 更多 UI 标签
]
# 过滤规则:
# 1. 纯数字行(页码)
# 2. 短 UI 标签(如"头像"、"昵称"单独出现)
# 3. 版本号(如"V1.3.9")
3.3 使用方式
# 基础用法 - 直接采集墨刀页面
python save_modao.py "https://modao.cc/app/xxx"
# 采集指定画布(第 1 个画布)
python save_modao.py "https://modao.cc/app/xxx" 1
# Debug 模式(覆盖已有文件,不创建新目录)
python save_modao.py "https://modao.cc/app/xxx" debug
3.4 输出目录结构
modao-export/
├── 会员福利模块V2.0/ # 项目名称(自动从标题提取)
│ ├── images/ # 采集的截图
│ │ ├── page_1_会员首页_入口.png
│ │ ├── page_2_会员权益_详情.png
│ │ └── ...
│ └── md/ # 原始 MD 文档
│ ├── index.md # 索引文件
│ ├── page_1_会员首页_入口.md
│ └── ...
3.5 原始 MD 输出示例
# 会员首页 - 会员入口
## 内容
- 页面顶部:'某App 你的ID:116'
- 头像区域:圆形头像,右侧显示用户昵称和等级
- 会员状态:'未开通'
- 开通按钮:橙色'立即开通'按钮
- 权益预览:'首充豪礼', '专属徽章', '专属客服'
...
注意:这个阶段的 MD 只有原始文字,没有结构化,不方便 AI 理解和检索。
四、阶段二:AI 结构化处理
4.1 脚本功能
image_to_md.py 核心能力:
- AI 图像理解 - 调用视觉大模型理解截图内容
- 结构化输出 - 生成规范化的 Markdown
- 增量处理 - 已处理的文件自动跳过
- 标注提取 - 完整保留序号、箭头、红字等标记
4.2 核心提示词设计
提示词是 AI 输出的关键,设计原则:
【核心原则】
1. 不编造:不添加任何图片中不存在的内容
2. 不遗漏:完整提取所有可见的文字、标注
3. 保留原文:严格按照原图的表述方式
4. 保留结构:还原原图的排版、层级、顺序
【输出格式】
输出JSON对象,字段根据图片内容自适应:
- 文档标题
- 内容区块(按原图顺序组织)
- 标注信息(序号、箭头标记等)
- 页面元素(按钮、输入框等)
4.3 使用方式
# 处理指定项目目录(自动查找 images/ 目录)
python image_to_md.py E:\project\需求文档\会员福利模块
# Debug 模式(覆盖已有文件)
python image_to_md.py E:\project\需求文档\会员福利模块 debug
4.4 输出目录结构
会员福利模块/
├── images/ # 原始截图(从 modao-export 复制)
│ └── page_1_会员首页_入口.png
└── ai-md/ # AI 结构化后的 MD
├── index.md # 索引文件
└── page_1_会员首页_入口.md
4.5 结构化 MD 输出示例
# page_1_会员首页_入口
## 文档标题
- **文档标题**: 新增会员福利发放入口
## 内容区块
### 区块标题: 页面顶部导航与功能区
- **区块内容**: 左上角:'某App', 左侧用户信息栏:'用户 ID:116 ●等级 Lv.33', '收藏', '消息 0', '设置', 右侧下拉菜单(点击'更多'后展开):, - 个人中心, - 会员中心, - 消息中心, - 设置中心, - 福利中心
### 区块标题: 主视觉区域(背景图+图标区)
- **区块内容**: 背景为渐变色+插画,右上角有'新用户专享'、'限时福利'等标签, 中间横向排列多个图标,标注为:'未开通'(共4个图标),其中第1个为'专属折扣',第2个为'专属客服',下方有小字'开通享多重权益', 右下角红色箭头指向一个按钮,标注:'新增会员福利入口', 底部权益展示区:包含'首充返利'、'专属徽章'、'专属客服'等图标及对应权益说明
### 区块标题: 会员福利功能模块(主操作区)
- **区块内容**: 标题:'会员福利发放 ①', 副标题:'上周积分:10000'(上方有红色箭头指向该文字), 左侧:'福利礼包'板块, - 已勾选项:'满减券'(橙色边框+对勾), - 其他选项:'折扣券'(含'查看详情'标签)、'兑换码'(含'查看详情'标签), - 下方两行:'月卡用户首开5折X1,数量10'、'月卡用户续费5折X1,数量10', - 底部说明:'<优惠券类福利可发放到用户ID,其他福利仅发放到主账号>', - 操作按钮:'确认发放'(橙色)、'已发放'(灰色), 右侧:'发放记录'表格(空表),上方有'数量 1'、'发放给 请输入ID'、'留送'按钮, 右下角弹窗:'福利发放说明'(带X关闭按钮), 1. 关于福利:'会员福利发放是针对会员周活跃达标的用户,进行奖励发放的模块,管理员可见及操作', 2. 关于领取时间:'每个自然周一的0点10分更新发放资格,获得奖励的管理员可在此操作。次周一0点后未操作的奖励将失效', 3. 关于优惠券发放:'多张优惠券可发放给一个人,也可发给不同的人,建议输入ID后验证'
## 标注信息
- **标注编号**: ① - 位置:页面左上角标题'会员福利发放'旁
- **标注编号**: ② - 位置:页面顶部右侧下拉菜单中'福利中心'项
- **标注编号**: ③ - 位置:页面中部红色箭头指向'新增会员福利入口'
- **标注编号**: ④ - 位置:页面中部红色箭头指向'上周积分:10000'
## 页面元素
- **下拉菜单**: 个人中心, 会员中心, 消息中心, 设置中心, 福利中心
- **按钮**: 确认发放(橙色), 已发放(灰色), 留送, 添加, 清空
- **输入框/计数器**: 数量 1 ^, 发放给 请输入ID ^
## 截图

五、完整工作流
5.1 Step by Step
# Step 1: 采集墨刀页面
python save_modao.py "https://modao.cc/app/xxx"
# Step 2: 复制 images 目录到目标位置
# 将 modao-export/项目名/images/ 复制到目标目录
# Step 3: AI 结构化处理
python image_to_md.py E:\project\需求文档\会员福利模块
# Step 4: 查看输出
# 打开 ai-md/index.md 查看所有结构化文档
5.2 完整目录示例
E:\project\
└── ocr-modao\
├── save_modao.py # 墨刀采集脚本
├── image_to_md.py # AI 结构化脚本
├── README_USAGE.md # 使用说明
├── 会员福利模块\
│ ├── images\ # 截图(手动复制或软链接)
│ │ ├── page_1.png
│ │ ├── page_2.png
│ │ └── ...
│ └── ai-md\ # AI 结构化输出
│ ├── index.md
│ ├── page_1.md
│ └── ...
└── modao-export\ # 采集原始文件
└── 会员福利模块V2.0\
├── images\
└── md\
六、进阶应用
6.1 AI 知识库
结构化后的 MD 可以接入 AI 知识库:
- 智能问答 - “会员福利的领取时间是什么?”
- 需求检索 - 搜索"优惠券发放",快速找到相关需求
- 需求关联 - 自动发现功能之间的依赖
6.2 自动生成测试用例
基于结构化文档,用 AI 生成测试用例:
| 功能 | 测试点 | 预期结果 |
|---|---|---|
| 会员入口 | 导航菜单显示 | 菜单中显示"福利中心" |
| 领取资格 | 周一0点10分后开放 | 符合条件时开放领取 |
| 数量校验 | 数量大于剩余 | 提示不能大于剩余数量 |
| 权限控制 | 普通管理员未授权 | 不可见入口 |
6.3 自动化测试
结合 Selenium/Appium:
# 读取 MD 中的操作步骤
def get_operation_steps(md_file):
with open(md_file) as f:
content = f.read()
# 解析"区块内容"字段
# 提取按钮名称、输入框、操作顺序
return steps
# 自动执行
for step in get_operation_steps("page_1.md"):
if step["type"] == "click":
driver.click(step["element"])
elif step["type"] == "input":
driver.input(step["element"], step["value"])
七、关键特性
| 特性 | 说明 |
|---|---|
| 增量生成 | 已处理的文件自动跳过,避免重复 API 调用 |
| 中断恢复 | 支持 Ctrl+C 中断,后续可续传 |
| 自动重试 | API 超时/失败时自动重试(最多2次) |
| 保留标注 | 完整提取序号、箭头、红字等标记 |
| 滚动拼接 | 长页面自动滚动截图并平滑拼接 |
| 智能过滤 | 自动过滤 UI 噪声,保留需求内容 |
八、环境配置
8.1 依赖安装
pip install openai pillow playwright numpy scipy
playwright install chromium
8.2 API 配置
在 image_to_md.py 中配置:
BAILIANT_API_KEY = "sk-xxx" # 阿里百炼 API Key
BAILIANT_MODEL = "qwen3-vl"
九、总结
通过两阶段处理流程,我们实现了:
- 从 URL 到结构化文档 - 墨刀 URL → 原始截图 → AI 结构化
- 效率提升 - 原本需要几小时的人工整理,现在分钟级完成
- 格式统一 - 输出的 Markdown 格式规范,便于 AI 理解和检索
- 知识沉淀 - 结构化文档可复用,支持 AI 知识库和测试用例生成
- 一键采集 - 自动化处理,减少人工干预
这套方案是 AI 辅助测试的入门级应用,适合团队日常使用。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)