聊聊 Prompt 是怎么一路进化到 Harness 的

0 阅读18分钟

写在前面

我本身并不热衷于体验新工具,但是我的同桌是个喜欢尝鲜的人,去年就已经给我疯狂安利 AI 了,当时我嗤之以鼻,现在我逐帧学习😶‍🌫️。

记得早前 AI 开始出现的时候,它的使用方式就是一问一答,我有什么问题就去咨询 AI,然后复制粘贴。去年我对 AI 编程的概念仍停留在加强版的自动补全,下意识觉得它不能拿来做事。今年年前 AI 风气我感觉也还没那么盛行,虽然期间各种模型也是不断迭代,但我还是停留在一问一答的模式吧!

年后不知道怎么的,突然大家都开始疯狂用 AI 了,就真的,很突然😲,公司也开始推了。用了之后就一发不可收拾了,这东西是真的香,搞的我现在都不会写代码了,大部分情况都是在 pua AI 写需求改 bug,自己则在摸鱼、等待、review 和验证,它实实在在的改变了开发流程。

所以本篇文章主要记录下我眼中 AI 的演变过程,文末还会附上几个常见的经典问题🔥。

prompt 如何演进

其实大模型(LLM)出现也才三年时间,一路走来,就两个东西变了:

  • 大模型自身变强了
  • 人们约束变多了

大模型的本质是预测下一个词出现的概率,化简来看就是一个巨大的函数 y=f(x, θ),输入 x,输出越来越准确的 y。这里我们不去讨论模型本身如何迭代(我也不懂啦,这种东西看完即忘😂),因为大部分情况我们是在给模型加约束。提到大模型,经常会蹦出一堆词:

Prompt -> Tool -> MCP -> Agent -> Skill -> Harness

听起来一个比一个高级,很多时候也容易混在一起,实际上你可以理解为从左到右就是给模型的上下文约束越来越多。大模型的能力确实越来越强了,但是它不能保证每次执行的效果都很好,所以为了把不确定的事情转换为确定的事情,就有了各种各样的约束和规范。它给我的感觉就是大概是这个样子:本来是一匹马儿能在大草原上随意奔跑,现在有了栅栏,马儿只能在合适的范围内奔跑:

image.png

接下来让我们来以一个简单的问题来推演一下 prompt 的演变过程:

今天适合骑车上班吗?

1、纯 prompt

假设你直接问模型这个问题,早期的模型可能会回答:

如果天气晴朗、路程不远,今天是适合骑车上班的。

乍一看,好像没毛病。但问题是:它真的知道今天的天气吗?它知道你在哪吗?它知道你几点出门吗?它知道你的上班路线吗?都不知道。所以这个回答本质上不是判断,而是根据常识补了一段“看起来合理”的话,纯 Prompt 的流程大体如下:

用户:今天适合骑车上班吗 -> LLM 读取上下文 -> 根据 LLM 预测下一个 token -> 生成一个看似合理的建议

纯 prompt 能做什么?

  • 解释骑车通勤要注意什么
  • 总结骑车的优缺点
  • 写一段通勤建议
  • 给一些常识性提醒

但它没法真正判断你今天适不适合骑车,因为这个问题需要实时信息和个人上下文。

2、Prompt Engineering

后来大家发现,模型虽然容易猜,但如果我们把要求写清楚,它会稳很多。这时候我们通常会把输入改成:

你是一个通勤建议助手。
用户问今天适合出行吗之类的问题时,不要直接下结论。
请先判断是否缺少城市、通勤时间、通勤距离、天气信息。
如果缺少关键信息,请先追问。
如果没有实时工具,请明确说明无法判断实时情况。

那模型可能会回答:

我需要知道你的城市、出发时间和通勤距离,才能判断今天是否适合骑车。如果没有实时天气和路线工具,我只能给出一般性建议。

这就靠谱多了。Prompt Engineering 的本质不是让模型突然知道了真实世界,而是让模型别太自由发挥。就像你带新人做事,如果你只说“帮我看看能不能骑车”,新人可能随便回答一句。但如果你说“先确认城市,再查天气,再看通勤时间,最后按适合/不适合/谨慎骑行回答”,这个事情就稳定很多了。

一个好的 Prompt 大体会具备以下这些描述:

要素在这个例子里怎么写作用
角色你是通勤建议助手限定回答身份
任务判断是否适合骑车上班明确目标
缺失信息城市、时间、距离避免瞎判断
约束没有实时数据就说明减少胡编
输出格式结论 + 原因 + 建议让回答稳定

所以这个阶段也涌现了一些提示词工程师、提示词收藏家、提示词卖家等,不过这些都是一次性的文本片段,很难持续维护和演进。另外还有一个根本问题就是: Prompt 写得再好,模型也不会凭空知道今天下不下雨。所以接下来Tool 出现了。

3、Tool

有了 Tool,事情就开始变得像样了,因为模型终于可以“查一下”了。

用户还是问:今天适合骑车上班吗?模型则可以先判断:

这个问题需要实时信息,我得查天气。

于是它开始调用天气工具,拿到温度、降雨概率、风力、空气质量、未来几小时的天气情况。然后再回答:

今天上午小雨,风力 4 级,路面可能湿滑,不太建议骑车;如果一定要骑,建议穿防水外套并避开高峰路段。

这就不是纯靠猜了。这是工具查询 + 语言组织的结果。Tool 阶段的流程如下:

用户:今天适合骑车上班吗 -> LLM 判断需要实时信息 -> 调用天气工具 -> 返回天气数据 -> LLM 生成通勤建议

这一步很关键。因为从这里开始,LLM 不再只是一个“会说话的模型”,而开始变成一个“能连接外部世界的模型”。不过 Tool 也有问题。如果只查天气,一个工具绰绰有余;但“骑车上班”显然不只需要天气信息,它可能还要查:

  • 地图路线
  • 通勤距离
  • 路况
  • 用户偏好
  • 公司打卡时间
  • 是否有共享单车点位

工具一多,问题就来了。每个工具都有自己的接口、参数、鉴权、返回格式。如果都让模型一个个硬接,这不得炸了。好在编程领域对这种问题早有成熟的解决方案,就是加个中间层,统一各种接口规范,于是 MCP 登场了。

4、MCP

MCP 解决的问题不是“模型会不会思考”。它解决的是:模型怎么稳定、统一、安全地连接和调用一堆外部工具。没有 MCP 的时候,LLM 和工具之间的关系就像下面这样子: image.png

这就像你桌上有一堆设备:一个圆孔、一个方孔、一个老式 USB、一个专用接口、一个还得单独装驱动。每接一个新设备,都要折腾半天。有了 MCP 之后就不一样了,它变成了一个插座: image.png

回到原来的问题“今天适合骑车上班吗”,Tool 解决的是:能不能查天气、查路线、查日历?MCP 解决的是:这些工具能不能用统一方式接进来、被发现、被调用、被治理?它的好处在于:

无 MCP有 MCP变化
每个工具单独适配工具按统一方式暴露接入成本下降
模型不知道有哪些工具可以发现可用工具调度更清楚
参数格式各不相同输入输出更结构化更容易编排
权限散落各处可以集中治理更适合企业落地

当然有时候我们也把内部工具叫 Tool,外部工具称为 MCP。

5、Agent

有了 Tool 和 MCP 之后,模型就能接各种各样的工具了。但“今天适合骑车上班吗”这个问题,依然不是调用一个工具就能解决。如何更好的统筹调度这些工具,那就要提到 Agent 了。一个像样的 Agent 应该会自己拆任务:

  1. 先确认用户在哪个城市
  2. 查询今天通勤时段天气
  3. 查询骑车路线和距离
  4. 判断是否下雨、风大、空气差
  5. 看用户日历有没有早会
  6. 综合给出建议
  7. 如果不适合骑车,给替代方案

这个阶段模型已经不只是回答问题,而是在完成任务。Agent 的流程大概是下面这个样子:

image.png

Agent 的核心:不是更会聊天,而是更会安排步骤,能做具体的事情了。以前你要一步步告诉模型:先查天气,再查路线,再看我有没有会议,最后给建议。现在你只说“今天适合骑车上班吗”,它自己就会拆解。当然 Agent 也会带来新问题:它会不会乱调用工具?它会不会查不该查的数据?它失败了怎么办?它调用太多工具浪费成本怎么办?它给错建议谁兜底?可能中途出错了,你纠正一下可以了,但是下次它也许还会犯同样的错误。

所以光有 Agent 还不够,我们还需要把常见任务沉淀下来,把这些上下文封装成稳定能力,这时候 Skill 出现了。

6、Skill

Tool 是一个工具,MCP 是统一接口,Agent 是会自己拆步骤的执行者,Skill 则更像是一套封装好的专项能力,一个成熟的 SOP。判读“今天是否适合骑车上班”,它不只是调用工具,还包含了一套稳定流程: image.png 这很像写代码。你当然可以每次都手写:先查天气,再查路线,再看会议,再判断是否适合骑车。但写多了你会发现:这不就是一个固定能力吗,为什么不封装起来?

Skill 的价值就在这里。它把反复出现的任务,封装成一个可复用的能力包,而这个能力包不是代码,就是一个普通的 md 文件,上手成本极低。Skill 里面通常会定义什么时候触发、需要哪些信息、缺信息时怎么追问、调哪些工具、判断规则是什么、输出格式是什么、哪些行为不能做。所以 Skill 比纯 Agent 更稳定。Agent 像“临场发挥的执行者”,Skill 像“训练好的标准动作”。不仅如此,它还把一次性 prompt 变成了可持续维护的文档。

写 Skill 很简单,但写好 Skill 很难。” 能够稳定运行的 Skill,背后往往意味着大量的试错、边界条件处理以及持续维护,还是需要专业的人来沉淀会更好一些,他们最了解工具能力的边界,也最清楚真实场景中的最佳使用方式,然后再共享出去。

7、Harness

是的,Harness 就这样突然出现了😂,它是真正让 LLM 应用落地的底座。到这里,我们其实已经从“模型能力”聊到“工程系统”了。真正落地时,最麻烦的往往不是模型本身,而是下面这些问题:

  • 用户位置能不能用?
  • 日历权限谁来管?
  • 工具调用失败怎么办?
  • 多个工具结果冲突怎么办?
  • 用户隐私怎么保护?
  • 模型能不能调用打车工具?
  • 能不能自动发消息?
  • 日志和审计怎么做?
  • Skill 怎么加载?
  • MCP Server 怎么管理?

这些东西加在一起,就接近 Harness,所以 Harness 也不是才出现,它一直都有,只是现在拥有了合适的名字。这样一来,Harness 视角下的“骑车上班”就变成了:

image.png

最终用户看到的可能只是一句话:

今天不太适合骑车。上午小雨,风力偏大,而且你 9 点有会,建议坐地铁;如果骑车,建议提前 20 分钟出门并带雨衣。

但系统在背后其实做了很多事:理解意图、判断需要哪些信息、检查权限、调用工具、综合分析、生成建议、控制边界、返回结果。所以现在做 LLM 应用,拼的已经不只是“模型会不会回答”,更重要的是:有没有一套系统,能让模型稳定、安全、可复用地完成任务。

演进小结

这里简单再用“今天适合骑车上班吗”来总结 prompt 的进化,大概是这样:

1、纯 Prompt:“如果天气好,骑车上班挺不错。”
2、Prompt Engineering:“我需要先知道城市、时间和通勤距离,不能直接判断。”
3、Tool:“我查了一下,今天上午有小雨。”
4、MCP:“天气、地图、日历这些工具,可以按统一方式接进来。”
5、Agent:“我查了天气、路线和日历,发现你 9 点有会,骑车风险有点高。”
6、Skill:“我可以稳定完成‘通勤方式建议’这个任务。”
7、Harness:“我能在权限、安全、上下文和工具编排都可控的系统里完成这件事。”

模型的进化不是从“不会聊天”到“会聊天”,而是从一个会预测下一个 token 的模型,逐渐变成一个能连接真实世界、调用外部工具、拆解任务、复用技能,并被工程系统安全托管的智能执行单元。这和前端工程也有点像,一开始写几个页面,直接写就行;后来页面多了,需要组件;组件多了,需要状态管理;项目大了,需要工程化;团队协作了,需要权限、规范、发布、监控。LLM 也是一样,一开始一个 Prompt 就能玩;后来要接 Tool;工具多了要 MCP;任务复杂了要 Agent;流程稳定了要 Skill;真正落地还得有 Harness。毕竟,用户真正想要的,也不是一句漂亮话

这里用表格加深下记忆(其实演进顺序不完全正确,但不用在意这些细节😬):

概念类比作用骑车上班例子
LLM大脑理解和生成
Tool做具体动作查天气、查路线
MCP插座标准化连接工具让天气、地图、日历统一接入
Agent执行者拆解并推进任务自己决定先查什么后查什么
Skill专项技能稳定复用能力骑车通勤建议助手
Harness身体和安全系统托管、编排、治理

常见疑惑

感觉 skill 好像也还是 prompt?

仔细看看,skill 好像和最初的 prompt 区别好像不大,你 prompt 写完整点,也可以是 skill,毕竟都是自然语言描述的 md 文档,也都能复用。你可以直接把所有 prompt 片段全部塞进上下文;也可以先人工挑选出需要的 prompt 再喂给大模型,这两个操作都是 ok 的。那 skill 的优势在哪呢:

  • token 成本低
  • 上下文占用少不冗余
  • 缓存命中率高
  • 可持续迭代
  • 更适合编排

prompt 能承载经验,但不适合承载很多经验。

为什么“预测下一个 token”能涌现推理能力?

大模型的本质是预测下一个词出现的概率,而“把下一个 token 预测对” 这件事,本身就逼着模型去学习很多比表面文字更深的结构,能够从更多维的角度去找到事物背后千丝万缕的关系。更准确的说,在真实语言数据里,要想持续预测正确,模型不得不学习隐藏在文本背后的状态跟踪、规则组合、因果链条和中间计算过程,推理能力可以看作这种高质量序列建模的副产品,当然这也是量变引起质变的效果。

如果是概率推测的话,那为什么同样的输入会有不同的输出结果呢?

因为模型输出的通常不是 “唯一正确的下一个 token”,而是 “一组候选 token 的概率分布”,我们提供的任意信息都会影响模型的生成结果。所以更准确的描述是“在给定上下文中,从一组候选词中挑选出下一个最合适的”,这个挑选过程就导致了结果的随机性,当然这个是有参数可调的,我们叫做温度(Temperature),温度是控制大模型输出“随机性/创造性”的一个参数:

  • 温度越低,输出越确定、越保守、越趋于一致(好像八股文
  • 温度越高,输出越发散、越有创造性(好像散文

有时候我觉得 AI 用的好不好也是个概率问题,用的好说明你的描述符合 AI 胃口罢了。现在的优化也是提示词优化,对当前来说可能效果好,后续也可能变差,单纯的模型升级也不能保证效果比以前好,然后你就需要对提示词进行一些修修补补,好听点就是优化。

模型为什么会产生幻觉?

幻觉(Hallucination)不是模型 “撒谎”,而是它在 “猜下一个词” 的机制下,被迫生成了它其实并不知道的内容。大模型本质上是一个语言概率预测器,不是一个事实数据库它总是要输出些什么,即使它其实没有可靠依据。这就是幻觉的根源,所以当它遇到不熟悉的问题时,它并不会停下来,而是继续生成最 “像正确答案” 的文本,听起来对,但可能不对。

每次请求到底给大模型发送了什么内容?

大模型本身是无状态的:它不会记住上一次的请求,所以每次都要把它“需要看到的所有信息”一次性重新发过去。假设你问一个 AI:“帮我总结上一封邮件”,实际发给模型的内容可能是这样(伪结构):

{
  "model": "xxx-pro", // 模型标识
  "messages": [ // 一组消息列表,包括系统提示词 && 历史对话 && 本轮用户输入
    {
      "role": "system",
      "content": "你是企业助手,遵守以下规范……输出使用中文……"
    }, {
      "role": "user",
      "content": "帮我看看这周日程"
    }, {
      "role": "assistant",
      "content": "本周你有 3 个会议……"
    }, {
      "role": "user", // 你的本次输入在这里
      "content": "帮我总结上一封邮件\n\n[邮件正文粘贴在这里]"
    }
  ],
  "tools": [ // 可选工具,tool 或者 skill 就在这里
    {"name": "get_email", "description": "...", "parameters": {...}},
    {"name": "search_docs", "description": "...", "parameters": {...}}
  ],
  "temperature": 0.3, // 采样参数:温度
  "max_tokens": 1024, // 采样参数:最多生成多少 token
  "stream": true // 其它元信息:是否流式返回
}

直接训练一个代码模型有没有搞头?

现在的大模型通常是在一个已经预训练好的基础模型上,用特定领域的数据集后训练成一个“新”模型。那能不能直接跳过通用模型,只用特定领域数据训练一个小而专的模型

当然是可以的,早期的 AI 代码补全也是这么做的。但只喂代码语料,模型在编码上也许会更确定,但另一个问题是它可能看不懂正常提示词。于是我们为了改善模型输入理解,又需要不断扩充基础语料,最终又会把它“训回”成一个通用模型。所以相比造一个纯粹的编码模型,让通用模型更好地理解和编写代码通常会更划算。

小结

那如何用好 AI 呢:

  • 用好的模型
  • 提供充分的上下文

模型本身效果咋样就是咋样,通常我们干预不了。我们主要完善的还是上下文工程,用工程思路去解决 LLM 的不确定性、效果和成本问题等。不过人与人之间差距还是很大的,如何更好的提问和描述问题,经验就在这里体现出来了。最好的状态就是:写好提示词,模型再差也能跑

现在风向也变了,你也不用去读什么各种源码了(节奏太快了,你没时间读,也读不下去),面试也不问这些了,就问你用没用过 AI、怎么用、如何提效,你说你没怎么用,那就出门右转。不过我最大的感触还是 AI 虽然让你开发更轻松了,但它同时压缩了需求的时间,也变相压缩了人的时间,好像视频倍速播放一样,根本停不下来😂(省下来的时间是为了摸鱼不是为了赶下一个需求)。