1. 懒加载
一句话:用到的时候才创建,而不是提前全部准备好。
懒加载的核心思想是延迟资源的初始化或加载时机,只在真正需要时才触发。这在软件开发中无处不在:
| 场景 | 懒加载表现 |
|---|---|
| 网页开发 | 图片滚到可视区域才加载,不是一口气全拉下来 |
Go sync.Once |
第一次调用时才初始化,后续复用 |
| ORM 关联查询 | 访问 user.Order 时才查订单表,不是查用户时就把订单一起拉出来 |
Python @property |
访问时才计算,不访问就不浪费算力 |
go
// sync.Once 就是典型的懒加载实现
var once sync.Once
var config *Config
func GetConfig() *Config {
once.Do(func() {
config = loadConfig() // 只在第一次调用时执行
})
return config
}
为什么重要: 在 Agent 开发中,懒加载是渐进式披露的基础------Skills 不会一次性全部加载到上下文里,而是用到时才加载,节省 token 和内存。
2. ROI(投资回报率)
Return On Investment,用来评估"做这件事值不值"。
ROI 不是技术概念,而是工程决策的思维方式。写代码前先想一想:
- 这个功能有多少用户会用?值不值得花三天?
- 这个重构能带来多少性能提升?和投入的时间成正比吗?
- 这个 PR 改了一堆格式问题,但引入了新 bug 的风险------划算吗?
参与开源项目时,ROI 思维同样适用:选择一个有 good-first-issue 标签的 PR 来做,比自己从零造轮子的 ROI 高得多。
3. 灰度发布
新版本先给一小部分用户用,没问题再全量推送。
灰度发布(也叫金丝雀发布 )的核心是控制爆炸半径:
-
降低风险: 如果有 bug,只影响 5% 的用户,而不是全部
-
压测验证: 先在小流量下观察并发表现,确认没问题再放量
-
A/B 测试: 对比新旧版本的用户行为数据,用数据说话
全量发布:100% 用户 → 新版本(出问题 = 全部受影响)
灰度发布:5% 用户 → 新版本 → 20% → 50% → 100%(每一步都有回滚机会)
4. Code Review(代码审查)
项目维护者对想要并入主分支的代码进行格式、规范和合理性检验。
Code Review 是开源协作中最核心的环节。你提交一个 PR 后,维护者会从以下几个维度审查:
- 正确性: 代码逻辑是否正确,边界情况是否处理
- 规范性: 命名、格式、注释是否符合项目风格
- 可维护性: 代码是否清晰易懂,是否引入了不必要的复杂度
- 测试覆盖: 是否有对应的测试,测试是否充分
对新手的建议: Code Review 的反馈不是对你个人的否定,而是对代码质量的把关。保持开放心态,逐条回应,很多维护者会耐心解释原因。
5. 动态检测(Dynamic Analysis)
代码运行起来之后才进行的检测:单元测试、内存泄漏检测、性能分析等。
和静态检测(不运行代码就能发现的问题,如 lint)相对,动态检测关注的是运行时行为:
bash
# Go 中常见的动态检测手段
go test ./... # 单元测试
go test -race ./... # 竞态条件检测
go test -bench=. -memprofile=mem.out # 内存性能分析
go test -cpuprofile=cpu.out ./... # CPU 性能分析
在 Agent 开发中,动态检测尤为重要------Agent 的行为是非确定性的,同一个问题可能走不同的工具调用路径,只有跑起来才能验证正确性。
6. CI/CD(持续集成 / 持续部署)
CI/CD 不是"开发完之后做的事",而是贯穿整个开发过程的自动化体系。
CI --- 持续集成(Continuous Integration)
每次 push 代码后,自动运行检查:
git push → CI 自动触发:
├─ 1. 安装依赖
├─ 2. go build(编译)
├─ 3. go test(跑测试)
├─ 4. golangci-lint(代码检查)
└─ 5. 全部通过? → 否:通知你修复
→ 是:继续 CD
以 GitHub Actions 为例,配置文件 .github/workflows/ci.yml:
yaml
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.22'
- run: go build ./...
- run: go test ./...
- run: golangci-lint run
CD --- 持续交付 / 持续部署
- 持续交付(Continuous Delivery): 代码通过 CI 后,自动准备好发布包,但需要人工点按钮才真正部署
- 持续部署(Continuous Deployment): 代码通过 CI 后,自动部署到生产环境,无需人工干预
和开源贡献的关系: 大多数开源项目都有 CI。你提交 PR 后会自动跑测试和 lint,如果 CI 红了,维护者通常不会合并你的代码。本地先跑 go test ./... 再提交,能省去很多来回。
7. 渐进式披露(Progressive Disclosure)
AI Agent 中 Skills 的加载策略:只加载简介,用到时才加载完整内容。
这是 Agent 开发中非常重要的优化策略。一个 Skill 由两部分组成:
-
Skill.md 简介部分 --- Agent 加载时只读这部分,知道"这个 Skill 能做什么"
-
详细使用方式 + 脚本 --- 等 Agent 真正决定使用这个 Skill 时,才加载到上下文中
Agent 启动时:
加载所有 Skill 的简介 → 知道有哪些能力可用用户提问时:
Agent 判断需要某个 Skill → 加载该 Skill 的详细内容
→ 调用对应的脚本执行
为什么这样设计: LLM 的上下文窗口有限,如果把所有 Skill 的完整内容都塞进去,token 会迅速耗尽。渐进式披露让 Agent 在"知道有什么"和"知道怎么用"之间找到平衡。
8. 循环依赖
包 A 依赖包 B,包 B 又依赖包 A --- 编译器直接报错,一个都跑不起来。
go
// package_a/a.go
package a
import "myproject/b"
func CallB() { b.DoSomething() }
// package_b/b.go
package b
import "myproject/a"
func DoSomething() { a.CallA() } // 编译器报错:循环导入
循环依赖是 Go 项目中最常见的编译错误之一。解决思路:
- 提取公共接口: 把 A 和 B 都依赖的部分抽到第三个包 C
- 接口反转: 让 B 定义接口,A 实现接口,依赖方向变为 A → 接口 ← B
- 合并包: 如果两个包耦合太深,考虑合并成一个包
go
// 用接口打破循环依赖
// package common 定义接口
package common
type Service interface {
DoSomething()
}
// package_a 实现接口
package a
import "myproject/common"
type Impl struct{}
func (i *Impl) DoSomething() { /* ... */ }
// package_b 依赖接口,不依赖 a
package b
import "myproject/common"
func Process(s common.Service) {
s.DoSomething()
}
9. ReAct(Reasoning + Acting)
让 LLM 在循环中交替进行推理和行动,而不是一次性输出答案。
核心循环
Thought(推理)→ Action(行动)→ Observation(观察)→ Thought → Action → Observation → ...
一个具体例子
假设用户问:"巴黎的首都是哪里?它的人口有多少?"
没有 ReAct 的普通 LLM: 直接回答,可能凭记忆编造数字。
用 ReAct 的 Agent:
Thought: 用户问了两件事,我需要先确认巴黎的首都,然后查人口数据。
Action: search("Paris capital of France")
Observation: Paris is the capital and most populous city of France.
Thought: 确认了巴黎是法国首都,现在查人口。
Action: search("Paris population 2024")
Observation: Paris has a population of approximately 2.1 million (city proper).
Thought: 我现在有了两个问题的答案。
Finish: 巴黎是法国的首都。城市人口约 210 万,大都会区约 1220 万。
为什么比"直接回答"好
| 问题 | 直接回答 | ReAct |
|---|---|---|
| 知识过时 | 编造答案 | 调用搜索获取最新信息 |
| 多步推理 | 一步到位容易出错 | 每步只做一件事,降低出错率 |
| 结果可验证 | 无法追溯 | 每步的 Thought 和 Observation 都留痕,可审计 |
| 复杂任务 | 力不从心 | 拆解成小步骤逐步完成 |
如果你了解过Agent就会知道这些常见的Tool
ReAct 是架构,Function Call / MCP / Skills 是实现手段。
- ReAct 定义了 Agent 的思维循环(Thought → Action → Observation)
- Function Call 是 Action 的具体实现方式(LLM 输出结构化的函数调用)
- MCP 让 Agent 能调用的工具来源标准化(任何 MCP Server 都可以作为 Action 的工具)
- Skills 是 Agent 的"能力模块",在 ReAct 循环中被按需加载和调用
用 Go 的思维方式类比:如果 ReAct 是一个接口定义,那么 Think、Act、ShouldFinish 就是这个接口的不同实现:
go
type Agent interface {
Think(context string) Thought
Act(action Action) Observation
ShouldFinish(obs Observation) bool
}
一句话总结: ReAct 不是"三思而后行"这种生活哲学,而是 Agent 的标准工作循环------想一步、做一步、看结果、再想下一步。你现在用的 Claude Code,底层就在跑这个循环。
总结
这 9 个概念可以分成三类:
软件工程基础: 懒加载、循环依赖、Code Review、动态检测、CI/CD
工程思维: ROI、灰度发布
AI Agent 专属: 渐进式披露、ReAct