前言

小模型的系统提示词很难一次写好。本文介绍一种用 GitHub Copilot(或其他大模型 Agent)当执行者,自动迭代小模型提示词的方法。

核心思路:写好测试用例 → Copilot Agent 跑用例 → 看错误 → 改提示词 → 提交 → 跑回归。人在关键节点介入。

为什么需要这套方法

  • 小模型对提示词敏感,模糊指令容易自由发挥
  • 调一次就要全量回归
  • 没有量化指标,全凭感觉
  • 大模型直接写提示词”答一次就完”,没形成闭环

核心技巧:Function Call 加 analysis 参数

小模型默认直接输出 tool_call,看不到思考过程。给 function call 强制加一个 analysis 参数,强制它先输出分析:

tools = [{
    "type": "function",
    "function": {
        "name": "search_order",
        "description": "查询订单物流",
        "parameters": {
            "type": "object",
            "properties": {
                "order_id": {"type": "string"},
                "analysis": {
                    "type": "string",
                    "description": "分析过程:意图识别 + 参数提取依据 + 置信度"
                }
            },
            "required": ["order_id", "analysis"]
        }
    }
}]

analysis 字段是 Copilot 定位错误的唯一线索。没有它,Agent 只能看到结果错,不知道错在哪。

真实案例:3.9 vs 3.11

让小模型判断版本号:compare("3.9", "3.11"),期望 3.11 > 3.9

没有 analysis 时

  • 小模型直接输出 "3.9 > 3.11"(按字符串比较确实成立)
  • Agent 只看到结果错,无法判断是参数传错、概念理解错、还是别的

加了 analysis 后,小模型输出:

analysis: "按字符串比较,'3.9' 第三个字符 '9' > '3.11' 的 '1',
所以 '3.9' > '3.11'"

Agent 立刻定位:

错误归因:模型把版本号当字符串做字典序比较,没识别 SemVer 语义
修改建议:明确"按 . 分割成段、每段整数比较",加 few-shot

黑盒变白盒,Agent 自主改提示词、commit、回归。

项目结构

prompt-trainer/
├── system_prompt.md      # Copilot 修改这里
├── test_cases.json       # 测试用例
├── tools.json            # function call 定义
├── run_cases.py          # 执行脚本
├── results/
│   ├── v1.json
│   ├── v2.json
│   └── ...
├── CHANGELOG.md          # 迭代记录
└── README.md

Step 1:测试用例

用例是迭代的依据,没有量化用例就没有优化。

{
  "cases": [
    {
      "id": "logistics_001",
      "input": "帮我查一下订单 20260504001 现在到哪了",
      "expected_tool": "search_order",
      "expected_args": {"order_id": "20260504001"}
    },
    {
      "id": "refund_001",
      "input": "我想退款,订单号是 20260504002",
      "expected_tool": "refund_order",
      "expected_args": {
        "order_id": "20260504002",
        "reason": "用户主动申请退款"
      }
    }
  ]
}

设计原则:

  • 覆盖所有参数组合(有/无/多值)
  • 包含边界情况(空字符串、特殊字符)
  • 包含歧义场景
  • 起步 30-50 条,迭代到 100+ 做回归

Step 2:初始提示词

从最简版本开始:

# 角色
你是客服助手,处理用户售后请求。

# 工具
- search_order(order_id): 查询订单物流
- refund_order(order_id, reason): 申请退款
- create_ticket(category): 创建工单

# 要求
根据用户输入选择工具并提取参数。

这个版本一定很差,但没关系,是迭代起点。

Step 3:给 Copilot Agent 的初始指令

# 项目目标
训练小模型(Qwen2.5-7B-Instruct)的系统提示词,
在 test_cases.json 上准确率达到 95%。

# 工作流程
1. 读取 system_prompt.md 和 test_cases.json
2. 运行 run_cases.py,生成 results/v{N}.json
3. 统计准确率
4. 未达标:
   - 分析错误用例的 analysis 字段
   - 定位错误归因
   - 修改 system_prompt.md
   - git commit -m "v{N+1}: 方向=xxx"
   - 更新 CHANGELOG.md
   - 回到第 2 步
5. 达标:输出总结报告

# 约束
- 只改 system_prompt.md,不改 test_cases.json
- 连续 3 轮无提升,停下来问人
- 每次 commit 前必须跑完所有用例
- analysis 字段不能为空
- commit 时必须标注"优化方向"

优化方向标签

避免 commit 信息写 “v3: 改了点东西”。明确标注优化方向,方便后期复盘:

方向 含义 典型修改
tool_dispatch 工具选择 加触发关键词、调整场景描述
param_extract 参数提取 补参数规则、加 few-shot
anti_hallucination 反幻觉 加”宁缺毋滥”、禁止编造
reasoning_chain 推理链 要求分步、显式分析
format_alignment 输出格式 调 analysis 字段结构
edge_case 边界情况 补空值/特殊字符规则

CHANGELOG.md 样例:

## v1 → v2 (anti_hallucination)
改动:增加"宁缺毋滥"原则
准确率:60% → 73%
剩余问题:物流查询和退款场景仍有混淆

## v2 → v3 (tool_dispatch)
改动:给 search_order 加触发关键词清单
准确率:73% → 85%

## v3 → v4 (anti_hallucination)
改动:增加"宁可追问也不要猜测退款金额"
准确率:85% → 91%
备注:anti_hallucination 方向两次优化效果递减,
下次换 param_extract 方向的 few-shot 负例

方向标签的作用:

  • 避免重复打补丁(看到历史记录就知道哪些方向试过)
  • 找到”重灾区”(多次迭代还没解决的方向)
  • 量化各方向贡献度(哪个方向提升最大)

Step 4:run_cases.py 骨架

import json
from openai import OpenAI

with open('test_cases.json') as f:
    cases = json.load(f)

with open('system_prompt.md') as f:
    system_prompt = f.read()

client = OpenAI(base_url="http://localhost:8000/v1", api_key="EMPTY")

results = []
for case in cases:
    resp = client.chat.completions.create(
        model="Qwen2.5-7B-Instruct",
        messages=[
            {"role": "system", "content": system_prompt},
            {"role": "user", "content": case['input']}
        ],
        tools=tools
    )
    tool_call = resp.choices[0].message.tool_calls[0]
    args = json.loads(tool_call.function.arguments)
    results.append({
        "case_id": case['id'],
        "expected": case['expected_tool'],
        "actual": tool_call.function.name,
        "args_match": tool_call.function.arguments == case['expected_args'],
        "analysis": args.get('analysis', '')
    })

accuracy = sum(1 for r in results if r['args_match']) / len(results)
print(f"准确率: {accuracy:.1%}")

Step 5:迭代过程

Copilot Agent 在一个会话里连续推进:

[Copilot] 读取 system_prompt.md v1
[Copilot] 跑 v1 → 准确率 60%(18/30)
[Copilot] 12 个错误用例,8 个是工具选错
[Copilot] 加触发关键词清单 → v2
[Copilot] v2 → 73%
[Copilot] 加"宁缺毋滥"原则 → v3
[Copilot] v3 → 85%
[Copilot] 连续 2 轮提升但增速放缓

人的介入时机:

  • 连续 3 轮无提升(止损线)
  • Agent 想改 run_cases.py(提醒改错了)
  • Agent 想换小模型(明确禁止)

Step 6:达标与收尾

量化目标:

  • 准确率 ≥ 95%
  • 幻觉率 < 2%
  • 工具选错率 < 1%

达标后 Agent 自主做:

  1. git tag v_final
  2. 更新 CHANGELOG.md 总结
  3. 输出最终报告(各方向贡献度统计)
  4. 复制到生产环境

人只做最后审阅:通读 CHANGELOG,决定是否真的达标。

常见问题

没有 analysis 字段怎么办?

改造原有 tools 定义,给每个工具都加 analysis 必填参数。代价是小模型输出会变长,但调试可控性大幅提升。

小模型完全跑不动测试?

  • 降低用例难度
  • 减少工具数量(先收敛到 1-2 个)
  • 换更大参数的模型(Qwen2.5-7B → Qwen3-8B)

CHANGELOG 应该谁写?

Agent 自动写。每轮 commit 必填。格式强制约束:

# .github/agents/prompt-trainer.md 的硬性要求
- CHANGELOG.md 每条记录必须包含:版本号、优化方向、改动摘要、准确率
- 优化方向必须是预定义标签之一

总结

  • Copilot Agent 当执行者,跑 执行→分析→修改→回归 闭环
  • 人当 PM,设定目标、约束、止损线
  • Function Call 加 analysis 参数,让小模型变白盒
  • 测试用例量化,迭代有依据
  • CHANGELOG 标签化,方便复盘
  • git 每次提交,随时回滚

本质上:指挥 Agent 团队,大模型当执行者,小模型当被优化对象,人当决策者。

参考链接


(完)