【Eino 框架入门】ChatModel 最简示例
用 Eino 调用大模型的最小代码。核心就是:创建 ChatModel → 构造 messages → 调用 Stream。
核心代码
// 1. 创建 ChatModel
cm, _ := openai.NewChatModel(ctx, &openai.ChatModelConfig{
APIKey: cfg.APIKey,
Model: cfg.Model,
BaseURL: cfg.BaseURL,
})
// 2. 构造 messages
messages := []*schema.Message{
schema.SystemMessage("You are a helpful assistant."),
schema.UserMessage("Hello!"),
}
// 3. 流式调用
stream, _ := cm.Stream(ctx, messages)
for {
frame, err := stream.Recv()
if errors.Is(err, io.EOF) {
break
}
fmt.Print(frame.Content)
}
stream.Close()
三个要点:
ChatModel是接口,不是具体类型 - 业务代码只依赖接口messages是[]*schema.Message,角色决定语义Stream()返回流,Generate()返回完整回复
Message 角色
schema.SystemMessage("...") // 系统指令,放最前面
schema.UserMessage("...") // 用户输入
schema.AssistantMessage("...", nil) // 模型回复
schema.ToolMessage("...", "call_id") // 工具结果
最简对话:一条 SystemMessage + 一条 UserMessage。
完整代码:交互式聊天
package main
import (
"bufio"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"os"
"strings"
"github.com/cloudwego/eino-ext/components/model/openai"
"github.com/cloudwego/eino/schema"
)
const systemPrompt = "You are a helpful assistant."
type config struct {
APIKey string `json:"api_key"`
Model string `json:"model"`
BaseURL string `json:"base_url,omitempty"`
}
func main() {
ctx := context.Background()
cfg, err := loadConfig("config.json")
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
cm, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
APIKey: cfg.APIKey,
Model: cfg.Model,
BaseURL: cfg.BaseURL,
})
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
fmt.Println("Eino Chat (type your question, Ctrl+C to exit)")
fmt.Println()
scanner := bufio.NewScanner(os.Stdin)
for {
fmt.Print("[user] ")
if !scanner.Scan() {
break
}
query := strings.TrimSpace(scanner.Text())
if query == "" {
continue
}
messages := []*schema.Message{
schema.SystemMessage(systemPrompt),
schema.UserMessage(query),
}
fmt.Print("[assistant] ")
stream, err := cm.Stream(ctx, messages)
if err != nil {
fmt.Fprintln(os.Stderr, err)
continue
}
for {
frame, err := stream.Recv()
if errors.Is(err, io.EOF) {
break
}
if err != nil {
fmt.Fprintln(os.Stderr, err)
break
}
if frame != nil {
fmt.Print(frame.Content)
}
}
stream.Close()
fmt.Println()
fmt.Println()
}
}
func loadConfig(path string) (*config, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("read config: %w", err)
}
var cfg config
if err := json.Unmarshal(data, &cfg); err != nil {
return nil, fmt.Errorf("parse config: %w", err)
}
if cfg.APIKey == "" {
return nil, fmt.Errorf("config: api_key is required")
}
if cfg.Model == "" {
return nil, fmt.Errorf("config: model is required")
}
return &cfg, nil
}
注意点
for {
frame, err := stream.Recv()
if errors.Is(err, io.EOF) {
break // 流正常结束,不是错误
}
if frame != nil {
fmt.Print(frame.Content) // frame 可能为 nil
}
}
stream.Close() // 必须关闭
frame.Content是增量片段,不是完整回复io.EOF是正常结束,err != nil && !errors.Is(err, io.EOF)才是错误frame可能为nil,需要判空- 必须调用
Close()
配置
config.json:
{
"api_key": "your-api-key",
"model": "gpt-4o-mini",
"base_url": ""
}
base_url 留空用 OpenAI 官方地址。智谱、DeepSeek、SiliconFlow 等兼容 OpenAI 格式的服务商,填对应地址即可。
运行
cd eino-demo/cmd/ch01
go run .
Eino Chat (type your question, Ctrl+C to exit)
[user] 写一个 Go HTTP 服务器
[assistant] package main...
这个示例的局限
每次输入都是新对话,模型记不住上一轮说了什么。要实现多轮对话,需要自己维护 history 并追加到 messages。下一章用 Agent + Runner 会更方便。