Back to blog
    API 日志转训练数据:利用云端 AI 历史记录进行微调
    training dataAPI logsfine-tuningmigrationon-device AIsegment:mobile-builder

    API 日志转训练数据:利用云端 AI 历史记录进行微调

    您现有的云端 AI API 日志就是一个现成的训练数据集。如何从 API 交互日志中提取、清洗和格式化微调数据,用于端侧模型。

    EErtas Team·

    如果您目前在使用云端 AI API(OpenAI、Anthropic、Google Gemini),您已经在生成训练数据了。每次 API 调用都包含一个输入(用户的请求)和一个输出(模型的回复)。这就是一个训练样本。

    您的 API 日志是从云端 AI 迁移到端侧 AI 的最快路径。您不需要从零开始创建数据集。您已经有了。

    API 日志包含什么

    一个典型的 API 调用日志条目:

    {
      "timestamp": "2026-03-15T14:22:03Z",
      "model": "gpt-4o-mini",
      "messages": [
        {
          "role": "system",
          "content": "You are the shopping assistant for StyleApp..."
        },
        {
          "role": "user",
          "content": "Find me a blue dress for a summer wedding"
        },
        {
          "role": "assistant",
          "content": "Here are some suggestions for a summer wedding...\n\n1. Floral midi dress in navy blue...\n2. Light blue chiffon maxi dress..."
        }
      ],
      "tokens_used": {"input": 1842, "output": 387},
      "latency_ms": 1203
    }

    这个日志条目已经是微调所需的精确格式。messages 数组就是一个训练对话。

    提取流程

    步骤 1:导出您的日志

    日志的存储位置取决于您的架构:

    如果您自己记录 API 调用: 从您的数据库(PostgreSQL、MongoDB 等)或日志聚合服务(Datadog、CloudWatch 等)导出。

    如果您使用 OpenAI 的 API: API 默认不存储日志。您需要自己的日志中间件。如果还没有,现在就设置。每次未来的 API 调用都是潜在的训练样本。

    # 简单的日志中间件示例
    import json
    import datetime
    
    def log_api_call(request_messages, response_content, model, tokens):
        log_entry = {
            "timestamp": datetime.datetime.utcnow().isoformat(),
            "model": model,
            "messages": request_messages + [
                {"role": "assistant", "content": response_content}
            ],
            "tokens_used": tokens,
        }
        with open("api_logs.jsonl", "a") as f:
            f.write(json.dumps(log_entry) + "\n")

    步骤 2:质量过滤

    不是每个 API 回复都是好的训练数据。过滤掉:

    失败的回复: 超时错误、格式错误的输出、拒绝回复。

    低质量输出: 用户立即重试的回复(表示不满意),或被截断的输出。

    异常值: 不代表典型行为的异常长或短的回复。

    离题交互: 如果用户偶尔提出离题问题,除非您希望模型处理它们,否则排除。

    def is_quality_example(log_entry):
        messages = log_entry["messages"]
        assistant_msg = next(
            (m for m in reversed(messages) if m["role"] == "assistant"), None
        )
        if not assistant_msg:
            return False
    
        content = assistant_msg["content"]
    
        # 过滤过短的回复
        if len(content) < 50:
            return False
    
        # 过滤错误回复
        if "I apologize" in content and "I cannot" in content:
            return False
    
        # 过滤截断的回复
        if log_entry.get("finish_reason") == "length":
            return False
    
        return True

    步骤 3:移除系统提示(可选)

    如果您在做微调,模型会从训练样本中学习行为。您可能不需要在训练数据中包含系统提示,因为微调后的模型会内化这些指令。

    两种方式:

    保留系统提示: 模型学习遵循这些特定指令。适用于系统提示简短且稳定的情况。

    移除系统提示: 模型学习行为模式而非显式指令。适用于系统提示较长(节省训练 token)或您希望行为是内在的而非依赖指令的情况。

    步骤 4:匿名化

    从训练数据中移除 PII:

    import re
    
    def anonymize_message(content):
        # 邮箱
        content = re.sub(r'\b[\w.-]+@[\w.-]+\.\w+\b', '[EMAIL]', content)
        # 电话号码
        content = re.sub(r'\b\d{3}[-.]?\d{3}[-.]?\d{4}\b', '[PHONE]', content)
        # 信用卡号
        content = re.sub(r'\b\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}\b', '[CARD]', content)
        # 地址(基础模式)
        content = re.sub(r'\d+\s+[\w\s]+(?:Street|St|Avenue|Ave|Road|Rd|Drive|Dr)\b', '[ADDRESS]', content)
        return content
    
    def anonymize_log(log_entry):
        for msg in log_entry["messages"]:
            msg["content"] = anonymize_message(msg["content"])
        return log_entry

    步骤 5:格式化为训练数据

    将过滤和匿名化后的日志转换为标准微调格式:

    def log_to_training_example(log_entry):
        messages = []
        for msg in log_entry["messages"]:
            if msg["role"] in ("system", "user", "assistant"):
                messages.append({
                    "role": msg["role"],
                    "content": msg["content"]
                })
        return {"messages": messages}
    
    # 处理所有日志
    training_data = []
    for log_entry in load_logs("api_logs.jsonl"):
        if is_quality_example(log_entry):
            anonymized = anonymize_log(log_entry)
            example = log_to_training_example(anonymized)
            training_data.append(example)
    
    # 写入训练文件
    with open("training_data.jsonl", "w") as f:
        for example in training_data:
            f.write(json.dumps(example) + "\n")

    需要多少日志

    每日 API 调用量收集 1,000 个样本所需时间收集 5,000 个样本所需时间
    1002-3 周(质量过滤后)2-3 个月
    5003-5 天2-3 周
    1,0002-3 天1-2 周
    5,0001 天3-5 天

    假设 50-70% 的原始 API 调用能通过质量过滤。在每天 500 次调用的情况下,您每天积累 250-350 个高质量样本。

    对于大多数任务,1,000 个高质量样本就足以产出表现良好的微调模型。从设置日志记录开始,几天到几周内就可以开始训练。

    蒸馏优势

    当您用大模型(GPT-4o、Claude Sonnet)的输出来微调小模型(1-3B)时,您正在执行知识蒸馏。小模型学习在您的特定任务上复现大模型的行为。

    结果:一个 3B 微调模型在您的领域任务上匹配大模型的性能,同时在设备端运行。这不是理论。研究和生产部署持续表明,微调的小模型在狭窄的领域特定任务上能匹配甚至超越经提示的大模型。

    您的 API 日志就是蒸馏数据集。大模型已经完成了工作。您只需要教一个小模型复制它。

    从日志到部署

    端到端流程:

    1. 设置 API 日志记录(如果尚未到位)
    2. 积累 1,000+ 个高质量样本(几天到几周)
    3. 提取、过滤、匿名化和格式化 日志
    4. 上传到微调平台 如 Ertas
    5. 选择基础模型(推荐 Llama 3.2 3B)
    6. 使用 LoRA 微调(30 分钟到 3 小时)
    7. 导出 GGUF(Q4_K_M 量化)
    8. 在移动应用中集成 llama.cpp
    9. A/B 测试 端侧模型与云端 API 的对比
    10. 迁移 当端侧模型达到质量标准时

    您的 API 日志不仅仅是成本中心。它们是通往端侧 AI 的桥梁。

    Ship AI that runs on your users' devices.

    Early bird pricing starts at $14.50/mo — locked in for life. Plans for builders and agencies.

    Keep reading