Agent-Native 架构完全指南:当代码终结之后,如何构建应用
原文标题: Agent-Native Architectures: How to Build Apps After Code Ends 作者: Dan Shipper & Claude(合著) 来源: Every.to 配套视频: YouTube 配套代码: Compound Engineering Plugin(7.5k Stars)
目录
- 引言:为什么是现在
- 五大核心原则
- 从原语到领域工具
- 文件作为通用接口
- Context.md 模式
- 文件与数据库的取舍
- 冲突管理
- Agent 执行模式
- 实现模式
- 产品层面的启示
- 移动端专属架构
- 高级模式
- 反模式(Anti-Patterns)
- 成功标准清单
- 配套工具:Compound Engineering Plugin
1. 引言:为什么是现在
"Software agents work reliably now."(软件 Agent 现在已经可以可靠地工作了。)
这篇指南的核心前提是:大语言模型(LLM)+ 工具访问能力 + 循环执行,这三者的结合使得 AI Agent 能够自主完成复杂的多步骤任务。
作者在开发 Reader(阅读应用)和 Anecdote(笔记/知识管理应用)的过程中总结出了这套架构思想。核心洞察来自 Claude Code 的实践:
"A really good coding agent is actually a really good general-purpose agent." (一个真正好的编程 Agent,实际上就是一个真正好的通用 Agent。)
这意味着 Claude Code 展示的能力——读取文件、编辑代码、执行命令、在循环中追求目标——可以推广到所有软件领域。Agent 不再是"附加功能",而应该成为应用架构的一等公民。
2. 五大核心原则
2.1 对等性(Parity)
定义: 用户通过 UI 能完成的所有操作,Agent 必须也能通过工具完成。
测试方法: 随便选一个 UI 操作——Agent 能完成它吗?如果不能,就是架构缺陷。
为什么重要:
- 如果 Agent 无法执行某些操作,它就永远无法自主完成涉及这些操作的任务
- 对等性是其他四个原则的基础
- 用户看到 Agent 的能力边界时会失去信任
2.2 粒度(Granularity)
"Tools should be atomic primitives. Features are outcomes achieved by an agent operating in a loop." (工具应该是原子化的原语。功能是 Agent 在循环中运行所达成的结果。)
核心纪律: 要改变行为,应该修改 prompt(提示词),而不是重构代码。
举例:
- ❌ 一个
processDocument()工具,内部包含分类、摘要、标签、存储等所有逻辑 - ✅ 分离的
classify()、summarize()、tag()、store()工具,由 Agent 根据 prompt 自行编排
2.3 可组合性(Composability)
"With atomic tools and parity, you can create new features just by writing new prompts." (有了原子工具和对等性,你只需编写新的 prompt 就能创建新功能。)
经典示例——"每周回顾"功能:
传统方式需要写一整套代码。Agent-Native 方式只需一个 prompt:
"回顾本周所有笔记和项目进展,总结关键进展,识别未完成的任务,
生成一份简洁的周报并保存到 /reviews/ 目录。"
Agent 利用已有的文件读取、搜索、写入工具,在循环中自主完成这一切。
2.4 涌现能力(Emergent Capability)
Agent 能够通过组合现有工具,完成开发者从未预想过的任务。这揭示了"潜在需求"(latent demand)——通过观察用户向 Agent 提出什么请求来指导产品开发。
"You're discovering, not guessing."(你是在发现需求,而不是猜测需求。)
信号模式:
- 成功信号: 用户请求 → Agent 成功完成 = 确认了功能需求
- 失败信号: 用户请求 → Agent 失败 = 暴露了工具/对等性的差距
2.5 持续改进(Improvement Over Time)
Agent-Native 应用可以在不发布新代码的情况下持续改进:
| 维度 | 方式 | 示例 |
|---|---|---|
| 累积上下文 | 状态通过 context 文件持久化 | Agent 记住用户偏好、项目进展 |
| 开发者级 prompt 优化 | 系统 prompt 迭代 | 改进指令措辞、增加示例 |
| 用户级 prompt 优化 | 用户自定义指令 | 用户设定个性化规则 |
| 自我修改(需谨慎) | Agent 修改自身行为 | 自动调整偏好设置 |
3. 从原语到领域工具
3.1 演进路径
阶段1:原语阶段
Agent 使用基础原语在循环中工作 → 灵活性最高,验证概念
阶段2:领域工具阶段
为常见操作添加领域特定工具 → 更快速,仍由 Agent 编排
阶段3:热路径优化阶段
对高频路径用优化代码实现 → 最快速,确定性执行
3.2 领域工具的设计原则
一个领域工具 = 一个概念上的用户操作
- 领域工具包含机械性验证(参数类型检查、格式校验等)
- 但判断应该留在 prompt 中,而不是硬编码在工具逻辑里
- 始终保留原语的可用性——领域工具是快捷方式,不是大门(shortcuts, not gates)
# ❌ 错误:把判断逻辑编码在工具里
def process_email(email):
if email.is_urgent: # 工具在做判断
priority = "high"
category = classify(email) # 工具在做分类
store(email, priority, category)
# ✅ 正确:工具只做机械操作,判断留给 Agent
# 工具:store_email(email, priority, category)
# Prompt:"评估邮件紧急程度(1-5),进行分类,然后存储。紧急度 ≥4 时发送通知。"
3.3 何时添加领域工具
- 某个操作模式反复出现(Agent 经常用 3-4 个原语完成同一件事)
- 需要性能优化(原语组合太慢)
- 需要原子性保证(多个操作要么全成功要么全失败)
- 需要锚定"领域词汇",让 Agent 和用户有共同语言
4. 文件作为通用接口
"Design for what agents can reason about—what would make sense to a human."
Agent 天然擅长处理文件,原因有五:
| 优势 | 说明 |
|---|---|
| 已知 | Agent 天然精通 bash/文件系统原语(ls, cat, mkdir, cp 等) |
| 可检查 | 用户能直接看到、编辑、删除 Agent 的工作成果 |
| 可移植 | 导出/备份轻而易举;数据归用户所有 |
| 跨设备同步 | iCloud 实现无服务器的多设备同步 |
| 自文档化 | /projects/acme/notes/ 比数据库查询更易读 |
推荐目录结构
Documents/
├── AgentCheckpoints/ # 临时数据(Agent 会话检查点)
│ └── {sessionId}.checkpoint
├── AgentLogs/ # 调试日志
│ └── {type}/{sessionId}.md
└── Research/ # 用户的实际工作内容
└── books/{bookId}/
├── full_text.txt # 主要内容
├── notes.md # 用户笔记
└── agent_log.md # Agent 操作记录
实体范围的命名规范
| 文件类型 | 命名模式 | 示例 |
|---|---|---|
| 实体数据 | {entity}.json |
library.json |
| 人类可读内容 | {content_type}.md |
introduction.md |
| Agent 推理记录 | agent_log.md |
每个实体一个 |
| 主要内容 | full_text.txt |
下载的文本 |
| 多卷内容 | volume{N}.txt |
volume1.txt |
| 外部来源 | {source_name}.md |
wikipedia.md |
| 检查点 | {sessionId}.checkpoint |
基于 UUID |
| 配置 | config.json |
功能设置 |
命名规范: 小写字母加下划线(my_file.md),不用驼峰命名。Markdown 用于人类可读内容;JSON 用于结构化数据。
5. Context.md 模式
Context.md 是 Agent 的可移植工作记忆。Agent 在每次会话开始时读取它,在状态变化时更新它。
推荐结构
# Context
## 我是谁(Who I Am)
[Agent 角色描述——我是什么类型的 Agent,我的职责是什么]
## 我对用户的了解(What I Know About This User)
- 兴趣画像
- 偏好设置
- 当前阅读/项目
## 现有资源(What Exists)
- 可用的资源清单
- 活跃的项目
- 用户配置
## 近期活动(Recent Activity)
- 最近操作的时间线
## 行为准则(My Guidelines)
- 行为约束
- 个性化规则
## 当前状态(Current State)
- 待处理任务
- 上次同步时间戳
为什么有效:
- 可移植性: 纯文本文件,任何系统都能读取
- 透明性: 用户可以直接查看和编辑 Agent "记住了什么"
- 无代码修改: 改变 Agent 行为只需编辑这个文件
- 跨会话连续性: Agent 在新会话中能"记住"之前的上下文
6. 文件与数据库的取舍
"Files for legibility, databases for structure. When in doubt, files." (文件用于可读性,数据库用于结构性。拿不定主意时,选文件。)
| 使用文件 | 使用数据库 |
|---|---|
| 用户需要阅读/编辑的内容 | 高频结构化数据 |
| 从版本控制中受益的配置 | 需要复杂查询的数据 |
| Agent 生成的工作成果 | 临时状态(会话、缓存) |
| 任何需要透明性的东西 | 关系型数据 |
| 大段文本内容 | 需要索引的数据 |
7. 冲突管理
当 Agent 和用户(或多个 Agent)同时操作文件时:
| 策略 | 说明 | 优缺点 |
|---|---|---|
| 原子写入 | 最后写入者获胜 | 简单,但可能丢失修改 |
| iCloud 冲突监控 | 监测同步冲突,生成 {filename} (conflict).md |
可检测但需要处理逻辑 |
| 分离空间 | Agent 写入草稿区,用户确认后合并 | 安全但增加复杂度 |
| 追加式日志 | 只追加不修改 | 永不冲突但文件会增长 |
| 文件锁定 | 操作前锁定文件 | 严格但可能造成死锁 |
实际建议: 日志和状态文件很少冲突。对于用户可编辑的内容,考虑将 Agent 输出与用户空间分开。
8. Agent 执行模式
8.1 完成信号(Completion Signals)
Agent 需要显式的完成机制,而不是启发式检测:
struct ToolResult {
let success: Bool // 操作是否成功
let output: String // 输出内容
let shouldContinue: Bool // 是否继续循环
}
.success("结果") // 操作成功,继续循环
.error("错误信息") // 操作失败,Agent 可以重试
.complete("完成") // 任务完成,停止循环
8.2 模型层级选择
| 任务类型 | 推荐层级 | 理由 |
|---|---|---|
| 研究 Agent | 均衡型(Balanced) | 需要工具循环和良好推理 |
| 聊天对话 | 均衡型(Balanced) | 速度足够快 |
| 复杂综合分析 | 强力型(Powerful) | 多来源分析需要强推理 |
| 快速分类 | 快速型(Fast) | 高频量、简单任务 |
纪律: 根据任务复杂度显式选择层级;避免默认使用最强力的模型。
8.3 部分完成追踪
struct AgentTask {
var status: TaskStatus // pending, in_progress, completed, failed, skipped
var notes: String? // 失败原因、已完成的内容
}
var isComplete: Bool {
tasks.allSatisfy { $0.status == .completed || $0.status == .skipped }
}
异常场景处理:
| 场景 | 处理方式 |
|---|---|
| Agent 达到最大迭代次数 | 保存检查点,后续可恢复继续 |
| Agent 在某个任务上失败 | 标记为失败并记录原因;其他任务可继续 |
| 网络错误 | 会话标记为失败;检查点保存到出错之前 |
8.4 上下文窗口限制
必须从一开始就为有限的上下文窗口做设计:
- 迭代精炼工具: 支持"摘要 → 详情 → 完整"的分层访问
- 中途整合: Agent 能在会话中期将已获取信息进行"压缩整合"
- 检查点预期: 假设上下文终将被填满——从一开始就为此设计
9. 实现模式
9.1 共享工作空间(Shared Workspace)
Agent 和用户应该在同一个数据空间中工作,而不是分隔的沙箱。
UserData/
├── notes/ ← Agent 和用户都可以读/写
├── projects/ ← Agent 组织,用户可覆盖
└── preferences.md ← Agent 读取,用户编辑
原则: 默认共享;仅在有具体安全需求时才使用沙箱隔离。
9.2 上下文注入(Context Injection)
系统 prompt 应包含:
| 类别 | 内容 | 示例 |
|---|---|---|
| 可用资源 | 数据清单 | "你有 42 条笔记、3 个项目" |
| 能力描述 | 可用操作 | "你可以创建、编辑、标签、搜索、组织" |
| 近期活动 | 用户操作时间线 | "用户最近编辑了项目 A" |
9.3 Agent 与 UI 的通信
enum AgentEvent {
case thinking(String) // 显示"思考中"指示器
case toolCall(String, String) // 显示正在使用的工具
case toolResult(String) // 显示工具结果(可选)
case textResponse(String) // 流式输出到聊天界面
case statusChange(Status) // 更新状态栏
}
"Silent agents feel broken. Visible progress builds trust." (沉默的 Agent 让人觉得它坏了。可见的进展能建立信任。)
考虑使用 ephemeralToolCalls 标志——隐藏内部检查操作,只显示有意义的动作。
10. 产品层面的启示
10.1 渐进式披露(Progressive Disclosure)
"Excel is the canonical example: grocery list or financial model, same tool."
| 特征 | 说明 |
|---|---|
| 简单入口 | 基础请求无需学习曲线即可完成 |
| 可发现的深度 | 用户在探索中发现新的能力 |
| 无天花板 | 高级用户能把系统推到开发者未曾预想的方向 |
10.2 潜在需求发现(Latent Demand Discovery)
传统方式: 想象功能 → 构建 → 看是否正确
Agent-Native 方式: 构建有能力的基础 → 观察用户请求什么 → 将涌现的模式正式化
"The agent becomes a research instrument for understanding what your users actually need."
迭代循环:
- 观察用户请求
- 对常见模式添加领域工具
- 为高频请求创建专用 prompt
- 移除未使用的工具
- 重复
10.3 审批与用户自主权
| 风险等级 | 可逆性 | 模式 | 示例 |
|---|---|---|---|
| 低风险 | 易逆转 | 自动执行 | 整理文件 |
| 低风险 | 难逆转 | 快速确认 | 发布到动态流 |
| 高风险 | 易逆转 | 建议+执行 | 代码修改 |
| 高风险 | 难逆转 | 明确审批 | 发送邮件 |
自我修改的可理解性原则: 当 Agent 修改自身行为时,必须确保可见性、可理解性、可回滚。
11. 移动端专属架构
11.1 独特优势与核心挑战
优势: 原生文件系统访问、丰富上下文(健康数据、位置、照片、日历)、本地应用个性化进化、iCloud 无服务器同步。
核心挑战:
"Agents are long-running. Mobile apps are not."
iOS 在应用进入后台后几秒钟就会暂停甚至终止它。必须有健壮的检查点、恢复和后台执行策略。
11.2 iOS 存储架构:iCloud 优先,本地兜底
if let url = fileManager
.url(forUbiquityContainerIdentifier: nil) {
return url.appendingPathComponent("Documents")
}
return fileManager.urls(
for: .documentDirectory,
in: .userDomainMask)[0]
推荐目录结构:
iCloud.com.{bundleId}/Documents/
├── Library/ # 用户的"图书馆"
├── Research/books/ # 研究资料
├── Chats/ # 聊天历史
└── Profile/ # 用户档案
11.3 检查点与恢复
struct AgentCheckpoint: Codable {
let agentType: String // Agent 类型
let messages: [[String: Any]] // 消息历史
let iterationCount: Int // 迭代计数
let taskListJSON: String? // 任务列表
let customState: [String: String] // 自定义状态
let timestamp: Date // 时间戳
}
func isValid(maxAge: TimeInterval = 3600) -> Bool {
Date().timeIntervalSince(timestamp) < maxAge
}
恢复流程:
- 加载中断的会话 → 扫描检查点目录
- 过滤有效性 → 默认1小时窗口
- 向用户显示恢复提示
- 恢复消息并继续 Agent 循环
- 用户关闭时删除检查点
11.4 后台执行策略
iOS 只给约 30 秒后台时间:
func prepareForBackground() {
backgroundTaskId = UIApplication.shared
.beginBackgroundTask(withName: "AgentProcessing") {
handleBackgroundTimeExpired()
}
}
func handleBackgroundTimeExpired() {
for session in sessions where session.status == .running {
session.status = .backgrounded
Task { await saveSession(session) }
}
}
func handleForeground() {
for session in sessions where session.status == .backgrounded {
Task { await resumeSession(session) }
}
}
策略优先级: 完成当前工具调用 → 保存检查点 → 优雅过渡到 backgrounded 状态。
长时间运行 Agent: 考虑服务端编排器(可运行数小时),移动应用只作为查看器/输入机制。
11.5 设备端 vs 云端
| 组件 | 设备端 | 云端 |
|---|---|---|
| 编排 | ✓ | |
| 工具执行(文件、照片、HealthKit) | ✓ | |
| LLM 调用 | ✓(Anthropic API) | |
| 检查点 | ✓(本地) | 可选(iCloud) |
| 长时间运行 Agent | 受 iOS 限制 | 可行(需服务器) |
12. 高级模式
12.1 动态能力发现(Dynamic Capability Discovery)
两个通用工具替代为每个 API 端点写专用工具:
list_available_types() → ["steps", "heart_rate", "sleep", ...]
read_data(type) → 读取任意已发现类型的数据
适用场景: 外部 API 需要完整访问(HealthKit、HomeKit、GraphQL);系统会增加新能力;需要灵活性。
不适用: 需要严格约束的 Agent;稳定简单的 API。
12.2 CRUD 完整性审计
对每一个实体,验证四种操作都可用:
| 操作 | 检查问题 |
|---|---|
| Create | Agent 能否创建新实例? |
| Read | Agent 能否查看已有实例? |
| Update | Agent 能否修改实例? |
| Delete | Agent 能否删除实例? |
常见遗漏: 有 create_note 和 read_notes,但忘了 update_note 和 delete_note。
13. 反模式(Anti-Patterns)
架构级反模式
| 反模式 | 描述 | 问题 |
|---|---|---|
| Agent 作为路由器 | 只识别意图然后调用函数 | 只用了 Agent 能力的一小部分 |
| 先建应用,再加 Agent | 传统构建功能,Agent 只做已有的事 | 错失涌现能力 |
| 请求/响应思维 | 收到输入,做一件事,返回输出 | 错过循环追求目标的能力 |
| 防御性工具设计 | 过度约束(严格枚举、验证层) | 阻止意外创新 |
| 代码编码快乐路径 | 代码处理所有边界情况 | 决策在代码中而非 Agent 判断中 |
最典型反模式——Agent 执行你的工作流而非追求结果
# ❌ 错误:你编写了工作流
def process_request(input):
category = categorize(input) # 你的代码在做决策
priority = score_priority(input) # 你的代码在做决策
store(input, category, priority)
if priority > 3: notify() # 你的代码在做决策
# ✅ 正确:Agent 在循环中追求结果
# 工具:store_item, send_notification
# Prompt:"评估紧急程度1-5,用你的评估结果存储,紧急度 ≥4 时发送通知。"
完整反模式清单
- 工作流形状的工具 — 将判断逻辑捆绑进单个工具
- 孤立 UI 操作 — 用户能做但 Agent 不能做
- 上下文饥饿 — Agent 不知道已有什么数据
- 无理由的门控 — 不必要地限制访问权限
- 人为能力限制 — 没有具体风险却限制能力
- 该动态却用静态映射 — 硬编码而非动态发现
- 启发式完成检测 — 用猜测而非显式信号判断完成(脆弱)
14. 成功标准清单
架构层面
- Agent 能完成用户通过 UI 能完成的所有操作(对等性)
- 工具是原子化原语;领域工具是快捷方式(粒度)
- 新功能通过 prompt 添加(可组合性)
- Agent 能完成未预想的任务(涌现能力)
- 行为变更通过 prompt 编辑,而非代码重构
实现层面
- 系统 prompt 包含可用资源和能力描述
- Agent 和用户在同一数据空间中工作
- Agent 操作立即反映在 UI 中
- 每个实体都有完整的 CRUD 能力
- 外部 API 适当使用动态发现
- Agent 使用显式信号报告完成
产品层面
- 简单请求无需学习曲线即可完成
- 高级用户能将系统推向意想不到的方向
- 通过观察请求来学习用户需求
- 审批要求与风险等级和可逆性匹配
移动端
- 检查点/恢复机制处理应用中断
- iCloud 优先存储,本地兜底
- 后台执行明智使用可用时间
终极测试
向 Agent 描述一个属于你应用领域内、但你没有为之构建特定功能的目标。它能在循环中自行找出实现方法直到成功吗? 如果能,你就构建了一个真正的 Agent-Native 应用。
15. 配套工具:Compound Engineering Plugin
核心理念: "Each unit of engineering work should make subsequent units easier—not harder."
工作流循环
Plan(计划)→ Work(执行)→ Review(评审)→ Compound(沉淀)→ 重复
| 命令 | 用途 |
|---|---|
/workflows:plan |
将功能创意转化为详细实施方案 |
/workflows:work |
使用 worktree 和任务管理执行 |
/workflows:review |
多 Agent 代码审查 |
/workflows:compound |
记录学习成果以备复用 |
工程哲学
80/20 分配: 80% 精力用于计划和评审,20% 用于执行。与传统比例恰好相反,目的是维持高代码质量并减少技术债务。
安装方式
Claude Code:
/plugin marketplace add https://github.com/EveryInc/compound-engineering-plugin
/plugin install compound-engineering
OpenCode:
bunx @every-env/compound-plugin install compound-engineering --to opencode
Codex:
bunx @every-env/compound-plugin install compound-engineering --to codex
总结:Agent-Native 的思维转变
| 传统思维 | Agent-Native 思维 |
|---|---|
| 功能是代码编写的结果 | 功能是 prompt 描述的结果 |
| 工具面向用户设计 | 工具面向 Agent 设计 |
| 猜测用户需要什么 | 观察用户向 Agent 请求什么 |
| 每次更新都需要发版 | 改进通过 prompt 和上下文迭代 |
| 应用有固定功能边界 | 应用的能力边界是动态的、可涌现的 |
| 数据透明可选 | 数据默认透明 |
核心格言: "Features are outcomes you describe, achieved by an agent with tools." 功能是你描述的成果,由拥有工具的 Agent 来实现。
本文档基于 Every.to 原文整理。