Nunchaku FLUX.1-dev 文生图持续集成:GitHub Actions自动化测试生成质量

Nunchaku FLUX.1-dev 文生图持续集成:GitHub Actions自动化测试生成质量 Nunchaku FLUX.1-dev 文生图持续集成GitHub Actions自动化测试生成质量最近在带团队做一个文生图应用最头疼的就是模型更新。每次提示词库一改或者模型版本一升级心里就直打鼓这次生成的图质量会不会掉下去以前全靠人工抽查费时费力不说还容易漏掉问题。后来我们琢磨了一套自动化流程用GitHub Actions把测试给跑起来了效果还不错今天就跟大家聊聊这个事。简单说就是给我们的Nunchaku FLUX.1-dev模型开发流程加了个“质检机器人”。代码或者模型一有变动这个机器人就自动启动按照预设的提示词生成一批图再用一些指标给这些图打分。如果分数没达标或者比上次差太多它就直接在GitHub上给我们提个醒这样我们就能第一时间知道哪里出了问题。1. 为什么需要自动化测试做文生图应用尤其是团队协作最怕的就是“黑盒更新”。你改了几十个提示词或者换了个新版的FLUX.1-dev模型怎么知道效果是变好了还是变坏了靠人眼一张张看不现实也容易主观。我们之前就踩过坑。有一次优化了提示词模板本地测试了几张图感觉挺好就合并到主分支了。结果上线后用户反馈生成某些特定主题的图片风格变得很奇怪质量明显下降。回头一查就是新提示词对某些边缘情况处理不好但我们人工测试时根本没覆盖到。所以自动化测试的核心价值就两点一是保证质量基线确保任何更新都不会让生成效果低于可接受的标准二是快速反馈开发或算法同学提交代码后几分钟内就能知道这次改动对生成质量的影响是正向还是负向不用等测试人员介入。2. 自动化测试流程设计我们的目标很明确搭建一个无人值守的、可重复的、指标化的质量评估流水线。整个流程围绕着GitHub仓库来设计因为我们的代码、提示词库、甚至模型配置都放在上面。2.1 核心流程概览整个流程可以概括为“事件触发 - 执行任务 - 评估反馈”三个步骤。事件触发当有新的代码提交到主分支main或者有人向提示词库文件比如prompts.json发起合并请求Pull Request时流程自动开始。执行任务GitHub Actions会启动一个虚拟环境在里面准备好Python、必要的依赖库以及访问我们Nunchaku FLUX.1-dev模型的API密钥。然后它会运行我们写好的测试脚本。评估反馈测试脚本会做两件事一是调用模型API用一批预设的测试提示词生成图片二是调用评估脚本计算这些生成图片的质量分数比如清晰度、与提示词的相关性等。最后把本次的分数和历史分数对比生成一份报告并更新到GitHub的提交状态或PR评论里。这样一来每次改动都像过一次“安检”合不合并数据说了算。2.2 关键技术组件要实现这个流程主要靠几个东西配合GitHub Actions这是我们的自动化引擎。它就像一个随时待命的机器人监听着仓库里的动静。我们写一个YAML配置文件比如.github/workflows/image_quality_test.yml告诉它“嘿如果发生了某某事件你就去某某地方按照某某步骤执行。”测试提示词库这是一组精心挑选的、有代表性的提示词。它们需要覆盖我们应用的主要场景比如人物、风景、物体、抽象概念也要包含一些容易出错的边界案例。这个库本身也是一个文件放在仓库里任何修改都会触发测试。质量评估脚本这是判断好坏的“裁判”。完全靠人工评价不现实所以我们用一些可量化的指标。常见的比如CLIP Score评估生成的图片与输入提示词的语义匹配程度。分数越高说明图越“扣题”。图像清晰度评估比如计算图像的拉普拉斯方差值越大通常表示图像越清晰、细节越多。美学评分有一些预训练的模型可以给图片的“好看程度”打分。 我们会综合几项指标给出一个总分。这个脚本也是Python写的在Actions里运行。Nunchaku FLUX.1-dev API这是我们的被测对象。测试脚本会通过HTTP请求调用它传入提示词和参数拿到生成的图片。3. 实战搭建GitHub Actions流水线光说原理有点干我们直接看看怎么把它搭起来。假设我们的项目仓库叫my-ai-image-app。3.1 第一步准备测试脚本首先在项目根目录创建一个scripts/文件夹里面放两个Python脚本。第一个是generate_test_images.py负责调用API生成图片# scripts/generate_test_images.py import os import requests import json from pathlib import Path # 从环境变量读取配置 API_BASE_URL os.getenv(FLUX_API_BASE, https://api.nunchaku.ai/v1) API_KEY os.getenv(FLUX_API_KEY) TEST_PROMPTS_FILE test_prompts.json OUTPUT_DIR Path(generated_images) def load_test_prompts(): 加载测试提示词库 with open(TEST_PROMPTS_FILE, r) as f: prompts_data json.load(f) return prompts_data[prompts] # 假设是列表格式 def call_flux_api(prompt, seed42): 调用FLUX.1-dev API生成图片 headers { Authorization: fBearer {API_KEY}, Content-Type: application/json } payload { model: FLUX.1-dev, prompt: prompt, num_inference_steps: 50, guidance_scale: 7.5, seed: seed, output_format: url # 假设API返回图片URL } try: response requests.post(f{API_BASE_URL}/images/generations, headersheaders, jsonpayload, timeout60) response.raise_for_status() result response.json() # 假设返回结构为 {data: [{url: ...}]} image_url result[data][0][url] return image_url except requests.exceptions.RequestException as e: print(f调用API失败提示词: {prompt[:50]}... 错误: {e}) return None def download_image(url, filename): 下载图片到本地 response requests.get(url, timeout30) response.raise_for_status() with open(filename, wb) as f: f.write(response.content) def main(): if not API_KEY: raise ValueError(请设置 FLUX_API_KEY 环境变量) OUTPUT_DIR.mkdir(exist_okTrue) prompts load_test_prompts() print(f开始为 {len(prompts)} 个提示词生成测试图片...) results [] for i, prompt_item in enumerate(prompts): prompt_text prompt_item[text] print(f处理 ({i1}/{len(prompts)}): {prompt_text[:60]}...) image_url call_flux_api(prompt_text, seedi) if image_url: image_path OUTPUT_DIR / ftest_{i:03d}.png download_image(image_url, image_path) results.append({ prompt: prompt_text, image_path: str(image_path), seed: i }) else: print(f 生成失败跳过。) # 保存本次生成记录 with open(generation_results.json, w) as f: json.dump({results: results}, f, indent2) print(测试图片生成完成。) if __name__ __main__: main()第二个是evaluate_quality.py负责评估图片质量# scripts/evaluate_quality.py import json import numpy as np from pathlib import Path from PIL import Image import cv2 # 这里需要安装一些库pip install Pillow opencv-python-headless torch torchvision def calculate_clip_score(image_path, prompt): 计算CLIP分数示例需替换为真实CLIP模型调用。 这里简化处理实际应用中应使用预训练的CLIP模型。 # 此处为模拟逻辑真实情况需加载CLIP模型进行计算 # 假设返回一个0-1之间的分数 return np.random.uniform(0.7, 0.95) # 模拟值 def calculate_image_sharpness(image_path): 使用拉普拉斯方差计算图像清晰度 image cv2.imread(image_path, cv2.IMREAD_GRAYSCALE) if image is None: return 0.0 laplacian_var cv2.Laplacian(image, cv2.CV_64F).var() # 归一化处理使其大致在0-1范围具体阈值需根据实际情况调整 normalized_score min(laplacian_var / 500.0, 1.0) return normalized_score def main(): with open(generation_results.json, r) as f: data json.load(f) evaluation_results [] total_clip_score 0 total_sharpness_score 0 for item in data[results]: image_path item[image_path] prompt item[prompt] if not Path(image_path).exists(): continue clip_score calculate_clip_score(image_path, prompt) sharpness_score calculate_image_sharpness(image_path) # 简单加权计算综合分权重可根据业务调整 composite_score clip_score * 0.6 sharpness_score * 0.4 result { prompt: prompt, image_file: Path(image_path).name, clip_score: round(clip_score, 4), sharpness_score: round(sharpness_score, 4), composite_score: round(composite_score, 4) } evaluation_results.append(result) total_clip_score clip_score total_sharpness_score sharpness_score num_valid len(evaluation_results) avg_clip total_clip_score / num_valid if num_valid 0 else 0 avg_sharpness total_sharpness_score / num_valid if num_valid 0 else 0 avg_composite (avg_clip * 0.6 avg_sharpness * 0.4) if num_valid 0 else 0 summary { total_tested: num_valid, average_scores: { clip_score: round(avg_clip, 4), sharpness_score: round(avg_sharpness, 4), composite_score: round(avg_composite, 4) }, details: evaluation_results } with open(quality_report.json, w) as f: json.dump(summary, f, indent2, ensure_asciiFalse) print(质量评估完成。) print(f平均综合分: {avg_composite:.4f} (CLIP: {avg_clip:.4f}, 清晰度: {avg_sharpness:.4f})) # 将综合分输出到环境变量供后续步骤使用 with open(os.environ[GITHUB_OUTPUT], a) as fh: print(fcomposite_score{avg_composite}, filefh) if __name__ __main__: main()3.2 第二步配置GitHub Actions工作流接下来在项目根目录创建.github/workflows/image_quality_test.yml文件# .github/workflows/image_quality_test.yml name: FLUX Image Quality Test on: push: branches: [ main ] paths: - test_prompts.json - scripts/** - model_configs/** pull_request: branches: [ main ] paths: - test_prompts.json - scripts/** - model_configs/** jobs: test-image-quality: runs-on: ubuntu-latest steps: - name: 检出代码 uses: actions/checkoutv4 - name: 设置Python环境 uses: actions/setup-pythonv5 with: python-version: 3.10 - name: 安装依赖 run: | pip install requests Pillow opencv-python-headless numpy # 如有其他依赖如torch, transformers用于CLIP也在此安装 - name: 生成测试图片 env: FLUX_API_KEY: ${{ secrets.FLUX_API_KEY }} FLUX_API_BASE: ${{ vars.FLUX_API_BASE }} run: python scripts/generate_test_images.py - name: 评估图片质量 id: evaluate run: python scripts/evaluate_quality.py - name: 上传生成图片作为工作流产物可选 uses: actions/upload-artifactv4 if: always() # 即使失败也上传便于调试 with: name: generated-test-images path: generated_images/ retention-days: 7 - name: 上传质量报告 uses: actions/upload-artifactv4 with: name: quality-report path: quality_report.json - name: 检查质量分数 id: check_score run: | COMPOSITE_SCORE${{ steps.evaluate.outputs.composite_score }} # 设置一个合格线例如0.75。可以从文件读取历史基线进行对比。 THRESHOLD0.75 echo 当前综合分: $COMPOSITE_SCORE, 阈值: $THRESHOLD if (( $(echo $COMPOSITE_SCORE $THRESHOLD | bc -l) )); then echo 质量分数低于阈值 echo ::error::图片生成质量综合分$COMPOSITE_SCORE低于合格线$THRESHOLD。 exit 1 else echo 质量检查通过。 fi3.3 第三步设置仓库密钥和环境变量为了让Actions能安全地调用我们的模型API需要把API密钥存起来。进入你的GitHub仓库页面。点击Settings-Secrets and variables-Actions。点击New repository secret创建一个名为FLUX_API_KEY的密钥值就是你的真实API密钥。如果需要也可以在Variables标签页下创建一个变量FLUX_API_BASE填入你的API基础地址。这样工作流运行时就能通过${{ secrets.FLUX_API_KEY }}安全地获取密钥了。4. 效果与收益这套流程跑起来以后给我们团队带来的变化是实实在在的。最直观的就是反馈速度。以前提个PR要等同事有空了才能手动测试快则半天慢则一两天。现在提交后几分钟GitHub Actions就自动跑完了测试结果直接贴在PR下面。如果分数不达标工作流状态直接显示失败红色的大叉非常醒目谁都不敢轻易合并。其次是质量稳定性。我们设定了一个综合分的合格线比如0.75也记录了每次主分支测试的历史平均分。任何导致综合分下降超过5%的改动都会触发警报。这相当于给我们的生成质量装了一个“稳压器”避免了因为追求某个特性而牺牲整体质量的情况。最后是团队协作更顺畅。算法工程师调整了模型参数前端同学修改了提示词拼接逻辑大家提交代码后都用同一套标准、同一批测试用例来验证效果。讨论问题的时候不再是“我感觉效果变差了”而是“这次CLIP分数下降了0.1我们看看是哪些提示词导致的”沟通效率高了很多。当然这套流程也不是一劳永逸的。测试提示词库需要随着业务发展不断维护和丰富质量评估指标也需要根据实际业务效果进行调优。但它为我们建立了一个自动化的、数据驱动的质量守护基础让迭代更有信心。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。