ChatModel - Qwen
Qwen 模型
一个针对 Eino 的 Qwen 模型实现,实现了 ToolCallingChatModel 接口。这使得能够与 Eino 的 LLM 功能无缝集成,以增强自然语言处理和生成能力。
特性
- 实现了
github.com/cloudwego/eino/components/model.Model - 轻松与 Eino 的模型系统集成
- 可配置的模型参数
- 支持聊天补全
- 支持流式响应
- 自定义响应解析支持
- 灵活的模型配置
安装
go get github.com/cloudwego/eino-ext/components/model/qwen@latest
快速开始
以下是如何使用 Qwen 模型的快速示例:
package main
import (
"context"
"fmt"
"log"
"os"
"github.com/cloudwego/eino-ext/components/model/qwen"
"github.com/cloudwego/eino/schema"
)
func main() {
ctx := context.Background()
// get api key: https://help.aliyun.com/zh/model-studio/developer-reference/get-api-key?spm=a2c4g.11186623.help-menu-2400256.d_3_0.1ebc47bb0ClCgF
apiKey := os.Getenv("DASHSCOPE_API_KEY")
modelName := os.Getenv("MODEL_NAME")
chatModel, err := qwen.NewChatModel(ctx, &qwen.ChatModelConfig{
BaseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1",
APIKey: apiKey,
Timeout: 0,
Model: modelName,
MaxTokens: of(2048),
Temperature: of(float32(0.7)),
TopP: of(float32(0.7)),
})
if err != nil {
log.Fatalf("NewChatModel of qwen failed, err=%v", err)
}
resp, err := chatModel.Generate(ctx, []*schema.Message{
schema.UserMessage("as a machine, how do you answer user's question?"),
})
if err != nil {
log.Fatalf("Generate of qwen failed, err=%v", err)
}
fmt.Printf("output: \n%v", resp)
}
func of[T any](t T) *T {
return &t
}
配置
可以使用 qwen.ChatModelConfig 结构体配置模型:
type ChatModelConfig struct {
// APIKey is your authentication key
// Use OpenAI API key or Azure API key depending on the service
// Required
APIKey string `json:"api_key"`
// Timeout specifies the maximum duration to wait for API responses
// If HTTPClient is set, Timeout will not be used.
// Optional. Default: no timeout
Timeout time.Duration `json:"timeout"`
// HTTPClient specifies the client to send HTTP requests.
// If HTTPClient is set, Timeout will not be used.
// Optional. Default &http.Client{Timeout: Timeout}
HTTPClient *http.Client `json:"http_client"`
// BaseURL specifies the QWen endpoint URL
// Required. Example: https://dashscope.aliyuncs.com/compatible-mode/v1
BaseURL string `json:"base_url"`
// The following fields correspond to OpenAI's chat completion API parameters
// Ref: https://platform.openai.com/docs/api-reference/chat/create
// Model specifies the ID of the model to use
// Required
Model string `json:"model"`
// MaxTokens limits the maximum number of tokens that can be generated in the chat completion
// Optional. Default: model's maximum
MaxTokens *int `json:"max_tokens,omitempty"`
// Temperature specifies what sampling temperature to use
// Generally recommend altering this or TopP but not both.
// Range: 0.0 to 2.0. Higher values make output more random
// Optional. Default: 1.0
Temperature *float32 `json:"temperature,omitempty"`
// TopP controls diversity via nucleus sampling
// Generally recommend altering this or Temperature but not both.
// Range: 0.0 to 1.0. Lower values make output more focused
// Optional. Default: 1.0
TopP *float32 `json:"top_p,omitempty"`
// Stop sequences where the API will stop generating further tokens
// Optional. Example: []string{"\n", "User:"}
Stop []string `json:"stop,omitempty"`
// PresencePenalty prevents repetition by penalizing tokens based on presence
// Range: -2.0 to 2.0. Positive values increase likelihood of new topics
// Optional. Default: 0
PresencePenalty *float32 `json:"presence_penalty,omitempty"`
// ResponseFormat specifies the format of the model's response
// Optional. Use for structured outputs
ResponseFormat *openai.ChatCompletionResponseFormat `json:"response_format,omitempty"`
// Seed enables deterministic sampling for consistent outputs
// Optional. Set for reproducible results
Seed *int `json:"seed,omitempty"`
// FrequencyPenalty prevents repetition by penalizing tokens based on frequency
// Range: -2.0 to 2.0. Positive values decrease likelihood of repetition
// Optional. Default: 0
FrequencyPenalty *float32 `json:"frequency_penalty,omitempty"`
// LogitBias modifies likelihood of specific tokens appearing in completion
// Optional. Map token IDs to bias values from -100 to 100
LogitBias map[string]int `json:"logit_bias,omitempty"`
// User unique identifier representing end-user
// Optional. Helps OpenAI monitor and detect abuse
User *string `json:"user,omitempty"`
// EnableThinking enables thinking mode
// https://help.aliyun.com/zh/model-studio/deep-thinking
// Optional. Default: base on the Model
EnableThinking *bool `json:"enable_thinking,omitempty"`
}
示例
文本生成
package main
import (
"context"
"fmt"
"log"
"os"
"github.com/cloudwego/eino-ext/components/model/qwen"
"github.com/cloudwego/eino/schema"
)
func main() {
ctx := context.Background()
// get api key: https://help.aliyun.com/zh/model-studio/developer-reference/get-api-key?spm=a2c4g.11186623.help-menu-2400256.d_3_0.1ebc47bb0ClCgF
apiKey := os.Getenv("DASHSCOPE_API_KEY")
modelName := os.Getenv("MODEL_NAME")
chatModel, err := qwen.NewChatModel(ctx, &qwen.ChatModelConfig{
BaseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1",
APIKey: apiKey,
Timeout: 0,
Model: modelName,
MaxTokens: of(2048),
Temperature: of(float32(0.7)),
TopP: of(float32(0.7)),
})
if err != nil {
log.Fatalf("NewChatModel of qwen failed, err=%v", err)
}
resp, err := chatModel.Generate(ctx, []*schema.Message{
schema.UserMessage("as a machine, how do you answer user's question?"),
})
if err != nil {
log.Fatalf("Generate of qwen failed, err=%v", err)
}
fmt.Printf("output: \n%v", resp)
}
func of[T any](t T) *T {
return &t
}
多模态理解(图片理解)
package main
import (
"context"
"encoding/base64"
"fmt"
"log"
"os"
"github.com/cloudwego/eino-ext/components/model/qwen"
"github.com/cloudwego/eino/schema"
)
func main() {
ctx := context.Background()
// get api key: https://help.aliyun.com/zh/model-studio/developer-reference/get-api-key?spm=a2c4g.11186623.help-menu-2400256.d_3_0.1ebc47bb0ClCgF
apiKey := os.Getenv("DASHSCOPE_API_KEY")
modelName := os.Getenv("MODEL_NAME")
chatModel, err := qwen.NewChatModel(ctx, &qwen.ChatModelConfig{
BaseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1",
APIKey: apiKey,
Timeout: 0,
Model: modelName,
MaxTokens: of(2048),
Temperature: of(float32(0.7)),
TopP: of(float32(0.7)),
})
if err != nil {
log.Fatalf("NewChatModel of qwen failed, err=%v", err)
}
image, err := os.ReadFile("./examples/generate_with_image/test.jpg")
if err != nil {
log.Fatalf("os.ReadFile failed, err=%v\n", err)
}
resp, err := chatModel.Generate(ctx, []*schema.Message{
{
Role: schema.User,
UserInputMultiContent: []schema.MessageInputPart{
{
Type: schema.ChatMessagePartTypeText,
Text: "What do you see in this image?",
},
{
Type: schema.ChatMessagePartTypeImageURL,
Image: &schema.MessageInputImage{
MessagePartCommon: schema.MessagePartCommon{
Base64Data: of(base64.StdEncoding.EncodeToString(image)),
MIMEType: "image/jpeg",
},
Detail: schema.ImageURLDetailAuto,
},
},
},
},
})
if err != nil {
log.Printf("Generate error: %v", err)
return
}
fmt.Printf("Assistant: %s\n", resp.Content)
}
func of[T any](t T) *T {
return &t
}
流式生成
package main
import (
"context"
"fmt"
"io"
"log"
"os"
"github.com/cloudwego/eino-ext/components/model/qwen"
"github.com/cloudwego/eino/schema"
)
func main() {
ctx := context.Background()
// get api key: https://help.aliyun.com/zh/model-studio/developer-reference/get-api-key?spm=a2c4g.11186623.help-menu-2400256.d_3_0.1ebc47bb0ClCgF
apiKey := os.Getenv("DASHSCOPE_API_KEY")
modelName := os.Getenv("MODEL_NAME")
cm, err := qwen.NewChatModel(ctx, &qwen.ChatModelConfig{
BaseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1",
APIKey: apiKey,
Timeout: 0,
Model: modelName,
MaxTokens: of(2048),
Temperature: of(float32(0.7)),
TopP: of(float32(0.7)),
})
if err != nil {
log.Fatalf("NewChatModel of qwen failed, err=%v", err)
}
sr, err := cm.Stream(ctx, []*schema.Message{
schema.UserMessage("你好"),
})
if err != nil {
log.Fatalf("Stream of qwen failed, err=%v", err)
}
var msgs []*schema.Message
for {
msg, err := sr.Recv()
if err != nil {
if err == io.EOF {
break
}
log.Fatalf("Stream of qwen failed, err=%v", err)
}
fmt.Println(msg)
// assistant: 你好
// finish_reason:
// : !
// finish_reason:
// : 有什么
// finish_reason:
// : 可以帮助
// finish_reason:
// : 你的吗?
// finish_reason:
// :
// finish_reason: stop
// usage: &{9 7 16}
msgs = append(msgs, msg)
}
msg, err := schema.ConcatMessages(msgs)
if err != nil {
log.Fatalf("ConcatMessages failed, err=%v", err)
}
fmt.Println(msg)
// assistant: 你好!有什么可以帮助你的吗?
// finish_reason: stop
// usage: &{9 7 16}
}
func of[T any](t T) *T {
return &t
}
工具调用
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"os"
"github.com/cloudwego/eino-ext/components/model/qwen"
"github.com/cloudwego/eino/schema"
)
func main() {
ctx := context.Background()
// get api key: https://help.aliyun.com/zh/model-studio/developer-reference/get-api-key?spm=a2c4g.11186623.help-menu-2400256.d_3_0.1ebc47bb0ClCgF
apiKey := os.Getenv("DASHSCOPE_API_KEY")
modelName := os.Getenv("MODEL_NAME")
cm, err := qwen.NewChatModel(ctx, &qwen.ChatModelConfig{
BaseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1",
APIKey: apiKey,
Timeout: 0,
Model: modelName,
MaxTokens: of(2048),
Temperature: of(float32(0.7)),
TopP: of(float32(0.7)),
})
if err != nil {
log.Fatalf("NewChatModel of qwen failed, err=%v", err)
}
err = cm.BindTools([]*schema.ToolInfo{
{
Name: "user_company",
Desc: "根据用户的姓名和邮箱,查询用户的公司和职位信息",
ParamsOneOf: schema.NewParamsOneOfByParams(
map[string]*schema.ParameterInfo{
"name": {
Type: "string",
Desc: "用户的姓名",
},
"email": {
Type: "string",
Desc: "用户的邮箱",
},
}),
},
{
Name: "user_salary",
Desc: "根据用户的姓名和邮箱,查询用户的薪酬信息",
ParamsOneOf: schema.NewParamsOneOfByParams(
map[string]*schema.ParameterInfo{
"name": {
Type: "string",
Desc: "用户的姓名",
},
"email": {
Type: "string",
Desc: "用户的邮箱",
},
}),
},
})
if err != nil {
log.Fatalf("BindTools of qwen failed, err=%v", err)
}
resp, err := cm.Generate(ctx, []*schema.Message{
{
Role: schema.System,
Content: "你是一名房产经纪人,结合用户的薪酬和工作,使用 user_company、user_salary 两个 API,为其提供相关的房产信息。邮箱是必须的",
},
{
Role: schema.User,
Content: "我的姓名是 zhangsan,我的邮箱是 zhangsan@bytedance.com,请帮我推荐一些适合我的房子。",
},
})
if err != nil {
log.Fatalf("Generate of qwen failed, err=%v", err)
}
fmt.Println(resp)
// assistant:
// tool_calls: [{0x14000275930 call_1e25169e05fc4596a55afb function {user_company {"email": "zhangsan@bytedance.com", "name": "zhangsan"}} map[]}]
// finish_reason: tool_calls
// usage: &{316 32 348}
// ==========================
// using stream
fmt.Printf("\n\n======== Stream ========\n")
sr, err := cm.Stream(ctx, []*schema.Message{
{
Role: schema.System,
Content: "你是一名房产经纪人,结合用户的薪酬和工作,使用 user_company、user_salary 两个 API,为其提供相关的房产信息。邮箱是必须的",
},
{
Role: schema.User,
Content: "我的姓名是 lisi,我的邮箱是 lisi@bytedance.com,请帮我推荐一些适合我的房子。",
},
})
if err != nil {
log.Fatalf("Stream of qwen failed, err=%v", err)
}
msgs := make([]*schema.Message, 0)
for {
msg, err := sr.Recv()
if err != nil {
break
}
jsonMsg, err := json.Marshal(msg)
if err != nil {
log.Fatalf("json.Marshal failed, err=%v", err)
}
fmt.Printf("%s\n", jsonMsg)
msgs = append(msgs, msg)
}
msg, err := schema.ConcatMessages(msgs)
if err != nil {
log.Fatalf("ConcatMessages failed, err=%v", err)
}
jsonMsg, err := json.Marshal(msg)
if err != nil {
log.Fatalf("json.Marshal failed, err=%v", err)
}
fmt.Printf("final: %s\n", jsonMsg)
}
func of[T any](t T) *T {
return &t
}
使用方式
组件初始化
Qwen 模型通过 NewChatModel 函数进行初始化,主要配置参数如下:
import "github.com/cloudwego/eino-ext/components/model/qwen"
apiKey := os.Getenv("DASHSCOPE_API_KEY")
model, err := qwen.NewChatModel(ctx, &qwen.ChatModelConfig{
BaseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1", // URL
// 基础配置
APIKey: "api-key", // API 密钥
Timeout: 30 * time.Second, // 超时时间
// 模型参数
Model: "qwen-ma", // 模型名称
MaxTokens: &maxTokens,// 最大生成长度
Temperature: &temp, // 温度
TopP: &topP, // Top-P 采样
Stop: []string{},// 停止词
PresencePenalty: &pp, // 存在惩罚
FrequencyPenalty: &fp, // 频率惩罚
// 高级参数
ResponseFormat: &format, // 响应格式
Seed: &seed, // 随机种子
LogitBias: map[string]int{}, // Token 偏置
User: &user, // 用户标识
EnableThinking: of(false), // 是否开启思考模式
Modalities: make([]qwen.Modality, 0), // 模型回复模态类型: ["text","audio"] 默认 text
Audio: &qwen.Audio{ // 音频输出参数,当模态存在audio时,此字段必填
Format: qwen._AudioFormatWav_,
Voice: qwen._AudioVoiceChelsie_,
},
})
生成对话
对话生成支持普通模式和流式模式:
// invoke模式
response, err := model.Generate(ctx, messages)
// 流式模式
stream, err := model.Stream(ctx, messages)
消息格式示例:
import (
"os"
"encoding/base64"
"github.com/cloudwego/eino/schema"
)
// base64 格式的图片数据
image, err := os.ReadFile("./examples/image/eino.png")
if err != nil {
log.Fatalf("os.ReadFile failed, err=%v\n", err)
}
imageStr := base64.StdEncoding.EncodeToString(image)
messages := []*schema.Message{
// 系统消息
schema.SystemMessage("你是一个助手"),
// 多模态消息(包含图片)
{
Role: schema.User,
UserInputMultiContent: []schema.MessageInputPart{
{
Type: schema.ChatMessagePartTypeImageURL,
Image: &schema.MessageInputImage{
MessagePartCommon: schema.MessagePartCommon{
Base64Data: &imageStr,
MIMEType: "image/png", // required when use Base64Data
},
Detail: schema.ImageURLDetailAuto,
},
},
{
Type: schema.ChatMessagePartTypeText,
Text: "这张图片是什么?",
},
},
},
}
工具调用
支持绑定工具和强制工具调用:
import "github.com/cloudwego/eino/schema"
// 定义工具
tools := []*schema.ToolInfo{
{
Name: "search",
Desc: "搜索信息",
ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{
"query": {
Type: schema.String,
Desc: "搜索关键词",
Required: true,
},
}),
},
}
// 绑定可选工具
err := model.BindTools(tools)
// 绑定强制工具
err := model.BindForcedTools(tools)
工具相关信息,可以参考 Eino: ToolsNode 使用说明
完整使用示例
直接对话
package main
import (
"context"
"time"
"github.com/cloudwego/eino-ext/components/model/qwen"
"github.com/cloudwego/eino/schema"
)
func main() {
ctx := context.Background()
// 初始化模型
model, err := qwen.NewChatModel(ctx, &qwen.ChatModelConfig{
APIKey: "your-api-key", // required
Timeout: 30 * time.Second,
Model: "qwen-max", // required
// 如果模型支持语音生成,并且有需求生成语音时,需要配置如下配置
// Modalities: []qwen.Modality{qwen.AudioModality, qwen.TextModality},
//Audio: &qwen.Audio{
// Format: qwen.AudioFormatMp3,
// Voice: qwen.AudioVoiceAlloy,
//},
},
})
if err != nil {
panic(err)
}
// base64 格式的图片数据
image, err := os.ReadFile("./examples/image/cat.png")
if err != nil {
log.Fatalf("os.ReadFile failed, err=%v\n", err)
}
imageStr := base64.StdEncoding.EncodeToString(image)
// 请求消息
messages := []*schema.Message{
schema.SystemMessage("你是一个图片生成助手,可以仿照用户给定的图片生成一个风格近似的图片"),
{
Role: schema.User,
UserInputMultiContent: []schema.MessageInputPart{
{
Type: schema.ChatMessagePartTypeImage,
Image: &schema.MessageInputImage{
MessagePartCommon: schema.MessagePartCommon{
Base64Data: &imageStr,
MIMEType: "image/png", // required when use Base64Data
},
Detail: schema.ImageURLDetailAuto,
},
{
Type: schema.ChatMessagePartTypeText,
Text: "Generate an image of a cat",
},
},
},
}
// 生成回复
response, err := model.Generate(ctx, messages)
if err != nil {
panic(err)
}
// 处理回复
/*
生成的多模态内容存储在 response.AssistantGentMultiContent 字段中
本例中最终生成的 message 形如:
AssistantMessage = schema.Message{
Role: schema.Assistant,
AssistantGenMultiContent : []schema.MessageOutputPart{
{Type: schema.ChatMessagePartTypeImageURL,
Image: &schema.MessageOutputImage{
MessagePartCommon: schema.MessagePartCommon{
Base64Data: &DataStr,
MIMEType: "image/png",
},
},
},
},
}
*/
fmt.Printf("Assistant: %s\n", resp)
}
流式对话
package main
import (
"context"
"time"
"github.com/cloudwego/eino-ext/components/model/qwen"
"github.com/cloudwego/eino/schema"
)
func main() {
ctx := context.Background()
// 初始化模型
model, err := qwen.NewChatModel(ctx, &qwen.ChatModelConfig{
APIKey: "your-api-key",
Timeout: 30 * time.Second,
Model: "qwen-max",
})
if err != nil {
panic(err)
}
// 准备消息
messages := []*schema.Message{
schema.SystemMessage("你是一个助手"),
schema.UserMessage("写一个故事"),
}
// 获取流式回复
reader, err := model.Stream(ctx, messages)
if err != nil {
panic(err)
}
defer reader.Close() // 注意要关闭
// 处理流式内容
for {
chunk, err := reader.Recv()
if err != nil {
break
}
print(chunk.Content)
}
}
更多示例
相关文档
最后修改
December 12, 2025
: chore: update websocket docs (#1479) (967538e)