这是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

  1. 访问 https://open.bigmodel.cn/
  2. 注册账号并完成实名认证
  3. 在控制台创建 API Key
  4. 充值或者领取免费资源包(推荐先领免费的试试)

创建 .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 API
  • providers/anthropic - Anthropic Claude,支持思考/推理
  • providers/google - Google Gemini 和 Vertex AI
  • providers/openrouter - OpenRouter 多模型聚合
  • providers/azure - Azure OpenAI Service
  • providers/bedrock - AWS Bedrock
  • providers/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() 方法执行一个循环:

  1. 准备模型调用(通过可选的 PrepareStepFunction
  2. 调用语言模型
  3. 验证并执行任何工具调用
  4. 检查停止条件
  5. 重复直到终止
工具系统

工具使模型能够执行代码和检索信息。使用 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
Google 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 - 返回结果

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐