ChatModel - Qianfan

Qianfan 模型

一个针对 Eino 的千帆模型实现,实现了 ToolCallingChatModel 接口。这使得能够与 Eino 的 LLM 功能无缝集成,以增强自然语言处理和生成能力。

特性

  • 实现了 github.com/cloudwego/eino/components/model.Model
  • 轻松与 Eino 的模型系统集成
  • 可配置的模型参数
  • 支持聊天补全
  • 支持流式响应
  • 自定义响应解析支持
  • 灵活的模型配置

安装

go get github.com/cloudwego/eino-ext/components/model/qianfan@latest

快速开始

以下是如何使用千帆模型的快速示例:

package main

import (
        "context"
        "fmt"
        "log"
        "os"

        "github.com/cloudwego/eino-ext/components/model/qianfan"
        "github.com/cloudwego/eino/schema"
)

func main() {
        ctx := context.Background()
        qcfg := qianfan.GetQianfanSingletonConfig()
        // How to get Access Key/Secret Key: https://cloud.baidu.com/doc/Reference/s/9jwvz2egb
        qcfg.AccessKey = "your_access_key"
        qcfg.SecretKey = "your_secret_key"
        modelName := os.Getenv("MODEL_NAME")
        cm, err := qianfan.NewChatModel(ctx, &qianfan.ChatModelConfig{
                Model:               modelName,
                Temperature:         of(float32(0.7)),
                TopP:                of(float32(0.7)),
                MaxCompletionTokens: of(1024),
                Seed:                of(0),
        })

        if err != nil {
                log.Fatalf("NewChatModel of qianfan failed, err=%v", err)
        }

        ir, err := cm.Generate(ctx, []*schema.Message{
                schema.UserMessage("hello"),
        })

        if err != nil {
                log.Fatalf("Generate of qianfan failed, err=%v", err)
        }

        fmt.Println(ir)
        // assistant: 你好!我是文心一言,很高兴与你交流。请问你有什么想问我的吗?无论是关于知识、创作还是其他任何问题,我都会尽力回答你。
}

func of[T any](t T) *T {
        return &t
}

配置

可以使用 qianfan.ChatModelConfig 结构体配置模型:

// ChatModelConfig config for qianfan chat completion
// see: https://cloud.baidu.com/doc/WENXINWORKSHOP/s/Wm3fhy2vb
type ChatModelConfig struct {
        // Model is the model to use for the chat completion.
        Model string

        // LLMRetryCount is the number of times to retry a failed request.
        LLMRetryCount *int

        // LLMRetryTimeout is the timeout for each retry attempt.
        LLMRetryTimeout *float32

        // LLMRetryBackoffFactor is the backoff factor for retries.
        LLMRetryBackoffFactor *float32

        // Temperature controls the randomness of the output. A higher value makes the output more random, while a lower value makes it more focused and deterministic. Default is 0.95, range (0, 1.0].
        Temperature *float32

        // TopP controls the diversity of the output. A higher value increases the diversity of the generated text. Default is 0.7, range [0, 1.0].
        TopP *float32

        // PenaltyScore reduces the generation of repetitive tokens by adding a penalty. A higher value means a larger penalty. Range: [1.0, 2.0].
        PenaltyScore *float64

        // MaxCompletionTokens is the maximum number of tokens to generate in the completion. Range [2, 2048].
        MaxCompletionTokens *int

        // Seed is the random seed for generation. Range (0, 2147483647).
        Seed *int

        // Stop is a list of strings that will stop the generation when the model generates a token that is a suffix of one of the strings.
        Stop []string

        // User is a unique identifier representing the end-user.
        User *string

        // FrequencyPenalty specifies the frequency penalty to control the repetition of generated text. Range [-2.0, 2.0].
        FrequencyPenalty *float64

        // PresencePenalty specifies the presence penalty to control the repetition of generated text. Range [-2.0, 2.0].
        PresencePenalty *float64

        // ParallelToolCalls specifies whether to call tools in parallel. Defaults to true.
        ParallelToolCalls *bool

        // ResponseFormat specifies the format of the response.
        ResponseFormat *ResponseFormat
}

示例

文本生成

package main

import (
        "context"
        "fmt"
        "log"
        "os"

        "github.com/cloudwego/eino-ext/components/model/qianfan"
        "github.com/cloudwego/eino/schema"
)

func main() {
        ctx := context.Background()

        qcfg := qianfan.GetQianfanSingletonConfig()
        // How to get Access Key/Secret Key: https://cloud.baidu.com/doc/Reference/s/9jwvz2egb
        qcfg.AccessKey = "your_access_key"
        qcfg.SecretKey = "your_secret_key"
        modelName := os.Getenv("MODEL_NAME")
        cm, err := qianfan.NewChatModel(ctx, &qianfan.ChatModelConfig{
                Model:               modelName,
                Temperature:         of(float32(0.7)),
                TopP:                of(float32(0.7)),
                MaxCompletionTokens: of(1024),
                Seed:                of(0),
        })

        if err != nil {
                log.Fatalf("NewChatModel of qianfan failed, err=%v", err)
        }

        ir, err := cm.Generate(ctx, []*schema.Message{
                schema.UserMessage("hello"),
        })

        if err != nil {
                log.Fatalf("Generate of qianfan failed, err=%v", err)
        }

        fmt.Println(ir)
}

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/qianfan"
        "github.com/cloudwego/eino/schema"
)

func main() {
        ctx := context.Background()
        qcfg := qianfan.GetQianfanSingletonConfig()
        // How to get Access Key/Secret Key: https://cloud.baidu.com/doc/Reference/s/9jwvz2egb
        qcfg.AccessKey = "your_access_key"
        qcfg.SecretKey = "your_secret_key"
        modelName := os.Getenv("MODEL_NAME")
        chatModel, err := qianfan.NewChatModel(ctx, &qianfan.ChatModelConfig{
                Model:               modelName,
                Temperature:         of(float32(0.7)),
                TopP:                of(float32(0.7)),
                MaxCompletionTokens: of(1024),
                Seed:                of(0),
        })

        if err != nil {
                log.Fatalf("NewChatModel of qianfan 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/qianfan"
        "github.com/cloudwego/eino/schema"
)

func main() {
        ctx := context.Background()
        qcfg := qianfan.GetQianfanSingletonConfig()
        // How to get Access Key/Secret Key: https://cloud.baidu.com/doc/Reference/s/9jwvz2egb
        qcfg.AccessKey = "your_access_key"
        qcfg.SecretKey = "your_secret_key"
        modelName := os.Getenv("MODEL_NAME")
        cm, err := qianfan.NewChatModel(ctx, &qianfan.ChatModelConfig{
                Model:               modelName,
                Temperature:         of(float32(0.7)),
                TopP:                of(float32(0.7)),
                MaxCompletionTokens: of(1024),
        })
        if err != nil {
                log.Fatalf("NewChatModel of qianfan failed, err=%v", err)
        }

        sr, err := cm.Stream(ctx, []*schema.Message{
                schema.UserMessage("hello"),
        })

        if err != nil {
                log.Fatalf("Stream of qianfan failed, err=%v", err)
        }

        var ms []*schema.Message
        for {
                m, err := sr.Recv()
                if err != nil {
                        if err == io.EOF {
                                break
                        }

                        log.Fatalf("Stream of qianfan failed, err=%v", err)
                }

                fmt.Println(m)
                ms = append(ms, m)
        }
        sm, err := schema.ConcatMessages(ms)
        if err != nil {
                log.Fatalf("ConcatMessages failed, err=%v", err)
        }

        fmt.Println(sm)
}

func of[T any](t T) *T {
        return &t
}

工具调用

package main

import (
        "context"
        "fmt"
        "log"
        "os"

        "github.com/cloudwego/eino-ext/components/model/qianfan"
        "github.com/cloudwego/eino/schema"
)

func main() {
        ctx := context.Background()
        qcfg := qianfan.GetQianfanSingletonConfig()
        // How to get Access Key/Secret Key: https://cloud.baidu.com/doc/Reference/s/9jwvz2egb
        qcfg.AccessKey = "your_access_key"
        qcfg.SecretKey = "your_secret_key"

        modelName := os.Getenv("MODEL_NAME")
        cm, err := qianfan.NewChatModel(ctx, &qianfan.ChatModelConfig{
                Model:               modelName,
                Temperature:         of(float32(0.7)),
                TopP:                of(float32(0.7)),
                MaxCompletionTokens: of(1024),
        })
        if err != nil {
                log.Fatalf("NewChatModel of qianfan failed, err=%v", err)
        }

        err = cm.BindTools([]*schema.ToolInfo{
                {
                        Name: "user_company",
                        Desc: "Query the user's company and position information based on their name and email",
                        ParamsOneOf: schema.NewParamsOneOfByParams(
                                map[string]*schema.ParameterInfo{
                                        "name": {
                                                Type: "string",
                                                Desc: "The user's name",
                                        },
                                        "email": {
                                                Type: "string",
                                                Desc: "The user's email",
                                        },
                                }),
                },
                {
                        Name: "user_salary",
                        Desc: "Query the user's salary information based on their name and email",
                        ParamsOneOf: schema.NewParamsOneOfByParams(
                                map[string]*schema.ParameterInfo{
                                        "name": {
                                                Type: "string",
                                                Desc: "The user's name",
                                        },
                                        "email": {
                                                Type: "string",
                                                Desc: "The user's email",
                                        },
                                }),
                },
        })
        if err != nil {
                log.Fatalf("BindTools of qianfan failed, err=%v", err)
        }

        resp, err := cm.Generate(ctx, []*schema.Message{
                {
                        Role:    schema.System,
                        Content: "You are a real estate agent. Use the user_company and user_salary APIs to provide relevant property information based on the user's salary and job. Email is required",
                },
                {
                        Role:    schema.User,
                        Content: "My name is zhangsan, and my email is zhangsan@bytedance.com. Please recommend some suitable houses for me.",
                },
        })

        if err != nil {
                log.Fatalf("Generate of qianfan failed, err=%v", err)
        }

        fmt.Println(resp)
}

func of[T any](t T) *T {
        return &t
}

使用方式

组件初始化

Qianfan 模型通过 NewChatModel 函数进行初始化,主要配置参数如下:

import "github.com/cloudwego/eino-ext/components/model/qianfan"
ctx := context.Background()
qcfg := qianfan.GetQianfanSingletonConfig()
// How to get Access Key/Secret Key: https://cloud.baidu.com/doc/Reference/s/9jwvz2egb
qcfg.AccessKey = "your_access_key"
qcfg.SecretKey = "your_secret_key"

model, err := qianfan.NewChatModel(ctx, &qianfan.ChatModelConfig{
    Model:                 "ernie-3.5-8k",      // 模型名称, 指定使用的模型,例如 ERNIE-Bot-4
    Temperature:           of(float32(0.7)),    // 温度,较高的值(如 0.8)会使输出更随机,而较低的值(如 0.2)会使其更具确定性。默认 0.95,范围 (0, 1.0]
    TopP:                  of(float32(0.7)),      // Top-p,影响输出文本的多样性,取值越大,生成文本的候选词越多。默认 0.7,范围 [0, 1.0]
    MaxCompletionTokens:   of(1024),            // 生成结果的最大 token 数量, 范围 [2, 2048]
    Seed:                  of(0),                 // 随机种子, 用于生成可复现结果, 范围 (0, 2147483647)
    Stop:                  make([]string, 0),     // 停止符, 一个字符串列表,当模型生成其中任何一个字符串时,会停止生成
    PenaltyScore:          nil,                   // 重复惩罚, 通过对已生成的 token 增加惩罚,减少重复。较高的值意味着较大的惩罚。范围 [1.0, 2.0]
    LLMRetryCount:         of(0),                 // 请求失败的重试次数
    LLMRetryTimeout:       nil,                   // 每次重试请求的超时时间(秒)
    LLMRetryBackoffFactor: nil,                   // 重试之间的等待时间的增长因子

    FrequencyPenalty:  nil, // 频率惩罚, 根据 token 在文本中出现的频率来惩罚新 token。较高的值会鼓励模型使用较不频繁的 token
    PresencePenalty:   nil, // 存在惩罚, 根据 token 是否已在文本中出现来惩罚新 token。较高的值会鼓励模型引入新概念
    ParallelToolCalls: nil, // 并行工具调用, 是否允许模型并行调用多个工具
    ResponseFormat:    &qianfan.ResponseFormat{}, // 指定响应的格式,例如 JSON
})

生成对话

对话生成支持普通模式和流式模式:

// 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/qianfan"
    "github.com/cloudwego/eino/schema"
)

func main() {
    ctx := context.Background()
    qcfg := qianfan.GetQianfanSingletonConfig()
    // How to get Access Key/Secret Key: https://cloud.baidu.com/doc/Reference/s/9jwvz2egb
    qcfg.AccessKey = "your_access_key"
    qcfg.SecretKey = "your_secret_key"
    
    model, err := qianfan.NewChatModel(ctx, &qianfan.ChatModelConfig{
        Model:                 "ernie-3.5-8k",      
        Temperature:           of(float32(0.7)),    
        TopP:                  of(float32(0.7)),     
    })
    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/qianfan"
    "github.com/cloudwego/eino/schema"
)

func main() {
    ctx := context.Background()
    qcfg := qianfan.GetQianfanSingletonConfig()
    // How to get Access Key/Secret Key: https://cloud.baidu.com/doc/Reference/s/9jwvz2egb
    qcfg.AccessKey = "your_access_key"
    qcfg.SecretKey = "your_secret_key"
    
    model, err := qianfan.NewChatModel(ctx, &qianfan.ChatModelConfig{
        Model:                 "ernie-3.5-8k",      
        Temperature:           of(float32(0.7)),    
        TopP:                  of(float32(0.7)),     
    })
    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)