mcpField
A
ai-switching-models

ai-switching-models

github.com/lebsral开发1261 次安装17 周前更新
代码仓库

关于 ai-switching-models

面向计划在 DSPy 程序中切换 AI 模型或提供商的开发者。该技能指导建立基准评估、针对各模型重新优化提示词并系统比较,同时保留任务签名和模块。

主要功能

  1. 01评估当前模型基准
  2. 02配置其他提供商
  3. 03针对各模型重新优化提示词
  4. 04通过评估比较候选模型
  5. 05为不同模块分配模型
  6. 06按模型保存优化程序

适用场景

  1. 01评估成本更低的模型
  2. 02在不同提供商间迁移任务
  3. 03为隐私需求测试本地模型
  4. 04评估模型版本更新导致的退化

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/lebsral/dspy-programming-not-prompting-lms-skills --skill ai-switching-models

安装后在 Claude Code 中输入/ai-switching-models确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

audit.yaml
examples.md
reference.md
evals/
evals.json

详细介绍

切换模型而不破坏现有功能

引导用户安全地切换 AI 模型或提供商。关键在于:经过优化的提示词无法直接迁移到其他模型(arxiv 2402.10949v2 — “The Unreasonable Effectiveness of Eccentric Automatic Prompts”)。DSPy 通过将任务定义(签名 + 模块)与特定于模型的提示词(由优化器编译)分离,解决了这个问题。

为什么切换模型会导致问题

经过手动调整的提示词特定于模型。针对 GPT-4o 设计的提示词,在 Claude、Llama 甚至 GPT-4o-mini 上的表现都会有所不同。研究表明,为某个模型优化的提示词,实际上可能会降低另一个模型的表现。

DSPy 让切换模型更安全,因为:

  • 签名定义任务是什么(输入、输出、类型)——与模型无关
  • 模块定义如何解决任务(思维链、ReAct 等)——与模型无关
  • 编译后的提示词(少样本示例、指令)特定于模型——但优化器会自动重新生成

**工作流:**保持程序不变,切换模型,重新优化。完成。

步骤 1:了解情况

询问用户:

  1. 你现在使用的是什么模型,想切换到什么模型?(例如,从 GPT-4o 切换到 Claude,从云端切换到本地)
  2. 为什么要切换?(成本、供应商多元化、性能退化、隐私)
  3. 你有评估指标和测试数据吗?(衡量切换是否有效所必需——如果没有,请先使用 /ai-improving-accuracy)

常见场景:

  • 降低成本——“GPT-4o 太贵了,我们能用更便宜的吗?”
  • 供应商多元化——“我们不能依赖单一提供商”
  • 性能退化——“提供商更新了模型,我们的输出变差了”
  • 数据隐私 / 合规——“我们需要在自己的基础设施上运行模型”

步骤 2:配置任意提供商

DSPy 底层使用 LiteLLM,因此你可以通过一个简单的字符串使用任何受支持的提供商:

import dspy

# OpenAI
lm = dspy.LM("openai/gpt-4o")
lm = dspy.LM("openai/gpt-4o-mini")

# Anthropic
lm = dspy.LM("anthropic/claude-sonnet-4-5-20250929")
lm = dspy.LM("anthropic/claude-haiku-4-5-20251001")

# Azure OpenAI
lm = dspy.LM("azure/my-gpt4-deployment")

# Google
lm = dspy.LM("gemini/gemini-2.0-flash")

# Together AI (open-source models)
lm = dspy.LM("together_ai/meta-llama/Llama-3-70b-chat-hf")

# Local models (via Ollama)
lm = dspy.LM("ollama_chat/llama3.1", api_base="http://localhost:11434")

# Any OpenAI-compatible server (vLLM, TGI, etc.)
lm = dspy.LM("openai/my-model", api_base="http://localhost:8000/v1", api_key="none")

dspy.configure(lm=lm)
环境变量

将 API 密钥设为环境变量——不要将其硬编码:

# .env file
OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=sk-ant-...
TOGETHER_API_KEY=...
AZURE_API_KEY=...
AZURE_API_BASE=https://your-resource.openai.azure.com/

完整的 100 多个受支持提供商列表,请参阅 LiteLLM 提供商文档。

步骤 3:评测当前模型

在更改任何内容之前,先测量基准表现。你需要一个指标和测试数据。

from dspy.evaluate import Evaluate

# Your existing program and metric
program = MyProgram()
program.load("current_optimized.json")  # load your production prompts

evaluator = Evaluate(
    devset=devset,
    metric=metric,
    num_threads=4,
    display_progress=True,
    display_table=5,
)

# Benchmark with your current model
current_lm = dspy.LM("openai/gpt-4o")
dspy.configure(lm=current_lm)
baseline_score = evaluator(program)
print(f"Current model baseline: {baseline_score:.1f}%")

如果你还没有指标或测试数据,请先使用 /ai-improving-accuracy 进行设置。

步骤 4:试用新模型(快速测试)

切换模型并运行评估,不要重新优化。这可以直观地展示问题——旧提示词无法直接迁移。

# Try the new model with your OLD optimized prompts
new_lm = dspy.LM("anthropic/claude-sonnet-4-5-20250929")
dspy.configure(lm=new_lm)

naive_score = evaluator(program)
print(f"Old model (optimized):  {baseline_score:.1f}%")
print(f"New model (old prompts): {naive_score:.1f}%")
print(f"Drop: {baseline_score - naive_score:.1f}%")

你通常会看到质量下降——这是预期结果。优化后的提示词是针对旧模型调优的。

步骤 5:针对新模型重新优化

现在针对新模型重新优化程序。使用相同的签名和模块——只有编译后的提示词会改变。

# Configure the new model
new_lm = dspy.LM("anthropic/claude-sonnet-4-5-20250929")
dspy.configure(lm=new_lm)

# Start from a fresh (unoptimized) program
fresh_program = MyProgram()

# Re-optimize for the new model
optimizer = dspy.MIPROv2(metric=metric, auto="medium")
optimized_for_new = optimizer.compile(fresh_program, trainset=trainset)

# Evaluate
reoptimized_score = evaluator(optimized_for_new)
print(f"Old model (optimized):      {baseline_score:.1f}%")
print(f"New model (old prompts):     {naive_score:.1f}%")
print(f"New model (re-optimized):    {reoptimized_score:.1f}%")

重新优化后的得分应能恢复大部分或全部质量。如果没有,则可以:

  • 新模型确实无法同样出色地处理此任务
  • 尝试更重度的优化(auto="heavy")
  • 先尝试 BootstrapFewShot,快速检查结果是否合理
选择合适的重新优化器
优化器速度质量上限适用场景
BootstrapFewShot快(几分钟)良好快速检查、预算有限、早期迭代
MIPROv2(auto="light")中等更好大多数切换场景的默认重新优化选项
MIPROv2(auto="medium")慢(30-60 分钟)最佳本地 / 小型模型、质量至关重要
MIPROv2(auto="heavy")非常慢最佳medium 仍表现不佳时的最后手段
快速重新优化(快速测试)

在决定运行完整的 MIPROv2 之前,可先快速检查:

optimizer = dspy.BootstrapFewShot(
    metric=metric,
    max_bootstrapped_demos=4,
    max_labeled_demos=4,
)
quick_optimized = optimizer.compile(fresh_program, trainset=trainset)
quick_score = evaluator(quick_optimized)

步骤 6:系统地比较模型

遍历候选模型,分别优化,并构建比较表:

candidates = [
    ("openai/gpt-4o", "GPT-4o"),
    ("openai/gpt-4o-mini", "GPT-4o-mini"),
    ("anthropic/claude-sonnet-4-5-20250929", "Claude Sonnet"),
    ("together_ai/meta-llama/Llama-3-70b-chat-hf", "Llama 3 70B"),
]

results = []
for model_id, label in candidates:
    lm = dspy.LM(model_id)
    dspy.configure(lm=lm)

    # Optimize for this model
    fresh = MyProgram()
    optimizer = dspy.BootstrapFewShot(metric=metric, max_bootstrapped_demos=4)
    optimized = optimizer.compile(fresh, trainset=trainset)

    # Evaluate
    score = evaluator(optimized)

    # Save the optimized program
    optimized.save(f"optimized_{label.lower().replace(' ', '_')}.json")

    results.append({"model": label, "score": score})
    print(f"{label}: {score:.1f}%")

# Print comparison table
print("\n--- Model Comparison ---")
print(f"{'Model':<25} {'Score':>8}")
print("-" * 35)
for r in sorted(results, key=lambda x: x["score"], reverse=True):
    print(f"{r['model']:<25} {r['score']:>7.1f}%")

如需使用 MIPROv2 进行更全面的比较,并跟踪成本和延迟,请参阅 examples.md。

步骤 7:在一个流水线中混用模型

不必所有任务都使用同一个模型。为不同步骤分配不同模型——简单任务用低成本模型,困难任务用高成本模型。

使用 dspy.context(临时,按调用)
cheap_lm = dspy.LM("openai/gpt-4o-mini")
expensive_lm = dspy.LM("openai/gpt-4o")

dspy.configure(lm=expensive_lm)  # default

class MyPipeline(dspy.Module):
    def __init__(self):
        self.classify = dspy.Predict(ClassifySignature)
        self.generate = dspy.ChainOfThought(GenerateSignature)

    def forward(self, text):
        # Cheap model for simple classification
        with dspy.context(lm=cheap_lm):
            category = self.classify(text=text)

        # Expensive model for complex generation
        return self.generate(text=text, category=category.label)
使用 set_lm(永久,按模块)
pipeline = MyPipeline()
pipeline.classify.set_lm(cheap_lm)
pipeline.generate.set_lm(expensive_lm)

请参阅 /ai-cutting-costs,了解更多按模块分配 LM 的成本优化模式。

步骤 8:保存并部署

为生产环境中可能使用的每个模型分别保存优化后的程序:

# Save per-model optimized programs
optimized_gpt4o.save("optimized_gpt4o.json")
optimized_claude.save("optimized_claude.json")
optimized_llama.save("optimized_llama.json")

# In production — load the right one
import os

model_name = os.environ.get("AI_MODEL", "openai/gpt-4o")
lm = dspy.LM(model_name)
dspy.configure(lm=lm)

program = MyProgram()
program.load(f"optimized_{model_name.split('/')[-1]}.json")

常见场景

从 GPT-4o 切换到 GPT-4o-mini(降低成本)
  1. 对 GPT-4o 基准版本进行评测(步骤 2)
  2. 使用旧提示词尝试 GPT-4o-mini——观察性能下降情况(步骤 3)
  3. 使用 MIPROv2 为 GPT-4o-mini 重新优化(步骤 4)
  4. 比较分数——如果质量足够接近,就可以投入使用
从 OpenAI 切换到 Anthropic(供应商多元化)
  1. 在环境中设置 Anthropic API 密钥
  2. 更改模型字符串:"openai/gpt-4o" 改为 "anthropic/claude-sonnet-4-5-20250929"
  3. 重新优化——不同模型需要不同的提示词
  4. 保留两个优化后的程序,通过环境变量进行切换
从云端切换到本地(数据隐私)
  1. 设置本地模型服务(Ollama、vLLM 或 TGI)
  2. 将 DSPy 指向该服务:dspy.LM("ollama_chat/llama3.1", api_base="http://localhost:11434")
  3. 重新优化——本地模型尤其需要重新优化
  4. 与大型云端模型相比,预期会有一定的质量取舍;请使用更强的优化方法
模型版本更新导致问题

当供应商更新其模型时(例如 GPT-4o 版本升级):

  1. 运行评测以确认性能回退
  2. 针对更新后的模型重新优化
  3. 保存新的优化程序
  4. 这就是为什么在工作流中进行评测和优化很重要——版本更新会成为常规操作,而不是紧急事件

何时不应切换模型

  • 你尚未设置评测。 如果没有指标和测试集,就无法判断新模型是更好还是更差。请先使用 /ai-improving-accuracy 设置评测。
  • 你在调试提示词质量,而不是模型。 如果当前模型的输出质量不佳,切换模型无法修复定义不清的签名或缺失的示例。请先优化当前配置。
  • 你只需要更快的响应,而不是不同的模型。 如果问题在于延迟,请先考虑缓存(dspy.cache)、更短的签名,或使用 dspy.Predict 代替 dspy.ChainOfThought,再考虑切换到能力较弱的模型。
  • 你的任务足够简单,任何模型都能胜任。 如果零样本 dspy.Predict 的得分已经达到 95% 以上,模型选择几乎无关紧要。请把精力放在其他方面。

注意事项

  • 在未重新优化的情况下,在不同模型间重复使用已优化的提示词。 Claude 默认会加载已保存的程序,只替换 LM 配置。编译后的少样本示例和指令是针对原始模型调优的,在其他模型上通常会导致效果下降。切换模型后,务必从全新的(未编译)程序开始重新优化。
  • 使用未优化的提示词或针对单个模型优化的提示词比较模型。 使用零样本提示词或针对某个模型优化的提示词运行候选模型,会导致排名失真。比较分数前,应分别优化每个候选模型,否则比较的只是提示词的适配度,而不是模型能力。
  • 模型对比时忘记固定裁判模型。 使用 LLM-as-judge 指标时,如果调用 dspy.configure(lm=candidate_lm) 而没有隔离裁判模型,裁判模型也会随之改变。在指标函数中使用 dspy.context(lm=judge_lm),这样所有候选模型使用的裁判模型就会保持一致。
  • 需要使用 set_lm 时却用了 dspy.context(反之亦然)。 dspy.context(lm=...) 是临时的,作用域限于 with 块——适用于单次调用覆盖。module.set_lm(lm) 则是永久的,会在优化期间持续生效——适用于模块应始终使用特定模型的情况。混淆二者会导致难以察觉的评估错误。
  • 期望本地模型不经过更深入的优化就能达到云端模型的质量。 较小的本地模型(7B-13B)通常需要更多自举示例和更深入的优化(auto="heavy"),才能接近云端模型的质量。可以先使用 BootstrapFewShot,设置 max_bootstrapped_demos=8;如果分数仍然较低,再改用 MIPROv2。

交叉参考

安装任意 skill:npx skills add lebsral/DSPy-Programming-not-prompting-LMs-skills --skill <name>

  • 切换前设置指标并进行评估——参见 /ai-improving-accuracy
  • 按模块分配模型以优化成本——参见 /ai-cutting-costs
  • 使用混合模型构建多步流水线——参见 /ai-building-pipelines
  • 将昂贵模型的能力蒸馏到廉价模型——参见 /ai-fine-tuning
  • 了解 DSPy 优化器以便重新优化——参见 /dspy-optimizers
  • 如果没有 /ai-do,请安装它——它会将任意 AI 问题路由到合适的 skill,是最高效的工作方式:npx skills add lebsral/DSPy-Programming-not-prompting-LMs-skills --skill ai-do

其他资源

  • 工作示例(成本迁移、供应商切换、模型对比)见 examples.md
  • API 参考(dspy.LM 构造函数、dspy.configure、dspy.context、优化器参数)见 reference.md

评论

支持 Markdown