资讯动态

从Prompt到Harness:AI工程演进与轻量智能体框架实操

发布时间:2026/9/26 7:16:39 来源:尧图企业网站定制
把同一个 Prompt 从 ChatGPT 里搬到一套带工具调用的智能体系统结果完全不是一回事这是我最近三个月最大的体感。前两天我还在为一个代码助手调整提示词模型输出稍微偏一点整个流程就崩后来我把注意力从“Prompt 怎么写”挪到“模型怎么被装配进一个可控的框架里”也就是 Harness 工程问题才真正开始被解决。这篇内容想聊的就是这个转变从 Prompt 到 HarnessAI 工程到底在演进什么以及我从零搭一个轻量 Harness 的实操记录。适合正在做智能体、做 RAG、做 AI 应用落地的人尤其是那种已经发现“提示词写得再好系统还是不稳”的开发者。1. 从Prompt到Harness一次必然的范式转移1.1 Prompt工程为什么重要又为什么不够用Prompt Engineering 的价值不用多讲它是最低成本的模型交互方式。写好一段指令、给几个 few-shot 例子、设计好思维链就能让模型输出接近预期。我在早期做 AI 工具时几乎所有“业务逻辑”都靠 Prompt 硬撑比如让模型输出固定 JSON、让模型判断用户意图、让模型从长文档里抽取字段。短时间内没问题因为模型能力确实强一段措辞激进的 Prompt 就能把行为掰过来。但 Prompt 有它天然的边界它只是一次模型调用的输入文本管不了状态、管不了工具、管不了生命周期。一旦场景变成多轮对话、工具调用、错误恢复问题就来了。模型返回了格式不对的 JSON你要么重试要么让用户重新说一遍模型调完一个工具后忘了上一步的结果你只能在上下文里反复强调。这些事靠 Prompt 解决就像靠一封写得再详细的邮件去管理一个实习生——邮件可以告诉他今天该干什么但没法保证他有权限、有资料、知道流程更没法在他做错的时候自动纠正。用一个生活一点的类比Prompt 是给新员工写的一段“工作指令”Harness 则是把岗位职责、审批流程、权限清单、工具链、复盘机制全部固定下来的管理体系。模型还是那个模型但它工作的环境变了行为稳定性就完全不一样。这也是为什么现在越来越多的团队把注意力从“调 Prompt”转向“搭 Harness”。1.2 Harness到底在套什么Harness 这个词直译是“马具”在 AI 工程里更准确的理解是“一套包裹着模型的受控运行框架”。它不是把模型关进笼子而是给模型装一个驾驶舱什么信息可以进入模型模型调用了什么工具输出怎么被校验意外情况怎么恢复全部由 Harness 接管。我实际体验过 DeepSeek Harness 这类工具之后对“进出口控制”这个说法感受特别深。它桌面版可以本地部署连接本地模型后可以开启思考模式整个交互不是“发一条 prompt 拿一段回复”而是模型在一个受控循环里不断读取状态、调用技能、产生结果。Harness 在输入侧做的事情包括系统指令注入、历史记忆加载、工具定义拼装、检索结果摘要在输出侧做的事情包括结构化校验、内容安全护栏、重试策略、下游动作分发。模型本身只负责“下一步输出什么”而 Harness 负责“这一步允许模型看到什么、下一步要拿模型的输出去做什么”。这种设计最直接的好处是Prompt 不再是唯一的行为控制点。以前你想让模型“用中文回答、输出 JSON、不要编造”全写进一段 Prompt现在这些约束可以拆到 Harness 的不同层语言约束放系统指令格式约束放输出解析器事实约束放到检索与工具结果里。任何一个环节出问题都能单独修而不是整段 Prompt 推倒重来。1.3 Agent与Harness一个管决策一个管运行很多人会把 Agent 和 Harness 混在一起说尤其是看到“DeepSeek Harness 多个智能体编排”这类词时更迷糊。我的理解是Agent 是决策主体它负责感知环境、决定下一步行动、调用工具Harness 是运行框架它负责让“感知-决策-行动”这个循环在一个稳定、可观测、可恢复的环境里转起来。维度AgentHarness核心定位智能决策循环运行控制框架主要机制推理、规划、工具调用状态管理、护栏、重试、编排关注点模型怎么想系统怎么跑典型问题决策对不对出错了能不能恢复例子ReAct Agent、Supervisor AgentLangGraph 编排层、DeepSeek Harness一张表格就能看明白Agent 关心的是“下一步该做什么”Harness 关心的是“这一套流程怎么被安全地执行完”。有了 Agent 不代表有了 Harness很多人的 ReAct Agent 跑起来乱跳、死循环、上下文爆炸就是因为只有决策循环、没有控制框架。反过来一个成熟的 Harness 里可以跑单个 Agent也可以编排多个 Agent这也是工程化的意义所在。2. Harness架构拆解与设计思路2.1 一个可靠Harness的最小组成一个能上线的 Harness再精简也要包含五个模块调度器控制主循环决定“模型→工具→模型→结束”的流转约束最大迭代次数和 token 预算。没有它Agent 可能陷入死循环。记忆存储管理短期会话上下文和长期业务记忆每个 Agent 各读各的避免全部塞进一条消息里。没有它多轮对话和多智能体场景全都撑不住。工具注册中心统一维护工具的名称、参数 schema、权限级别和调用方式。没有它模型面对的是一堆不可控的外部函数权限和安全无从谈起。护栏在模型输入前拦截敏感请求在模型输出后做内容与格式校验。没有它模型输出可以直接炸掉下游流程。模型接口负责连接实际模型服务统一处理超时、重试、流式输出和思考模式字段。没有它上层业务会跟具体模型 SDK 耦合死。这五个模块不是各干各的它们的协作顺序决定了 Harness 的稳定程度。我通常把流程定成调度器收到用户请求后先从记忆存储装载上下文再让护栏做输入检查接着通过模型接口调用模型生成动作如果是工具调用就走工具注册中心执行结果写回记忆然后回到调度器继续下一轮。这样一个闭环里每个环节都可以插桩、打日志、加超时问题定位非常清晰。2.2 用LangGraph搭建可编排的Harness骨架选 LangGraph 做 Harness 骨架原因是它把流程表达成图状态机节点是处理函数边是流转条件状态对象在节点间传递。这比手写 while 循环管理 Agent 状态要清晰得多尤其是要加条件分支、中断恢复、多人协作介入点的时候。一个最简骨架只需要一个状态类、三个节点函数和一条条件边from typing import Annotated, TypedDict from langgraph.graph import StateGraph, END class HarnessState(TypedDict): messages: list tools: list iterations: int def agent_node(state: HarnessState): # 调用模型决定是回复用户还是调用工具 response call_model(state[messages]) return {messages: state[messages] [response]} def tools_node(state: HarnessState): # 执行模型要求的工具调用 result execute_tool(state[messages][-1].tool_calls) return {messages: state[messages] [tool_message(result)]} def should_continue(state: HarnessState): last state[messages][-1] if last.tool_calls and state[iterations] 10: return tools return END graph StateGraph(HarnessState) graph.add_node(agent, agent_node) graph.add_node(tools, tools_node) graph.add_edge(agent, tools, conditionshould_continue) graph.set_entry_point(agent)这里的关键点是 iterations 字段它不只是计数器更是“安全阀”。很多 Agent 失控的原因不是模型不聪明而是循环没有收敛条件模型反复认为自己需要调用工具。我在实际项目里会把最大迭代次数设置成 8 到 12超过后强制要求模型直接生成最终回复必要时再叠加 token 预算检查。另一个值得注意的功能是 checkpointer也就是检查点机制。它可以把每一轮执行的状态持久化系统中途宕机或网络闪断后能从最近一个稳定的检查点恢复而不是整轮重跑。对生产环境来说这个能力比模型选型还重要。2.3 工具注册与模型调用的两条路径取舍让模型使用工具有两条主流路径Function Calling 和提示词注入。Function Calling 指的是模型原生支持结构化工具调用模型会直接输出一个“调某个函数、传这些参数”的动作对象。这条路解析稳定、不容易出现格式错误适合支持 function calling 的模型尤其是需要严格参数校验的业务场景。提示词注入则是把工具说明写进 system prompt让模型自己用文本形式输出调用意图通常约定 JSON适合老模型、不支持 function calling 的本地部署模型或者你不想过分依赖供应商特有接口的场景。对比维度Function Calling提示词注入解析稳定性高模型原生输出结构化动作低需要自己解析文本依赖程度依赖模型供应商能力任何文本模型都可用上下文开销工具 schema 仍需占用工具说明和示例更占 token灵活度受工具 schema 限制可以在 prompt 里自由变通推荐场景生产系统、强约束工具链快速验证、自定义模型接入两条路在 Harness 里并不冲突。我现在的做法是工具注册中心维护一份统一的工具 schema优先走 Function Calling当某个接入的模型不支持时Harness 自动降级为提示词注入模式把同样的 schema 渲染成文本塞给模型。这样底层的工具定义只有一份切换模型不影响业务逻辑。DeepSeek Harness 里常被提到的 skill本质上也是把这条路再往上提一层一个 skill 就是“一段固定的 prompt 一组工具定义 一段后处理逻辑”的封装。模型不再面对散装工具而是面对粒度更合适的“技能”比如“分析项目结构”“生成接口文档”。这对降低决策难度、减少无效工具调用非常有帮助我后面实操里也沿用了这个思路。3. 从零实现一个轻量Harness实操记录3.1 环境准备与项目结构我习惯先用最轻的方式搭一套可运行的 Harness验证完再上 LangGraph 这类编排框架。这里的实操记录以 Python 为例依赖尽量少pip install langgraph langchain-core openai如果接本地模型需要准备一个兼容 OpenAI 协议的推理服务比如 vLLM 或 Ollama 这类工具端口通常留在 8000 或 11434 附近。项目结构我建议这样放harness_demo/ ├── main.py # 入口启动交互 ├── harness.py # Harness 核心逻辑 ├── tools.py # 工具注册与执行 ├── memory.py # 会话记忆读写 ├── config.py # 模型地址、密钥、参数 └── skills/ ├── project_analyzer.py # 高频 skill 示例 └── doc_writer.py目录拆得干净一点后面替换模型、增加工具、沉淀 skill 会顺手很多。很多项目一开始把所有代码塞进一个文件到需要加第二个 Agent 的时候就开始痛苦这个结构虽然简单但模块边界已经够用。3.2 核心模块实现用一次可收敛的循环核心的 Harness 类可以精简到几十行重点在于把“循环-护栏-重试”固定成模板import json import time class Harness: def __init__(self, model_client, tools, max_iterations10, system_prompt, timeout30): self.model model_client self.tools tools self.max_iterations max_iterations self.system_prompt system_prompt self.timeout timeout self.messages [{role: system, content: system_prompt}] def run(self, user_input): self.messages.append({role: user, content: user_input}) for i in range(self.max_iterations): response self.model.chat( messagesself.messages, timeoutself.timeout) content response[content] tool_calls response.get(tool_calls) if not tool_calls: self.messages.append( {role: assistant, content: content}) return self.safe_answer(content) # 执行工具调用 for call in tool_calls: result self.tools.execute(call[name], call[arguments]) self.messages.append({ role: tool, tool_call_id: call[id], content: json.dumps(result, ensure_asciiFalse) }) self.messages.append( {role: assistant, content: content, tool_calls: tool_calls}) # 超过迭代上限后的兜底 fallback 系统未能完成本次任务请调整描述后重试。 return self.safe_answer(fallback) def safe_answer(self, content): # 输出侧校验 if len(content) 20000: content content[:20000] ...(已截断) return content这里面有几个容易踩的细节。第一每次模型响应如果带 tool_calls一定要把原样写回 messages并且把每条工具执行结果用 tool_call_id 对应上否则模型下一次看到的就是一份残缺历史容易重复调用同一个工具。第二超时和重试要在模型接口层做而不是在最外层做我见过很多项目只在 catch 里打日志然后整个流程就断了正确做法是对网络错误做一次或两次退避重试。第三fallback 文案不要指望模型生成固定字符串最稳因为此时已经是异常分支了。3.3 接入本地模型并配置“思考模式”本地模型的接入我一直用这种配置方式放在 config.py 里import os MODEL_BASE_URL os.getenv(MODEL_BASE_URL, http://localhost:8000/v1) MODEL_NAME os.getenv(MODEL_NAME, deepseek-r1) ENABLE_THINKING os.getenv(ENABLE_THINKING, true).lower() true MAX_TOKENS int(os.getenv(MAX_TOKENS, 8192)) TEMPERATURE float(os.getenv(TEMPERATURE, 0.6))这里最需要注意的是思考模式。开启思考模式后模型响应里除了正常的 content 字段还会多出一个 reasoning_content 或者 thinking 字段里面是模型的推理过程。很多人在接入时直接把整个响应塞回 messages结果用户看到一堆“思考过程”甚至上下文被推理文本撑爆。我踩过的坑是思考内容应该单独保存到日志或内存的 observability 通道里用来调试模型决策但不应该进入用户可见的对话历史也不应该在下一次模型调用时当普通消息传回去。有些模型如果历史里混入了大量推理文本会明显变“懒”后续回复质量下降。DeepSeek Harness 连接本地模型时的思考模式配置本质也是在处理这一层让推理过程可见、可控、可追踪但不污染最终交付内容。3.4 多智能体编排Supervisor模式实操当任务复杂度超过单个 Agent 的处理能力时就需要多个 Agent 协作。最简单实用的编排模式是 Supervisor主管模式一个主管 Agent 负责任务拆解和结果裁决多个工作 Agent 各司其职。在 LangGraph 里这本质上是“主管节点决定把消息路由给哪个工作节点”。def supervisor_node(state): decision call_model([ {role: system, content: 你是主管决定下一步交给哪个agent。}, *state[messages] ]) return {next: parse_next(decision)} def route_after_supervisor(state): if state[next] coder: return coder_agent if state[next] reviewer: return reviewer_agent return END多个智能体编排时我特别强调“记忆分区”。不要让所有 Agent 共享同一份 memory 对象否则 B Agent 会读到 A Agent 的中间草稿输出变得颠三倒四。我通常给每个 Agent 加一个命名空间前缀比如 coder.memory、reviewer.memory主管 Agent 拥有一个全局 memory 用来读各 Agent 的结论摘要。这样一来串扰问题基本被消灭问题定位也更容易。4. 实践中的常见问题与排查实录4.1 提示词被内容策略拦截的应急处理开发过程中最迷幻的报错之一就是 invalid prompt 这类提示它表示你的输入被服务端的内容安全策略拦下了。这个问题的触发原因可能只是一个措辞不一定是你真写了什么敏感内容。比如让模型“扮演一个越狱角色”来测试逻辑、或者输入里包含大段包含特定关键词的网页文本都可能触发检查。我现在的处理流程是先把任务描述改成合规的业务化表达明确说明用途和分析目标再把超长输入拆成多个段落分批处理降低单次请求的“关键词密度”最后加一个重试机制在收到这这类报错时提示用户调整输入而不是直接让整个流程灰屏。这里尤其提醒一句正确做法是调整表达和流程而不是想着怎么绕过内容策略那既不稳妥也不可持续。把安全护栏当成 Harness 的固有环节来设计后续交付到复杂环境才不会被反复打回。4.2 输出格式不稳与JSON解析失败模型输出不稳是家常便饭。你要的是合法 JSON它给你 Markdown 代码块要的是 enum 值它给你一句解释。以前我习惯在 Prompt 里疯狂补约束比如“必须严格输出 JSON不要写任何其他内容”效果有但还是会偶发失败。后来我把重心移到 Harness 的输出解析层先尝试按代码块提取再尝试直接 JSON 解析最后用正则兜底提取最外层花括号内容。三种策略都失败才走重试。代价是解析代码多了但稳定率从“偶尔崩”变成“可预期”。还有一个细节是启用模型的 JSON Mode 或者 structured output 参数这比任何 prompt 约束都可靠因为约束发生在模型解码阶段而不是文本生成后的碰运气。4.3 多个智能体上下文串扰多智能体跑起来后最常见的诡异现象是“A 写代码B 评审B 评论里出现了 A 的草稿片段”。排查下来原因几乎都是消息总线或 memory 对象没有做隔离。解决不难按角色命名空间分区读写时指定 namespace主管只读各 Agent 的最终结果摘要。另外一个隐蔽问题是中间状态的 tool_call_id 冲突两个 Agent 各自调用工具时id 必须区分开否则模型会把失败的工具结果算到另一个 Agent 头上。串联 Agent 的名称为每个 id 加上前缀是我实践下来的最稳方案。4.4 版本选择与回退升级翻车复盘项目版本迭代也会给 Harness 带来大坑。我遇到过升级到新版本后会话恢复行为异常、skill 加载不兼容的情况最后老老实实回退到 v0.1.5-rc.2 这个稳定版本才恢复正常。那次事故给我的教训有三条任何 Harness 相关依赖都要锁定版本并写进 lock 文件升级前必须跑一遍已有回归集至少把常用 skill 和工具调用流程完整过一遍保留旧版本的配置文件与启动脚本副本以备快速回退。pip freeze requirements.lock # 回退时直接安装锁文件里的版本 pip install -r requirements.lock我也见过桌面客户端类工具闪退的问题通常和上下文超长、内存占用过高有关。排查时先看日志文件如果日志里有 OOM 或 buffer 超限的记录就该限制上下文窗口、缩短单轮工具结果长度或者换用更轻量的模型配置。这类问题在 Harness 里特别值得处理因为它不像普通 API 调用那样看得到明显错误而是整个进程没了。5. 从工程视角看Harness的下一步5.1 Harness工程的核心价值可测试、可观测、可回滚把话题从具体工具拉回工程层面。模型能力像发动机Harness 是底盘发动机再强没有转向、刹车和仪表盘车就没法上路。Harness 工程最值钱的三件事第一是可测试性——Prompt、skill、工具组合都应该是可以放进 CI 的测试用例而不是靠人肉在对话框里点第二是可观测性——每次模型调用的输入输出、token 消耗、推理过程、工具结果都要有记录出了事故能回溯第三是可回滚——所有行为变更都应该有版本异常时能一键切回旧逻辑。这三点在纯 Prompt 时代几乎做不到因为行为藏在文本里没法 diff、没法版本化、没法自动化回归。到了 Harness 阶段行为被结构化成代码和配置工程手段就全部可以套用了。这是我认为“从 Prompt 到 Harness”最本质的演进AI 应用从“调参的艺术”变成了“可维护的系统”。5.2 落地路径建议从Prompt-only到平台化不是每个项目都需要一上来就搭全套 Harness但演进路径值得提前规划。我建议按这样的阶段走阶段一Prompt 模板管理。把高频的 Prompt 统一存放、统一变量替换至少先把重复劳动消掉。阶段二函数封装。把一个完整的“取上下文→调模型→校验输出→返回结果”流程包成一个函数让业务方不直接碰模型 SDK。阶段三图编排。引入 LangGraph把多步骤流程、条件分支、多 Agent 路由画成图把循环和恢复机制落地。阶段四平台化。沉淀 skill、统一工具注册、加观测面板让 team 里多个人可以协作维护。一个很实用的切入点是把你最常用的 Prompt 先沉淀成 skill。比如“分析项目结构好用的 prompt”很多人天天在编辑器里手动复制一段指令去理解新仓库但如果把它做成一个 skill输入仓库路径Harness 自动调用目录遍历、读关键文件、生成结构分析与依赖梳理价值就不再是一个提示词而是一个可复用工具。这算是我推荐的最小区块改造方案从最简单的一两个高频场景开始让团队先体会 Harness 的好处再逐步铺开。我在实际落地中还有一个习惯会把每次都失败的 prompt 记录成“bad_case 样本”把它们加进 Harness 的回归测试集里。只要一次修复让这类样本通过了后面再升级模型或改动 skill 都不会轻易回归。这个习惯救了我好几次比写任何架构文档都管用。说实话我自己刚接触 Harness 这个概念时也怀疑过是不是换了个词包装老东西真动手把一套带工具调用、带记忆隔离、带输出校验的框架搭起来之后我才确认这确实是工程方式的转移。Prompt 仍然是重要的它决定模型一层输出质量和风格但如果你希望 AI 应用稳定地在生产环境里跑起来决定下限的往往是 Harness。最后再分享一个小技巧把模型的思考记录和最终交付内容分开存储这个习惯可以让你在调试时同时拥有“模型的内心活动”和“用户看到的结果”排查问题会快非常多。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑