第1天:创建你的第一个 AI Agent - 30天复刻了一个 Claude Code
本文介绍了30天复刻Claude Code项目的第一部分,主要目标是理解Fantasy库的基本用法并实现简单的LLM调用。项目使用Go语言开发,基于Charmbracelet开发的AI Agent SDK Fantasy,该库提供了统一的LLM调用接口、工具系统、流式输出等功能。文章详细说明了项目创建步骤、API Key获取方式、代码编写过程以及运行方法。核心代码展示了如何通过Fantasy库连接
这是30天复刻Claude code第一篇
项目地址: https://github.com/JiayuXu0/MiniCode 欢迎大家 Star,感谢感谢
本节目标
- 理解 Fantasy 库的基本用法
- 创建最简单的 LLM 调用
- 成功运行第一次 AI 对话
最终效果
$ ./minicode "你好,介绍一下你自己"
你好!我是由Z.ai训练的大语言模型,你可以叫我GLM。
我的目标是根据你的指令提供有帮助、准确且安全的信息。我可以处理和生成文本,
帮助你完成各种任务,比如:
- 回答问题:无论是科学知识、历史事件还是生活常识
- 撰写文本:帮你写邮件、文章、报告、故事
- 翻译语言:支持多种语言之间的互译
- 编写代码:协助编写、调试和解释代码
- 头脑风暴:为你提供创意和灵感
总而言之,我是一个强大的信息和创意工具。随时欢迎你向我提问!
Step 1: 创建项目
# 创建项目目录(如果还没有的话)
mkdir -p minicode
cd minicode
# 初始化 Go module
go mod init github.com/JiayuXu0/MiniCode
# 获取 Fantasy 库
go get charm.land/fantasy
# 获取环境变量加载库
go get github.com/joho/godotenv
Fantasy 是什么?
Fantasy 是 Charmbracelet 开发的 AI Agent SDK,它提供了:
- 统一的 LLM 调用接口(支持 OpenAI、Anthropic、智谱 AI、Google 等)
- 强大的工具(Tool)系统
- 流式输出(Streaming)
- 多轮对话管理
- 结构化数据提取
我们这个项目就是要基于 Fantasy 来复现 Claude Code 的核心功能。
Step 2: 获取 API Key
你需要一个 LLM 的 API Key。我们选择智谱 AI,因为它对中文支持最好,而且有免费额度。
注册智谱 AI
- 访问 https://open.bigmodel.cn/
- 注册账号并完成实名认证
- 在控制台创建 API Key
- 充值或者领取免费资源包(推荐先领免费的试试)
创建 .env 文件保存 API Key:
OPENAI_API_KEY=你的_API_KEY
重要:记得把 .env 添加到 .gitignore,不然 API Key 会泄露到 GitHub 上!
Step 3: 编写代码
创建 main.go:
package main
import (
"context"
"fmt"
"log"
"os"
"charm.land/fantasy"
"charm.land/fantasy/providers/openaicompat"
"github.com/joho/godotenv"
)
const (
baseURL = "https://open.bigmodel.cn/api/coding/paas/v4"
modelID = "glm-4.7"
systemPrompt = "你是一个有帮助的 AI 助手,请用中文回答用户的问题。"
)
func main() {
// 1. 加载环境变量
if err := godotenv.Load(); err != nil {
log.Printf("警告: 无法加载 .env 文件: %v", err)
}
// 2. 检查命令行参数
if len(os.Args) < 2 {
fmt.Println("用法: go run . <你的问题>")
fmt.Println("示例: go run . \"你好,介绍一下你自己\"")
os.Exit(1)
}
prompt := os.Args[1]
// 3. 获取 API Key
apiKey := os.Getenv("OPENAI_API_KEY")
if apiKey == "" {
fmt.Fprintln(os.Stderr, "错误: 请设置 OPENAI_API_KEY 环境变量")
os.Exit(1)
}
ctx := context.Background()
// 4. 创建 Provider
provider, err := openaicompat.New(
openaicompat.WithBaseURL(baseURL),
openaicompat.WithAPIKey(apiKey),
openaicompat.WithName("zai"),
)
if err != nil {
fmt.Fprintf(os.Stderr, "创建 provider 失败: %v\n", err)
os.Exit(1)
}
// 5. 获取语言模型
model, err := provider.LanguageModel(ctx, modelID)
if err != nil {
fmt.Fprintf(os.Stderr, "获取模型失败: %v\n", err)
os.Exit(1)
}
// 6. 创建 Agent(无工具)
agent := fantasy.NewAgent(model, fantasy.WithSystemPrompt(systemPrompt))
// 7. 生成响应
result, err := agent.Generate(ctx, fantasy.AgentCall{Prompt: prompt})
if err != nil {
fmt.Fprintf(os.Stderr, "生成响应失败: %v\n", err)
os.Exit(1)
}
// 8. 打印响应
fmt.Println(result.Response.Content.Text())
}
Step 4: 运行
# 编译
go build -o minicode .
# 运行
./minicode "你好,介绍一下你自己"
代码解析
导入包
import (
"context" // Go 标准库:上下文管理
"fmt" // Go 标准库:格式化输出
"log" // Go 标准库:日志输出
"os" // Go 标准库:系统交互
"charm.land/fantasy" // Fantasy 核心包
"charm.land/fantasy/providers/openaicompat" // OpenAI 兼容适配器
"github.com/joho/godotenv" // .env 文件加载
)
Fantasy :
fantasy- 核心接口和类型fantasy/providers/openaicompat- OpenAI 兼容接口的适配器fantasy/anthropic- Anthropic Claude 适配器fantasy/openai- OpenAI GPT 适配器
智谱 AI 的 API 是 OpenAI 兼容的,所以可以用 openaicompat 包。
常量和变量
const (
baseURL = "https://open.bigmodel.cn/api/coding/paas/v4"
modelID = "glm-4.7"
systemPrompt = "你是一个有帮助的 AI 助手,请用中文回答用户的问题。"
)
prompt := os.Args[1]
apiKey := os.Getenv("OPENAI_API_KEY")
const定义常量,编译时确定:=短变量声明,自动推导类型os.Args[0]是程序名,os.Args[1]是第一个参数
Context
ctx := context.Background()
Context 用于传递取消信号、超时控制等。
函数式选项模式
provider, err := openaicompat.New(
openaicompat.WithBaseURL(baseURL),
openaicompat.WithAPIKey(apiKey),
openaicompat.WithName("zai"),
)
这是一种常见的设计模式,参数可选,想配什么就传什么。
错误处理
if err != nil {
fmt.Fprintf(os.Stderr, "创建 provider 失败: %v\n", err)
os.Exit(1)
}
Go 的错误处理:函数返回 error,调用者必须显式检查。
深入了解 Fantasy
Fantasy 是一个 Go AI Agent SDK,通过统一的 API 跨多个语言模型提供商构建 AI 应用程序。它将提供商特定的实现抽象在一致的接口后面,支持在 OpenAI、Anthropic、Google、OpenRouter、AWS Bedrock、Azure 和其他提供商之间切换。
核心架构
Fantasy 的架构围绕三个基本抽象构建:
核心组件:
| 组件 | 类型 | 用途 |
|---|---|---|
Provider |
接口 | 为特定模型创建 LanguageModel 实例的工厂 |
LanguageModel |
接口 | 模型交互的核心接口(文本生成、流式输出、结构化输出) |
Agent |
结构体 | 协调多步骤交互,支持工具调用和停止条件 |
Message |
结构体 | 表示对话中的单条消息,包含角色和异构内容部分 |
AgentTool |
结构体 | 定义模型可以调用的可执行函数 |
工作流程:从代码到 AI 提供商
应用代码 → Agent → LanguageModel → Provider → 外部 AI API
↓ ↓ ↓ ↓ ↓
提示词 协调逻辑 模型接口 API 适配 Claude/GPT/GLM
关键代码实体
Provider 接口
Provider 接口是所有提供商实现的入口点:
type Provider interface {
LanguageModel(ctx context.Context, name string) (LanguageModel, error)
}
每个提供商包都实现这个接口:
providers/openai- OpenAI Chat Completions API 和 Responses APIproviders/anthropic- Anthropic Claude,支持思考/推理providers/google- Google Gemini 和 Vertex AIproviders/openrouter- OpenRouter 多模型聚合providers/azure- Azure OpenAI Serviceproviders/bedrock- AWS Bedrockproviders/openaicompat- 通用的 OpenAI 兼容提供商(Groq、xAI、Hugging Face)
LanguageModel 接口
LanguageModel 接口定义了模型交互的四个核心方法:
| 方法 | 用途 | 返回 |
|---|---|---|
Generate(ctx, Call) |
同步文本生成 | Response, error |
Stream(ctx, Call, callbacks) |
带回调的流式文本生成 | Response, error |
GenerateObject(ctx, ObjectCall) |
带JSON 模式的结构化输出 | ObjectResponse, error |
StreamObject(ctx, ObjectCall, callback) |
流式结构化输出 | ObjectResponse, error |
所有提供商实现都必须满足这个接口,确保跨不同 AI 服务的行为一致。
Agent 系统
Agent 类型协调与语言模型的多步骤交互,处理工具执行和停止条件。通过 NewAgent() 和选项创建:
WithSystemPrompt(string)- 设置系统指令WithTools(...AgentTool)- 注册可用工具WithMaxOutputTokens(int)- 限制输出长度WithTemperature(float64)- 控制随机性WithStopConditions(...StopCondition)- 定义终止条件
Agent 的 Generate() 和 Stream() 方法执行一个循环:
- 准备模型调用(通过可选的
PrepareStepFunction) - 调用语言模型
- 验证并执行任何工具调用
- 检查停止条件
- 重复直到终止
工具系统
工具使模型能够执行代码和检索信息。使用 NewAgentTool() 定义:
tool := fantasy.NewAgentTool(
"get_weather",
"获取天气信息",
func(ctx context.Context, input struct {
City string `json:"city"`
}) fantasy.ToolResponse {
return fantasy.NewTextResponse(fmt.Sprintf("%s 天气晴朗", input.City))
},
)
agent := fantasy.NewAgent(model, fantasy.WithTools(tool))
工具函数签名接受:
context.Context- 用于取消和超时inputStruct- 从模型的工具调用解析的输入- 返回:
ToolResponse, error
工具返回的 ToolResponse 可以包含文本、错误或媒体内容。
内容类型系统
Fantasy 使用多态内容系统来表示多样化的 AI 交互:
Message 类型包含 MessagePart 实现的切片,支持:
- 多模态输入(文本 + 图像,通过
FilePart) - 工具调用工作流(
ToolCallPart→ 执行 →ToolResultPart) - 推理透明度(o1/o3/o4 模型的
ReasoningPart) - 单条消息中的异构内容
支持的提供商
| 提供商 | 包 | 模型 | 特殊功能 |
|---|---|---|---|
| OpenAI | providers/openai |
GPT-4o, o1, o3, o4 | Responses API, 推理模型 |
| Anthropic | providers/anthropic |
Claude 3/4 | 思考块, 提示缓存, Vertex AI |
providers/google |
Gemini 1.5/2.0 | 原生结构化输出, Vertex AI, 安全设置 | |
| OpenRouter | providers/openrouter |
100+ 模型 | 多提供商聚合, 统一 API |
| Azure | providers/azure |
Azure 上的 OpenAI | 企业集成 |
| AWS Bedrock | providers/bedrock |
Claude, Titan 等 | AWS 原生 |
| OpenAI 兼容 | providers/openaicompat |
Groq, xAI, HF | 通用兼容层 |
每个提供商将 Fantasy 的统一 API 转换为提供商特定格式,同时保持一致的行为。
设计理念
多提供商抽象
Fantasy 提供一个跨提供商工作的单一 API,无需应用程序级代码更改。通过以下方式访问提供商特定功能:
Call结构体中的ProviderOptions映射Response结构体中的ProviderMetadata映射- 提供商特定的选项类型(例如
openai.ProviderOptions)
这种设计允许应用程序在需要时保持可移植性,同时访问高级功能。
类型安全与灵活性
SDK 使用 Go 的类型系统来实现编译时安全性,同时保持运行时灵活性:
- 通过泛型函数进行强类型工具输入/输出
- 多态内容的类型区分 JSON 序列化
- 通过
Opt[T]助手的可选指针类型 - 通过选项函数的结构化配置
原生 Go 集成
Fantasy 被设计为原生 Go 库:
- 基于上下文的取消和超时
- 标准错误处理模式
- 编译为原生机器代码,无运行时依赖
- HTTP 客户端自定义支持
VS Code 调试配置
创建 .vscode/launch.json:
{
"version": "0.2.0",
"configurations": [
{
"name": "运行 MiniCode",
"type": "go",
"request": "launch",
"mode": "debug",
"program": "${workspaceFolder}",
"args": ["你好"],
"envFile": "${workspaceFolder}/.env"
}
]
}
常见问题
Q: 报错 “错误: 请设置 OPENAI_API_KEY 环境变量”
确保创建了 .env 文件并且包含:
OPENAI_API_KEY=你的_API_KEY
Q: 报错 “余额不足或无可用资源包”
登录 https://open.bigmodel.cn/ 充值或领取免费资源包。
Q: 响应很慢
GLM-4.7 通常需要 3-5 秒响应。如果需要更快:
- 使用 GLM-4-Flash
- 检查网络连接
Q: 想使用其他模型(OpenAI、Claude 等)
修改 Provider:
// OpenAI
import "charm.land/fantasy/providers/openai"
provider, _ := openai.New(
openai.WithAPIKey(os.Getenv("OPENAI_API_KEY")),
)
// Anthropic Claude
import "charm.land/fantasy/providers/anthropic"
provider, _ := anthropic.New(
anthropic.WithAPIKey(os.Getenv("ANTHROPIC_API_KEY")),
)
关键概念:
Provider- LLM 提供商的抽象LanguageModel- 具体的 AI 模型Agent- 带有 System Prompt 和 Tools 的 LLM 封装AgentCall- 调用参数AgentResult- 返回结果
更多推荐



所有评论(0)