Eino - 从0到1跑通大模型调用

Eino - 从0到1跑通大模型调用

前言

Eino 是字节跳动开源的 AI 应用开发框架,提供了丰富的大模型组件支持。本文将基于 代码示例,详细介绍如何使用 Eino 框架实现单轮对话、多轮对话、流式输出以及模型参数配置,帮助你从零开始掌握大模型调用,代码链接

一、环境准备

1.1 安装依赖

bash 复制代码
go get github.com/cloudwego/eino@latest
go get github.com/cloudwego/eino-ext/components/model/openai@latest
go get gopkg.in/yaml.v2@latest

1.2 配置文件

创建 config.yml 配置文件:

yaml 复制代码
model:
  base_url: "https://api.minimaxi.com/v1"
  api_key: "your-api-key"
  model_name: "MiniMax-M2.7"
  timeout: 30          # 超时时间(秒)
  temperature: 0.7     # 控制输出随机性,范围 [0.0, 2.0]
  top_p: 0.9          # 核采样参数,范围 [0.0, 1.0]
  max_tokens: 500     # 最大生成token数量

app:
  host: "0.0.0.0"
  port: 8080

二、统一配置管理

为了代码复用,我们定义统一的配置结构体:

go 复制代码
// Config 模型配置
type Config struct {
    Model ModelConfig `yaml:"model"`
    App   AppConfig   `yaml:"app"`
}

// ModelConfig 大模型配置
type ModelConfig struct {
    BaseURL    string  `yaml:"base_url"`
    APIKey     string  `yaml:"api_key"`
    ModelName  string  `yaml:"model_name"`
    Timeout    int     `yaml:"timeout"`
    Temperature float64 `yaml:"temperature"`
    TopP       float64 `yaml:"top_p"`
    MaxTokens  int     `yaml:"max_tokens"`
}

// AppConfig 应用配置
type AppConfig struct {
    Host string `yaml:"host"`
    Port int    `yaml:"port"`
}

// loadConfig 加载配置文件
func loadConfig(configPath string) (*Config, error) {
    data, err := os.ReadFile(configPath)
    if err != nil {
        return nil, fmt.Errorf("读取配置文件失败: %w", err)
    }

    var config Config
    if err := yaml.Unmarshal(data, &config); err != nil {
        return nil, fmt.Errorf("解析配置文件失败: %w", err)
    }

    return &config, nil
}

通过命令行参数指定配置文件路径:

go 复制代码
configPath := flag.String("config", "config.yml", "配置文件路径")
flag.Parse()

三、单轮对话实现

单轮对话是最基础的场景,用户发送一条消息,AI 返回一条响应。

3.1 完整代码

go 复制代码
func main() {
    // 0. 解析命令行参数
    configPath := flag.String("config", "config.yml", "配置文件路径")
    flag.Parse()

    // 1. 加载配置
    cfg, err := loadConfig(*configPath)
    if err != nil {
        log.Fatalf("加载配置失败: %v", err)
    }

    ctx := context.Background()

    // 2. 创建 ChatModel
    chatModel, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
        APIKey:  cfg.Model.APIKey,
        Model:   cfg.Model.ModelName,
        BaseURL: cfg.Model.BaseURL,
    })
    if err != nil {
        log.Fatalf("创建失败: %v", err)
    }

    // 3. 构建消息
    messages := []*schema.Message{
        schema.SystemMessage("你是一个懂得哲学的程序员。"),
        schema.UserMessage("什么是存在主义?"),
    }

    // 4. 生成响应
    response, err := chatModel.Generate(ctx, messages)
    if err != nil {
        log.Fatalf("生成失败: %v", err)
    }

    // 5. 输出结果
    fmt.Printf("回答:\n%s\n", response.Content)

    // 6. 输出 Token 使用统计
    if response.ResponseMeta != nil && response.ResponseMeta.Usage != nil {
        fmt.Printf("\nToken 使用统计:\n")
        fmt.Printf("  输入 Token: %d\n", response.ResponseMeta.Usage.PromptTokens)
        fmt.Printf("  输出 Token: %d\n", response.ResponseMeta.Usage.CompletionTokens)
        fmt.Printf("  总计 Token: %d\n", response.ResponseMeta.Usage.TotalTokens)
    }
}

3.2 核心步骤解析

步骤 说明
1. 加载配置 从 YAML 文件读取模型配置
2. 创建 ChatModel 初始化大模型客户端
3. 构建消息 使用 schema.SystemMessageschema.UserMessage 构建对话
4. 生成响应 调用 chatModel.Generate() 获取非流式响应
5. 输出结果 打印 AI 回复内容和 Token 使用统计

四、模型参数配置

不同的应用场景需要不同的参数配置,Eino 提供了丰富的参数支持。

4.1 完整代码

go 复制代码
func main() {
    configPath := flag.String("config", "config.yml", "配置文件路径")
    flag.Parse()
    cfg, err := loadConfig(*configPath)
    if err != nil {
        log.Fatalf("加载配置失败: %v", err)
    }

    ctx := context.Background()

    // 示例1: 基础配置
    basicExample(ctx, cfg)

    // 示例2: 高级配置
    advancedExample(ctx, cfg)

    // 示例3: 创意写作配置
    creativeExample(ctx, cfg)
}

// 基础配置示例
func basicExample(ctx context.Context, cfg *Config) {
    timeout := time.Duration(cfg.Model.Timeout) * time.Second
    if timeout == 0 {
        timeout = 30 * time.Second
    }

    temperature := float32(cfg.Model.Temperature)
    maxTokens := cfg.Model.MaxTokens

    chatModel, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
        APIKey:     cfg.Model.APIKey,
        Model:      cfg.Model.ModelName,
        BaseURL:    cfg.Model.BaseURL,
        Timeout:    timeout,
        Temperature: &temperature,
        MaxTokens:   &maxTokens,
    })
    // ...
}

// 高级配置示例 - 精确控制输出
func advancedExample(ctx context.Context, cfg *Config) {
    temperature := float32(0.7)
    topP := float32(0.9)
    maxTokens := 500
    presencePenalty := float32(0.6)
    frequencyPenalty := float32(0.5)

    chatModel, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
        APIKey:  cfg.Model.APIKey,
        Model:   cfg.Model.ModelName,
        BaseURL: cfg.Model.BaseURL,
        Timeout: 30 * time.Second,

        // 生成参数
        Temperature: &temperature,
        TopP:        &topP,
        MaxTokens:   &maxTokens,

        // 停止序列 - 遇到这些文本时停止生成
        Stop: []string{"\n\n", "总结:"},

        // 惩罚参数 - 控制重复度
        PresencePenalty:  &presencePenalty,
        FrequencyPenalty: &frequencyPenalty,
    })
    // ...
}

// 创意写作配置 - 高随机性
func creativeExample(ctx context.Context, cfg *Config) {
    temperature := float32(1.2)
    topP := float32(0.95)
    maxTokens := 800
    presencePenalty := float32(0.3)
    frequencyPenalty := float32(0.3)

    chatModel, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
        // 高温度设置,适合创意写作
        Temperature: &temperature,
        TopP:        &topP,
        MaxTokens:   &maxTokens,

        // 减少重复惩罚,允许一定的重复
        PresencePenalty:  &presencePenalty,
        FrequencyPenalty: &frequencyPenalty,
    })
    // ...
}

4.2 参数详解

参数 类型 说明 推荐值
Temperature *float32 控制输出随机性,值越高越随机 0.7 (基础)、1.2+ (创意)
TopP *float32 核采样参数,值越低越聚焦 0.9 (基础)、0.95 (创意)
MaxTokens *int 最大生成 token 数量 500 (基础)、800+ (长文本)
PresencePenalty *float32 存在惩罚,鼓励新话题 0.6 (精确)、0.3 (创意)
FrequencyPenalty *float32 频率惩罚,减少重复 0.5 (精确)、0.3 (创意)
Stop []string 停止序列,遇到这些文本停止生成 -

4.3 场景化配置建议

场景 Temperature TopP MaxTokens PresencePenalty FrequencyPenalty
基础问答 0.7 0.9 500 0.6 0.5
技术文档 0.7 0.9 500 0.6 0.5
创意写作 1.2+ 0.95 800+ 0.3 0.3
代码生成 0.3-0.5 0.9 1000+ 0.6 0.5

五、多轮对话实现

多轮对话需要维护对话历史,将 AI 的回复也加入消息列表,实现上下文理解。

5.1 完整代码

go 复制代码
func main() {
    configPath := flag.String("config", "config.yml", "配置文件路径")
    flag.Parse()
    cfg, err := loadConfig(*configPath)
    if err != nil {
        log.Fatalf("加载配置失败: %v", err)
    }

    ctx := context.Background()

    // 创建 ChatModel
    timeout := time.Duration(cfg.Model.Timeout) * time.Second
    if timeout == 0 {
        timeout = 30 * time.Second
    }

    temperature := float32(cfg.Model.Temperature)
    maxTokens := cfg.Model.MaxTokens

    chatModel, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
        APIKey:     cfg.Model.APIKey,
        Model:      cfg.Model.ModelName,
        BaseURL:    cfg.Model.BaseURL,
        Timeout:    timeout,
        Temperature: &temperature,
        MaxTokens:   &maxTokens,
    })
    if err != nil {
        log.Fatalf("创建失败: %v", err)
    }

    // 对话历史
    messages := []*schema.Message{
        schema.SystemMessage("你是一个懂得哲学的程序员。"),
    }

    scanner := bufio.NewScanner(os.Stdin)
    fmt.Println("开始对话(输入 'exit' 退出):")

    for {
        fmt.Print("\n你: ")
        if !scanner.Scan() {
            break
        }

        userInput := strings.TrimSpace(scanner.Text())
        if userInput == "exit" {
            fmt.Println("再见!")
            break
        }

        if userInput == "" {
            continue
        }

        // 添加用户消息
        messages = append(messages, schema.UserMessage(userInput))

        // 生成 AI 响应
        response, err := chatModel.Generate(ctx, messages)
        if err != nil {
            log.Printf("生成失败: %v", err)
            continue
        }

        // 添加 AI 响应到历史,实现多轮对话
        messages = append(messages, response)

        fmt.Printf("\nAI: %s\n", response.Content)
    }
}

5.2 核心逻辑

复制代码
┌─────────────────────────────────────────────────────┐
│                    对话循环                          │
├─────────────────────────────────────────────────────┤
│                                                     │
│   用户输入 ──→ 添加到 messages ──→ chatModel.Generate │
│                                                     │
│         ▲                                          │
│         │                                          │
│         │ append(response)                         │
│         │                                          │
│   AI 响应 ◀── messages ◀────────────────────────────┤
│                                                     │
└─────────────────────────────────────────────────────┘

5.3 关键点

  1. 对话历史累积 :每次对话后,将用户消息和 AI 响应都加入 messages 列表
  2. SystemMessage 保持不变:系统提示只在列表开头,不重复添加
  3. 上下文理解:通过传递完整对话历史,AI 能够理解上下文

六、流式输出实现

流式输出让 AI 的回复可以逐字显示,带来更好的用户体验。

6.1 完整代码

go 复制代码
func main() {
    configPath := flag.String("config", "config.yml", "配置文件路径")
    flag.Parse()
    cfg, err := loadConfig(*configPath)
    if err != nil {
        log.Fatalf("加载配置失败: %v", err)
    }

    ctx := context.Background()

    // 创建 ChatModel
    timeout := time.Duration(cfg.Model.Timeout) * time.Second
    if timeout == 0 {
        timeout = 30 * time.Second
    }

    temperature := float32(cfg.Model.Temperature)
    maxTokens := cfg.Model.MaxTokens

    chatModel, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
        APIKey:     cfg.Model.APIKey,
        Model:      cfg.Model.ModelName,
        BaseURL:    cfg.Model.BaseURL,
        Timeout:    timeout,
        Temperature: &temperature,
        MaxTokens:   &maxTokens,
    })
    if err != nil {
        log.Fatalf("创建失败: %v", err)
    }

    // 构建消息
    messages := []*schema.Message{
        schema.SystemMessage("你是一个懂得哲学的程序员。"),
        schema.UserMessage("什么是存在主义?"),
    }

    // 流式生成
    stream, err := chatModel.Stream(ctx, messages)
    if err != nil {
        log.Fatalf("流式生成失败: %v", err)
    }
    defer stream.Close() // 记得关闭流

    fmt.Print("AI 回复: ")

    // 逐块接收并打印
    for {
        chunk, err := stream.Recv()
        if err != nil {
            if errors.Is(err, io.EOF) {
                // 流结束
                break
            }
            log.Fatalf("接收失败: %v", err)
        }

        // 打印内容(打字机效果)
        fmt.Print(chunk.Content)
    }

    fmt.Println("\n\n完成!")
}

6.2 流式 vs 非流式

特性 非流式 (Generate) 流式 (Stream)
返回时机 完整响应生成后 逐 token 返回
用户体验 等待时间长 即时响应,打字机效果
代码复杂度 简单 需要处理循环接收
适用场景 短回复、需要完整结果 长回复、实时交互

6.3 流式处理流程

复制代码
chatModel.Stream()
       │
       ▼
   stream.Recv() ──→ chunk.Content ──→ fmt.Print()
       │                            (逐字打印)
       │ chunk
       ▼
   stream.Recv()
       │
       │ io.EOF
       ▼
    结束

七、Token 使用统计

Eino 提供了完整的 Token 使用统计,通过 response.ResponseMeta.Usage 获取:

go 复制代码
if response.ResponseMeta != nil && response.ResponseMeta.Usage != nil {
    fmt.Printf("输入 Token: %d\n", response.ResponseMeta.Usage.PromptTokens)
    fmt.Printf("输出 Token: %d\n", response.ResponseMeta.Usage.CompletionTokens)
    fmt.Printf("总计 Token: %d\n", response.ResponseMeta.Usage.TotalTokens)

    // 缓存 Token(如果有)
    if response.ResponseMeta.Usage.PromptTokenDetails.CachedTokens > 0 {
        fmt.Printf("缓存 Token: %d\n", response.ResponseMeta.Usage.PromptTokenDetails.CachedTokens)
    }
}

八、总结

本文详细介绍了使用 Eino 框架调用大模型的四种核心场景:

场景 方法 特点
单轮对话 chatModel.Generate() 简单直接,一次请求一次响应
参数配置 ChatModelConfig 灵活控制输出质量
多轮对话 维护 messages 历史 上下文理解,连续交互
流式输出 chatModel.Stream() 即时响应,打字机效果

关键配置参数

  • Temperature:控制随机性,创意场景调高,精确场景调低
  • TopP:核采样,与 Temperature 二选一使用
  • MaxTokens:限制输出长度,避免过长回复
  • PresencePenalty / FrequencyPenalty:控制重复内容

运行方式

bash 复制代码
# 单轮对话
go run single_chat.go -config ../../config.yml

# 参数配置示例
go run param_conf.go -config ../../config.yml

# 多轮对话
go run multi_chat.go -config ../../config.yml

# 流式输出
go run stream_chat.go -config ../../config.yml
相关推荐
不会写DN3 小时前
IPv4 与 IPv6 的核心区别
计算机网络·面试·golang
华农DrLai3 小时前
怎么用大模型生成推荐的训练数据?Data Augmentation怎么做?
数据库·人工智能·大模型·nlp·prompt
Flandern11113 小时前
Go程序员学习AI大模型项目实战02:给 AI 装上“大脑”:从配置解包到流式生成的深度拆解
人工智能·后端·python·学习·golang
guslegend5 小时前
4月11日(Codex使用)
人工智能·大模型
CoderJia程序员甲6 小时前
GitHub 热榜项目 - 日榜(2026-04-11)
人工智能·ai·大模型·github·ai教程
见合八方7 小时前
AI大模型入门教程:从零开始理解ChatGPT背后的技术
ai·chatgpt·大模型·llm·入门教程
ん贤7 小时前
Go GC 非玄学,而是 CPU 和内存的权衡
开发语言·后端·golang·性能调优·gc
Dontla17 小时前
go语言Windows安装教程(安装go安装Golang安装)(GOPATH、Go Modules)
开发语言·windows·golang
铁东博客17 小时前
Go实现周易大衍筮法三变取爻
开发语言·后端·golang