AI编程新范式:使用Claude Code辅助开发Qwen-Image-Edit-F2P应用脚本

1. 引言

你有没有过这样的经历?脑子里有个不错的应用想法,比如想做一个能批量处理图片的自动化工具,但一想到要写代码、调接口、处理各种异常,热情就凉了半截。从想法到实现,中间隔着一条叫“编码实现”的鸿沟。

最近我在尝试用Qwen-Image-Edit-F2P这个图像编辑模型做一些有趣的应用,发现它的API功能挺强大,能实现智能抠图、背景替换、风格转换这些效果。但每次手动调用API、处理返回结果,效率实在太低。我想写个脚本把这些流程自动化,可又不想花太多时间在重复的编码工作上。

这时候我想到了Claude Code——不是那个聊天机器人,而是专门为编程设计的AI助手。它就像个坐在你旁边的编程伙伴,你告诉它你想做什么,它就能帮你写出代码,还能解释代码逻辑,甚至帮你调试错误。

这篇文章我就带你体验一下,怎么用Claude Code辅助开发一个完整的Qwen-Image-Edit-F2P应用脚本。整个过程下来,我感觉编程方式真的变了——不再是孤独地对着屏幕敲代码,而是和AI一起“结对编程”,把更多精力放在创意和逻辑设计上。

2. 环境准备与项目规划

2.1 你需要准备什么

开始之前,我们先看看需要哪些东西。其实要求不高,大部分你可能都已经有了:

  • Python环境:3.8或更高版本就行。如果你还没装Python,去官网下载安装包,一路下一步就能搞定。
  • 代码编辑器:VS Code、PyCharm、Sublime Text都行,选你习惯的。我用的VS Code,因为它和Claude Code配合起来比较方便。
  • Claude Code访问:这个需要你有相应的使用权限。如果你还没有,可以看看相关的开发者平台。
  • Qwen-Image-Edit-F2P API密钥:要调用图像编辑服务,你得先申请一个API密钥。通常去模型的官方平台注册账号就能拿到。

2.2 我们的脚本要做什么

在动手写代码之前,先想清楚脚本要完成哪些功能。我计划做一个图片批量处理工具,主要功能包括:

  1. 读取本地图片:能自动扫描指定文件夹里的所有图片文件
  2. 调用编辑API:把图片上传到Qwen-Image-Edit-F2P,按照指定指令进行编辑
  3. 保存处理结果:把编辑后的图片下载保存到本地
  4. 错误处理:万一API调用失败或者网络有问题,脚本不能直接崩溃
  5. 进度显示:处理大量图片时,得让用户知道进行到哪一步了

有了这个清晰的目标,我们就能更有针对性地向Claude Code提问,让它帮我们生成对应的代码。

2.3 创建项目结构

好的项目结构能让后续开发更顺利。我在本地新建了一个文件夹,里面准备放这些文件:

qwen-image-editor/
├── main.py              # 主脚本文件
├── config.py            # 配置文件(放API密钥)
├── requirements.txt     # 依赖包列表
├── input_images/        # 存放待处理的图片
└── output_images/       # 存放处理后的图片

现在环境准备好了,目标也明确了,接下来就进入最有趣的部分——和Claude Code一起写代码。

3. 与Claude Code结对编程实战

3.1 第一次对话:搭建基础框架

我打开Claude Code,输入了第一个提示:

“我想用Python写一个脚本,调用Qwen-Image-Edit-F2P的API来编辑图片。需要能读取本地图片文件,调用API,然后保存处理后的图片。请帮我写一个基础框架。”

Claude Code几乎立刻就给出了回复。它先解释了一下大致的思路,然后给出了完整的代码:

import os
import requests
from pathlib import Path
from typing import List, Optional

class QwenImageEditor:
    def __init__(self, api_key: str, base_url: str = "https://api.example.com/v1"):
        """
        初始化图像编辑器
        
        Args:
            api_key: Qwen-Image-Edit-F2P的API密钥
            base_url: API基础地址
        """
        self.api_key = api_key
        self.base_url = base_url
        self.headers = {
            "Authorization": f"Bearer {api_key}",
            "Content-Type": "application/json"
        }
    
    def edit_image(self, image_path: str, instruction: str) -> Optional[bytes]:
        """
        编辑单张图片
        
        Args:
            image_path: 图片文件路径
            instruction: 编辑指令,比如“换背景为白色”“调整亮度”
            
        Returns:
            编辑后的图片二进制数据,失败时返回None
        """
        try:
            # 读取图片文件
            with open(image_path, 'rb') as f:
                image_data = f.read()
            
            # 准备请求数据
            payload = {
                "image": image_data,
                "instruction": instruction,
                "model": "qwen-image-edit-f2p"
            }
            
            # 调用API
            response = requests.post(
                f"{self.base_url}/images/edit",
                headers=self.headers,
                json=payload,
                timeout=30
            )
            
            if response.status_code == 200:
                return response.content
            else:
                print(f"API调用失败: {response.status_code} - {response.text}")
                return None
                
        except Exception as e:
            print(f"处理图片时出错: {e}")
            return None
    
    def save_image(self, image_data: bytes, output_path: str):
        """
        保存图片到本地
        
        Args:
            image_data: 图片二进制数据
            output_path: 保存路径
        """
        with open(output_path, 'wb') as f:
            f.write(image_data)
        print(f"图片已保存: {output_path}")

# 使用示例
if __name__ == "__main__":
    # 从配置文件读取API密钥
    API_KEY = "your_api_key_here"
    
    editor = QwenImageEditor(API_KEY)
    
    # 编辑单张图片
    result = editor.edit_image("input_images/example.jpg", "换背景为纯白色")
    if result:
        editor.save_image(result, "output_images/edited_example.jpg")

这段代码已经相当完整了,但我发现几个可以改进的地方。首先,API调用部分可能需要根据Qwen-Image-Edit-F2P的实际接口调整。其次,错误处理可以更细致一些。

3.2 第二次对话:完善批量处理功能

单张图片处理还不够,我需要批量处理功能。于是我又问Claude Code:

“上面的代码只能处理单张图片。现在我需要批量处理一个文件夹里的所有图片,支持常见的图片格式(jpg、png、webp),并且要显示处理进度。请帮我扩展这个功能。”

Claude Code这次给出了更详细的实现:

import os
import time
from concurrent.futures import ThreadPoolExecutor, as_completed
from tqdm import tqdm  # 进度条库

class BatchImageEditor(QwenImageEditor):
    def __init__(self, api_key: str, base_url: str = "https://api.example.com/v1"):
        super().__init__(api_key, base_url)
        self.supported_formats = {'.jpg', '.jpeg', '.png', '.webp', '.bmp'}
    
    def get_image_files(self, folder_path: str) -> List[str]:
        """
        获取文件夹中所有支持的图片文件
        
        Args:
            folder_path: 文件夹路径
            
        Returns:
            图片文件路径列表
        """
        image_files = []
        folder = Path(folder_path)
        
        if not folder.exists():
            print(f"文件夹不存在: {folder_path}")
            return image_files
        
        for file_path in folder.iterdir():
            if file_path.suffix.lower() in self.supported_formats:
                image_files.append(str(file_path))
        
        print(f"找到 {len(image_files)} 张图片")
        return image_files
    
    def process_single_image(self, image_path: str, instruction: str, output_dir: str) -> bool:
        """
        处理单张图片并保存
        
        Args:
            image_path: 图片路径
            instruction: 编辑指令
            output_dir: 输出目录
            
        Returns:
            处理是否成功
        """
        try:
            # 生成输出文件名
            input_file = Path(image_path)
            output_filename = f"edited_{input_file.stem}{input_file.suffix}"
            output_path = Path(output_dir) / output_filename
            
            # 调用父类方法编辑图片
            edited_image = self.edit_image(image_path, instruction)
            
            if edited_image:
                self.save_image(edited_image, str(output_path))
                return True
            else:
                print(f"处理失败: {image_path}")
                return False
                
        except Exception as e:
            print(f"处理图片时出错 {image_path}: {e}")
            return False
    
    def process_folder(self, input_folder: str, output_folder: str, instruction: str, 
                      max_workers: int = 3):
        """
        批量处理文件夹中的所有图片
        
        Args:
            input_folder: 输入文件夹路径
            output_folder: 输出文件夹路径
            instruction: 编辑指令
            max_workers: 最大并发数(避免API限流)
        """
        # 创建输出文件夹
        os.makedirs(output_folder, exist_ok=True)
        
        # 获取所有图片文件
        image_files = self.get_image_files(input_folder)
        if not image_files:
            print("没有找到可处理的图片")
            return
        
        print(f"开始批量处理 {len(image_files)} 张图片...")
        print(f"编辑指令: {instruction}")
        
        # 使用进度条
        successful = 0
        failed = 0
        
        with tqdm(total=len(image_files), desc="处理进度") as pbar:
            # 使用线程池并发处理
            with ThreadPoolExecutor(max_workers=max_workers) as executor:
                # 提交所有任务
                future_to_file = {
                    executor.submit(self.process_single_image, img, instruction, output_folder): img
                    for img in image_files
                }
                
                # 处理完成的任务
                for future in as_completed(future_to_file):
                    image_file = future_to_file[future]
                    try:
                        success = future.result()
                        if success:
                            successful += 1
                        else:
                            failed += 1
                    except Exception as e:
                        print(f"任务执行出错 {image_file}: {e}")
                        failed += 1
                    
                    pbar.update(1)
                    pbar.set_postfix({"成功": successful, "失败": failed})
        
        print(f"\n处理完成!成功: {successful}, 失败: {failed}")
        
        # 避免频繁调用API,添加延迟
        time.sleep(1)

# 使用示例
if __name__ == "__main__":
    # 安装进度条库:pip install tqdm
    API_KEY = "your_api_key_here"
    
    editor = BatchImageEditor(API_KEY)
    
    # 批量处理图片
    editor.process_folder(
        input_folder="input_images",
        output_folder="output_images",
        instruction="智能抠图,背景透明化",
        max_workers=2  # 保守一点,避免触发API限流
    )

这段代码加了很多实用功能:自动识别图片格式、进度显示、并发处理。Claude Code还贴心地提醒我要安装tqdm库来显示进度条。

3.3 第三次对话:添加配置文件和错误处理

现在基础功能都有了,但API密钥硬编码在代码里不安全,错误处理也可以更完善。我继续向Claude Code提问:

“请帮我添加配置文件功能,把API密钥和其他设置放到config.py里。另外,需要更完善的错误处理,包括网络重试、API限流处理、无效图片跳过等。”

Claude Code给出了配置文件和增强错误处理的方案:

首先创建config.py

# config.py - 配置文件

# Qwen-Image-Edit-F2P API配置
QWEN_API_KEY = "your_actual_api_key_here"  # 替换为你的真实API密钥
QWEN_BASE_URL = "https://api.example.com/v1"  # 根据实际API地址修改
QWEN_TIMEOUT = 30  # 请求超时时间(秒)

# 图片处理配置
SUPPORTED_FORMATS = ['.jpg', '.jpeg', '.png', '.webp', '.bmp', '.gif']
MAX_FILE_SIZE_MB = 10  # 最大文件大小(MB)
DEFAULT_INSTRUCTION = "智能优化图片质量"  # 默认编辑指令

# 并发和重试配置
MAX_WORKERS = 3  # 最大并发数
MAX_RETRIES = 3  # 失败重试次数
RETRY_DELAY = 2  # 重试延迟(秒)

# 路径配置
DEFAULT_INPUT_FOLDER = "input_images"
DEFAULT_OUTPUT_FOLDER = "output_images"

然后更新主脚本,加入重试机制和更健壮的错误处理:

import json
import time
from datetime import datetime
import config

class RobustImageEditor(BatchImageEditor):
    def __init__(self):
        super().__init__(config.QWEN_API_KEY, config.QWEN_BASE_URL)
        self.max_retries = config.MAX_RETRIES
        self.retry_delay = config.RETRY_DELAY
        self.error_log = []
    
    def edit_image_with_retry(self, image_path: str, instruction: str) -> Optional[bytes]:
        """
        带重试机制的图片编辑
        
        Args:
            image_path: 图片路径
            instruction: 编辑指令
            
        Returns:
            编辑后的图片数据,失败时返回None
        """
        for attempt in range(self.max_retries):
            try:
                # 检查文件大小
                file_size_mb = os.path.getsize(image_path) / (1024 * 1024)
                if file_size_mb > config.MAX_FILE_SIZE_MB:
                    error_msg = f"文件过大: {image_path} ({file_size_mb:.1f}MB > {config.MAX_FILE_SIZE_MB}MB)"
                    print(error_msg)
                    self.error_log.append({
                        "file": image_path,
                        "error": error_msg,
                        "time": datetime.now().isoformat()
                    })
                    return None
                
                # 调用API
                result = self.edit_image(image_path, instruction)
                
                if result:
                    return result
                else:
                    if attempt < self.max_retries - 1:
                        wait_time = self.retry_delay * (attempt + 1)
                        print(f"第{attempt + 1}次尝试失败,{wait_time}秒后重试...")
                        time.sleep(wait_time)
                    else:
                        error_msg = f"达到最大重试次数仍失败: {image_path}"
                        print(error_msg)
                        self.error_log.append({
                            "file": image_path,
                            "error": error_msg,
                            "time": datetime.now().isoformat()
                        })
                        
            except requests.exceptions.Timeout:
                if attempt < self.max_retries - 1:
                    wait_time = self.retry_delay * (attempt + 1)
                    print(f"请求超时,{wait_time}秒后重试...")
                    time.sleep(wait_time)
                else:
                    error_msg = f"请求超时,达到最大重试次数: {image_path}"
                    print(error_msg)
                    self.error_log.append({
                        "file": image_path,
                        "error": error_msg,
                        "time": datetime.now().isoformat()
                    })
                    
            except Exception as e:
                error_msg = f"处理图片时发生错误 {image_path}: {str(e)}"
                print(error_msg)
                self.error_log.append({
                    "file": image_path,
                    "error": error_msg,
                    "time": datetime.now().isoformat()
                })
                return None
        
        return None
    
    def save_error_log(self, log_file: str = "error_log.json"):
        """保存错误日志到文件"""
        if self.error_log:
            with open(log_file, 'w', encoding='utf-8') as f:
                json.dump(self.error_log, f, ensure_ascii=False, indent=2)
            print(f"错误日志已保存到: {log_file}")
    
    def process_folder_robust(self, input_folder: str = None, output_folder: str = None, 
                            instruction: str = None):
        """
        健壮的批量处理流程
        
        Args:
            input_folder: 输入文件夹,默认为配置中的路径
            output_folder: 输出文件夹,默认为配置中的路径
            instruction: 编辑指令,默认为配置中的指令
        """
        # 使用默认值
        input_folder = input_folder or config.DEFAULT_INPUT_FOLDER
        output_folder = output_folder or config.DEFAULT_OUTPUT_FOLDER
        instruction = instruction or config.DEFAULT_INSTRUCTION
        
        print("=" * 50)
        print("Qwen图像批量处理工具")
        print(f"输入文件夹: {input_folder}")
        print(f"输出文件夹: {output_folder}")
        print(f"编辑指令: {instruction}")
        print("=" * 50)
        
        # 检查文件夹
        if not os.path.exists(input_folder):
            print(f"错误: 输入文件夹不存在 - {input_folder}")
            return
        
        # 创建输出文件夹
        os.makedirs(output_folder, exist_ok=True)
        
        # 清空错误日志
        self.error_log = []
        
        # 批量处理
        self.process_folder(input_folder, output_folder, instruction, config.MAX_WORKERS)
        
        # 保存错误日志
        self.save_error_log()
        
        print("处理完成!")

# 主程序入口
def main():
    """主函数"""
    print("Qwen图像编辑脚本启动...")
    
    # 创建编辑器实例
    editor = RobustImageEditor()
    
    # 开始处理
    editor.process_folder_robust()
    
    print("程序执行完毕")

if __name__ == "__main__":
    main()

现在这个脚本已经相当完善了。Claude Code不仅帮我写了代码,还解释了为什么要这样设计:配置文件让设置更灵活,重试机制提高了稳定性,错误日志方便后续排查问题。

4. 实际运行与调试

4.1 安装依赖和准备环境

代码写好了,但要运行起来还需要一些准备工作。Claude Code提醒我需要安装几个Python库:

# 创建requirements.txt文件
echo "requests>=2.28.0
tqdm>=4.64.0
pathlib>=1.0.1" > requirements.txt

# 安装依赖
pip install -r requirements.txt

然后需要准备好config.py文件,填入真实的API密钥和接口地址。Claude Code还提醒我:

“记得在运行前创建input_images文件夹,并放入一些测试图片。输出文件夹output_images会自动创建。建议先用一两张图片测试,确保API调用正常后再进行批量处理。”

4.2 测试运行

我放了几张图片到input_images文件夹,然后运行脚本:

python main.py

第一次运行就遇到了问题——API返回了认证错误。我把错误信息复制给Claude Code:

“运行脚本时出现错误:{'error': 'Invalid API key'}。我的API密钥在config.py里设置的是正确的,这是什么问题?”

Claude Code很快给出了排查建议:

  1. 检查API密钥是否有空格或换行
  2. 确认API基础地址是否正确
  3. 尝试用curl命令直接测试API
  4. 检查网络连接和防火墙设置

我按照建议检查,发现是base_url设置错了。修正之后,脚本成功运行起来,看着进度条一点点前进,图片一张张被处理,那种感觉真的很棒。

4.3 处理常见问题

在测试过程中,我还遇到了几个其他问题:

问题1:有些图片处理特别慢 Claude Code建议我添加超时设置和并发控制,避免同时处理太多图片。

问题2:API返回了尺寸限制错误 Claude Code帮我添加了文件大小检查,自动跳过过大的图片。

问题3:输出图片格式不一致 有些图片处理后格式变了,Claude Code建议在保存时统一格式,或者保留原始格式。

经过几轮调试,脚本变得越来越稳定。Claude Code在这个过程中就像个不知疲倦的编程伙伴,随时帮我分析问题、修改代码。

5. 扩展功能与优化建议

5.1 添加更多实用功能

基础功能稳定后,我想让脚本更强大一些。我又向Claude Code提了几个需求:

需求1:支持不同的编辑模式

“Qwen-Image-Edit-F2P应该支持多种编辑模式,比如抠图、背景替换、风格转换等。请帮我扩展脚本,让用户可以选择不同的模式。”

Claude Code给出了一个灵活的方案:

# 在config.py中添加
EDIT_MODES = {
    "remove_bg": "智能抠图,背景透明化",
    "replace_bg": "替换背景为纯色",
    "enhance": "智能增强图片质量",
    "cartoon": "转换为卡通风格",
    "sketch": "转换为素描风格"
}

# 在主脚本中添加模式选择
def interactive_mode():
    """交互式模式"""
    print("请选择编辑模式:")
    for i, (mode, desc) in enumerate(config.EDIT_MODES.items(), 1):
        print(f"{i}. {desc} ({mode})")
    
    choice = input("\n请输入模式编号(1-5): ").strip()
    
    mode_mapping = list(config.EDIT_MODES.keys())
    if choice.isdigit() and 1 <= int(choice) <= len(mode_mapping):
        selected_mode = mode_mapping[int(choice) - 1]
        instruction = config.EDIT_MODES[selected_mode]
        
        # 询问是否使用自定义指令
        custom = input(f"使用默认指令 '{instruction}'?(y/n): ").lower()
        if custom == 'n':
            instruction = input("请输入自定义编辑指令: ").strip()
        
        return instruction
    else:
        print("无效选择,使用默认模式")
        return config.DEFAULT_INSTRUCTION

需求2:添加图片预览功能

“处理前后如果能有个对比预览就好了,不用每次都打开文件夹查看。”

Claude Code建议可以生成HTML报告:

def generate_html_report(input_folder: str, output_folder: str):
    """生成HTML格式的处理报告"""
    html_content = """
    <!DOCTYPE html>
    <html>
    <head>
        <title>图片处理报告</title>
        <style>
            body { font-family: Arial, sans-serif; margin: 20px; }
            .image-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 20px; }
            .image-pair { border: 1px solid #ddd; padding: 10px; border-radius: 5px; }
            .image-pair img { max-width: 100%; height: auto; }
            .caption { text-align: center; margin-top: 5px; color: #666; }
        </style>
    </head>
    <body>
        <h1>图片处理报告</h1>
        <p>生成时间: {timestamp}</p>
        <div class="image-grid">
    """
    
    # 收集图片对
    image_pairs = []
    for img_file in os.listdir(input_folder):
        if img_file.lower().endswith(('.jpg', '.jpeg', '.png', '.webp')):
            input_path = os.path.join(input_folder, img_file)
            output_path = os.path.join(output_folder, f"edited_{img_file}")
            
            if os.path.exists(output_path):
                image_pairs.append((img_file, input_path, output_path))
    
    # 添加图片对到HTML
    for filename, input_path, output_path in image_pairs:
        html_content += f"""
            <div class="image-pair">
                <div>
                    <img src="{input_path}" alt="原图">
                    <div class="caption">原图: {filename}</div>
                </div>
                <div>
                    <img src="{output_path}" alt="处理后">
                    <div class="caption">处理后: edited_{filename}</div>
                </div>
            </div>
        """
    
    html_content += """
        </div>
    </body>
    </html>
    """
    
    # 添加时间戳
    from datetime import datetime
    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    html_content = html_content.format(timestamp=timestamp)
    
    # 保存HTML文件
    report_path = os.path.join(output_folder, "processing_report.html")
    with open(report_path, 'w', encoding='utf-8') as f:
        f.write(html_content)
    
    print(f"处理报告已生成: {report_path}")
    return report_path

5.2 性能优化建议

Claude Code还主动给出了一些优化建议:

  1. 缓存机制:对相同的图片和指令,可以缓存处理结果,避免重复调用API
  2. 批量请求:如果API支持,可以一次上传多张图片,减少网络请求次数
  3. 本地预处理:一些简单的操作(如调整尺寸、格式转换)可以在本地完成,减少API调用
  4. 异步处理:使用asyncio实现真正的异步处理,提高吞吐量

这些建议让我看到了脚本进一步优化的方向。虽然当前版本已经能满足基本需求,但有了这些思路,未来可以做得更好。

6. 总结

和Claude Code一起开发这个Qwen图像编辑脚本的过程,让我对AI辅助编程有了新的认识。以前写这种工具脚本,从设计到调试至少得花一两天时间,这次只用了几个小时就完成了核心功能。

最大的感受是,Claude Code不仅仅是个代码生成器,它更像是个经验丰富的编程搭档。它能理解我的意图,给出合理的实现方案,还能在我遇到问题时提供排查思路。我不再需要反复搜索文档、在Stack Overflow上找答案,大部分问题都能通过对话解决。

这个脚本现在虽然简单,但已经具备了生产可用的基础:健壮的错误处理、进度显示、批量并发、配置管理。更重要的是,整个开发过程很顺畅,我可以把精力集中在功能设计上,而不是语法细节上。

如果你也想尝试AI辅助编程,我的建议是:先从一个小项目开始,明确需求,然后一步步和AI对话完善。不要指望AI一次性能给出完美代码,而是要把它当作思考伙伴,通过多次迭代让代码越来越好用。


获取更多AI镜像

想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐