关于解决Eino不兼容音音频输入的问题

17 阅读2分钟

有没有同学在用Eino调用阿里全模态模型的时候,特别是传音频的时候,但是又不想直接传音频的base64格式,而是直接输入音频的公网url,遇到了一些问题。

这是阿里官网的格式:

    messages=[
        {
            "role": "user",
            "content": [
                {
                    "type": "input_audio",
                    "input_audio": {
                        "data": "https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20250211/tixcef/cherry.wav",
                        "format": "wav",
                    },
                },
                {"type": "text", "text": "这段音频在说什么"},
            ],
        },
    ],

那我们怎么移植到go的Eino框架里面呢?

audio := schema.MessageInputAudio{
       MessagePartCommon: schema.MessagePartCommon{ // 明确指定嵌入的结构体字段名
          URL:      &audioURL,   // 指针类型,正确赋值
          MIMEType: "audio/wav", // 媒体类型匹配wav格式
       },
    }
    AudioPart := schema.MessageInputPart{
       Type:  schema.ChatMessagePartTypeAudioURL,
       Audio: &audio,
    }
    //TODO:构造多模态信息
    multiAut := schema.Message{
       Role:                  "user",
       UserInputMultiContent: []schema.MessageInputPart{AudioPart},
    }

    // 4. 核心修复:直接构建消息列表,放弃模板机制(无动态变量无需模板)
    messages := []*schema.Message{
       // 系统消息
       {
          Role: schema.System,
          Content: `你好你是语音情感助手!,请先转录用户的音频
`,
       },
       // 用户消息(直接传序列化后的字符串)
       &multiAut,
    }

这里我们可以看到我们直接传入URL会报错,官方源码直接把我们的url判定为非空,然后直接返回错误了,这就是错误的根源。

case schema.ChatMessagePartTypeAudioURL:
    if part.Audio == nil {
       return comMessage, errors.New("the 'audio' field is required for parts of type 'audio_url'")
    }
    if part.Audio.Base64Data != nil {
       format, ok := mimeType2AudioFormat[part.Audio.MIMEType]
       if !ok {
          return comMessage, fmt.Errorf("the 'format' field is required when type is audio_url, use SetMessageInputAudioFormat to set it")
       }
       comMessage.MultiContent = append(comMessage.MultiContent, openai.ChatMessagePart{
          Type: openai.ChatMessagePartTypeInputAudio,
          InputAudio: &openai.ChatMessageInputAudio{
             Data:   *part.Audio.Base64Data,
             Format: format,
          },
       })
    } else if part.Audio.URL != nil {
       format, ok := mimeType2AudioFormat[part.Audio.MIMEType]
       if !ok {
          return comMessage, fmt.Errorf("the 'format' field is required when type is audio_url, use SetMessageInputAudioFormat to set it")
       }
       //TODO:源码改动,这是已经改动的。
       comMessage.MultiContent = append(comMessage.MultiContent, openai.ChatMessagePart{
          Type: openai.ChatMessagePartTypeInputAudio,
          InputAudio: &openai.ChatMessageInputAudio{
             Data:   *part.Audio.URL,
             Format: format,
          },
       })
       //以下注释的地方就是原代码,这里我们按照格式修改源代码。
       //return comMessage, errors.New("for user role, audio message part does not accept URL, only base64 data is supported")
    } else {
       return comMessage, errors.New("audio message part must have url or base64 data")
    }

简化版 Debug & 代码修改流程

  1. 从业务代码 model.Stream(ctx, messages) 点进 Stream 方法;
  2. Stream 方法里找到 cm.cli.Stream(ctx, in, opts...),继续点进该底层实现;
  3. 找到请求构建代码 genRequest,在其中定位到消息构建的分支逻辑,选择 buildMessageFromUserInputMultiContent 方法并点击进入;
  4. 在该方法的 for _, part := range inMsg.UserInputMultiContent 循环里,找到 part.Audio.URL != nil 分支;
  5. 将该分支内的代码替换为指定的音频格式校验和消息构建代码。

总结:这算是Eino留下来的一个坑吧,从多模态的字段上看他是支持URL,但是实际情况把把这条路堵死了,就只能从stream一步一步追溯,修改他原本的逻辑了。