
ComfyUI Qwen-Image-Edit-F2P自动化测试框架使用Python脚本进行回归测试你是不是也遇到过这样的烦恼在ComfyUI里部署好了Qwen-Image-Edit-F2P这个强大的图像编辑模型每次更新节点、调整参数或者换了台服务器心里总有点打鼓它还能像之前一样稳定工作吗生成的效果会不会有变化手动测试太费时间了。上传几张图输入几个提示词再一张张对比效果一次两次还行要是每周、每天都要做简直是个噩梦。更别提万一哪天模型更新了你根本没法快速判断新版本是好是坏。今天我就来分享一个我们团队在实际项目中用起来的“笨办法”——用Python脚本搭建一个自动化回归测试框架。这个框架能帮你自动完成从发送请求、生成图片到对比结果、生成报告的全过程。简单来说就是让机器替你“盯”着模型一旦效果有偏差马上就能知道。1. 为什么需要自动化测试框架在聊具体怎么做之前我们先想想为什么这事儿值得花时间去做。想象一下你负责维护一个基于Qwen-Image-Edit-F2P的在线服务。今天服务器升级了系统明天ComfyUI更新了一个新版本后天你又想试试不同的采样器参数。每一次变动都可能像在黑暗中摸索——你不知道它会不会把原本好好的功能搞砸。手动测试的局限性太大了。它慢、容易出错、而且不可重复。你今天测试用的提示词和图片下周可能就忘了。更关键的是你很难量化“效果变好或变坏了”。你说这张图“好像”没之前清晰了但到底差了多少呢自动化测试框架就是为了解决这些问题而生的。它能帮你保证一致性确保模型的核心编辑能力比如换背景、改风格、修复瑕疵在环境变更后依然稳定。快速反馈模型或代码一有更新跑一遍测试脚本几分钟内就能知道有没有引入问题。量化评估不再是模糊的“感觉”而是通过图像指纹、相似度分数等具体数据来判断变化。解放人力把开发者从重复的劳动中解放出来去关注更重要的模型调优和功能开发。接下来我们就一步步看看怎么用Python把这个“自动化质检员”给搭建起来。2. 环境准备与框架设计思路开始写代码之前得先把台子搭好。这个框架不复杂核心就是模拟一个用户去跟ComfyUI的API打交道。2.1 你需要准备什么首先确保你的工作环境里有这几样东西一个正在运行的ComfyUI服务里面已经部署并可以正常使用Qwen-Image-Edit-F2P工作流。这是我们的测试对象。Python 3.7或更高版本这是我们的脚本语言。几个关键的Python库用pip安装一下很快。pip install requests pillow imagehash pandasrequests用来发送HTTP请求到ComfyUI的API。pillow(PIL)Python的图像处理库用来加载和保存图片。imagehash计算图片“指纹”的神器我们靠它来比较图片差异。pandas最后生成测试报告表格的时候会用到处理数据很方便。2.2 框架是怎么运转的整个框架的流程就像一条自动化流水线[准备测试用例] - [发送API请求] - [下载生成图片] - [计算并对比图像指纹] - [生成测试报告]准备测试用例我们事先定义好一组“标准考题”。包括测试用的原始图片路径、针对这张图的编辑提示词比如“把背景换成海滩”、“转换成卡通风格”、以及这张图在“标准答案”状态下的图像指纹这个我们叫它“基准指纹”。发送请求脚本读取测试用例按照ComfyUI API要求的格式打包成JSON数据发送POST请求。获取结果ComfyUI处理完后会返回一个图片ID或URL脚本再根据这个信息把生成的图片下载到本地。计算与对比用imagehash库为刚下载的图片计算一个哈希值比如感知哈希pHash这个值就是它的“指纹”。然后拿这个指纹去和之前保存的“基准指纹”做比较计算它们之间的汉明距离。距离越小说明两张图越相似。生成报告把每一个测试用例的结果成功/失败、相似度分数、生成图片路径记录下来最后汇总成一个清晰的HTML或CSV报告一眼就能看出哪些测试通过了哪些失败了。思路清晰了我们就可以动手搭建了。3. 构建核心测试脚本我们来把上面的思路变成代码。我会把关键部分拆开讲解你可以把它们组合成一个完整的Python文件比如叫qwen_image_edit_tester.py。3.1 第一步与ComfyUI API对话ComfyUI通过HTTP API与外界通信。我们需要一个函数来触发工作流并获取结果。这里的关键是构造正确的promptJSON。import requests import json import time import os class ComfyUI_Tester: def __init__(self, server_address127.0.0.1:8188): self.server_address server_address self.base_url fhttp://{self.server_address} # ComfyUI的API端点 self.prompt_url f{self.base_url}/prompt self.history_url f{self.base_url}/history self.view_url f{self.base_url}/view def trigger_workflow(self, prompt_json): 触发ComfyUI工作流执行 try: response requests.post(self.prompt_url, json{prompt: prompt_json}) response.raise_for_status() # 检查HTTP错误 result response.json() prompt_id result[prompt_id] print(f工作流已触发任务ID: {prompt_id}) return prompt_id except requests.exceptions.RequestException as e: print(f触发工作流失败: {e}) return None代码解释我们创建了一个ComfyUI_Tester类来管理所有操作。trigger_workflow函数接收一个prompt_json参数。这个JSON就是你在ComfyUI界面上点击“Queue Prompt”时生成的那个复杂数据结构。通常你需要先用ComfyUI手动运行一次正确的工作流然后通过“Save (API Format)”按钮来获取这个JSON模板。函数将prompt发送给ComfyUI的/prompt接口并返回一个prompt_id用于后续查询结果。3.2 第二步获取生成的图像触发工作流后ComfyUI是异步处理的。我们需要轮询历史记录直到任务完成然后获取生成的图片。def get_generated_images(self, prompt_id, output_node_idNone): 根据prompt_id获取生成的图片 max_retries 30 # 最大重试次数 retry_interval 2 # 重试间隔(秒) for i in range(max_retries): try: # 查询执行历史 history_response requests.get(self.history_url) history_data history_response.json() if prompt_id in history_data: outputs history_data[prompt_id][outputs] # 找到包含图片的输出节点 for node_id, node_output in outputs.items(): if images in node_output: images node_output[images] if images: # 默认取第一个图片或根据指定节点ID获取 if output_node_id is None or node_id output_node_id: image_info images[0] filename image_info[filename] subfolder image_info.get(subfolder, ) # 构建图片访问URL并下载 image_url f{self.view_url}?filename{filename}subfolder{subfolder}typeoutput return self.download_image(image_url, filename) print(f未在输出中找到图片 outputs: {outputs}) return None else: print(f任务 {prompt_id} 仍在处理中... (尝试 {i1}/{max_retries})) time.sleep(retry_interval) except Exception as e: print(f查询结果时出错: {e}) time.sleep(retry_interval) print(f获取结果超时prompt_id: {prompt_id}) return None def download_image(self, image_url, filename): 下载图片到本地 try: response requests.get(image_url) response.raise_for_status() # 保存到当前目录的output文件夹 os.makedirs(test_output, exist_okTrue) save_path os.path.join(test_output, filename) with open(save_path, wb) as f: f.write(response.content) print(f图片已保存至: {save_path}) return save_path except Exception as e: print(f下载图片失败: {e}) return None代码解释get_generated_images函数会每隔2秒查询一次任务历史/history最多查询30次即等待1分钟。一旦在历史记录中找到对应的prompt_id就去它的输出里找图片。找到图片信息后构建一个直接访问图片的URL/view接口然后调用download_image函数将其下载到本地的test_output文件夹。output_node_id参数是可选的如果你的工作流有多个输出节点可以用它来指定获取哪个节点的图片。3.3 第三步计算和对比图像指纹图片下载下来了怎么判断它和“标准答案”是否一致呢逐像素比较太严格且对颜色、亮度微小变化不敏感。我们使用感知哈希pHash。from PIL import Image import imagehash def compute_image_hash(image_path): 计算图片的感知哈希(pHash) try: with Image.open(image_path) as img: # 转换为RGB模式并调整大小使哈希计算更稳定 img img.convert(RGB) hash_value imagehash.phash(img) return hash_value except Exception as e: print(f计算图片哈希失败 {image_path}: {e}) return None def compare_image_hashes(hash_current, hash_baseline, threshold5): 比较两个图像哈希值的汉明距离 threshold: 容错阈值距离小于此值则认为测试通过 if hash_current is None or hash_baseline is None: return False, float(inf) hamming_distance hash_current - hash_baseline # imagehash库重载了减号运算符 is_similar hamming_distance threshold return is_similar, hamming_distance代码解释compute_image_hash函数使用imagehash.phash()计算图片的感知哈希。pHash会考虑图片的频率和色彩分布即使图片有轻微的尺寸、亮度或色彩调整其哈希值也基本不变非常适合我们的回归测试。compare_image_hashes函数计算两个哈希值之间的汉明距离即不同比特位的数量。距离为0表示完全一致。我们设置一个阈值比如5距离小于等于阈值就认为测试通过。这个阈值可以根据你对“变化”的容忍度来调整。3.4 第四步组装测试流程与管理用例现在我们把所有零件组装起来并管理我们的测试用例。import pandas as pd class QwenImageEditTester: def __init__(self, comfyui_server): self.comfyui ComfyUI_Tester(comfyui_server) self.test_cases [] # 存储测试用例 self.results [] # 存储测试结果 def add_test_case(self, name, prompt_json, baseline_image_path, input_image_pathNone): 添加一个测试用例 # 计算基准图片的哈希值 baseline_hash compute_image_hash(baseline_image_path) if baseline_hash is None: print(f警告无法计算基准图片哈希 {baseline_image_path}跳过此用例。) return test_case { name: name, prompt_json: prompt_json, baseline_image_path: baseline_image_path, baseline_hash: baseline_hash, input_image_path: input_image_path # 对于图生图编辑需要原图 } self.test_cases.append(test_case) print(f已添加测试用例: {name}) def run_test_suite(self): 运行所有测试用例 print(f开始运行测试套件共 {len(self.test_cases)} 个用例...) for i, test_case in enumerate(self.test_cases): print(f\n--- 执行用例 {i1}: {test_case[name]} ---) # 1. 触发工作流 prompt_id self.comfyui.trigger_workflow(test_case[prompt_json]) if not prompt_id: self.record_result(test_case, False, 触发工作流失败) continue # 2. 获取生成图片 generated_image_path self.comfyui.get_generated_images(prompt_id) if not generated_image_path: self.record_result(test_case, False, 获取生成图片失败) continue # 3. 计算哈希并对比 current_hash compute_image_hash(generated_image_path) is_pass, distance compare_image_hashes(current_hash, test_case[baseline_hash]) # 4. 记录结果 result_status 通过 if is_pass else 失败 self.record_result(test_case, is_pass, f哈希距离: {distance}, generated_image_path) # 5. 所有用例执行完毕后生成报告 self.generate_report()代码解释我们创建了一个更上层的QwenImageEditTester类来管理整个测试过程。add_test_case方法用于添加测试用例。每个用例需要名称、触发工作流的JSON、作为“标准答案”的基准图片路径。对于图生图编辑可能还需要输入图片路径这个信息可以整合到prompt_json里。run_test_suite方法是核心控制器它遍历所有测试用例按顺序执行“触发-获取-对比”的流程并记录每一步的结果。3.5 第五步记录结果与生成报告最后我们需要把测试结果清晰地呈现出来。def record_result(self, test_case, is_pass, message, generated_image_pathNone): 记录单个测试用例的结果 result { 测试用例: test_case[name], 状态: 通过 if is_pass else 失败, 基准图片: test_case[baseline_image_path], 生成图片: generated_image_path or N/A, 详细信息: message, 通过: is_pass } self.results.append(result) status_icon ✅ if is_pass else ❌ print(f{status_icon} 用例 {test_case[name]} {result[状态]} - {message}) def generate_report(self, report_pathtest_report.html): 生成HTML格式的测试报告 if not self.results: print(没有测试结果可生成报告。) return df pd.DataFrame(self.results) # 计算通过率 total_cases len(df) passed_cases df[通过].sum() pass_rate (passed_cases / total_cases * 100) if total_cases 0 else 0 # 生成HTML html_content f html head titleQwen-Image-Edit-F2P 回归测试报告/title style body {{ font-family: Arial, sans-serif; margin: 40px; }} .summary {{ background-color: #f4f4f4; padding: 20px; border-radius: 5px; margin-bottom: 30px; }} .pass {{ color: green; }} .fail {{ color: red; }} table {{ border-collapse: collapse; width: 100%; }} th, td {{ border: 1px solid #ddd; padding: 12px; text-align: left; }} th {{ background-color: #4CAF50; color: white; }} tr:nth-child(even) {{ background-color: #f2f2f2; }} /style /head body h1 Qwen-Image-Edit-F2P 自动化回归测试报告/h1 div classsummary h2测试概览/h2 pstrong执行时间:/strong {pd.Timestamp.now()}/p pstrong总用例数:/strong {total_cases}/p pstrong通过用例:/strong {passed_cases}/p pstrong失败用例:/strong {total_cases - passed_cases}/p pstrong通过率:/strong span class{pass if pass_rate 90 else fail}{pass_rate:.1f}%/span/p /div h2详细结果/h2 {df.to_html(indexFalse, classesresults-table, escapeFalse)} /body /html with open(report_path, w, encodingutf-8) as f: f.write(html_content) print(f\n测试报告已生成: {os.path.abspath(report_path)})代码解释record_result方法将每个用例的结果存入一个字典列表。generate_report方法使用pandas将结果列表转换成DataFrame然后生成一个美观的HTML报告。报告包含了测试概览总数、通过数、通过率和详细的表格结果并用颜色区分通过和失败的用例一目了然。4. 快速上手运行你的第一次自动化测试框架搭好了怎么用呢我们写一个简单的示例脚本。假设你已经有一个ComfyUI工作流它能接收一张猫的图片并根据提示词“将背景替换为星空”进行编辑。你之前运行过一次得到了满意的结果图cat_with_stars_baseline.png。第一步获取并准备prompt.json在ComfyUI中配置好工作流加载猫的图片连接好Qwen-Image-Edit-F2P节点设置好提示词然后点击“Queue Prompt”旁边的“Save (API Format)”将其保存为prompt_template.json。用文本编辑器打开它找到其中代表“输入图片”和“提示词”的节点记下它们的node_id。第二步创建测试脚本run_test.py# run_test.py import json from qwen_image_edit_tester import QwenImageEditTester # 假设上面的类保存在这个文件 def main(): # 1. 初始化测试器指定你的ComfyUI服务器地址 tester QwenImageEditTester(comfyui_server127.0.0.1:8188) # 2. 加载你的工作流API模板 with open(prompt_template.json, r, encodingutf-8) as f: prompt_template json.load(f) # 3. 关键步骤动态修改prompt中的输入 # 假设你的输入图片节点ID是 12 提示词节点ID是 23 input_image_node_id 12 prompt_text_node_id 23 # 修改为本次测试用的具体图片和提示词 test_image_path input_images/test_cat.jpg test_prompt_text 将背景替换为星空并添加一些发光粒子效果 # 注意这里需要根据你的prompt结构来赋值。通常是修改inputs里的image和text字段。 # 示例结构请根据你的实际JSON调整: prompt_template[input_image_node_id][inputs][image] test_image_path prompt_template[prompt_text_node_id][inputs][text] test_prompt_text # 4. 添加测试用例 tester.add_test_case( name测试-猫咪换星空背景, prompt_jsonprompt_template, # 使用修改后的prompt baseline_image_pathbaseline_images/cat_with_stars_baseline.png ) # 你可以继续添加更多用例... # tester.add_test_case(name测试-风景图转油画, ...) # 5. 运行测试套件 tester.run_test_suite() if __name__ __main__: main()第三步运行并查看报告在命令行中执行python run_test.py脚本会自动执行测试。完成后打开生成的test_report.html文件你就能看到本次回归测试的详细结果了。5. 让测试更完善一些实用建议基本的框架跑通了但要让它在实际项目中更可靠还可以考虑下面几点建立可靠的基准库在模型状态最稳定、效果最好的时候运行一批涵盖核心功能的测试如换背景、改风格、修复、扩图等将生成的图片作为“黄金标准”保存下来并计算好它们的哈希值。这个基准库是后续所有比较的基石。测试用例的设计用例要覆盖核心功能、边界情况如空提示词、复杂图片和常见用户操作。提示词也要多样化。阈值Threshold的设定哈希距离的阈值需要根据测试调整。太严如0会导致无关紧要的渲染差异也报错太松如20则可能漏掉真正的功能退化。可以从一个中间值如5开始根据测试结果慢慢调整。集成到CI/CD把这个测试脚本放到你的Git仓库里并配置到Jenkins、GitHub Actions等CI/CD工具中。每次代码提交或模型更新时自动运行实现真正的持续集成。失败用例的调试当测试失败时报告里会有生成图片的路径。手动打开这张图与基准图对比分析差异原因。是提示词理解有误还是生成质量确实下降了这能帮你快速定位问题。整体用下来这套基于Python的自动化测试框架确实能省不少心。它把原本需要人工反复操作的验证工作变成了一个按一下按钮或自动触发就能完成的标准流程。虽然搭建初期需要花点时间准备测试用例和基准图但一旦跑起来它就像个不知疲倦的哨兵能帮你牢牢守住模型功能稳定性的底线。尤其是在团队协作或频繁迭代的场景下它的价值会更加明显。下次再更新ComfyUI节点或者调整服务器环境时你就可以更加从容和自信了。如果你正在管理类似的AI应用不妨试着搭建一套从小范围的几个核心测试用例开始逐步完善它。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。