本文以 Cherry Studio 的 AI 运行时为例,说明 Prompt Engineering、Context Engineering、Loop Engineering、Harness Engineering 如何从单一提示词演化为可上线的 Agent 系统。
写在前面:为什么要研究别人的 Agent
笔者所在团队正在实现一款 AI 桌面端工具,既要把 Chat 做顺手,也要让 Agent Work 能真正接住多步骤任务。
说起来很美好:聊天、查资料、调工具、改文件、等审批、任务中断后继续跑......把这些关键词写在需求里只要一分钟,真正落到工程里却是一串连续的"那这个谁来管?"。
本着"他山之石,可以攻玉"的想法,团队选择研究社区内成熟的开源实现。Cherry Studio 已经把多模型调用、Chat、Agent Session、工具、知识库和运行时控制串成了一条相对完整的链路,非常适合作为拆解 Agent 工程问题的样本。
本文不是要照搬某个产品,而是想借它的实现回答一个更朴素的问题:
一个 AI 桌面端工具,怎样从"能聊两句",走到"能把事办完,而且别把事办砸"?
从 Chat 到 Agent Work:为什么只会写 Prompt 不够
早期 LLM 应用通常只有一个输入框和一段系统提示词:
text
用户问题 + System Prompt → 模型回答
这足以完成一次性问答。可当 Agent Work 要开始调用工具、读取用户资料、跨会话记忆、等待审批,甚至在失败后恢复时,事情就不是"再补两句 Prompt"这么简单了。
AI Agent 的工程化可以拆成四层:
text
Prompt 定义目标、角色和行为策略
Context 提供当前任务所需的可信事实
Loop 推进「思考---行动---观察---继续」的任务过程
Harness 约束工具、权限、生命周期和可观测性
它们的分工很明确:
- Prompt 告诉模型应当做什么,但不能强制模型只做允许的事。
- Context 提供证据,但不能决定任务何时结束。
- Loop 让模型完成多步任务,但不能替代权限和审批。
- Harness 可以限制和托管模型行为,却不能弥补错误的目标与缺失的事实。
所以,成熟的 Agent 产品不是"更长的 Prompt",而是四层共同组成的运行系统。
Agent 能力是怎样一步步长出来的
Prompt Engineering:先让模型听懂人话
第一阶段最直观:角色设定、输出格式、few-shot 示例、约束和拒答策略,能写的先写进 Prompt。
它解决的是模型"不知道应该如何回答"的问题,但存在明显边界:
- 事实会过期,Prompt 不能承担知识库。
- 用户偏好和项目约定跨会话丢失。
- Prompt 无法可靠限制文件写入、网络访问等副作用。
- 工具调用成功与否、重试次数和结束条件无法仅靠自然语言约束。
Prompt 仍是 Agent 的行为契约,但不应被误认为安全策略或状态机。
例如"删除文件前请先确认"可以写在 Prompt 中;真正拦住删除动作的,必须是运行时权限和审批。
Context Engineering:再让模型看到该看的东西
随着上下文窗口和 RAG 出现,问题从"如何说清要求"变成"此刻到底该把什么交给模型"。
Context Engineering 关注:
- 对话历史的选择、截断、压缩和分支。
- 文件、图片、网页、代码工作区等附件的表示方式。
- 长期记忆与临时事件的分层。
- 知识库的按需检索、引用与权限范围。
- 工具结果的大小、可信度和生命周期。
把聊天记录、附件和知识库切片一股脑灌进去,看似省事,token 和噪声却会很快失控。
关键原则不是"尽可能多放入上下文",而是让模型在当前步骤拿到最少但足够、可追溯的证据。
Loop Engineering:工具会调了,也得知道什么时候停
当模型可以调用工具后,单次请求演化为循环:
text
模型决定下一步
→ 调用工具
→ 获得工具结果
→ 基于结果继续推理、再次调用工具或结束
Loop Engineering 的工作,就是把这个过程变成可控制的程序:步数上限、停止条件、错误语义、取消、用户插话、暂停审批和结果流式传输。否则 Agent 很容易从"认真办事"变成"原地转圈"。
Harness Engineering:最后,得有人给它划边界
真正上线的 Agent 还需要一个运行 Harness,把模型能力放在受控边界中:
- 哪些工具可见、可调用、需要审批或被禁用。
- 哪些目录、知识库和 MCP 服务可访问。
- 会话如何创建、续接、恢复、取消和持久化。
- 工具执行、模型调用、失败和成本如何观察。
Harness 是从可演示 Demo 走向可靠产品的分水岭。模型可以提出动作,但最终能不能做、怎样做,不能只由模型说了算。
把原理落回 Cherry Studio
Prompt:从一段文案到可组合的行为契约
Cherry Studio 有两条主要运行路径:
- 通用 Chat 基于 AI SDK 的
Agent。 - Agent Session 由
AgentSessionRuntimeService托管,并可使用 Claude Code Runtime Driver。
通用 Chat 的系统提示词由 buildAgentParams 调用 assembleSystemPrompt 组装,包含 Assistant 指令和延迟暴露工具的指引。参数、工具、模型适配插件和 Hook 由同一参数管线构造,详见 Params Pipeline。
Agent Session 的 Prompt 更接近"个人 Agent 的操作手册"。PromptBuilder 会组合:
- 工作区
system.md或内置身份提示。 - Web、自动化、Memory 等工具使用策略。
- 用户自定义 instructions。
- 工作区、语言、频道安全和运行环境信息。
- Agent 身份与长期记忆文件。
其身份和记忆文件分工明确:
| 文件 | 语义 | 上下文策略 |
|---|---|---|
SOUL.md |
Agent 的角色、语气、原则与边界 | 每次系统提示词加载 |
USER.md |
用户偏好、称呼、时区和个人背景 | 每次系统提示词加载 |
memory/FACT.md |
长期事实、项目决策和可复用经验 | 每次系统提示词加载 |
memory/JOURNAL.jsonl |
一次性事件和会话日志 | 仅通过工具按需检索 |
这种设计避免了把所有历史事件永久塞入 Prompt,同时让稳定的身份和长期事实跨工作区、跨会话保留。
这一层的落地要点
- Prompt 只表达行为目标、策略和解释;权限必须由运行时强制。
- 将稳定信息与易变信息分开。易变信息应通过 Context 或工具读取。
- 对 Prompt 建立版本、评审和回归用例,而不是把它当作不可测试的字符串。
- 给工具提供明确的使用时机和失败处理指引,避免模型盲目重试。
Context:按需取证,而非整库灌入
Context 是一次模型调用真实可见的信息集合,不等同于聊天历史。
Cherry Studio 的 Chat 通过 ChatContextProvider 为不同 topic 类型准备请求:持久聊天、临时聊天和 Agent Session 各自拥有不同的持久化与派发逻辑。完整调用路径见 Core Architecture。
知识库使用 Agentic RAG,而非预先把所有检索文本插入 Prompt:
text
kb_list
→ 发现知识库和资料结构
kb_search
→ 返回少量相关 chunk 与 conceptId
kb_read
→ 读取全文、分页或 grep,核验上下文
模型回答
→ 引用检索结果
该工具链的共享实现位于 src/main/ai/tools/knowledgeLookup.ts,底层由 KnowledgeService 查询本地知识库。知识库绑定是范围上限:每轮在 Composer 中选择的库只能收窄范围,不能扩大 Agent 已绑定的可见范围。
这与"把全部资料切片后塞入 system prompt"相比有三个收益:
- 控制 token 消耗和上下文噪声。
- 允许模型先发现资料,再深读证据。
- 可以把来源、conceptId 和原文定位传递到最终回答。
这一层别图省事
- 将长期身份、当前任务、资料检索和工具结果放入不同 Context 层。
- 对 RAG 分别评估召回率、引用正确性、答案正确性和延迟;不要只评估"回答看起来是否合理"。
- 资料库、工作区和用户数据必须有明确 scope;UI 限制不是安全边界,主进程仍应校验。
- 大文件和长历史要可分页、可压缩、可回读,而不是无限增长。
Loop:把模型调用变成可终止的任务过程
AI SDK 路径中的 Agent 包装了 ToolLoopAgent。它对一次流式执行提供:
stopWhen:步数上限和特性贡献的停止条件。prepareStep:每一步开始前改写或补充请求。onStepFinish:汇总 usage、记录步骤结果。onToolExecutionStart/onToolExecutionEnd:围绕工具执行的观测。onFinish、onAbort、onError:区分正常完成、取消和错误。
该 Agent stream 是单次流式执行,不会把新用户消息直接注入正在进行的普通 Chat Loop。普通聊天的 steering 由 Stream Manager 在上层处理:当前执行安全结束后再启动 continuation。Agent Session 则有自己的 follow-up 队列和 Driver 能力;Claude Code Driver 可在下一次工具调用前通过 PreToolUse 注入 steer。
这一区分很重要:用户插话不是简单 append 一条消息,而是会影响当前执行的边界、持久化顺序和是否需要重新构造上下文。
这一层最怕边界不清
- 定义可观测的终止状态:成功、步数耗尽、用户取消、等待审批、不可重试失败。
- 不把所有错误当作 retry;工具失败、权限拒绝、模型错误和用户取消有不同处理路径。
- 对每个 Loop 设置预算:最大步骤、最大耗时、最大工具调用数和 token/cost 上限。
- 把用户 steering 设计为有明确边界的状态迁移,避免修改正在执行的历史。
详细实现见 Agent Loop 和 Agent Session Runtime。
Harness:将 Agent 放进可管理的产品运行时
Harness 不是单个类,而是一组控制面和运行时宿主能力。
text
模型请求
→ 工具选择与策略
→ 权限检查 / 用户审批
→ MCP 或本地工具执行
→ 流式事件、持久化与可观测性
→ UI 与外部渠道
Cherry Studio 的关键组成包括:
| 能力 | 实践 |
|---|---|
| 工具面 | Tool Registry 管理内置工具、MCP 工具和延迟暴露元工具;Claude Code Runtime 通过 MCP bridge 提供对应工具。 |
| 权限与审批 | 工具可声明 needsApproval;Main 是审批状态唯一写入者,Renderer 只提交用户决定。 |
| 工作区边界 | Agent Session 使用 workspace 作为 cwd,并对越界路径和高风险工具执行额外检查。 |
| 会话宿主 | AiStreamManager 管理流、订阅、取消和持久化;AgentSessionRuntimeService 管理长生命周期 Agent Session。 |
| 外部生态 | MCP Runtime 管理用户配置的 MCP server、工具目录和 OAuth 等连接生命周期。 |
| 可观测性 | AI SDK / Claude Code 路径可接入 trace、usage 记录和工具执行事件。 |
工具审批体现了 Harness 的核心原则:模型能够提出动作,但不能自行绕过产品授权。对于需要确认的调用,运行时将 stream 置于等待状态;用户决定后由 Main 恢复或拒绝执行。详见 Tool Approval。
这一层必须足够硬
- 最小权限原则:只暴露当前任务真正需要的工具、目录和知识库。
- 把"模型建议执行"与"系统实际执行"分离。
- 对破坏性工具、网络写入、凭据访问和工作区外文件操作设置强制审批。
- 保留端到端 trace,至少覆盖模型调用、工具调用、审批等待、失败和成本。
- 将运行时能力和具体模型/SDK 解耦,使 Provider 或 Agent Driver 可替换。
四层合起来,任务是怎么跑完的?
下面是一条"查询私有技术文档并生成答案"的简化路径:
text
Prompt
→ 说明何时应查询资料、如何引用来源
Context
→ 当前用户问题、会话历史、知识库 scope
Loop
→ kb_list → kb_search → kb_read → 生成答案
Harness
→ 只开放已授权 kb_* 工具;记录工具调用;处理审批和取消
如果其中任何一层缺失,系统都会退化:
| 缺失层 | 典型后果 |
|---|---|
| Prompt | 模型不知道何时检索、何时停止或如何表达引用。 |
| Context | 模型只能猜测,或因上下文过多而被噪声干扰。 |
| Loop | 工具调用无法形成多步任务,错误和取消语义混乱。 |
| Harness | 模型拥有不受控的副作用,难以审核、恢复和运维。 |
开工前,先过一遍这张清单
在为一个新 Agent 功能投入开发前,先把下面的问题过一遍。
别等工具都接完了,才开始讨论"审批放哪儿""中断以后怎么办"。
Prompt
- 模型的目标、边界、输出格式和工具策略是否明确?
- 哪些内容必须随产品版本更新,哪些属于用户可配置指令?
Context
- 当前调用真正需要哪些历史、附件、记忆和资料?
- 敏感数据、知识库和工作区的 scope 是否由服务端验证?
- 每个检索结论能否回到原始证据?
Loop
- 何时完成、暂停、重试、取消和失败?
- 是否定义了步骤、时间、工具和成本预算?
- 用户在执行中补充要求时,状态如何安全迁移?
Harness
- 工具是否最小化暴露,危险动作是否需要审批?
- 模型、工具、会话、流和持久化分别由谁拥有?
- 是否能追踪一次任务的模型调用、工具输入输出、审批和成本?
小结
Prompt Engineering 仍然重要,但它只是 Agent 工程的开始:
Prompt 定义目标,Context 提供证据,Loop 推进任务,Harness 约束行动。
Cherry Studio 的价值不只在于连接多个模型,也在于将提示词、上下文、工具循环和运行时控制拆分为独立但协同的系统。对正在建设 AI 桌面端工具的团队而言,这种拆分能让 Agent 能力从"改一段 Prompt 的试验"走向可评审、可测试、可观测和可持续演进的软件工程。