1. 为什么你的 Agent 跑通 demo 却上不了生产如果你已经跟着前面的系列把单 Agent 工具调用、多 Agent 编排都跑通了大概率会遇到一个很尴尬的阶段demo 里一切顺滑一放到真实输入上就开始出洋相。要么多跑几步陷进死循环要么 token 烧得比预期快十倍要么工具调错参数还一路错到底多 Agent 一并发直接把 LLM 配额打爆。这些问题不是模型不够聪明而是你缺了一套围绕模型的工程系统。这套系统我习惯叫它 Harness中文可以理解成“驾驭系统”。核心等式很简单Agent Model Harness。模型是引擎负责理解、推理、生成Harness 是方向盘和刹车负责上下文管理、约束执行、验证循环、状态隔离。一辆没有方向盘和刹车的跑车引擎再强也是灾难。一个没有 Harness 的 Agent模型再强也会在复杂场景里失控。这篇是 S9 三部曲的开篇先给方法论总纲再落地第一块硬能力——Agent 评估。评估不只看最终结果对不对更要看轨迹工具选对没、参数填对没、用了几步、有没有兜圈。配合 SWE-bench、Tau-bench 这类 benchmark 和在线 A/B才能建立可复现的评估闭环。下面我会给出可直接复制的 Harness 配置骨架、轨迹埋点方案和 benchmark 跑分验证动作你可以边看边搭。2. TaoToken 前置把模型接入层先固定下来在搭 Harness 之前得先把模型接入这层固定住否则后面换模型、做 A/B、跑 benchmark 都会乱。我自己的做法是统一走一个兼容 OpenAI 协议的入口这样 Harness 里的模型适配层只需要改一个 base_url 和 key不用动业务代码。TaoToken 的 API 地址是https://taotoken.net/api兼容 OpenAI 的/v1/chat/completions格式。你需要在控制台创建一个 API Key然后把它写进环境变量别硬编码进代码。创建入口在控制台的 API Keys 页面模型对话调试可以在模型对话页直接试长期跑编码类 Agent 任务的话可以看下 Coding Plan。这里要强调一点Harness 的可拆卸性支柱要求模型相关部分做成可插拔适配层。所以你的配置里模型名、base_url、key 都应该是外部注入的而不是写死在 Agent 逻辑里。这样模型迭代时你只需要换配置不用重写 Harness。export TAOTOKEN_API_KEYsk-你的key export TAOTOKEN_BASE_URLhttps://taotoken.net/api环境变量设好之后先别急着写 Agent用一条 curl 确认接入层是通的。这一步很关键很多后面排查半天的“Agent 不工作”其实只是接入层没通。curl -s $TAOTOKEN_BASE_URL/v1/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d { model: claude-sonnet-4-20250514, messages: [{role: user, content: 只回复两个字通了}], max_tokens: 16 }返回里能看到choices[0].message.content是“通了”说明接入层没问题。接下来所有 Harness 配置都基于这个入口。3. 可复制的 Harness 配置骨架Harness 的配置我建议分成两块一块是 Agent 运行时配置settings.json管模型、工具、循环上限、预算一块是评估与轨迹配置config.toml管埋点、benchmark、A/B 分流。分开的好处是运行时和评估解耦改评估不影响线上行为。3.1 settings.json运行时骨架这份配置覆盖了六大支柱里的上下文架构、架构约束、自验证循环、可拆卸性。字段我都加了注释你按自己场景改值就行。{ harness_version: s9.1, model: { provider: taotoken, base_url_env: TAOTOKEN_BASE_URL, api_key_env: TAOTOKEN_API_KEY, name: claude-sonnet-4-20250514, fallback_models: [qwen-max, gpt-4o], adapter: openai_compatible }, context: { max_context_ratio: 0.4, compress_threshold: 0.35, keep_recent_turns: 6, summary_model: qwen-turbo }, loop: { max_steps: 25, max_tool_calls_per_step: 3, dead_loop_detection: { enabled: true, repeat_action_threshold: 3, repeat_observation_threshold: 2 }, self_verify: { enabled: true, checkpoint_every_steps: 5, verify_prompt: 检查当前进展是否偏离目标若偏离请给出纠正动作 } }, budget: { max_tokens_per_task: 120000, max_cost_usd_per_task: 0.5, on_exceed: abort_with_trace }, tools: { whitelist: [search, read_file, write_file, run_test], deny_dangerous: true, require_approval: [write_file, run_shell] }, isolation: { per_agent_context: true, shared_memory: false, subagent_max_depth: 2 } }几个关键点解释一下。max_context_ratio设 0.4 是因为上下文利用率超过 40% 后推理质量会明显下滑这是实测出来的经验值。dead_loop_detection里repeat_action_threshold设 3意思是同一个动作重复三次就判定为兜圈直接中断并记录轨迹。budget里的on_exceed设成abort_with_trace超预算时不是静默失败而是带着完整轨迹退出方便你回放定位。3.2 config.toml评估与轨迹骨架评估配置单独放一份管轨迹埋点、benchmark 任务集、A/B 分流比例。[harness] version s9.1 trace_dir ./traces trace_format jsonl [trace] enabled true capture [thought, action, action_input, observation, step_index, token_used, latency_ms] redact_keys [api_key, authorization] sample_rate 1.0 [benchmark] suite [swe_bench_lite, tau_bench_retail, gaia_dev] run_on [prompt_change, model_change, tool_change] baseline_file ./benchmarks/baseline.json report_dir ./benchmarks/reports [eval] judge_model claude-sonnet-4-20250514 judge_rubric ./eval/rubric.md human_spot_check_ratio 0.1 regression_set ./eval/regression_tasks.jsonl [ab_test] enabled true split { control 0.9, treatment 0.1 } metrics [task_success_rate, p95_latency_ms, cost_per_task] min_sample_size 200 significance_level 0.05trace.capture里我特意把thought、action、observation都抓了因为轨迹评估的核心就是这三样。redact_keys防止 key 泄漏进轨迹文件。ab_test里的min_sample_size和significance_level是为了做统计显著性判断避免把随机波动误判成优化效果。4. 轨迹埋点与 benchmark 跑分验证配置写好了接下来是让它真正跑起来产生数据。轨迹埋点和 benchmark 跑分是评估闭环的两条腿缺一不可。4.1 轨迹埋点每一步都留痕轨迹埋点的最小实现是在 Agent 循环里插一个 recorder每一步把 thought、action、observation 写进 jsonl。下面是一个 Python 骨架你可以直接嵌进自己的 Agent 循环。import json, time, os from datetime import datetime class TraceRecorder: def __init__(self, trace_dir, task_id, sample_rate1.0): self.path os.path.join(trace_dir, f{task_id}.jsonl) self.sample_rate sample_rate self.step 0 os.makedirs(trace_dir, exist_okTrue) def record(self, thought, action, action_input, observation, token_used, latency_ms): self.step 1 entry { ts: datetime.utcnow().isoformat(), step_index: self.step, thought: thought, action: action, action_input: action_input, observation: observation, token_used: token_used, latency_ms: latency_ms, } with open(self.path, a, encodingutf-8) as f: f.write(json.dumps(entry, ensure_asciiFalse) \n) def close(self, final_result, success): summary { ts: datetime.utcnow().isoformat(), type: summary, total_steps: self.step, final_result: final_result, success: success, } with open(self.path, a, encodingutf-8) as f: f.write(json.dumps(summary, ensure_asciiFalse) \n)在 Agent 循环里这样用recorder TraceRecorder(./traces, task_idtask_001) for step in range(max_steps): t0 time.time() thought, action, action_input agent.plan(state) observation agent.execute(action, action_input) recorder.record(thought, action, action_input, observation, token_usedagent.last_token_used, latency_msint((time.time() - t0) * 1000)) if agent.is_done(observation): recorder.close(observation, successTrue) break跑完一个任务后./traces/task_001.jsonl里就是完整轨迹。你可以写个分析脚本算工具选择准确率、步数效率、有无重复动作。import json from collections import Counter def analyze_trace(path): steps [] with open(path, encodingutf-8) as f: for line in f: obj json.loads(line) if obj.get(type) ! summary: steps.append(obj) actions [s[action] for s in steps] action_counts Counter(actions) repeated {a: c for a, c in action_counts.items() if c 3} total_tokens sum(s[token_used] for s in steps) return { total_steps: len(steps), repeated_actions: repeated, total_tokens: total_tokens, avg_latency_ms: sum(s[latency_ms] for s in steps) / max(len(steps), 1), } print(analyze_trace(./traces/task_001.jsonl))输出里如果repeated_actions非空说明 Agent 在兜圈需要回去看那几步的 thought 和 observation定位是工具返回不清晰还是 prompt 没约束好。4.2 benchmark 跑分横向标尺自建任务集容易自说自话benchmark 提供客观横向对比。我一般跑三个SWE-bench Lite 测代码修复、Tau-bench 测工具使用加业务策略、GAIA dev 测多步推理加工具加多模态。跑分脚本的核心是固定任务集、固定模型配置、记录轨迹和结果。import json, subprocess def run_benchmark(suite_name, agent_config, tasks_file): results [] with open(tasks_file, encodingutf-8) as f: tasks [json.loads(line) for line in f] for task in tasks: agent build_agent(agent_config) recorder TraceRecorder(./traces/bench, task_idtask[id]) result agent.run(task[input], recorderrecorder) recorder.close(result, successresult[success]) results.append({ task_id: task[id], success: result[success], steps: result[steps], tokens: result[tokens], }) summary { suite: suite_name, total: len(results), success_rate: sum(r[success] for r in results) / len(results), avg_steps: sum(r[steps] for r in results) / len(results), avg_tokens: sum(r[tokens] for r in results) / len(results), } with open(f./benchmarks/reports/{suite_name}.json, w) as f: json.dump({summary: summary, details: results}, f, ensure_asciiFalse, indent2) return summary print(run_benchmark(swe_bench_lite, agent_config, ./benchmarks/swe_lite.jsonl))跑完之后和baseline.json对比看成功率有没有退化、步数有没有变多、token 有没有涨。改动 prompt 或换模型后必须重跑这是防退化的底线。4.3 在线 A/B离线到线上的闭环离线 benchmark 过了上线后用 A/B 验证真实效果。按 config.toml 里的 90:10 分流跑够 200 个样本后用统计方法判断差异是否显著。from scipy import stats def ab_significance(control_success, control_n, treatment_success, treatment_n): p1 control_success / control_n p2 treatment_success / treatment_n p_pool (control_success treatment_success) / (control_n treatment_n) se (p_pool * (1 - p_pool) * (1/control_n 1/treatment_n)) ** 0.5 z (p2 - p1) / se if se 0 else 0 p_value 2 * (1 - stats.norm.cdf(abs(z))) return {control_rate: p1, treatment_rate: p2, p_value: p_value, significant: p_value 0.05} print(ab_significance(180, 200, 22, 20))三指标要一起看成功率、P95 延迟、单任务成本。强模型能提成功率但会增成本和延迟得权衡。我踩过的坑是只看成功率就全量切新版结果成本翻倍、延迟涨了 40%后来改成按场景分流才稳住。5. 本篇常见错排查轨迹文件为空或只有 summary检查TraceRecorder.record是否真的在循环里被调用以及trace.enabled是否为 true。常见原因是 Agent 循环提前 break 了没走到 record。benchmark 跑分波动大先确认任务集是否固定、模型温度是否设成 0。温度非 0 时同一任务多次跑结果会飘评估必须固定随机种子或温度。A/B 判断显著但线上没感觉检查样本量是否够、分流是否真的随机。90:10 分流下 treatment 只有 20 个样本时 p 值不可信至少跑到 min_sample_size。死循环检测误杀repeat_action_threshold设太小会把正常的重试也判成兜圈。建议先设 3 到 5观察轨迹后再调。上下文压缩后质量下降compress_threshold设太低会频繁压缩丢失关键信息。先设 0.35配合keep_recent_turns保留最近几轮原文。模型适配层换模型后报错检查adapter是否统一走 openai_compatible以及新模型名是否在 fallback 列表里。可拆卸性支柱要求换模型只改配置如果还要改代码说明适配层没做干净。6. 把评估闭环跑起来再谈优化Harness 六大支柱里评估对应的是自验证循环和熵治理的落地基础。没有轨迹和 benchmark你根本不知道改动是让 Agent 变好还是变坏。所以顺序上先把接入层固定TaoToken 的 API Keys 和接入文档再把 settings.json 和 config.toml 配好然后跑通轨迹埋点和 benchmark 跑分最后接上在线 A/B。这套闭环建起来之后后面第 22 篇的可观测 Trace、Loop Engineering、容错硬边界第 23 篇的成本压缩、安全白名单、部署版本化才有验证和迭代的依据。如果你现在还在调模型对话阶段可以先去模型对话页把接入层跑通如果已经在做长期编码类 AgentCoding Plan 那条线更适合你接入和排障相关的细节都在接入文档里。评估闭环不是一次性的活是每次改动都要跑的例行动作跑顺了Agent 工程化才算真正起步。