|
在前两篇里,我们从概念与基础实践(上),聊到了 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 缓存,直接决定了成本和延迟: Manus 的做法: 把「让前缀保持稳定」当作工程目标来设计整个系统: 1)所有状态都只追加(append-only),而不是频繁重写前面的内容。2)JSON 等结构化内容必须是「可重复序列化」的
- 避免带时间戳、随机 ID 写进 prompt。ingFang SC", system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.034em;background-color: transparent;">
ingFang SC", system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.034em;background-color: transparent;">3)需要「断缓存」的地方要显式标记
- 例如:上下文中插入一个特殊 marker,告诉推理引擎「从这之后不能继承 KV」。
可以直接抄的实践: 如果你现在有一个多轮 Agent: - 把「每轮重新构造 prompt」改为「固定前缀 + 逐条 append 日志」;
- 对所有 JSON/结构化上下文,写一个“稳定序列化函数”,保证每次顺序一致。
你会立刻看到成本和延迟下降。
2. 遮蔽工具,而不是「动态删工具」随着 Agent 越做越复杂,工具数量会爆炸。 直觉上,你可能会想: 「当前状态用不到的工具,就从 tool list 里删了吧?」
Manus 给出的结论是:不要删,用「遮蔽」代替「移除」。 为什么不能删? 1)工具列表变了,前缀就变了,KV 缓存大概率失效。
- 它可能还会「想用」,结果发现 schema 不在;
Manus 的做法: - 工具列表是稳定的(写死在 system / tool 定义里);
- 真正的「可用性」由一个状态机 + 解码时 logits mask控制:
- 当前状态不允许用 Tool A,就在解码时把「调用 Tool A」相关 token 的概率屏蔽掉;
- 需要强制走某个工具,就给那个工具的 token 增强权重。
这背后的理念是: 「行为空间」控制放在「解码时」,而不是「上下文结构变更」上。
3. 把文件系统当成「终极上下文」长上下文实际遇到的 3 个老问题: - 上下文超过一定长度后,模型性能反而下降(注意力腐蚀、信息稀释);
Manus 的选择是: 把文件系统当成「上下文的一部分」,而不是单纯的“外部存储”。 - 「写文件」= 把大段内容 offload 到磁盘;
示例设计思路: [Observation] 已爬取页面:https://example.com/a 内容已保存至:/data/pages/a.md 内容摘要:这是一篇关于上下文工程的博客,包含 A/B/C 三部分……
要点: - 上下文里只留「引用」和「摘要」,大体量内容放文件系统。
- 文件系统本身就变成了「长期记忆 + scratchpad」,而且天然无限扩容。
4. 用「复述」操控注意力把目标不断推到上下文末尾Manus 的一个任务,动辄要 50 次工具调用。 这么长的交互链,很容易出现: Manus 的手法非常简单: 不断在上下文末尾「复述任务目标 / todo 列表」。 - 这些复述会出现在最新的几条 message 里,从而被模型的注意力高权重地看到。
可借鉴的 prompt 片段: [System 或 Tool 输出的一部分] 当前任务全局目标(请始终对齐): 1. 为用户完成 X 功能 2. 确保代码具备单元测试与基本文档 3. 最终以 README + 源码压缩包形式交付
当前未完成子任务列表: - [ ] 完成模块 A 的接口定义 - [ ] 实现模块 A 核心逻辑 - [ ] 为模块 A 添加 3 条单测 ……
这其实是在「手动操控注意力分配」: 通过把关键目标信息「挪到最近的 token 段」,对冲长上下文的「中间信息遗忘」问题。
5. 把「错误」保留在上下文里在多步骤任务中,失败是常态而不是例外。 很多团队做 Agent 的第一反应是: 「这条调用失败了,那我就别让模型看到失败日志,重新组织一下,给它一个“干净”的上下文。」
Manus 的选择是反过来: 这实际上为模型提供了负样本 + 反事实反馈: - 它会在内部更新「先验」:
Action2A这类行为的后验成功率变得更低;
换句话说: 「上下文」本身就成了在线 RLHF / 经验回放的一部分。
6. 刻意引入「多样性」,避免被 Few-shot「锁死」Few-shot 是大家非常熟悉的技巧,但在 Agent 场景里,会带一个隐藏坑: - 在遇到不适用的场景时,反而「照猫画虎」、产生幻觉或过拟合式行为。
Manus 的经验是: 适度地在「行动 / 观察」序列中加入结构化的微小变化, 例如:
目的不是制造混乱,而是: - 放宽它的泛化空间,让它更敢于在新情况里调整策略,而不是照搬样本。
从 Vibe Coding 到 Spec-DrivenKiro 展示的「规范驱动 + 上下文工程」另一端的典型,是 Kiro —— 它解决的是「AI 参与软件开发」里一个更长期的问题: 不是「怎么把功能做出来」, 而是「怎么在半年、一年后,这个系统还可维护、可协作」。
1. Vibe Coding 的局限:所谓 Vibe Coding,本质是: Prompt → Code 「你把需求模糊地说一遍,模型帮你写大段代码。」
短期很爽,长期有几个致命问题: 1) 指望用户写出高质量 Prompt 不现实
- 更不会系统性覆盖边界条件、非功能需求(性能、安全等)。ingFang SC", system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.034em;background-color: transparent;">
ingFang SC", system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.034em;background-color: transparent;">2) 快速生成的代码技术债极高
- 很适合 demo,不适合长期演进。ingFang SC", system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.034em;background-color: transparent;">
ingFang SC", system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.034em;background-color: transparent;">3) 开发者对生成代码的理解有限
- 一旦需要手动调试 / 重构 / 扩展,就会非常痛苦;
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]
- 先把「系统在什么条件下,该做什么」说清楚。ingFang SC", system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.034em;background-color: transparent;">
ingFang SC", system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.034em;background-color: transparent;">2)设计规范(design.md)
- 架构与模块划分、接口与调用流程、数据模型 / 表结构;
ingFang SC", system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.034em;background-color: transparent;">3)任务拆分(tasks.md)
这三层本身,就是极佳的「上下文素材」:
3. 上下文工程 + Spec-Driven:如何落地到你自己的项目?如果你想在团队里引入这种模式,可以按下面这条路线做: Step 1:把自然语言需求结构化 - 为 PM 或开发者提供一个「需求模板」,让他们按模板写;
- 或者提供一个「需求助手 Agent」,把杂乱描述转成「EARS 风格」的条目。
示例: WHEN 用户在移动端点击「一键下单」 THE SYSTEM SHALL 创建一条订单记录,并在 3 秒内返回下单结果。
Step 2:让模型基于需求生成 design draft - 修改后的 design 再作为后续所有 Agent 的「高优先级上下文」。
Step 3:自动从 design 拆出 tasks 例子: Task: 为订单模块新增 create_order API - 修改文件:/backend/order/api.py - 要求: - 接收参数:user_id, item_id, quantity - 检查库存 & 用户有效性 - 写入数据库并返回订单号 - 添加至少 3 条单元测试用例
Step 4:Agent 执行 task 时的上下文构成 这样做的结果是: - 上下文是可复用的(新成员 / 新任务都复用同一个 spec + design);
从「上下文工程」到「环境工程」当我们把 Manus、Kiro 这些实践放在一起看,会发现一条共同的趋势: 以前我们在「给模型塞信息」; 现在我们在「给模型建设一个可操作的环境」。
用一个简单的对比表来概括: 1. 什么是「环境工程」?可以理解为: 在「上下文」之上,再加一层「可感知、可操控、可演化的环境」。
环境包含的要素不只有: 还包括: 1) 可持续演化的「世界状态」
- 外部服务状态(工单系统、CI/CD 状态等)。ingFang SC", system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.034em;background-color: transparent;">
在环境工程视角下:
2. 上下文工程 vs 环境工程:实现上的关键差异你可以这样理解演进路径: 1) 上下文工程解决的是:
- 「在这一次调用里,我怎么把信息组织好,让模型少犯错?」
- 「在一个长生命周期里,
模型怎么“活着”, 怎么记住过去、影响未来、和他人协作?」
具体差异体现在: 状态持久化方式不同 - 上下文工程:更多依赖「一次调用的 context window + 一些外部存储」;
- 环境工程:世界状态(文件、数据库、任务系统)才是主角,上下文更像「观察窗口」。
控制粒度不同 - 上下文工程:控制的是「这次调用里,模型看到的 tokens」;
- 环境工程:控制的是「模型在哪些地方有读写权限、能触发哪些外部事件」。
目标函数不同 - 环境工程:看的是「长期业务指标」和「整体系统行为」。
3. 你现在就可以做的「类环境工程」实践哪怕你暂时不搭一个完整的「环境系统」,也可以先做几件很实在的事情: 1) 把「文件系统 / 数据库」正式纳入 Agent 设计
- 明确:给 Agent 设计合理的读写 API,而不是让它随便乱写。
- 任务列表 + 状态(pending / running / done / failed);
3) Agent 可以通过工具把人类反馈变成「环境的一部分」
- 这些反馈会变成 Agent 之后决策的一部分(类似 Manus 保留错误)。
- 为高风险操作设计「两步走」流程(先 plan,再 confirm)。
上下文只是起点,环境才是终局回到开头那句话: 上下文工程解决的是—— 「怎么在一页纸之内,把信息塞好、让模型别太蠢。」
而Manus、Kiro、Claude Code这些产品已经在告诉我们: |