前言
那天我在医院打针。
肠道炎症性疾病,免疫相关,必须定期去打生物制剂。输液管扎在左手上,右手掏出手机,通过 Halo 的远程功能连上几公里外家里的电脑。按住语音按钮,压低声音说几句话,转成文字,发给 AI。
旁边的病友大概以为我是个网瘾青年。但我是在跟 AI 聊天——让它生成代码、跑测试、发版本。那天下午,AI 浏览器的一个重大功能就是这样从病床上 ship 出去的。
三个月后,Cowork、openclaw 火遍全球。
朋友发来链接,各种"下一个 GPT 时刻"的新闻铺天盖地。我看了一眼,心里冒出来一句话:原来这套做法今天成为热点了。
当天我把 Halo 开源了。
起源
2024 年,我在一个内部探索项目里面临一个架构选择:构建多个专职 Agent 组成的编排系统,还是押注单一的通用 Agent Loop。
当时主流方向是前者。论文百花齐放——观察者、验证者、测试者各司其职,通过人工编排的调用关系协同完成任务。每天早上读这些 paper,架构图一个比一个精巧。
我的直觉告诉我这条路不会长久。
大模型的能力在快速进化。今天需要三个 Agent 配合才能完成的事,明天一个 Agent 加上更好的工具调用就能做到。那些精巧的编排架构,本质上是在用工程复杂度弥补模型能力的不足——模型一旦跨过某个能力门槛,这些架构就变成纯粹的负担。Agent 的终局应该极简:一个循环,LLM 自己推理、自己决策、自己调用工具,跑到任务完成为止。
我们选了单循环这条路。
当时内网可选的开源模型是千问 2.5-72B,能力和今天的商业模型差了几个量级。模型对工具指令的遵循极弱:以代码编辑为例,它几乎无法通过简单的搜索替换完成修改——标点、空格、缩进经常丢失,匹配失败率极高。我们不得不构建一套容错编辑算法:精心设计的标记符号让模型标注修改区域,用相似度匹配代替精确匹配,允许一定偏差,定位后再做替换和校验。
但容错编辑只是工具层的一个例子。早期模型真正的瓶颈在更基础的地方——长任务里 LLM 会丢失方向,仅靠原始的工具调用,根本驱动不动一个复杂任务走到终点。
办法只有一条:尽可能从环境向 LLM 注入反馈。
反馈的一个来源是工具调用本身。每个工具返回什么信息、错误时怎么提示,直接决定模型下一步的判断。模型试图打开一个超大文件,不可能把全部内容塞回上下文——那就在返回值里明确告诉它"文件过大,建议先用 grep 缩范围",把下一步动作直接写进结果里。让 LLM 不仅知道刚才那一步失败了,还知道接下来该怎么走。
另一个来源是系统层。用另一个 LLM 在外面观察主循环,检测它是否偏离目标、是否陷入循环、方向是否正确,再把纠偏信息注入回主 Loop。整个 Loop 的设计目标,就是不断从环境中提取信息、用最贴近目标的上下文喂给主模型。
那段时间我做过一次面向产品和开发同事的内部分享,主题是大模型应用怎么落地。那时"上下文工程""harness engineering"这些名词都还没出现,但有一件事已经看清了:Agent 系统的能力,不来自某一句提示词,也不来自某一次工具调用,而是从整个系统的协调里生长出来的。提示,按它最终是谁写的,可以分成三层。这是那次分享的主线,也是后来支撑我们所有工程决策的隐线——后来出现的那些新名词,也都没跳出这条线。
L1——提示由人写。
这是大多数人第一次接触"提示工程"时认识的形态。你写一段话给模型,模型回一段话给你。整个工程的全部就在推敲那段话怎么写——措辞、顺序、要不要给示例、要不要让它扮演角色、上下文里多放还是少放参考信息。它的边界非常清晰:人能想到多少,模型就能配合做多少;人想不到的,整个调用就到此为止。翻译、摘要、改写、生成单段代码——所有"一次回答就能解决"的任务,工程量都集中在这里。直到今天,绝大多数关于"提示工程技巧"的文章讲的都是这一层。
L2——提示由模型自己写。
这是 L1 到 Agent 之间的关键质变,但很容易被忽略。它的核心不是"多轮对话",也不是"加上工具调用"——而是模型上一轮的输出,原封不动成为下一轮提示的主体。人给一个初始指令,之后每一轮"思考-行动-观察"的内容都由模型自己生成,再由一段薄薄的程序逻辑把它转写回去递给模型自己。程序不在编写提示,它只是把模型说过的话递回给模型自己。
ReAct 是这一层最早被广泛讨论的技术模板,但 ReAct 不是 L2 的本质——它只是这套思路的一种具体格式。L2 真正发生的事是:AI 第一次开始自己驱动自己。模型的中间推理被允许写出来、被允许直接成为下一步的输入。这个看起来很小的设计,让模型可以处理远超单次调用所能完成的任务——它不再只是回答问题,而是在自己生成的提示流里持续推进。
我当时给同事的比喻是:把 Agent 系统想象成被关在房间里的一个人。门外的人给它递进去第一张纸条,写着"查最近三年四大银行的贷款利率并画出对比图"。它在纸条背面写下"我应该先用搜索工具查利率"。门外的人不写新指令,只是把纸条原样翻面递回去,附上"搜索结果是 ××"。它再写下"现在我应该把数据传给图表工具"。如此反复。整个房间里没人在帮它"想"——它在用自己写下的字给自己续命。
L3——提示由整个系统协作生产。
当任务复杂到单个循环顶不住时——上下文塞太满、注意力开始飘移、目标开始走样——单一模型实例的自我驱动会失效。这时唯一的解法是把任务拆开,让多个模型实例同时工作,每个有自己的上下文窗口、自己专属的几把工具;外部用工具反馈、状态记录、子任务调度、监督校验把它们连起来。最终,每一次注入到任意一个模型的那串字符串,已经不再只来自模型自己——它是来自工具的返回、来自其他模型的输出、来自系统状态的快照、来自人类设定的约束……被一整套机制协作组装出来。
这就是一整套现代 Agent。
L3 和 L2 的差别不只是"任务变复杂了"。差别在于提示的生产开始由整个系统承担——单个模型不再独自驱动自己,工具、状态、调度、其他模型都成为提示的共同作者。从这一刻起,工程的重心不再是"如何写好那段提示",也不再是"如何让模型驱动自己",而是如何让整套系统稳定地、持续地、向着任务目标。今天大多数被讨论的所谓"Agent 平台""Agent 框架",做的都是这件事。
我特意把 Agent 放进"提示工程"这个标题下,不只是为了给"非技术人员科普",而是从技术底层看,除了模型微调和响应速度优化,Agent 工程的核心工作,不管多大的系统里,都在围绕"LLM input"做组合优化。
讲到这里,"提示工程"这个词的范围必须讲清楚,因为它后来被一堆新名词覆盖了。
狭义的提示工程指写一句、几句给 LLM 看的指令——怎么措辞、怎么排版、给几个示例。L1 的全部工程都在这一层。
广义的提示工程指任何最终注入到 LLM 上下文里的那串字符串的设计与组装。这串字符串可以是人写的,可以是工具返回的,可以是另一个 LLM 生成的,可以是系统经过几十轮循环聚合出来的——不管它从哪里来、经过多少层中介、被起过多少名字,最终落到 LLM input 里的,就是"提示"。
我们用的一直是广义。
2024 年我们做的就是 L3 范畴里的广义提示工程。容错编辑返回的"建议你下一步用 grep 缩范围"是提示;和 LLM 喊"你跑偏了"是提示;从环境抓回来塞进主上下文的诊断信息也是提示。来源不同——工具、监督模型、系统状态——最终都汇进那张递给主模型的纸条。
十月,我们完成了内部路演:输入一个 bug 或一项需求,系统自主探索代码、调用工具、循环推进,直到修复完成。它奏效了。
两个月后,Anthropic 发布了《Building Effective Agents》——核心论点是最成功的 Agent 实现用的不是复杂框架,而是简单、可组合的模式:LLM 加工具,在环境反馈中循环。2025 年初 Windsurf 发布,展示的自动化能力同方向。后来的 Claude Code 也是——一个极简的循环,所有工程精力都倾注在提示词与上下文的反复打磨上。
两年后回头看,这件事被分了门类。新名词陆续登场——上下文工程关注每次往模型窗口里放什么、什么时候放、什么时候清掉;harness engineering 关注模型之外那一整套系统怎么持续运转、纠错、观测、恢复;评估、记忆、子 Agent 编排也各自有了独立命名。"Agent = Model + Harness"这种公式开始流传,模型被比作 CPU,模型之外被比作操作系统。
这些概念的共同底层只有一件事:决定模型每一步看到什么。
这些抽象当然有价值——清晰的子概念有助于工程协作,把"模型之外那一整套系统"打包成一个统一命名也是有用的。但有另一面值得讲清楚:不要让新名词屏蔽掉底层那件唯一在发生的事。模型自身的能力你改不动,能改的只有它每一步看到的那串字符串。所有命名各异的努力——上下文管理、工具反馈、记忆、子 Agent、harness——最终都会序列化成文本注入 LLM input。
这就是广义的提示工程。只要大模型底层靠 input 驱动这件事不变,它就还在。
但这些 Agent——包括我们做的——有一个共同的局限:它们全部活在开发环境里。IDE、终端、后台服务——为技术用户构建,需要人坐在工位上操作。同事想试用,第一个问题是"什么是 npm"。我出门在外,想让 AI 继续工作,做不到。
这不是 Agent 能力的问题,是交付形态的问题。
通用 Agent 的下一步不是更强的编程工具,而是让它走出开发环境:双击就能用,手机上也能远程指挥,定时自主执行不需要人盯着。AI 不该只是你坐在它面前时才工作的工具——它应该像一个真正的员工,你给目标,它自己推进,在关键节点等你做决策。
2025 年 10 月,我把这套引擎搬进了一个新的桌面应用——支持 macOS、Windows、Linux 三端外加 Web,名字叫 Halo。它不是开发者工具,是桌面 AI 工作站。本书后面十三章拆解的,就是这个系统。
给谁看的
我写这本书,不是给想了解"Agent 是什么"的人看的。
AI Agent 领域在过去两年经历了密集的概念更迭:多角色协作到单循环 Agent,再到"上下文工程""harness engineering"。名词不断翻新,但底层在解决的问题始终是同一个:每次调用大模型时,它应该看到什么、不看到什么。上下文窗口有限,放什么进去、压缩什么、丢弃什么,直接决定了 Agent 的行为质量。这是一个取舍问题,不是工程优化——而取舍的答案,藏在实际跑过生产环境的系统里,不在概念的翻新中。
这本书不造概念。它拆一个真实系统——30 万行代码、跑在生产环境的 AI 数字员工平台——告诉你每一层是怎么做的、为什么这样做、放弃了什么。
具体来说,这本书适合三类读者:
想造 Agent 平台的开发者。 你有 2-5 年经验,能读 TypeScript。你不满足于调 API 拿结果,你想知道:Agent Loop 里一轮 query 到底经历了什么?上下文超限时怎么压缩才不丢关键信息?多个 AI 模型的协议差异怎么在边界处消化?这本书从引擎内核讲到浏览器自动化,每一层都有源码和取舍讨论。
正在构建 AI 工具的独立开发者。 你可能已经做了一个 AI 助手或自动化工具,但卡在"从能用到好用"的阶段。流式渲染闪烁怎么解决?定时调度在桌面端怎么做到可靠?AI 浏览器怎么绕过反爬检测?这些生产级问题的答案散落在 Halo 的代码和本书各章里,按兴趣跳读就行。
好奇"一个人加 AI 能做到什么程度"的人。 第十三章专门回答这个问题。不是鸡汤,是方法论:需求澄清的三步螺旋、AI 律法的三层抽象、交叉审查工作流——这些方法是我用来构建整个 Halo 的,也是用来写这本书的。
这本书和市面上大多数 AI Agent 书籍的主要区别在于:它是打开引擎盖讲的,不是在引擎盖外面讲的。 每一个设计决策都有对应的源码行、失败过的备选方案和明确的取舍理由。举两个具体例子来说明"深度"的含义:第三章讲 Compact 策略时,你会读到为什么三种压缩策略要在运行时动态选择而不是预设——因为同一段对话在不同阶段的信息密度分布完全不同,静态策略会系统性地丢掉关键内容。第六章讲 Escalation 时,你会读到为什么数字员工遇到需要人类决策的情况要"终止 run + 等待恢复"而不是阻塞等待——因为 Agent Loop 是有界的协程,不能无限期持有资源,阻塞模型本身就是架构错误。这种层级的讨论贯穿全书十三章。
这本书不做什么
写一本技术书,不承诺的事和承诺的事同样重要。
这本书不讲通用 LLM 原理——从自注意力机制到 MoE 架构,市面上有足够多的书在讲这些。它不预测 AI 的终极形态——每一章讲的是"已经跑在生产环境的东西",不是"三个月后可能会有的东西"。它不提供"最佳实践"——这个领域变化太快,半年前的"最佳"今天可能是"反模式"。它只提供"当前架构下,这个约束为什么导致这个设计"——这种问题驱动的论证方式,应对的是趋势而非版本。
这本书也不卖 Halo。Halo 免费、开源,我不靠它赚钱。选它做拆解对象,我承认有利益冲突——这是我自己的项目。选它不是因为它最好,开源 Agent 现在不少,每一个都各有取舍。选它的原因更直接一些:在所有开源 Agent 里,这是我最熟悉的一个,每一处设计当初为什么这么写、为什么没那么写,我心里都有数——这部分内容拆别人的项目通常补不出来。本书讲的方法论本身适用于任何 Agent 系统;Halo 的源码只是让所有结论都可以被你自己 git clone 下来验证。
Halo 的代码 100% 由 AI 生成,但每一处架构决策、每一个复杂模块的拆分都经过密集的人工指导和审查——如何让 AI 生成的 30 万行代码不腐化、不堆积,是第十三章的主题。作为 Electron 桌面应用,对性能敏感。Halo 把作者过去在大型在线文档系统和 VSCode 研究中积累的工程经验都用进来:首屏极致性能优化、模块分层懒加载、渲染层共享、文件句柄独立 worker、流式 Markdown 解析与渲染——这些桌面工程的硬骨头,在书的中后章节有专门讨论。
我在第十三章章末留了一处关于技术与幸福的个人思考。不是写在这里,是因为它不是引言,是结语。
2026 年春,于 Halo 数字员工日常运行中