koach 开发者文档

微调与评测

通用模型的问题往往不是「不够聪明」,而是不按你的业务口径说话。 微调解决口径问题,评测解决「怎么证明它真的变好了」的问题。 这两件事通常要一起做 —— 没有评测集的微调,效果只能靠感觉。

什么时候需要微调

先说什么时候需要。以下情况用提示词或 Skill 更快也更便宜:

真正值得微调的是这几类:

准备数据

训练文件是 JSONL,每行一个完整对话。至少 500 条,1000 到 3000 条是效果与成本的 平衡点。

{"messages":[{"role":"system","content":"你是「晨跑营」的带队教练。"},{"role":"user","content":"下雨天还跑吗"},{"role":"assistant","content":"小雨照跑,注意换双抓地好的鞋,跑完立刻换干衣服。雷雨就停,我们群里会提前通知改期。"}]}
    {"messages":[{"role":"system","content":"你是「晨跑营」的带队教练。"},{"role":"user","content":"我 BMI 30,能跟着跳绳课练吗"},{"role":"assistant","content":"先不上跳绳。你这个体重下膝踝的冲击太大,我们先用快走加固定单车打四周基础,等体重降下来再加跳跃类。"}]}
数据质量比数量重要得多

1000 条由真人教练审核过的对话,效果远好于 10000 条从日志里随便捞的。 尤其要保证「拒绝类」样本的比例 —— 模型该说不的时候说不, 这部分样本缺失是微调后翻车最常见的原因。

上传文件

POST /v1/files
train = client.files.create(
        file=open("train.jsonl", "rb"),
        purpose="fine-tune",
    )
    valid = client.files.create(
        file=open("valid.jsonl", "rb"),
        purpose="fine-tune",
    )
    print(train.id)   # file_01JQ9C7T4M
curl "$KOACH_BASE_URL/files" \
      -H "Authorization: Bearer $KOACH_API_KEY" \
      -F purpose="fine-tune" \
      -F file="@train.jsonl"

创建微调任务

POST /v1/fine-tuning/jobs
参数类型说明
modelstring必填 基座模型。keepace-litekeepace-pro
training_filestring必填 上一步返回的文件 ID
validation_filestring选填 验证集。强烈建议提供,否则无法判断是否过拟合
methodstring默认 lora lora 快且便宜,适合话术与口径对齐; sft 全参微调,适合需要改变知识边界的场景
suffixstring选填 产出模型名的后缀,便于区分。如 morning-run-v2
hyperparametersobject选填 n_epochs / learning_rate_multiplier / batch_size,不传时自动推断
eval_suitestring选填 训练完成后自动跑一次领域评测,见下一节
job = client.fine_tuning.jobs.create(
        model="keepace-lite",
        training_file=train.id,
        validation_file=valid.id,
        suffix="morning-run-v2",
        extra_body={
            "method": "lora",
            "eval_suite": "sport.safety.v3",   # 训练后自动跑安全性评测
        },
    )
    print(job.id, job.status)   # ftjob_01JQ9D2K8R  queued

查询进度

GET /v1/fine-tuning/jobs/{job_id}

状态依次为 queuedrunningevaluatingsucceeded。 LoRA 在千条量级通常 40 分钟内完成,全参 SFT 要几个小时。

{
      "id": "ftjob_01JQ9D2K8R",
      "object": "fine_tuning.job",
      "status": "succeeded",
      "model": "keepace-lite",
      "fine_tuned_model": "ft:keepace-lite:acme:morning-run-v2:9dK2",
      "method": "lora",
      "trained_tokens": 1284310,
      "metrics": {
        "train_loss": 0.412,
        "valid_loss": 0.487
      },
      "eval_report": "evalrun_01JQ9F1X3B"
    }

拿到 fine_tuned_model 后当作普通模型 ID 用即可, 不需要改任何调用代码:

resp = client.chat.completions.create(
        model="ft:keepace-lite:acme:morning-run-v2:9dK2",
        messages=[{"role": "user", "content": "我 BMI 30,能跟着跳绳课练吗"}],
    )

领域评测

这是 L2 交付物里最容易被低估的一块。通用榜单分数说明不了模型在你的业务里能不能用; 能进内部评审会的是一份写清楚了评分口径、样本构成和失败案例的报告。

内置评测集

套件题量考察
sport.safety.v3860 损伤禁忌、疾病边界、特殊人群(孕产、青少年、高龄)的拒答与转诊准确率
sport.prescription.v21,240 训练处方的科学性:强度分区、周期化、加量幅度、恢复安排
sport.metrics.v2720 运动数据解读:心率、配速、功率、VO₂max、HRV 的口径与归因
nutrition.basic.v1540 营养建议、热量估算、补剂边界

发起评测

POST /v1/evaluations

可以同时评多个模型做横向对比 —— 这是最常见的用法: 拿你现在在用的通用模型、keepace 原版、微调后的版本跑同一套题。

run = client.evaluations.create(
        suite="sport.safety.v3",
        models=[
            "keepace-lite",
            "ft:keepace-lite:acme:morning-run-v2:9dK2",
        ],
        extra_body={
            "judge": "hybrid",        # llm | human | hybrid
            "human_sample_rate": 0.1, # 抽 10% 交专业团队人工复核
            "report_format": "pdf",
        },
    )
    print(run.id)   # evalrun_01JQ9F1X3B
curl "$KOACH_BASE_URL/evaluations" \
      -H "Authorization: Bearer $KOACH_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "suite": "sport.safety.v3",
        "models": ["keepace-lite", "ft:keepace-lite:acme:morning-run-v2:9dK2"],
        "judge": "hybrid",
        "human_sample_rate": 0.1,
        "report_format": "pdf"
      }'
参数类型说明
suitestring必填 内置套件 ID,或你自己上传的评测集文件 ID
modelsarray必填 待评模型,最多 5 个。可以混入非 koach 的模型做对照, 此时需要额外提供其 endpoint 与密钥
judgestring默认 llm llm 全自动;human 全人工; hybrid 自动评分 + 抽样人工复核
human_sample_ratenumber默认 0.05 人工复核比例。hybrid 时生效
rubricobject选填 覆盖默认评分维度与权重,用于对齐你自己的验收标准

评测结果

{
      "id": "evalrun_01JQ9F1X3B",
      "suite": "sport.safety.v3",
      "status": "completed",
      "results": [
        {
          "model": "keepace-lite",
          "overall": 0.842,
          "dimensions": {
            "contraindication_recall": 0.871,
            "referral_precision":      0.906,
            "special_population":      0.764,
            "false_refusal_rate":      0.058
          }
        },
        {
          "model": "ft:keepace-lite:acme:morning-run-v2:9dK2",
          "overall": 0.913,
          "dimensions": {
            "contraindication_recall": 0.948,
            "referral_precision":      0.921,
            "special_population":      0.882,
            "false_refusal_rate":      0.041
          }
        }
      ],
      "human_reviewed": 86,
      "report_url": "https://api.koach.ai/v1/evaluations/evalrun_01JQ9F1X3B/report.pdf",
      "failures_url": "https://api.koach.ai/v1/evaluations/evalrun_01JQ9F1X3B/failures.jsonl"
    }
先看 failures.jsonl,再看总分

总分从 0.84 提到 0.91 只是一个数字,真正有用的是失败样本文件 —— 它会告诉你模型具体在哪一类问题上还会出错。 我们见过总分很高但在孕产人群上系统性出错的模型, 这种问题只有翻失败样本才发现得了。

批量推理

给全量用户生成周报、给整个课程库打标签这类离线任务,不要用在线接口硬跑。 批量接口有独立额度池,单价按五折计, 代价是延迟按小时计算。

POST /v1/batches 24 小时内完成
# 每行是一个标准的 chat/completions 请求体
    batch_file = client.files.create(
        file=open("weekly_reports.jsonl", "rb"),
        purpose="batch",
    )

    batch = client.batches.create(
        input_file_id=batch_file.id,
        endpoint="/v1/chat/completions",
        completion_window="24h",
    )
    print(batch.id, batch.status)   # batch_01JQ9G8M2V  validating

私有化下的微调

私有化部署里这套接口完全一致,但训练发生在客户自己的机器上, 数据与产出的权重都不出域。差别只有两点:

本页内容属于 koach 商业化合作方案的一部分。接口定义可能在正式签约前调整, 以合同附件中的版本为准。

koach · AI by Keep 隐私政策 用户协议 联系我们