标签 反模式 下的文章

像构建 Claude Code 一样构建应用:揭秘 Agent-native 架构的 5 大核心原则

本文永久链接 – https://tonybai.com/2026/01/13/agent-native-architecture

大家好,我是Tony Bai。

软件智能体(Software Agents)现在已经能够可靠地工作了。Claude Code 证明了,只要赋予一个大语言模型(LLM)访问 Bash 和文件系统的权限,并让它在一个循环中运行直到达成目标,它就能自主完成复杂的多步骤任务。

而这里有一个令人惊讶的发现:一个优秀的编程 Agent,本质上就是一个优秀的通用 Agent。

支撑 Claude Code 重构代码库的同一套架构,同样可以用来整理你的文件、管理阅读列表,或自动化你的工作流。通过 Claude Code SDK、Google adk-go,这种能力变得触手可及。你可以构建一种全新的应用:其功能不再是你写死的代码,而是你描述的结果(Outcome)——由一个装备了工具的 Agent,在一个循环中自主实现。

这开启了一个全新的领域:软件开始像 Claude Code 一样工作,但应用场景远超编程。这就是 AnthropicDan Shipper 联合发布的最新架构理念 —— Agent-native Architecture(智能体原生架构)

本文将深入拆解这份文档中的核心原则与架构模式,带你领略这一前沿范式。


核心原则

要构建 Agent-native 应用,我们需要遵循以下 5 大核心原则:

平权 (Parity)

原则: 用户通过 UI 能做的任何事,Agent 必须能通过工具(Tools)完成。

这是基石。如果没有平权,其他一切都无从谈起。你必须确保 Agent 拥有一套完整的工具集,能够覆盖 UI 的所有能力。

随机挑选一个 UI 动作。Agent 能完成吗?这是验证这一原则的测试标准!

颗粒度 (Granularity)

原则: 工具应该是原子级(Atomic)的原语。功能特性(Features)是由 Agent 在循环中通过组合工具达成的结果。

工具是基本能力。功能特性是 Prompt 描述的一个结果,由 Agent 动态组合工具来实现。

要改变软件的行为,你是通过修改 Prompt,还是重构代码?如果是前者,说明颗粒度对了。

可组合性 (Composability)

原则: 拥有了原子工具和平权,你可以仅通过编写新 Prompt 来创造新功能。

比如:想要一个“每周回顾”功能?这只是一个 Prompt:

“检查本周修改过的文件。总结关键变更。基于未完成项和截止日期,建议下周的三个优先级事项。”

Agent 会自动调用 list_files、read_file 并结合自身判断力。你描述结果,Agent 负责循环执行。

涌现能力 (Emergent Capability)

原则: Agent 可以完成你从未显式设计过的任务。

这是 Agent-native 的飞轮效应:

  1. 你构建原子工具和平权能力。
  2. 用户提出了你未预料到的需求。
  3. Agent 组合工具完成了任务(或失败,揭示了工具缺口)。
  4. 你观察模式,添加领域工具或专用 Prompt 来优化。
  5. 重复上述过程。

然后,通过观察你的应用能处理领域内的开放式请求,来验证Agent是否具备这种涌现能力。

随时间进化 (Improvement over time)

原则: Agent-native 应用通过积累上下文和 Prompt 优化而变得更好,而无需重新发版。

与传统软件不同,Agent-native的应用可以通过以下方式自我进化:

  • 积累上下文: 状态通过上下文文件(Context Files)跨会话持久化。
  • 开发者级优化: 你推送更新的 Prompt,所有用户受益。
  • 用户级定制: 用户修改 Prompt 以适应自己的工作流。

实践中的原则

有了原则,我们要如何落地?以下是具体的工程实践指南。

解决“平权”问题

想象一个笔记 App。用户说:“总结我关于这次会议的笔记,并标记为紧急。”

如果 UI 能做,但 Agent 做不到,Agent 就会卡住。

修正方案: 建立一张能力映射表(Capability Map)。

每当添加一个 UI 功能时,都要问:Agent 能达成这个结果吗?如果不能,添加相应的原语工具。将这作为一条工程纪律遵守和贯彻!

解决“颗粒度”问题:原子化 vs 捆绑逻辑

要知道:Agent 追求的是通过判断力(Judgment)达成结果,而不是执行死板的序列。

  • ❌ 错误做法(捆绑逻辑):
    • 工具:classify_and_organize_files(files)
    • 问题:决策逻辑是你写死的代码(充斥着if、else等)。要改变行为,必须重构代码。灵活性差。
  • ✅ 正确做法(原子化):
    • 工具:read_file, write_file, move_file, bash
    • Prompt:“整理下载文件夹…”
    • 优势:Agent 负责决策并组合工具完成。要改变行为,只需修改 Prompt。Agent 拥有了更强地灵活性。

演进路线:从原语到领域工具

一开始,只提供纯粹的原语(Bash, 文件操作)。这能证明架构可行,并揭示 Agent 真正需要什么。

当模式涌现后,有意识地添加领域工具(Domain Tools)

  1. 词汇表 (Vocabulary): create_note 工具教会了 Agent 你的系统中“笔记”是什么。
  2. 护栏 (Guardrails): 某些操作需要验证,不应完全交给 Agent 判断。
  3. 效率 (Efficiency): 将常用操作打包,提升速度和降低成本。

领域工具的规则: 它们应该代表用户视角的一个概念性动作。它们可以包含机械验证,但不要包含“做什么”或“是否做”的判断——这属于 Prompt。同时,默认保持原语可用,不要为了这种封装而关闭底层权限,除非有安全理由。

升级成为代码 (Graduating to Code)

有些操作因为性能或可靠性原因,需要从“Agent 编排”升级成为“优化代码”。

  1. 阶段 1: Agent 在循环中使用原语(灵活,验证概念)。
  2. 阶段 2: 添加领域工具(更快,但仍由 Agent 编排)。
  3. 阶段 3: 针对热点路径,用优化代码实现(极快,确定性)。

注意: 即使升级为代码,操作时,Agent 仍应保留直接触发该代码的能力,并保留回退到原语的能力以处理边缘情况。


架构模式:文件即通用接口

为什么 Claude Code 如此依赖文件系统?因为 Bash + Filesystem 是最经受考验的 Agent 接口。

  • 已知的: Agent 天生懂 cat, grep, mv。
  • 可检查的: 用户能直接看到、编辑、移动文件。没有黑盒。
  • 可移植的: 导出和备份极其简单。
  • 自文档化: /projects/acme/notes/ 这种路径本身就携带了语义,比 SELECT * FROM notes WHERE id=123 更容易让 Agent 理解。

实体作用域目录 (Entity-scoped directories)

这是一种推荐的文件结构模式:

{entity_type}/{entity_id}/
├── primary content  (主内容)
├── metadata         (元数据)
└── related materials (相关素材)

例如:Research/books/{bookId}/ 包含全文、笔记、来源和 Agent 日志。

context.md 模式

Agent 需要知道它在处理什么。系统 Prompt 应该注入以下内容:

  • 可用资源:
    “`markdown
    ## Available Data

    • 12 notes in /notes
    • 3 active projects in /projects
    • Preferences at /preferences.md
      “`
  • 能力 (Capabilities): 描述它能做什么(创建、搜索、整理)。
  • 最近活动 (Recent Activity): 用户刚刚做了什么,Agent 上一步做了什么。

文件 vs 数据库

  • 用文件: 用户需要读写的内容、配置、Agent 生成的内容、大文本。
  • 用数据库: 高频结构化数据、复杂查询、临时状态(Session/Cache)、强关系数据。

冲突处理: 建议采用 Shared Workspace 模式。Agent 和用户在同一个数据空间工作,不搞沙盒。这需要处理并发写入(如 Last-write-wins 或文件锁)。


成功标准与反模式

在构建 Agent-native 应用时,我们经常会不自觉地滑回传统的软件工程思维。以下是具体的反模式对照表

Agent 作为路由器 (Agent as Router)

  • 反模式: Agent 的唯一工作就是分析用户意图,然后调用一个写死的 run_workflow() 函数。
  • 问题: 你把 Agent 的智能降级成了 if/else。如果用户需求稍微偏离你的预设(例如:“这次运行工作流但跳过最后一步”),系统就会崩溃。
  • 正确姿势: Agent 应该负责执行,而不仅仅是路由。它应该拥有组成该工作流的原子工具,并根据情况决定调用顺序。

“请求/响应”思维 (Request/Response Thinking)

  • 反模式: 认为 Agent 就像一个 API:给一个输入,它吐出一个输出。
  • 问题: 这完全丢失了 Master Loop(Agent主循环) 的价值。真正的 Agent 是追求结果(Outcome)的。它可能会尝试、失败、分析错误、再尝试,直到结果达成。
  • 正确姿势: 给 Agent 一个目标,让它在一个循环中运行,并在过程中处理意外情况。

防御性工具设计 (Defensive Tool Design)

  • 反模式: 你因为习惯了防御性编程,所以把工具的输入限制得很死(例如:严格的 Enums,层层校验)。
  • 问题: 这虽然安全,但也扼杀了涌现能力。Agent 无法用你没想到的方式使用工具。
  • 正确姿势: 默认保持工具开放。除非有明确的安全风险(如删库),否则允许 Agent 传入任何它认为合理的参数。

工作流形状的工具 (Workflow-shaped Tools)

  • 反模式: 创建一个名为 analyze_and_organize_files() 的工具。
  • 问题: 你把“判断逻辑”捆绑进了工具里。如果要改变组织方式,必须重写代码。
  • 正确姿势: 拆解为 read、analyze、move。让 Agent 在运行时决定如何组织。

“幸福路径”思维 (Happy Path in Code)

  • 反模式: 在代码里写死了所有边缘情况的处理逻辑(if error_code == 500: retry)。
  • 问题: 如果代码处理了所有边缘情况,Agent 就只是一个无脑调用者。
  • 正确姿势: Agent-native 架构允许 Agent 用判断力处理边缘情况。如果遇到 500 错误,Agent 可以决定是重试、还是检查网络、还是通知用户。

成功的终极测试 (The Ultimate Test)

描述一个你的应用领域内的结果,但针对一个你从未专门开发过的功能。

看看Agent 能否通过在一个循环中运行,自主搞定它?如果能,你构建的就是一个真正的 Agent-native 应用。

小结:把判断力还给 Agent

当我们谈论 Agent-native 时,我们到底在谈论什么?

其实,这是一场关于“控制权移交”的变革。在传统的软件工程中,程序员试图用代码穷尽所有的逻辑分支,我们追求的是确定性。但在 Agent-native 的世界里,我们学会了放手

我们不再编写死板的“步骤”,而是提供灵活的“原语”。我们把“如何做(How)”的判断力,从代码中剥离出来,归还给了 Agent。

  • 平权 让 Agent 拥有了和人一样的行动力。
  • 颗粒度 赋予了 Agent 自由组合的能力。
  • 涌现 让我们看到了软件在设计之外的可能性。

这并不意味着代码不重要了,而是代码的角色变了——从“指挥官”变成了“军火库”。你负责提供精良的武器(工具),而 Agent 负责在前线根据战况(上下文)灵活作战。

这,才是 AI 时代软件架构的终极形态。

资料链接:https://every.to/guides/agent-native


你的 Agent 构想

Agent-native 架构为我们打开了一扇通往无限可能的大门。如果让你用这种架构重新设计你最熟悉的一个应用(比如待办清单、邮件客户端),你会赋予它哪些以前做不到的“涌现能力”?

欢迎在评论区分享你的脑洞! 让我们一起畅想软件的未来形态。

如果这篇文章颠覆了你对软件架构的认知,别忘了点个【赞】和【在看】,并转发给你的产品经理和架构师朋友!


体验最成功的Agent-native应用

Agent-native 不仅仅是一种架构,更是一种全新的开发体验。而目前市面上最成熟、最顶级的 Agent-native 实践,就是 Claude Code 本身。

想要真正理解什么是“原子化工具”?什么是“循环中的判断力”?最好的方式不是空谈理论,而是亲自去体验一个优秀的 Agent 是如何工作的。

欢迎关注我的极客时间专栏《AI 原生开发工作流实战》。我们将深入 Claude Code 的实战场景,带你亲眼见证它如何利用这些架构原则,把一个模糊的需求变成完美运行的代码。

扫描下方二维码,开启你的AI原生开发之旅。


你的Go技能,是否也卡在了“熟练”到“精通”的瓶颈期?

  • 想写出更地道、更健壮的Go代码,却总在细节上踩坑?
  • 渴望提升软件设计能力,驾驭复杂Go项目却缺乏章法?
  • 想打造生产级的Go服务,却在工程化实践中屡屡受挫?

继《Go语言第一课》后,我的《Go语言进阶课》终于在极客时间与大家见面了!

我的全新极客时间专栏 《Tony Bai·Go语言进阶课》就是为这样的你量身打造!30+讲硬核内容,带你夯实语法认知,提升设计思维,锻造工程实践能力,更有实战项目串讲。

目标只有一个:助你完成从“Go熟练工”到“Go专家”的蜕变! 现在就加入,让你的Go技能再上一个新台阶!


商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。

Go 语言的“反模式”清单:来自资深 Gopher 血泪教训的 10 条“不要做”

本文永久链接 – https://tonybai.com/2025/12/15/go-language-anti-patterns-10-donts

大家好,我是Tony Bai。

“有哪些‘不要做’的教训,是你花了好几年才学会的?”

近日,在 r/golang 社区,这个简单的问题,引爆了一场关于 Go 语言“反模式”与“最佳实践”的集体反思。帖子下数百条评论,汇集了无数 Gopher 在真实项目中用“血与泪”换来的宝贵经验。这些教训,往往不是关于某个高深的算法,而是关于那些看似“理所当然”,却在不经意间为代码埋下地雷的日常习惯。

这篇文章,正是对这场集体智慧的一次系统性梳理。我们从中提炼出 10 条最核心的“不要做”法则,它们如同一份“避坑指南”,能帮助你绕开那些最常见的陷阱,更快地从一名“会写 Go 的程序员”,成长为一名“懂 Go 的工程师”。

不要过度封装包

Don’t overpackage things

初学者往往有一种冲动,想把代码组织成“语义化”的、层层嵌套的包结构。internal/models, internal/services, internal/repositories…… 这种源自其他语言(如 Java)的模式,在 Go 的世界里,往往是一种过早的、不必要的复杂性

社区忠告:从一个 main.go 文件开始。努力思考,是否真的有必要将代码拆分到多个文件/包中。Go 的包,其主要目的是封装和依赖管理,而不是单纯的文件夹分类。在小型或中型项目中,一个清晰的、扁平的包结构,远比一个复杂的“企业级”目录树更易于维护。

不要滥用 channel 和 goroutine

Don’t just add in channels

并发是 Go 的“名片”,这使得许多开发者(尤其是新手)有一种“锤子心态”——看到任何问题,都想用 goroutine 和 channel 来解决。然而,不必要的并发,是复杂性和 bug 的温床。

社区忠告

  • 先问“是否需要”:你真的需要并发吗?如果不需要在线程间传递消息,你可能根本不需要 channel。一个简单的 sync.WaitGroup 或 sync.Mutex,在很多场景下都比 channel 更简单、更直接。
  • 并发不是免费的:Go 让创建 goroutine 变得异常简单,但这并不意味着它是零成本的。过多的 goroutine 会增加调度器的负担,而 channel 的滥用则会使数据流变得难以追踪和调试。

不要盲目追求 DRY

Don’t be zealous about DRY

DRY 是编程的基本原则,但在 Go 的哲学中,它有一个更重要的“上级”——清晰性。为了消除几行重复代码,而引入一个复杂的接口或一个晦涩的辅助函数,往往得不偿失。

社区忠告:“一点点复制,胜过一点点依赖 (a little copy-paste is better than a little dependency)。” 当你发现自己在为了 DRY 而绞尽脑汁时,请停下来问问自己:这份重复,是否真的带来了维护上的痛苦?如果不是,那么接受它,可能是一个更明智的选择。

不要在同一个 PR 中既重构又添加新功能

Don’t refactor and add features in the same PR

在添加一个新功能时,顺手“优化”一下周围的代码,这看起来很高效。但实际上,这会让 Code Review 变得异常痛苦。Reviewer 无法清晰地分辨,哪些改动是为新功能服务的,哪些是纯粹的重构。这不仅增加了审查的难度,也提高了引入新 Bug 的风险。

社区忠告:遵循“童子军军规”——“让营地比你来时更干净”——是好的。但请将它分解为两个独立的、目标明确的 PR:一个只做重构,另一个(基于重构后的代码)只添加新功能。

不要跳过写测试,“就这一次”

Don’t skip writing tests “just this once”

这是所有开发者都曾屈服过的诱惑。“这个改动太小了”、“我百分之百确定它是对的”、“项目赶时间”…… 每一次“就这一次”的妥协,都在为未来的“技术雪崩”添砖加瓦。

社区忠告:将测试视为代码不可分割的一部分。在 Go 中,编写测试是如此简单和自然,以至于没有任何借口可以跳过它。你今天节省下来的 10 分钟,可能会在未来,让你或你的同事,花费数天时间去调试一个本可避免的生产问题。

不要害怕使用 sync.Cond

channel 非常强大,但它并非解决所有并发同步问题的“银弹”。社区中有一种“反 sync”的情绪,认为所有同步都应该用 channel 来完成。

社区忠告:sync.Cond 是一个被低估了的、极其强大的并发原语。当你需要基于某个特定条件来唤醒一个或多个等待的 goroutine 时(例如,一个任务队列的消费者在队列为空时等待),sync.Cond 往往比用 channel 实现的复杂信令机制,要更简单、更高效。不要因为不熟悉,就回避它。

不要返回接口

Returning interfaces. Don’t do it.

在函数签名中返回一个接口,看似遵循了“依赖倒置”的高级原则,甚至觉得这样更“灵活”。但实际上,这往往是一种过早的、有害的抽象。它剥夺了用户访问底层具体类型特有功能的能力,并且如果未来需要添加新方法,接口的变更会极其痛苦。

社区忠告:遵循 Go 的经典谚语:“接收接口,返回结构体 (Accept interfaces, return structs)。

  • 接收接口:让你的函数接收一个只包含其所需最小方法集的接口作为参数。这使得你的函数更容易被测试和复用(你可以传入任何满足该接口的实现,包括 Mock 对象)。
  • 返回结构体:让你的函数返回一个具体的类型(通常是指针)。这给了调用者最大的灵活性。

经典范例

看看标准库中的 os.Open,它返回的是 *os.File(具体结构体),而不是 io.Reader(接口)。
* 为什么这样做? 因为 *os.File 不仅能读(Read),还能关闭(Close)、获取状态(Stat)、甚至改变权限(Chmod)。
* 灵活性:如果它返回的是接口,用户就无法使用 Chmod 等特有功能了。而返回结构体,用户既可以使用其全部功能,也可以在需要时,轻松地将其赋值给 io.Reader 接口来使用。这就是“返回结构体”带来的自由。

(注:只有当返回的类型是包内私有的、不希望外部直接访问的实现细节时,返回接口才是有意义的,例如 context.WithCancel 返回的是 Context 接口。)

不要过度依赖依赖

Don’t add dependencies without vetting

为了解决一个小问题,而引入一个庞大的、闪亮的第三方库。这在 Node.js 生态中很常见,但在 Go 社区,这通常被视为一种“危险信号”。

社区忠告

  • 先求诸标准库:在引入任何依赖之前,先问问自己:这个问题,标准库真的解决不了吗?
  • 审慎评估:如果必须引入依赖,请仔细评估它:它的依赖树有多深?社区是否活跃?维护者是否可靠?一个简单的依赖,可能会为你整个项目,带来潜在的供应链安全风险和维护噩梦。

不要盲从

Don’t do [or not do] something simply because an authoritative voice recommended it

盲目地遵循某个“大神”、某篇“爆款”博客文章、或者某个“权威”推荐的模式,而没有结合自己的具体场景进行批判性思考。

社区忠告:上下文决定一切。YAGNI (You Aren’t Gonna Need It) 是一个好原则,但有时你确实需要提前设计。微服务很好,但有时单体就是最佳选择。没有银弹。最好的实践,是那些在你的团队、你的项目中,被证明行之有效的实践。

不要忘记,代码是给人读的

忘记了代码的最终读者是人类,而不是编译器。编写只有自己能看懂的“聪明”代码,或者忽略文档和注释的重要性。

社区忠告

  • 编写能让你的未来“自已”不会痛骂你的代码。
  • 好的设计不是增加,而是保持本质的简单。代码即是负债 (Code is liability)。
  • 不要忽视清晰文档的重要性。

小结:在“坑”里成长

这份清单,远非全部。社区的讨论中还充满了诸如“不要用 singleton 来做 mock”、“不要滥用 init 函数”、“不要在疲劳时 Review 代码”等无数宝贵的经验。

它们共同指向了一个核心思想:成为一名优秀的 Go 工程师,其过程不仅仅是学习语言的特性,更是一个不断反思、不断“踩坑”、并从“坑”中总结出属于自己“不要做”清单的修炼过程。希望这份来自社区的集体智慧,能让你在这条路上,走得更稳、也更远。

资料链接:https://www.reddit.com/r/golang/comments/1pib68y/whats_a_dont_do_this_lesson_that_took_you_years/


还在为“复制粘贴喂AI”而烦恼?我的新专栏 AI原生开发工作流实战 将带你:

  • 告别低效,重塑开发范式
  • 驾驭AI Agent(Claude Code),实现工作流自动化
  • 从“AI使用者”进化为规范驱动开发的“工作流指挥家”

扫描下方二维码,开启你的AI原生开发之旅。


你的Go技能,是否也卡在了“熟练”到“精通”的瓶颈期?

  • 想写出更地道、更健壮的Go代码,却总在细节上踩坑?
  • 渴望提升软件设计能力,驾驭复杂Go项目却缺乏章法?
  • 想打造生产级的Go服务,却在工程化实践中屡屡受挫?

继《Go语言第一课》后,我的《Go语言进阶课》终于在极客时间与大家见面了!

我的全新极客时间专栏 《Tony Bai·Go语言进阶课》就是为这样的你量身打造!30+讲硬核内容,带你夯实语法认知,提升设计思维,锻造工程实践能力,更有实战项目串讲。

目标只有一个:助你完成从“Go熟练工”到“Go专家”的蜕变! 现在就加入,让你的Go技能再上一个新台阶!


商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。

如发现本站页面被黑,比如:挂载广告、挖矿等恶意代码,请朋友们及时联系我。十分感谢! Go语言第一课 Go语言进阶课 AI原生开发工作流实战 从 0 开始构建 Agent Harness Go语言精进之路1 Go语言精进之路2 Go语言第一课 Go语言编程指南
商务合作请联系bigwhite.cn AT aliyun.com
这里是 Tony Bai的个人Blog,欢迎访问、订阅和留言! 订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠 ,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您希望通过微信捐赠,请用微信客户端扫描下方赞赏码:

如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:

以太币:

如果您喜欢通过微信浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:
本站Powered by Digital Ocean VPS。
选择Digital Ocean VPS主机,即可获得10美元现金充值,可 免费使用两个月哟! 著名主机提供商Linode 10$优惠码:linode10,在 这里注册即可免费获 得。阿里云推荐码: 1WFZ0V立享9折!


View Tony Bai's profile on LinkedIn
DigitalOcean Referral Badge

文章

评论

  • 正在加载...

分类

标签

归档



View My Stats