在前两篇里,我们从概念与基础实践(上),聊到了 LangChain、Claude Code 等工程化案例(中)。
这篇我们会把故事收尾:
一端是 Manus 和 Kiro 代表的工程极致;
另一端,是比「上下文工程」更远一步的「环境工程」视角。
从 Manus 看「极致工程化」的上下文设计

(manus System Architecture and Workflow)
很多人第一次看到 Manus 的结构图时,第一反应都是:
这已经不是「调 prompt」,而是「围绕 KV 缓存和上下文做系统软件工程」了。
我们从 6 个关键点来拆解 Manus 的做法,这其实就是一套可借鉴的「高级上下文工程 checklist」。
1. 围绕 KV Cache 设计
把「缓存命中率」当成核心 KPI
对于复杂 Agent,一个典型 loop 是这样的:
上下文越来越长 → 输出其实很短 → 输入 token : 输出 token ≈ 100 : 1
这时候,有没有命中 KV 缓存,直接决定了成本和延迟:
-
以某些模型为例: -
缓存输入:$0.3 / 100 万 token -
未缓存输入:$3 / 100 万 token -
成本差一个数量级。

Manus 的做法:
把「让前缀保持稳定」当作工程目标来设计整个系统:
-
键顺序不乱跳; -
不加多余的随机字段; -
避免带时间戳、随机 ID 写进 prompt。
-
而不是每一轮都把前缀重构一遍。 -
例如:上下文中插入一个特殊 marker,告诉推理引擎「从这之后不能继承 KV」。
可以直接抄的实践:
如果你现在有一个多轮 Agent:
把「每轮重新构造 prompt」改为「固定前缀 + 逐条 append 日志」; 对所有 JSON/结构化上下文,写一个“稳定序列化函数”,保证每次顺序一致。
你会立刻看到成本和延迟下降。
2. 遮蔽工具,而不是「动态删工具」
随着 Agent 越做越复杂,工具数量会爆炸。
直觉上,你可能会想:
「当前状态用不到的工具,就从 tool list 里删了吧?」
Manus 给出的结论是:不要删,用「遮蔽」代替「移除」。
为什么不能删?
-
它可能还会「想用」,结果发现 schema 不在; -
这会制造额外的困惑和错误。
Manus 的做法:
-
工具列表是稳定的(写死在 system / tool 定义里); -
真正的「可用性」由一个状态机 + 解码时 logits mask控制: -
当前状态不允许用 Tool A,就在解码时把「调用 Tool A」相关 token 的概率屏蔽掉; -
需要强制走某个工具,就给那个工具的 token 增强权重。
这背后的理念是:
「行为空间」控制放在「解码时」,而不是「上下文结构变更」上。
3. 把文件系统当成「终极上下文」
长上下文实际遇到的 3 个老问题:
-
工具返回的内容很大,极易炸掉上下文窗口; -
上下文超过一定长度后,模型性能反而下降(注意力腐蚀、信息稀释); -
即便有 KV 缓存,长输入本身就很贵。
Manus 的选择是:
把文件系统当成「上下文的一部分」,而不是单纯的“外部存储”。
-
让 Agent 学会: -
「写文件」= 把大段内容 offload 到磁盘; -
「读文件」= 需要时再读回局部内容或摘要; -
在上下文里只保留: -
文件路径 / URL; -
简短的摘要或指针。
示例设计思路:
[Observation]
已爬取页面:https://example.com/a
内容已保存至:/data/pages/a.md
内容摘要:这是一篇关于上下文工程的博客,包含 A/B/C 三部分……
要点:
上下文里只留「引用」和「摘要」,大体量内容放文件系统。 文件系统本身就变成了「长期记忆 + scratchpad」,而且天然无限扩容。
4. 用「复述」操控注意力
把目标不断推到上下文末尾
Manus 的一个任务,动辄要 50 次工具调用。
这么长的交互链,很容易出现:
-
忘记初始目标; -
中途被局部问题带偏; -
「中间信息」淹没重要指令。
Manus 的手法非常简单:
不断在上下文末尾「复述任务目标 / todo 列表」。
-
每完成几个步骤,就更新一次: -
当前大目标; -
剩余的子任务 / todo; -
这些复述会出现在最新的几条 message 里,从而被模型的注意力高权重地看到。
可借鉴的 prompt 片段:
[System 或 Tool 输出的一部分]
当前任务全局目标(请始终对齐):
1. 为用户完成 X 功能
2. 确保代码具备单元测试与基本文档
3. 最终以 README + 源码压缩包形式交付
当前未完成子任务列表:
- [ ] 完成模块 A 的接口定义
- [ ] 实现模块 A 核心逻辑
- [ ] 为模块 A 添加 3 条单测
……
这其实是在「手动操控注意力分配」:
通过把关键目标信息「挪到最近的 token 段」,对冲长上下文的「中间信息遗忘」问题。
5. 把「错误」保留在上下文里
在多步骤任务中,失败是常态而不是例外。
很多团队做 Agent 的第一反应是:
「这条调用失败了,那我就别让模型看到失败日志,重新组织一下,给它一个“干净”的上下文。」
Manus 的选择是反过来:
- 所有错误尝试都保留在上下文中
: -
包括失败的 action; -
完整的报错信息 / stacktrace; -
让模型「看到」: -
我试过 A,失败了; -
后来我改成 B,才成功。
这实际上为模型提供了负样本 + 反事实反馈:
-
它会在内部更新「先验」: Action2A这类行为的后验成功率变得更低; -
下一次遇到相似情形就更不容易犯同样的错。
换句话说:
「上下文」本身就成了在线 RLHF / 经验回放的一部分。
6. 刻意引入「多样性」,避免被 Few-shot「锁死」
Few-shot 是大家非常熟悉的技巧,但在 Agent 场景里,会带一个隐藏坑:
-
如果你上下文里的例子都长得「特别统一」: -
模型会强烈模仿这种 pattern; -
在遇到不适用的场景时,反而「照猫画虎」、产生幻觉或过拟合式行为。
Manus 的经验是:
适度地在「行动 / 观察」序列中加入结构化的微小变化,
例如:
不完全相同的序列化模板; 稍微不同的字段顺序; 不同但等价的自然语言措辞; 在安全范围内加入一点「格式噪音」。
目的不是制造混乱,而是:
-
提醒模型「这只是 pattern,不是硬规则」; -
放宽它的泛化空间,让它更敢于在新情况里调整策略,而不是照搬样本。
从 Vibe Coding 到 Spec-Driven

Kiro 展示的「规范驱动 + 上下文工程」
另一端的典型,是 Kiro —— 它解决的是「AI 参与软件开发」里一个更长期的问题:
不是「怎么把功能做出来」,
而是「怎么在半年、一年后,这个系统还可维护、可协作」。
1. Vibe Coding 的局限:
所谓 Vibe Coding,本质是:
Prompt → Code
「你把需求模糊地说一遍,模型帮你写大段代码。」
短期很爽,长期有几个致命问题:
1) 指望用户写出高质量 Prompt 不现实
-
大部分人不会自然地写出严谨的需求; -
更不会系统性覆盖边界条件、非功能需求(性能、安全等)。
-
缺文档、缺测试、缺架构约束; -
很适合 demo,不适合长期演进。
-
一旦需要手动调试 / 重构 / 扩展,就会非常痛苦; -
多人协作时,风格和结构更是灾难。
2. Spec-Driven 的核心理念:
「先把世界说清楚,再让模型动手」
Spec-Driven Development 的基本路径是:
Spec → Design → Tasks → Code
而不是:
Prompt → Code
具体来说,至少包含三层规范:
1)需求规范(requirements.md)
-
由 LLM 初稿、人类审阅 / 修改、再由 LLM 迭代细化。
WHEN [condition/event] THE SYSTEM SHALL [expected behavior]
-
用接近 EARS 这种结构化自然语言格式: -
先把「系统在什么条件下,该做什么」说清楚。
-
架构与模块划分、接口与调用流程、数据模型 / 表结构; -
前端/后端分层方式。
-
每个 task 是一个可执行的 TODO; -
可以一键触发「让 Agent 来执行这个任务」。
-
把实现过程拆成一系列任务(task):
这三层本身,就是极佳的「上下文素材」:
-
需求 → 指导「做什么」; -
设计 → 约束「怎么做」; -
任务 → 限定「现在只做这一小块」。
3. 上下文工程 + Spec-Driven:如何落地到你自己的项目?
如果你想在团队里引入这种模式,可以按下面这条路线做:
Step 1:把自然语言需求结构化
-
为 PM 或开发者提供一个「需求模板」,让他们按模板写; -
或者提供一个「需求助手 Agent」,把杂乱描述转成「EARS 风格」的条目。
示例:
WHEN 用户在移动端点击「一键下单」
THE SYSTEM SHALL 创建一条订单记录,并在 3 秒内返回下单结果。
Step 2:让模型基于需求生成 design draft
-
Prompt 里明确要求输出: -
模块结构、主要接口、关键类 / 函数; -
依赖关系。 -
人类审阅 + 修改; -
修改后的 design 再作为后续所有 Agent 的「高优先级上下文」。
Step 3:自动从 design 拆出 tasks
-
每个 task: -
描述清楚要修改 / 新增的文件; -
输入/输出; -
完成标准(含测试要求)。
例子:
Task: 为订单模块新增 create_order API
- 修改文件:/backend/order/api.py
- 要求:
- 接收参数:user_id, item_id, quantity
- 检查库存 & 用户有效性
- 写入数据库并返回订单号
- 添加至少 3 条单元测试用例
Step 4:Agent 执行 task 时的上下文构成
-
System 指令:团队通用编码规范; -
长期记忆: -
项目信息(类似 CLAUDE.md); -
代码风格、数据库约定、安全策略; -
当前 task 相关: -
requirements.md 中相关条目; -
design.md 中对应章节; -
相关文件内容(检索或路径 + 摘要); -
输出要求: -
修改后的代码 diff; -
更新后的文档 / 测试代码; -
对变更的自然语言说明。
这样做的结果是:
-
模型不会「凭感觉写代码」,而是遵循一整套约束; -
上下文是可复用的(新成员 / 新任务都复用同一个 spec + design); -
项目长期维护成本大幅下降。
从「上下文工程」到「环境工程」

当我们把 Manus、Kiro 这些实践放在一起看,会发现一条共同的趋势:
以前我们在「给模型塞信息」;
现在我们在「给模型建设一个可操作的环境」。
用一个简单的对比表来概括:
|
|
|
|
|
|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1. 什么是「环境工程」?
可以理解为:
在「上下文」之上,再加一层「可感知、可操控、可演化的环境」。
环境包含的要素不只有:
-
文本上下文; -
工具列表; -
RAG 的知识库;
还包括:
1) 可持续演化的「世界状态」
-
文件系统 / 数据库; -
任务看板 / 日志系统; -
外部服务状态(工单系统、CI/CD 状态等)。
-
权限系统; -
安全 / 合规规则; -
资源限制(时间、预算、算力)。
-
成功 / 失败信号; -
业务 KPI(例如转化率、留存、报错率); -
用户显式反馈(点赞 / 否定 / 修改)。
-
多个 Agent 与人类、与其他系统的协作流程; -
协作协议(谁能改什么,冲突如何解决)。
在环境工程视角下:
-
模型不再是「被动读 prompt」, -
而是「在一个世界里感知、决策、执行、积累经验」。
2. 上下文工程 vs 环境工程:实现上的关键差异
你可以这样理解演进路径:
1) 上下文工程解决的是:
-
「在这一次调用里,我怎么把信息组织好,让模型少犯错?」
-
「在一个长生命周期里,
模型怎么“活着”,
怎么记住过去、影响未来、和他人协作?」
具体差异体现在:
-
状态持久化方式不同
-
上下文工程:更多依赖「一次调用的 context window + 一些外部存储」; -
环境工程:世界状态(文件、数据库、任务系统)才是主角,上下文更像「观察窗口」。 -
控制粒度不同
-
上下文工程:控制的是「这次调用里,模型看到的 tokens」; -
环境工程:控制的是「模型在哪些地方有读写权限、能触发哪些外部事件」。 -
目标函数不同
-
上下文工程:成功与否更多由「本次回复质量」衡量; -
环境工程:看的是「长期业务指标」和「整体系统行为」。
3. 你现在就可以做的「类环境工程」实践
哪怕你暂时不搭一个完整的「环境系统」,也可以先做几件很实在的事情:
1) 把「文件系统 / 数据库」正式纳入 Agent 设计
-
哪些信息必须 offload 到外部世界; -
哪些只在上下文里做 ephemeral 存储;
-
明确:给 Agent 设计合理的读写 API,而不是让它随便乱写。
-
读取任务状态; -
更新任务状态; -
写入执行日志。
-
任务列表 + 状态(pending / running / done / failed); -
每个任务对应的文件 / 资源;
-
例如一个简单的任务表.
-
标记任务成功 / 失败; -
在日志里写「为啥失败」;
-
人类不只是改 prompt,而是通过: -
这些反馈会变成 Agent 之后决策的一部分(类似 Manus 保留错误)。
-
明确哪些目录 / 表可以读写; -
哪些 API 需要人工确认才能调用; -
为高风险操作设计「两步走」流程(先 plan,再 confirm)。
上下文只是起点,环境才是终局

回到开头那句话:
上下文工程解决的是——
「怎么在一页纸之内,把信息塞好、让模型别太蠢。」
而 Manus、Kiro、Claude Code 这些产品已经在告诉我们:
-
真正要做的是: -
给模型一个可操作的「世界」; -
让它能在里面看、记、想、做、犯错、纠正; -
上下文只是模型「看这个世界的窗口」; - 环境才是模型真正活着的地方。


