自动训练LLM Harness:跨模型与跨Benchmark的Prompt优化实战

发布时间:2026/9/4 2:44:24
自动训练LLM Harness:跨模型与跨Benchmark的Prompt优化实战 最近在团队里推动一个 LLM 应用改造时遇到一个很典型的困境模型从 GPT-4 换到 DeepSeek再换到其他开源模型每次都要重新调试提示词、调整工具描述、修改调用链路的容错逻辑。Prompt 调优占用了大量时间但换一个模型后之前的经验几乎全部作废。更让人焦虑的是微调一个领域模型不仅成本高效果还会因为基座模型升级而失效。后来看到一篇 Hacker News 上的帖子标题是 “Auto-train the harness, not the LLM: cross-model, cross-benchmark gains”一下子点醒了问题核心我们一直在试图“训练模型”但真正不稳定、真正需要系统化优化的其实是模型外围那一整套 harness。这篇博客会把这条思路完整拆开。我们会先讲清楚什么是 LLM harness为什么“训练 harness”比训练模型本身更符合中小团队的实际约束然后给出一个最小可运行的自动训练原型用代码演示如何让提示词、示例和调用策略在不改模型权重的前提下自动搜索优化并且让优化结果在跨模型、跨 benchmark 场景下仍然可用。文章偏工程实践适合正在做 LLM 应用落地、频繁切换模型、或者对“自动优化 Prompt”感兴趣的后端开发者和算法工程师。1. 为什么说“应该训练 harness而不是训练 LLM”1.1 模型能力很强但应用系统不是只由模型组成当我们说“做一个 LLM 应用”时实际代码里通常不只有一次模型调用。一个完整的智能问答、Agent 任务或文档处理系统往往包含下面这些组件系统提示词和角色设定。用户请求的预处理、上下文压缩、历史会话管理。工具Function的名称、描述、参数 Schema。模型输出后的解析、校验、纠错、重试策略。多步任务中的工作流编排逻辑。针对不同模型版本或不同供应商的兼容适配层。这些东西可以统称为 LLM 的外层系统。在业界这个概念也常被称为 harness。Harness 在英文里原本有“控制、驾驭、测试夹具”的含义。在 LLM 领域它有两层常见的理解第一层是“评估 harness”也就是 running a model through a standard benchmark harness指一套固定的测试流程用来公平评估不同模型的能力。第二层是“应用 harness”指的是生产系统中包装模型、控制模型行为的那套工程代码与提示编排逻辑。你可以把它理解为“马的缰绳和马车框架”——模型是马harness 是缰绳、车架与轮子组成的载具系统。本文讨论的 auto-train the harness重点在第二层同时也会借助第一层的思路来做效果衡量。1.2 微调模型是重投入优化 harness 是轻杠杆不少团队遇到 LLM 效果不达标时第一反应是微调模型。但微调有几个现实问题训练数据获取和清洗成本高需要领域专家标注。训练和部署链路复杂小团队不一定有 GPU 资源和 MLOps 平台。模型升级后旧微调权重往往需要重新适配维护成本高。微调主要提升的是模型的“知识”和“行为习惯”但应用效果问题很多出在提示方式不合理、缺少示例、工具调用格式不稳定等外层因素。换个角度思考同一个模型在糟糕的 harness 和精心设计的 harness 下效果差异可以非常大。既然外层系统对效果影响如此显著为什么不把外层系统当作一个“可训练对象”来自动优化这里的“训练”不是梯度下降而是一种更广义的搜索与优化过程。它自动尝试不同的提示词写法、不同的示例组合、不同的工具描述、不同的输出结构然后通过评测指标筛选出效果最好的配置。简单说模型权重不动动的是上层配置与策略。1.3 跨模型与跨 benchmark 是衡量 harness 是否优秀的关键手工调 Prompt 的另一个问题是优化出来的提示词往往“过拟合”到了某一个模型上。换一个模型同样的 Prompt 可能效果骤降。好的 harness 训练应该追求两个泛化性质Cross-model在一组模型上搜索到的 harness直接迁移到另一组未见过的模型上仍然能带来效果增益。这要求优化过程不过度依赖某一个模型的特殊偏好。Cross-benchmark在多个任务数据集或评测集上做联合优化得到的 prompt 不是只在一个 benchmark 上刷分而是能适应多个 benchmark。这可以防止 harness 学习到数据集的表面特征而不是真正通用的任务理解模式。如果一个 harness 训练方法能做到上述两点那么团队就能把“适配新模型”的耗时从几天压缩到几小时。你会得到一个稳定的“模型开关”——底层模型随便换上层 harness 保持不变或只需要很小的调整。2. Harness 训练中的核心概念为了后续代码不被概念绕晕我们先把几个关键名词统一口径。2.1 什么是“训练样本”里的输入输出传统机器学习中训练样本是(x, y)x 是输入特征y 是标签。在 harness 训练中我们同样有输入输出但它们不是用来训练模型权重而是用来评判一次模型调用是否成功输入用户问题、任务描述、检索到的上下文片段、可用的工具定义。输出模型返回的回答或结构化结果。标签或验证器我们希望模型输出满足的条件例如“答案包含指定实体的 ID”“JSON 里 date 字段格式合法”“分类结果在允许集合内”。这些(输入, 标签, 验证器)三元组构成一个小型评测集。Harness 训练的目标就是找到一个外层策略让模型在这个评测集上的成功率最高。2.2 可搜索的 Harness 参数既然要自动训练就必须明确哪些东西是可以被程序修改的。实践中至少有三类可搜索对象指令模板。系统提示词不是固定字符串而是由多个候选模块组成比如角色、任务步骤、输出格式要求、禁止项。我们可以让程序选择不同模板组合。少样本示例。从一批标注示例中挑选哪些放进 Prompt、按什么顺序排列、放几个这些都可以搜索。工具描述与参数 Schema。同一功能可以写不同风格的工具说明自动选择更清晰、更不容易被模型误解的描述。此外对于 Agent/多步任务场景还包括 4. 推理策略。是一次性生成还是先规划后执行是否允许模型先输出思考过程。 5. 校验与重试逻辑。模型输出不合法时是直接报错还是带着错误信息让模型修正。这些参数组合在一起会形成一个非常大的搜索空间。手工枚举不现实所以才需要“自动训练”。2.3 自动训练的本质是黑盒优化Harness 训练的通用框架可以概括为三步定义策略候选空间。在评测集上执行策略并计算分数。用优化算法生成下一轮候选策略迭代若干轮。这个过程中我们不需要对模型做反向传播也不修改任何权重。模型只是一个“评分函数”把策略变成 Prompt再调用模型得到输出然后用验证器判断成功与否。因此适用于这种场景的算法包括随机搜索、网格搜索、贝叶斯优化、进化算法、基于 LLM 的 Prompt 生成器。有一个非常重要的点harness 训练和 AutoML自动机器学习很像但优化对象不是模型结构或超参数而是“模型外层的所有文本与流程决策”。很多团队已经在用类似方法做 Prompt 自动优化但把它称为“训练”更容易让人意识到这里有一套完整的工程闭环。3. 整体流程设计从“手工调 Prompt”到“自动训练”正式写代码前我们先设计一个完整流程方便后续对照。我们可以把整个 harness 训练过程分成 6 个阶段准备评测集。人工构造或从线上日志采样 20-100 个有代表性的问题并为每个问题写好预期答案的校验规则。定义 harness 模板。把所有可能变化的文字都抽成带占位符的模板参数由配置对象提供。实现模型调用层。统一封装不同模型的调用接口保证评测代码与具体模型解耦。设计候选生成器。输入历史最优参数输出下一批候选参数。最简单的实现是随机扰动。执行评测循环。对每个候选参数跑完整个评测集记录成功率、平均延迟、Token 消耗。选择最优参数。跑多轮后把效果最好的参数导出为 JSON 文件供线上服务加载。如果使用多任务评测和多模型评测可以将分数做加权平均。比如总分 0.5 × 模型A在任务1上的得分 0.3 × 模型B在任务1上的得分 0.2 × 模型A在任务2上的得分这样做的好处是程序不会再挑一个只讨好单一模型或单一数据集的 harness。4. 代码实战最小可用的 Auto-harness 训练原型下面这部分是整篇文章的核心。我们会从零构造一个可以运行的 Python 原型。它不依赖任何重型框架只需要openai库或兼容 OpenAI 协议的 SDK以及一个带有OPENAI_API_KEY的环境变量。如果你没有付费模型也可以把调用层替换成本地部署的 vLLM 服务。4.1 项目结构auto_harness/ ├── config.py # 配置文件定义评测集路径、模型列表、搜索轮数 ├── harness.py # Harness 抽象模板渲染 调模型 ├── evaluator.py # 评测集与验证器 ├── optimizer.py # 随机搜索 / 进化优化逻辑 ├── main.py # 训练入口 └── best_harness.json # 训练结束后导出的最优配置为了避免篇幅过长我们不会一次性贴出全部代码而是按照功能模块拆开讲解最后运行时把所有模块串起来即可。4.2 定义 Harness 数据类我们先用一个 dataclass 表示一个 harness 配置。每个字段都是可搜索对象。文件路径auto_harness/harness.pyfrom dataclasses import dataclass, asdict from typing import Optional dataclass class HarnessConfig: # 系统提示词的角色描述 role: str 你是一个专业的技术客服。 # 是否需要模型先输出分析和思考步骤 enable_chain_of_thought: bool False # 示例数量 few_shot_num: int 2 # 输出格式要求 output_format: str 请直接输出 JSON。 # 是否允许模型在输出前补充解释 allow_explanation: bool False # 温度 temperature: float 0.0 property def system_prompt(self) - str: parts [self.role] if self.enable_chain_of_thought: parts.append(请先一步步分析再给出最终结论。) parts.append(self.output_format) if not self.allow_explanation: parts.append(不要输出额外解释。) return \n.join(parts) def to_dict(self): return asdict(self)这里要注意HarnessConfig的每个字段都可以在训练过程中被随机调整或变异。字段越细搜索空间越大但同时评测成本也越高。实际项目中建议先固定大部分字段只搜索 2-4 个关键字段。4.3 实现统一的模型调用层为了让“跨模型”验证成立我们需要一个统一封装。这里先以 OpenAI 兼容接口为例后续要接入 DeepSeek、Kimi、Qwen 等模型时只需修改base_url和model参数。继续在 harness.py 中添加import os from openai import OpenAI class LLMClient: def __init__(self, model: str, base_url: str None, api_key: str None): self.model model self._client OpenAI( api_keyapi_key or os.getenv(OPENAI_API_KEY), base_urlbase_url or os.getenv(OPENAI_BASE_URL, https://api.openai.com/v1), ) def chat(self, messages, temperature: float 0.0) - str: resp self._client.chat.completions.create( modelself.model, messagesmessages, temperaturetemperature, ) return resp.choices[0].message.content or 接下来定义一个run_harness函数它接收HarnessConfig、问题文本和示例返回模型输出。这个函数是评测过程中的“一次动作”。def run_harness( client: LLMClient, cfg: HarnessConfig, question: str, examples: list[dict], ) - str: user_content question if examples: example_block for item in examples[: cfg.few_shot_num]: example_block f问题{item[question]}\n答案{item[answer]}\n\n user_content example_block 问题 question messages [ {role: system, content: cfg.system_prompt}, {role: user, content: user_content}, ] return client.chat(messages, temperaturecfg.temperature)4.4 构造评测集与验证器评测集不需要很大。Harness 训练的关键不是数量而是覆盖度。我们至少需要覆盖典型问题、边界问题、格式严格问题三类。验证器决定了什么样的输出算“成功”。文件路径auto_harness/evaluator.pyimport json import re from dataclasses import dataclass from typing import Callable dataclass class EvalCase: question: str expected_entity: str validator: Callable[[str], bool] def check(self, output: str) - bool: return self.validator(output) def build_default_cases() - list[EvalCase]: def contains_order_id(text: str) - bool: return ORD-123456 in text def is_valid_json_with_name(text: str) - bool: text text.strip() text re.sub(r^json\s*|\s*$, , text) try: obj json.loads(text) return isinstance(obj, dict) and name in obj and date in obj except Exception: return False cases [ EvalCase( question请帮我查一下订单 ORD-123456 的物流状态。, expected_entityORD-123456, validatorcontains_order_id, ), EvalCase( question把下面信息转成 JSON张三2024-05-01。, expected_entity姓名和日期, validatoris_valid_json_with_name, ), ] return cases这里用到的验证器比较简单。真实项目里你可以用基于规则的字段校验、数据库查询之后的结果比对甚至用另一个模型做裁判。评测越接近线上真实反馈harness 训练就越有意义。4.5 评测函数计算一组候选的整体分数在一个训练轮次中我们要对同一份评测集跑完所有候选配置。为了体现 cross-benchmark我们让评测集包含两个不同任务并且分别计算分数最后取平均。文件路径auto_harness/evaluator.pyfrom collections import defaultdict def evaluate_candidate( client: LLMClient, cfg, cases_by_task: dict[str, list[EvalCase]], examples: list[dict], ) - dict: 返回示例 { order_query: 1.0, json_transform: 0.5, overall: 0.75, detail: [...] } task_scores {} detail [] for task_name, cases in cases_by_task.items(): correct 0 for case in cases: output run_harness(client, cfg, case.question, examples) ok case.check(output) detail.append({task: task_name, question: case.question, ok: ok, output: output}) if ok: correct 1 task_scores[task_name] correct / len(cases) task_scores[overall] sum(task_scores.values()) / len(task_scores) task_scores[detail] detail return task_scores上面代码中我们把两类问题拆分成两个 task分别统计成功率。这个细节很重要。如果一个 harness 只在某个 benchmark 上强在另一个任务上拖后腿那么“平均分”会教训优化器不要只刷单点能力。4.6 最简单的自动训练器随机变异搜索我们先把第一版训练器写成随机变异。虽然算法简单但它足以验证整个闭环初始化一个种子配置每轮生成若干变异体跑分保留最优。文件路径auto_harness/optimizer.pyimport copy import json import random from dataclasses import fields from harness import HarnessConfig def mutate(cfg: HarnessConfig) - HarnessConfig: new_cfg copy.deepcopy(cfg) field_choices { role: [ 你是一个专业的技术客服。, 你是一个严谨的 JSON 数据转换助手只输出结构化数据。, 你是一个帮助用户解决问题的智能助理。, ], output_format: [ 请直接输出 JSON。, 请输出简洁的文本。, 请使用 Markdown 格式尽量清晰有条理。, ], enable_chain_of_thought: [True, False], allow_explanation: [True, False], temperature: [0.0, 0.2, 0.5], } field_name random.choice(list(field_choices.keys())) if field_name temperature: new_cfg.temperature random.choice(field_choices[field_name]) elif field_name enable_chain_of_thought: new_cfg.enable_chain_of_thought random.choice(field_choices[field_name]) elif field_name allow_explanation: new_cfg.allow_explanation random.choice(field_choices[field_name]) else: setattr(new_cfg, field_name, random.choice(field_choices[field_name])) return new_cfg def random_search( client, base_cfg: HarnessConfig, cases_by_task: dict, examples: list[dict], num_rounds: int 10, candidates_per_round: int 4, ): best_cfg copy.deepcopy(base_cfg) best_score evaluate_candidate(client, best_cfg, cases_by_task, examples)[overall] history [{round: 0, score: best_score, cfg: best_cfg.to_dict()}] for round_idx in range(1, num_rounds 1): round_candidates [] for _ in range(candidates_per_round): cand mutate(best_cfg) score evaluate_candidate(client, cand, cases_by_task, examples)[overall] round_candidates.append((score, cand)) round_candidates.sort(keylambda x: x[0], reverseTrue) best_round_score, best_round_cfg round_candidates[0] if best_round_score best_score: best_cfg best_round_cfg best_score best_round_score history.append({round: round_idx, score: best_round_score, cfg: best_round_cfg.to_dict()}) print(fRound {round_idx}, best{best_score:.3f}, round_best{best_round_score:.3f}) return best_cfg, best_score, history随机搜索的优点是简单、稳定、不容易陷入复杂逻辑 bug。缺点是效率不高当搜索空间很大时需要很多轮才可能找到好配置。后续可以把“变异”部分升级成“LLM 作为变异器”让模型基于上一轮失败样本生成新的提示词收敛速度会明显增加。4.7 合并多模型的分数让最优 harness 具备跨模型能力上面的代码只针对一个 client 进行评测。要实现 cross-model 增益核心改动是在计算总分时遍历多个模型然后求平均分。优化器可以改成接受clients: list[LLMClient]每个候选配置都要经过所有模型验证。优化器收到多模型评分后会更倾向于选择“所有模型都表现稳定”的配置。def evaluate_cross_model( clients: list[LLMClient], cfg, cases_by_task: dict, examples: list[dict], ) - dict: all_overall [] per_model {} for client in clients: result evaluate_candidate(client, cfg, cases_by_task, examples) per_model[client.model] result all_overall.append(result[overall]) return { per_model: per_model, cross_model_overall: sum(all_overall) / len(all_overall), }训练时把最优对象从“单模型最高分”换成“跨模型平均分最高”。这样找出来的 harness 才不是某个模型专属的提示词技巧而是更通用、更稳定的交互方式。4.8 main.py 入口文件路径auto_harness/main.pyimport argparse import json import os from harness import HarnessConfig, LLMClient from evaluator import build_default_cases from optimizer import random_search def group_cases_by_task(cases): # 这里简化处理将同一份评测集按前一半/后一半拆成两个伪任务。 # 真实项目应显式标注 task_name。 tasks { task_order: cases[: len(cases) // 2], task_json: cases[len(cases) // 2:], } return tasks def main(): parser argparse.ArgumentParser() parser.add_argument(--models, nargs, default[gpt-4o-mini]) parser.add_argument(--rounds, typeint, default6) parser.add_argument(--candidates, typeint, default3) parser.add_argument(--output, defaultbest_harness.json) args parser.parse_args() clients [LLMClient(modelm) for m in args.models] cases build_default_cases() tasks group_cases_by_task(cases) examples [] # 演示代码里暂不提供 few-shot 示例 base_cfg HarnessConfig() best_cfg, best_score, history random_search( clients[0], # 第一个版本只在一个模型上搜索 base_cfg, tasks, examples, num_roundsargs.rounds, candidates_per_roundargs.candidates, ) with open(args.output, w, encodingutf-8) as f: json.dump(best_cfg.to_dict(), f, ensure_asciiFalse, indent2) print(fBest score: {best_score:.3f}) print(json.dumps(best_cfg.to_dict(), ensure_asciiFalse, indent2)) if __name__ __main__: main()这个入口为了控制篇幅做了一定简化。真实使用时你需要把group_cases_by_task替换成带任务标签的数据结构并且在多轮训练时加上每一轮的完整日志。4.9 运行方式和预期结果在项目根目录执行export OPENAI_API_KEYsk-xxxx export OPENAI_BASE_URLhttps://api.openai.com/v1 python main.py --models gpt-4o-mini --rounds 8 --candidates 4运行过程中会打印类似下面的日志Round 1, best0.500, round_best0.500 Round 2, best0.500, round_best0.750 Round 3, best0.750, round_best0.750 Round 4, best0.750, round_best1.000由于评测集只有两条用例分数比较粗糙。真实项目中建议每个 task 至少准备 20 条评测样本并通过多次运行取平均分来降低单个样例造成的随机波动。在样例少的情况下一个 harness 的微小调整就可能让分数从 50% 跳到 100%这并不代表它真的变强了。如果你把评测集扩展到 60 条并且拆成 3 个 task训练出的 harness 会更加可信。5. 从 Demo 走向生产Harness 训练在项目中的落地模式代码原型已经跑通了。接下来我们讨论这套思路在真实项目里怎么落地以及适合哪些业务场景。5.1 适合 Harness 训练的业务场景最适合的场景有两个特征一是模型输出存在清晰的成功 / 失败标准二是业务对多模型适配有诉求。比如以下场景工单自动分类与实体抽取。系统需要从用户描述中提取订单号、设备型号、故障类型。这类任务的验证器可以基于规则判断非常适合自动搜索提示词。结构化数据转换。要求模型把非结构化文本转成 JSON。JSON 字段类型、必填字段、枚举值都可以作为验证器。Agent 工具调用。模型能否在需要时调用正确工具、传入正确参数是完全可以自动化评测的。若工具名描述不当harness 训练会找到更好的工具描述方式。多语言客服回答。可以用召回率、关键词覆盖率、敏感内容拦截等多个维度的验证器来衡量某个提示词策略更好。相对不适合的场景是完全主观的开放性问答。比如“写一首诗”或者“帮我写一段文案”这类任务很难设计自动化验证器。人工评测也可以做但成本会显著抬高。5.2 如何让训练结果真正跨模型“跨模型增益”不是天然发生的它需要你在训练阶段就主动施加约束。常见做法有以下三种多模型联合评分。训练时不只对一个模型调优而是让候选 harness 同时跑 2-3 个模型比如一个开源模型、一个商业旗舰模型、一个轻量模型。只有平均分高的配置才被保留。刻意降低对模型风格化要求的 Prompt。如果提示词里写“请扮演 OpenAI GPT-4”那换到其他模型自然不适应。优化器需要避免这种特定模型绑定的表达。避免评测集过拟合。评测样本如果只来自某一个业务模块训练出的 harness 在该模块上也许有效换个模块就失效。所以评测集必须定期从真实流量中抽样替换覆盖多种句式、多种意图。结合 search 热词来看最近社区里大量讨论的 LLM 框架、Agent harness、LLM Studio 等工具本质上都是在解决同一件事把模型调用、工具编排和评测标准化。如果你的项目已经用了 LangChain、LlamaIndex 或自研 Agent 框架你要做的其实不是再引一个新的框架而是在现有框架之上增加一层可自动搜索的配置层并且把评测集当作一等公民。5.3 线上运行与训练阶段分离生产系统里我们不能让优化器直接改线上 Prompt。更稳妥的方式是训练阶段与线上运行分离离线训练定期从日志中抽样问题构造评测集跑若干轮 harness 训练。灰度发布将训练出的 best_harness.json 发布到灰度环境用线上真实流量的业务指标验证。全量发布灰度指标通过后再全量发布并将新配置加入版本历史。这个发布流程与传统机器学习模型上线非常相似。Harness 同样存在“模型漂移”问题当底层 LLM API 升级后之前的配置效果可能变化所以你还需要一个监控任务定期用线上样本回归旧配置一旦分数下降就触发新一轮训练。6. 常见问题与失败点排查下面列出我在实践和预研中遇到的高频问题供大家对照。问题现象常见原因解决思路训练轮数很多但分数不涨评测集太小随机波动掩盖了真实差异扩充评测集至少 50 条以上重复多次取平均分搜索出的 Prompt 换个模型就失效训练时只用了单模型改用多模型联合评分分数取平均值某个 benchmark 分数很高其他任务下降没有做 cross-benchmark 平均按任务分组统计用加权平均作为优化目标每次调 Prompt 成本很高评测集调用次数过多先在小规模集上粗筛再在大集上精排对相似候选去重模型本身能力不足harness 怎么调都没用任务难度超过模型能力边界考虑更换更强的基座模型或降低单步任务复杂度验证器把错误输出误判为正确校验规则太宽松增加必填字段、格式正则、边界值等硬约束自动生成的 Prompt 偏离品牌或安全要求搜索空间内包含不安全文本增加候选词白名单 / 安全校验层过滤后才允许参与评测训练结果偏向某一种用户表达评测集分布与实际线上不一致定期从真实日志采样补充评测集而不是只在手工样例上训练6.1 为什么“评测集”比“优化算法”更重要很多团队看到 auto-train harness 之后第一反应是去找一个高级的优化算法比如贝叶斯优化或者进化策略。但真实工程经验是评测集质量决定了效果上限优化算法只是逼近这个上限。如果评测集只有 10 条数据再强的搜索算法也很难找到真正通用的配置。如果验证器写得不够严格那么优化器会非常“聪明”地找到能骗过验证器、但实际线上效果很差的策略。所以我建议先在评测集和验证器上投入 70% 的精力再考虑优化算法。6.2 成本控制与缓存设计Harness 训练会大量调用模型 API。比如 20 条评测集、每轮 4 个候选、跑 10 轮就是 800 次模型调用。如果每个样本都比较长这个成本不可忽视。实际工程建议对模型输出做缓存。同一候选配置、同一问题如果历史已经跑过直接读取缓存结果。先粗筛后精排。在小评测集上跑 2-3 轮把明显不好的候选丢弃再在大评测集上精排。控制候选之间的重复度。两个候选如果只有温度参数不同可能需要多轮才能区分并不是训练初期的优先选择。7. 工程化最佳实践7.1 把 Harness 配置作为代码仓库的一部分Harness 配置不像模型权重那样不可读它是一份 JSON 或 YAML。你应该把它纳入 Git 管理并且记录每一份配置的评测分数、评测时间、模型版本。这样线上出问题时可以快速回滚到上一份好配置。推荐配置文件结构{ version: 2025.06.01-r3, models: [gpt-4o-mini, qwen-plus], harness: { role: 你是一个严谨的数据转换助手。, output_format: 请直接输出 JSON。, enable_chain_of_thought: false }, eval_result: { task_order: 1.0, task_json: 0.95, overall: 0.975 }, created_at: 2025-06-01T10:30:00Z }7.2 设计好验证器分层验证器不要只写成一个布尔函数建议分成三层格式层。检查输出是否为合法 JSON、是否包含代码块、是否以指定字符开头。内容层。检查是否包含关键实体、是否回答了问题核心。业务规则层。检查是否触碰敏感词、是否包含非法操作建议。每一层独立校验。记录失败时是哪一层没通过对后续人工分析非常有价值。7.3 避免 Prompt 过拟合到评测集防止过拟合是 harness 训练最容易被忽视的一环。以下信号说明可能过拟合了训练分数很高但线上真实满意度没有提升。候选配置里出现了评测集里的特有实体。比如评测集中大量出现某种固定订单号搜索出的 Prompt 可能悄悄在示例中加入了这些数据导致模型“背答案”。换一个时间段的数据后分数明显下滑。缓解方法包括定期更新评测集、禁止从评测集直接复制长文本作为示例、增加多样化任务、使用多个模型做联合验证。7.4 把“人工经验”编码进初始候选自动训练不代表完全抛弃人工经验。恰恰相反把强人工设计的几种 Prompt 作为初始候选再让优化器在附近做扰动往往比完全随机搜索效果好得多。这也是进化算法里的常见策略初始种群要优秀后代才更容易收敛到高分区。如果你在团队内部已经积累了各种“Prompt 调优经验”不要只写在文档里而是把它们拆成可枚举的模板片段放进搜索空间这才是知识沉淀的最好方式。8. 总结与后续学习方向这篇博客围绕“Auto-train the harness, not the LLM”展开核心是想说明一个工程思路当 LLM 应用效果不好时修改模型权重不是唯一选择甚至不应该作为第一选择。模型外围的提示词模板、示例选择、工具描述、输出校验与重试策略本身就是一个可以被自动化搜索和优化的系统。优化这个系统就是“训练 harness”。我们通过一个最小 Python 原型演示了如何定义可搜索的 HarnessConfig、如何准备评测集与验证器、如何做随机变异搜索以及为什么要在训练阶段引入多模型和多任务联合评分来实现 cross-model 与 cross-benchmark 的增益。如果这篇文章对你有所启发可以试着把它用在自己的项目里先挑一个已有明确效果瓶颈的场景构造 50 条评测数据写 2-3 个基础验证器再跑一个最简单的随机搜索。你会发现哪怕不用任何高级优化库把 Prompt 调优从“手工活”变成“自动闭环”之后团队的迭代效率也会有明显提升。接下来如果你想继续深入可以从这几个方向入手阅读 DSPy 等自动提示优化框架的源码了解它如何把模块化签名与优化器结合研究一下 Agent 场景下的工具调用评测把 Function Calling 的工具描述也纳入搜索空间或者尝试用贝叶斯优化替换代码里的随机搜索对比不同优化算法在 Prompt 搜索上的收敛效果。如果本文对你有帮助欢迎收藏备用。后续我也会继续分享 LLM 工程化、Agent 评测和自动优化相关的实战笔记。