OpenCode 学习笔记 01:核心架构 - Tool + Provider + Agent Loop
Phase 1 完整实现:理解 AI Agent 的三大支柱
一个 AI Agent 的核心只有三件事:定义工具、调用 LLM、循环执行。
本篇目标
理解 AI Agent 的核心架构,并在 tiny-agent 中实现:
- Config:依赖注入风格的配置管理,支持 proxy
- Tool:定义 AI 可以使用的工具
- Provider:统一调用多个 LLM
- Agent Loop:ReAct 循环执行
一、AI Agent 的本质
1.1 从 ChatBot 到 Agent
ChatBot 只能聊天:
用户: 帮我看看 package.json
AI: 我无法访问你的文件系统...
Agent 可以行动:
用户: 帮我看看 package.json
AI: [调用 read 工具] → 读取文件 → 这是一个 TypeScript 项目...
关键区别:Agent 有工具,能做事。
1.2 ReAct 模式
AI Agent 的思考方式叫 ReAct(Reasoning + Acting):
┌─────────────────────────────────────────────────────────┐
│ ReAct 循环 │
│ │
│ 用户输入 → LLM 思考 → 需要工具? │
│ │ │
│ ┌─────────────┴─────────────┐ │
│ ↓ 是 ↓ 否 │
│ 执行工具 输出回答 │
│ ↓ │
│ 返回结果给 LLM │
│ ↓ │
│ 继续思考... ─────────────────────→ │
│ │
└─────────────────────────────────────────────────────────┘
核心思想:边想边做,做完再想,循环往复。
1.3 四个核心模块
| 模块 | 职责 | 类比 |
|---|---|---|
| Config | 管理配置(API Key、proxy...) | 大脑的设置 |
| Tool | 定义 AI 可以使用的工具 | AI 的手脚 |
| Provider | 调用 LLM(GPT/Claude...) | AI 的大脑 |
| Agent Loop | 循环执行直到完成 | AI 的思考方式 |
二、配置系统(依赖注入)
2.1 为什么需要配置系统?
参考 planning-api 的 Settings 设计,我们需要:
- 集中管理:API Key、Base URL 统一配置
- 支持 Proxy:公司内网可能需要代理访问 LLM API
- 方便切换:开发用便宜模型,生产用强模型
2.2 设计要点
环境变量驱动:
| 环境变量 | 说明 | 示例 |
|---|---|---|
OPENAI_API_KEY | OpenAI API 密钥 | sk-xxx 或 proxy key |
OPENAI_BASE_URL | OpenAI 代理地址 | https://proxy.example.com/v1 |
ANTHROPIC_API_KEY | Anthropic API 密钥 | |
ANTHROPIC_BASE_URL | Anthropic 代理地址 | |
DEFAULT_PROVIDER | 默认使用的 Provider | openai / anthropic / cerebras |
Proxy 模式:如果公司有统一的 AI 网关,只需配置 BASE_URL 指向网关即可。
2.3 OpenCode 对应
OpenCode 的配置在 config/config.ts,支持:
- 多 Provider 配置
- 模型别名映射
- 自定义 headers
三、Tool 系统
3.1 Tool 是什么?
Tool 是 AI 可以调用的能力。每个 Tool 包含:
| 属性 | 说明 | 示例 |
|---|---|---|
name | 工具名称 | read |
description | 给 LLM 看的说明 | "读取文件内容" |
parameters | 参数定义(JSON Schema) | { path: string, limit?: number } |
execute | 实际执行的函数 | 读取文件并返回内容 |
3.2 为什么用 Zod?
Zod 是 TypeScript 的 schema 验证库,作用:
- 定义参数类型:LLM 返回的参数会被验证
- 自动生成描述:
.describe()告诉 LLM 参数含义 - 类型推导:TypeScript 自动推导参数类型
OpenCode 的所有工具参数都用 Zod 定义。
3.3 Tool 工作流程
LLM 决定调用工具
↓
生成 JSON 参数 { "path": "package.json" }
↓
Zod 验证参数 ✓
↓
执行 execute 函数
↓
返回结果给 LLM
3.4 OpenCode 的工具列表
OpenCode 有 20+ 个工具,主要分类:
| 类别 | 工具 | 功能 |
|---|---|---|
| 文件操作 | read, write, edit, glob | 读写搜索文件 |
| 代码理解 | grep, codesearch | 搜索代码 |
| 命令执行 | bash | 执行 shell 命令 |
| 网络 | websearch, webfetch | 搜索和获取网页 |
| 交互 | question | 向用户提问 |
| 任务 | task, todo | 子任务管理 |
我们从最简单的 read 开始实现。
四、Provider 抽象
4.1 为什么需要 Provider?
不同 LLM 有不同的 API:
| Provider | SDK | 调用方式 |
|---|---|---|
| OpenAI | openai | chat.completions.create() |
| Anthropic | @anthropic-ai/sdk | messages.create() |
| 其他 | 各种 SDK | 各种方式 |
Provider 层统一这些差异,让上层代码不关心具体用哪个模型。
4.2 Vercel AI SDK
OpenCode 使用 Vercel AI SDK(ai 库)来统一 LLM 调用:
- 统一接口:
generateText()/streamText()适用所有模型 - 工具支持:内置 Tool 调用支持
- 流式输出:支持流式响应
我们也使用这个库。
4.3 模型选择
tiny-agent 支持的模型:
| Provider | 模型 | 特点 |
|---|---|---|
| OpenAI | gpt-4o, gpt-4o-mini | 全能,速度快 |
| Anthropic | claude-3-5-sonnet | 代码能力强 |
| Cerebras | llama-3.3-70b | 开源,便宜 |
通过 provider:model 格式指定,如 openai:gpt-4o-mini。
五、Agent Loop
5.1 核心逻辑
Agent Loop 的本质是一个循环:
- 发送消息给 LLM
- LLM 返回文本或工具调用
- 如果是工具调用,执行工具,把结果加入消息
- 重复 1-3,直到 LLM 不再调用工具
5.2 maxSteps 防护
为防止无限循环,设置最大步骤数:
- 默认值:10 步
- 超过后:强制停止,返回当前结果
OpenCode 的 Agent 也有类似机制。
5.3 一次完整的执行
用户: "读取 package.json 并告诉我项目名称"
Step 1:
LLM 思考 → 需要 read 工具
调用 read({ path: "package.json" })
返回文件内容
Step 2:
LLM 看到文件内容
思考 → 已经有了需要的信息
输出 "这个项目叫 tiny-agent"
完成(2 步)
六、实现总结
6.1 目录结构
tiny-agent/src/
├── config/
│ └── index.ts # 配置管理(依赖注入)
├── tool/
│ ├── tool.ts # Tool 抽象定义
│ └── read.ts # read 工具
├── provider/
│ └── index.ts # Provider 抽象(支持 proxy)
├── agent/
│ └── loop.ts # Agent Loop
└── index.ts # 入口
6.2 核心函数
| 模块 | 核心函数 | 功能 |
|---|---|---|
| Config | Settings.get() | 获取配置 |
| Tool | Tool.define() | 定义工具 |
| Provider | Provider.getModel() | 获取模型实例 |
| Agent | AgentLoop.run() | 执行 Agent 循环 |
6.3 运行
cd external/tiny-agent
# 配置(直连或 proxy)
export OPENAI_API_KEY=your-key
export OPENAI_BASE_URL=https://your-proxy.com/v1 # 可选
# 运行
bun dev
七、与 OpenCode 对比
| 方面 | tiny-agent | OpenCode |
|---|---|---|
| Config | 简单环境变量 | 完整配置文件 + CLI |
| Tool | 简化版 | 含权限、元数据、截断 |
| Provider | AI SDK 直接使用 | 自己封装转换层 |
| Agent Loop | 单循环 | 支持多 Agent 切换 |
OpenCode 额外功能(后续篇章讲解):
- 工具权限控制(
permission/) - 输出截断(防止 token 爆炸)
- 多 Agent 模式(build/plan/explore)
- 会话持久化
- Snapshot 回滚
八、关键理解
-
Config 是依赖注入
- 集中管理,支持 proxy
- 方便切换不同环境
-
Tool 是 AI 的手脚
- 让 AI 能做事,而不只是聊天
- Zod 保证参数正确
-
Provider 是 AI 的大脑
- 不同模型,统一接口
- 支持 proxy 访问
-
Agent Loop 是 AI 的思考方式
- ReAct:边想边做
- maxSteps 防止失控
下一步
Phase 2 将实现更多工具:
| 工具 | 功能 | 学习重点 |
|---|---|---|
write | 写入文件 | 如何安全写文件 |
edit | 编辑文件 | 模糊匹配如何工作 |
bash | 执行命令 | 如何处理危险命令 |
grep | 搜索代码 | 正则匹配 |
让 Agent 真正能写代码!
代码仓库
完整代码:external/tiny-agent/
cd external/tiny-agent
make dev # 运行