
Harness 这个词最近在我们几个做 AI 应用的朋友群里出现的频率高得离谱。有人问 deepseek harness 到底是个什么东西有人拿着手上的 agent 项目纠结要不要再包一层 harness还有人干脆把 harness 和 agent 当成一个词在用。我一开始也犯嘀咕直到把手上三个已经上线跑着的项目从头到尾撸了一遍才把这几层东西的边界彻底捋清楚。这篇文章就干一件事把 LLM、Agent、Skill、Harness 这四个词摆到同一张桌子上讲清楚谁是谁、谁调用谁、各自解决什么问题、什么场景下必须上、什么场景下纯属过度设计。不管你是刚接触大模型、还在纠结这几个名词区别的新手还是已经写过几版 agent、想搞清楚生产环境到底缺哪一块的老手都能从里面扒到能直接抄的东西。我会尽量少讲虚的概念多讲我在真实项目里踩过的坑和最后落地的方案。1. 先把四个词摆到一张桌子上它们的关系全景1.1 一句话给四个词各自定位如果只能用一个类比我会这么说LLM 是发动机Agent 是驾驶员Skill 是工具箱Harness 是整台车——包括底盘、方向盘、刹车、仪表盘、安全带那一整套。LLM大语言模型提供最底层的推理和生成能力。它是一块算力密集的“大脑”输入文本或图文输出文本或结构化数据。它本身不会动也不记事。Agent智能体负责“决定下一步做什么”。它拿到目标后判断该不该调用工具、调用哪个、拿回结果后要不要继续本质是一个循环调度器。Skill技能把某一种具体能力封装好比如查数据库、发请求、做数学计算、生成一份报告。它是一个可以被 Agent 反复调用的、输入输出明确的单元。Harness工程外壳让上面三个东西在生产环境里稳定、可观测、可控制、可复现地跑起来的那层工程代码。会话管理、上下文裁剪、工具注册、权限校验、重试、日志、评测全都属于它。这个类比的好处是它解释了为什么很多人会把它们混着用因为它们确实是在同一台车上只是层级完全不同。拿发动机当整车去卖是新手最常见的误解。1.2 为什么这四个词总被混着用我先说个真实经历。去年我帮一个团队看他们的 agent 项目代码里有个叫Agent的类点进去一看里面干了这些事拼 prompt、调模型、解析 JSON、执行工具、存上下文、做重试、打日志、控制并发。八百多行全在一个类里。这就是问题的根源当你只做一个小 demo 的时候这四层是天然糊在一起的。改个 prompt 就完事工具直接写死在 if-else 里上下文就一个数组往后加。你根本感觉不到需要分层。但一旦项目要上生产需求就来了要能换模型今天用 A明天换 B 做成本对比、要能加工具业务方每周提新需求、要能出问题排查用户说它答错了你得能回放、要控制成本不能每次都把全量历史塞进去。这四件事恰好对应四层各自的职责。混着写改一个动全身分开写每层各司其职。所以这四个词被混用不是因为这些概念本身模糊而是因为大部分人的项目还没复杂到必须把它们拆开。你一旦拆过就再也回不去了。1.3 调用链从用户一句话到最终输出我把一次完整请求的流向画在脑子里是这样一条链用文字描述用户输入 →Harness接收建立会话、加载历史、做安全校验 → 把整理好的上下文交给Agent→ Agent 把任务拆解决定调用哪个Skill→ Skill 执行可能是一次 HTTP 请求、一次数据库查询、一次本地计算→ 结果回到 Agent → Agent 判断是否完成未完成继续循环 → 完成后把最终结果交给Harness→ Harness 做格式化、记录日志、更新会话状态 → 返回给用户。注意这条链里LLM 是被 Agent 反复调用的而且每次调用的 prompt 都不一样——因为 Agent 每轮都要把“现在的情况 可用的工具 之前的结果”重新组织一遍再喂给模型。下表把四层的职责、典型实现、出错时的表现整理了一下方便你对照自己的项目找位置层核心职责典型实现位置出错时的典型表现LLM理解与生成模型服务商 API答非所问、幻觉、格式错乱Agent决策与循环你自己写的调度逻辑死循环、工具选错、任务跑偏Skill执行具体能力一个个独立函数/服务参数错、超时、返回脏数据Harness工程保障应用主框架上下文爆掉、状态丢失、无法排查这张表建议你截图存一下后面每次出问题先定位到是哪一层的锅能省掉大量瞎改的时间。2. LLM能力底座以及它天生的三个短板2.1 无状态、无记忆、无手脚LLM 最大的特点也是所有麻烦的起点就是它天生是无状态的。你调一次 API它给你一个回复然后就结束了。它不记得你上一句说了什么也不记得你是谁。很多人第一次调 API 会懵为什么我第二句话它就不认识我了答案是——你得自己把历史对话拼进去。所谓“多轮对话”其实是每次请求都带着全部历史重新问一遍。这个机制后面会牵扯出一大堆问题上下文长度、成本、裁剪策略Harness 里有很大一块工作就是在处理这件事。另外两个短板无记忆它只能看见你这次塞进去的东西。想让它在多次会话之间记住用户偏好你得自己搞一套存储向量库、键值库都行然后在每次请求时把相关记忆检索出来拼进上下文。无手脚它不能上网、不能读你的数据库、不能发消息。它只会“说”。要让它“做”必须有人替它执行这就是 Agent 和 Skill 存在的理由。我用一个生活化的类比LLM 就像一个知识渊博但失忆的顾问每次见面都是第一次。你想让他连续帮你干活得每次把前情提要都复述一遍还得给他配个助理Agent去跑腿Skill。2.2 上下文窗口的账要算清楚上下文窗口是 LLM 一次能“看见”的最大 token 数。这个概念看着简单实际是 Harness 里最容易翻车的地方。假设你用的是 128K 窗口的模型别急着高兴。这 128K 要塞的东西包括系统提示词往往几千 token工具定义每个工具的 schema动辄几百 token工具一多就上千对话历史越聊越长检索到的记忆或文档本轮的实际输入留给输出的空间我做过一次真实的统计一个中等复杂的 agent 项目光系统提示加工具定义就吃掉了近 8000 token。这还没开始聊呢。如果你按“对话历史无脑往数组里加”的方式做聊到二三十轮上下文就爆了。提示永远给输出预留至少 25% 的窗口余量。很多模型在接近窗口上限时输出质量会明显下降不是硬性报错而是悄悄变傻这种问题最难查。所以 Harness 里必须有一套上下文裁剪策略。常见做法有三种我在不同项目里都用过滑动窗口只保留最近 N 轮。简单粗暴但对早期重要信息会丢失。摘要压缩把旧对话让模型自己总结成一段摘要替换掉原文。省 token但有信息损耗。检索式历史全存起来每轮只检索最相关的几段塞进去。最省 token但要维护向量库复杂度高。我的经验是对话类产品用滑动窗口加摘要任务类产品用检索式。选哪个取决于你的场景是“连续性对话”还是“独立任务”。2.3 为什么裸调 LLM 干不了复杂活有人会想既然 LLM 这么强我直接把“帮我查一下库存然后下单”这句话丢给它不就行了答案是不行原因有三第一它没有实时信息。你不给它工具它只能靠训练时的知识瞎编。问它今天的库存它编得比谁都像真的。第二它不会执行动作。就算它“知道”该下单它也没有下单的渠道。它只能输出一段文字说“建议下单”。第三它不会纠错。单次调用是“一锤子买卖”模型答错了就是错了没有第二次机会。复杂任务需要“做一步、看结果、再调整”这必须靠循环。这三点合起来就是 Agent 要补的洞。所以纯 LLM 调用能干的活基本限于翻译、总结、分类、单轮问答、格式化改写。一旦任务需要“多步 外部信息 纠错”你就得上 Agent。3. Agent把“想”变成“做”的调度器3.1 Agent 的最小结构循环 工具 状态剥掉所有花哨的说法一个 Agent 的最小结构就是三样东西一个循环不断地“问模型 → 拿结果 → 执行 → 把结果塞回去 → 再问”。一组工具告诉模型它有哪些“手脚”可以用。一份状态记录当前跑到哪了、已经拿到了什么。伪代码大概长这样def run_agent(goal, tools, max_steps10): state {goal: goal, history: []} for step in range(max_steps): # 1. 组装当前上下文 prompt build_prompt(state, tools) # 2. 问模型 response llm.chat(prompt) # 3. 解析模型想干什么 action parse_action(response) if action.type final: return action.answer # 4. 执行工具 result execute_tool(action.name, action.args) # 5. 把结果记进状态进入下一轮 state[history].append({action: action, result: result}) return 达到最大步数任务未完成这段代码不到二十行但已经包含了 Agent 的全部核心。你会发现真正的难点根本不在这个循环本身而在每个环节的细节prompt 怎么拼、模型输出怎么解析、工具报错了怎么办、步数超了怎么办。这些恰恰是 Harness 要兜住的。max_steps这个参数特别重要。我见过不止一个项目因为忘了设上限模型在某个死循环里反复调用同一个工具一夜之间烧掉几百块 API 费用。任何 Agent 循环都必须有硬性的步数上限和超时这是血泪教训。3.2 主流范式对比ReAct、Plan-and-Execute、ReflectionAgent 的“怎么想”有几种主流套路理解它们的差异比记住名字重要得多。ReActReason Act是最常见的让模型每一步都先输出一段“思考”再输出一个动作。简单、灵活适合步骤数不多、环境反馈快的任务。缺点是每步都要调一次模型步骤一多成本和延迟都上去了。Plan-and-Execute先让模型把整个计划列出来第一步做什么、第二步做什么然后照着计划执行。好处是思路清晰、可提前评估成本坏处是环境变化时计划会失效得能重新规划。Reflection在拿到结果后让模型自己检查一遍“这个结果对不对、有没有更好的做法”再决定是否重来。质量提升明显但成本也翻倍。我整理了一张对比表范式适合场景调模型次数主要风险ReAct步骤少、反馈快中步骤多时成本失控Plan-and-Execute结构清晰的长任务较少计划僵化、环境变化失效Reflection对质量要求高多成本翻倍、可能过度纠结实操建议先用 ReAct 跑通发现哪里不行再针对性加东西。别一上来就上复杂范式大部分任务 ReAct 就够了。3.3 手搓一个最小 Agent 的实操过程我用一个真实的例子带你走一遍。目标是“根据用户提问决定是查订单还是退货”。第一步定义工具 schema。这是新手最容易偷懒的地方。工具描述写得含糊模型就会选错工具。我的做法是把每个工具的用途、参数、返回都写清楚甚至写上“什么时候不该用”。{ name: query_order, description: 根据订单号查询订单状态和详情。当用户询问某个已有订单的情况时使用。不要用它查询退货进度。, parameters: { order_id: {type: string, description: 订单号格式为 16 位数字} } }注意 description 里那句“不要用它查询退货进度”这是防呆设计。工具描述里写清楚边界比写清楚用途更能减少误调用。第二步设计输出格式。让模型以固定的 JSON 返回动作比如{action: query_order, args: {order_id: ...}}。这里会踩一个坑模型经常在 JSON 外面套一层解释文字。我的处理方式是要求“只输出 JSON”同时在解析时用正则做容错兜底。热词里提到的“修复 llm 返回 json 的 java 库”就是专门解决这类问题的原理无非是在解析失败时尝试补全括号、去掉多余文字。第三步写执行循环。就是前面那段伪代码的完整实现。关键是加三个保护步数上限、单步超时、异常捕获。第四步做日志。每一步的输入、输出、耗时都记下来。这不是可选项。当用户说“它答错了”时你唯一的排查依据就是这些日志。跑通之后你会发现这个最小 Agent 大概两百行代码就能写完。剩下的九成工作量全在 Harness 那一层。4. Skill能力封装的最小可复用单元4.1 Skill 与 Agent 的区别用一句话说透这个区别是提问最多的。我的回答是Agent 负责“决定做什么”Skill 负责“怎么把一件事做好”。Agent 是决策者它不知道细节只知道“我有一个叫查订单的技能”。Skill 是执行者它不关心为什么被调用只管拿参数、干活、返回结果。类比一下Agent 是餐厅服务员Skill 是后厨的一道道菜。服务员负责接待、点单、上菜的顺序每道菜怎么做是厨师的事服务员不需要知道。你换一道菜加一个 Skill服务员的话术不用改你换个服务员换 Agent 实现菜的配方也不用动。这种解耦就是分层最大的价值。在具体产品里Skill 这个名字的叫法五花八门。有的生态叫“插件”有的叫“工具”有的叫“技能”比如某些编程助手里就把可复用的能力封装叫 skill你可能会看到 codex skill、仓颉 skill 这类说法数学建模里也有把整套解题流程封成 skill 的玩法。名字不同本质一样一个输入输出明确、可以独立测试、可以被 Agent 反复调用的单元。4.2 Skill 的粒度怎么切粒度是设计 Skill 时最容易犯错的地方。切太细Agent 要调用十几次才能完成一件事成本和延迟都爆炸切太粗一个 Skill 里塞了一堆逻辑复用性差还容易出错。我总结的判断标准有三条单一职责一个 Skill 只干一件事。如果它的名字里出现了“并且”比如“查询并修改订单”就该拆开。可独立测试给一组输入能明确判断输出对不对不需要跑整个 Agent。参数可控参数个数控制在 5 个以内太多了模型填错率会飙升。举个例子“发送通知”这个 Skill如果同时支持邮件、短信、站内信参数就会变复杂。更好的做法是拆成三个 Skill或者用一个channel参数加清晰枚举。我倾向后者因为渠道之间逻辑相似拆开反而增加维护成本。4.3 一个 Skill 的标准结构拆解一个能在生产环境跑的 Skill除了核心逻辑还要包含这些东西。我按重要性排序元数据名称、描述、参数 schema。这是给模型看的直接决定它会不会正确调用。参数校验模型给的参数不可信必须校验。缺失、类型错、超范围都要拦下来并返回明确的错误信息让 Agent 有机会修正。超时控制任何外部调用都必须设超时。没有超时的 Skill 就是一个定时炸弹。幂等设计同样的参数调两次结果应该一致。对于写操作这一点尤其重要因为 Agent 可能因为重试而重复调用。结构化返回返回给模型的内容要简洁、明确。不要把整个数据库记录原样扔回去模型会被淹没。只返回它决策需要的关键字段。我用一个表格把正反两种做法对比一下维度该做的不该做的描述写清用途和边界只写名字参数校验 明确错误信任模型直接透传调用设超时和重试无限等待返回精简关键字段原样返回大对象注意Skill 的返回内容会直接进入上下文吃掉宝贵的 token。我曾经因为一个查询 Skill 返回了完整 JSON导致上下文迅速撑满Agent 反而变笨了。把返回精简到“决策必需”是我踩过坑之后养成的习惯。5. Harness真正决定 Agent 能不能上生产的那层工程5.1 Harness 是什么为什么最近被反复提Harness 这个词直译是“马具”或“挽具”——套在马身上、让你能驾驭它的那套装备。放到 AI 语境里特别贴切模型是那匹力气很大的马Harness 就是让你能安全驾驭它的整套装置。今年这个词被反复提起尤其是围绕 deepseek harness 这类工具的讨论多了之后很多人开始意识到大家之前一直在卷模型和 Agent 逻辑却忽略了一个事实——决定一个 AI 产品能不能真正用起来的往往不是模型多强而是外面这层工程做得好不好。一个直观的对比两个团队用同一个模型、同样的工具集做同一个任务。团队 A 的 Agent 成功率 60%团队 B 能做到 95%。差距几乎全在 Harness 层——上下文管理、错误处理、重试策略、结果校验。模型是同一匹驾驭水平不一样。如果你把 deepseek harness 这类工具打开看它代表的正是这一层东西的实体化把模型接入、工具调用、会话管理、权限控制、执行环境打包成一个可以直接对话或编程调用的外壳。它不生产能力它组织能力。5.2 Harness 工程到底包含哪些东西我把 Harness 的组成拆成六大块每一块都是我实际项目中真实要写的代码第一块会话与状态管理。用户是谁、聊到哪了、上次的结果是什么。这决定了用户能不能“接着上次继续”。简单项目用内存字典生产环境必须持久化到数据库。第二块上下文工程。包括裁剪、压缩、检索、拼装。前面讲过这是 token 成本和质量的主战场。这里甚至要考虑“把工具定义动态化”——只把当前任务相关的工具塞进去而不是全部。第三块工具注册与调度。一个统一的工具注册中心Skill 在这里登记自己。Agent 只需要拿到注册表就能知道有什么可用。加新工具时只改注册不改 Agent 逻辑。第四块执行环境与沙箱。如果 Agent 能执行代码或操作系统命令必须隔离。给它的权限要最小化文件系统、网络访问都要限制。这块做不好就是安全事故。第五块可观测性。日志、链路追踪、指标统计。每一次模型调用、每一次工具执行、每一步的耗时和结果都要能回放。没有这层你排查问题基本靠猜。第六块评测与回归。你改了 prompt 或换了模型怎么知道是变好还是变坏了必须有固定的测试集自动跑分。这块最容易被忽略但它是持续迭代的前提。5.3 Harness 与 Agent 的边界划分这两者最容易被搞混因为它们在代码里经常挨着。我用一个判断标准决策逻辑归 Agent保障逻辑归 Harness。“下一步该调用哪个工具”是决策归 Agent。“调用工具超时了要不要重试、重试几次”是保障归 Harness。“这个任务做完了没有”是决策归 Agent。“上下文快满了要不要压缩”是保障归 Harness。按这个标准划分你会发现 Harness 是要做成通用的——换个 Agent 实现Harness 不用变Agent 是要做成可替换的——业务逻辑变了换掉 Agent底下的东西都不动。我做项目时的一个具体做法把 Agent 定义成一个接口只暴露“输入上下文、输出动作”这一个方法。Harness 调用这个接口完全不关心里面是 ReAct 还是别的范式。这样后期想换范式做对比实验改一个实现类就行。6. 四者协同实战一次请求的完整生命周期6.1 端到端链路拆解我把前面所有东西串起来走一遍“用户问我上周下的那单到哪了”。第 1 步Harness接收请求识别用户身份从数据库加载该用户最近几轮对话。同时启动一个计时器和链路追踪 ID。第 2 步Harness调用上下文工程模块。因为历史有点长触发摘要压缩把前 20 轮压成一段 300 字的摘要加上最近 5 轮原文拼成上下文。第 3 步Harness → Agent把整理好的上下文和工具注册表交给 Agent。第 4 步Agent → LLM组装 prompt 问模型。模型返回{action: query_order, args: {order_id: ...}}。但注意用户只说了“上周那单”没给订单号——这就是真实场景里的坑。第 5 步Harness参数校验发现order_id缺失拦截返回明确错误给 Agent。这一步如果做了Agent 会转而先调用“查询用户订单列表”的 Skill如果没做模型可能编一个订单号出来直接查错。第 6 步Agent → Skill调用订单列表 Skill拿到该用户上周的三个订单。第 7 步Agent → LLM把列表喂回去模型判断出用户可能指哪一个或直接反问用户确认。假设这里选择反问。第 8 步Harness格式化输出、记录完整链路日志、更新会话状态、计算本次消耗的 token 和费用。整个流程你会发现真正“聪明”的决策只占三分之一剩下的全是工程。参数校验、上下文压缩、错误拦截、日志记录这些看似枯燥的代码才是一个 AI 产品能不能稳定用的分水岭。6.2 关键配置与参数示例下面是一份我实际项目中用的配置模板可以直接改着用agent: max_steps: 8 # 循环硬上限防死循环 step_timeout_ms: 20000 # 单步超时 total_timeout_ms: 120000 # 整体超时 context: max_tokens: 32000 # 我的可用窗口不是模型最大值 reserve_for_output: 8000 # 给输出留的余量 strategy: summary # 压缩策略window / summary / retrieval keep_recent_turns: 5 # 保留最近几轮原文 llm: temperature: 0.2 # 工具调用场景压低随机性 max_retries: 2 retry_backoff_ms: 1000 observability: log_level: full # 每一步输入输出都记 trace_enabled: true几个参数我解释一下为什么这么设max_tokens设 32000 而不是模型的 128000是因为实际可用窗口要扣掉输出余量和安全缓冲盲目设大反而容易在边界处出问题。temperature压到 0.2是因为工具调用场景需要模型输出稳定、格式一致随机性太高会导致同样的输入给出不同的工具选择。max_retries设 2 而不是更多是因为重试成本高而且很多错误重试也没用比如参数错需要在 Harness 里先区分错误类型再决定重不重试。6.3 常见问题排查速查表我把这几年遇到的典型问题整理成表出问题时按表现对号入座现象最可能的原因排查方向Agent 反复调同一个工具工具返回内容模型看不懂 / 缺终止条件检查 Skill 返回格式看看是不是信息不明确越聊越傻前期信息丢失上下文裁剪策略太激进检查摘要是否丢了关键信息调整保留轮数工具选错工具描述含糊、边界没写清重写 description加“不要用于……”的说明输出 JSON 解析失败模型多输出了解释文字用容错解析库prompt 里强调只输出 JSON成本突然暴涨循环次数失控 / 上下文膨胀查 max_steps 和 token 统计定位是哪一步偶发超时某个 Skill 没设超时逐个 Skill 检查网络调用超时配置这张表我贴在工位上过。大部分“模型不行”的抱怨最后都定位到 Harness 的某个疏漏上。模型确实会犯错但把错误概率从一个可接受的数字压到一个可上线的数字靠的就是这些看似琐碎的工程处理。7. 几个我踩过的坑和真实项目心得第一个坑是过早分层。我刚开始学这套东西的时候一个简单需求也非要搭一套 Agent Harness结果一个星期都在写框架业务逻辑没写多少。后来想明白了如果你的任务就是“单轮问答”或者“固定流程调用一次模型”压根不需要 Agent更不需要 Harness。分层是为了应对变化如果你的东西不会变就别分。判断标准是你预计未来会换模型、加工具、改流程吗三个都不,就直接裸调。第二个坑是信任模型的参数。我做过一个 Skill参数是文件名模型给的路径有时候带引号、有时候多空格。一开始我直接透传结果文件找不到排查了半天。后来加了参数清洗和校验所有问题消失。教训是模型给的任何东西都要当成不可信输入来处理就像处理用户表单一样。第三个坑是日志记太粗。早期我只记了“调用模型成功/失败”结果用户反馈答错时我完全不知道模型当时看到了什么上下文。后来改成每一步的完整输入输出都落盘注意脱敏排查效率提升了一个量级。这个改动当时看起来“浪费存储”实际救了我无数次。第四个经验是关于评测集。我在第二个项目里开始维护一个固定测试集大概 50 条覆盖各种边界的输入每次改 prompt 或换模型就跑一遍。这个习惯让我避免了“改了 A 结果弄坏了 B”的经典问题。测试集的构建没什么捷径就是把线上真实的好案例和坏案例慢慢攒起来。第五个心得是关于换模型。因为分层的缘故我换模型只改 Harness 里的一个配置项。但换完一定要重跑评测集因为不同模型对 prompt 的敏感度完全不同。同一个 promptA 模型能正确输出 JSONB 模型可能就加了一堆解释。别假设换模型是无痛的它一定需要重新调优 prompt 和校验逻辑。最后说一个关于成本的体会。很多人只盯着模型单价其实上下文管理对成本的影响更大。我做过一次优化把上下文从平均 20000 token 压到 8000成本直接降了六成质量几乎没变。省 token 比换便宜模型更有效而且不影响效果。这也是为什么我一直强调 Harness 这层动手改的价值——它不性感但它是真金白银。