7-多模态-让AI助手充满诗意

1-背景故事

前几篇我们从零开始,一步步创建并让AI助手有了各种能力。

但是只会文字的AI助手,终究还是差了那么点人味不是么?

好在现在已经有了多模态模型,可以理解并输出图片、文字、音频、视频等各种内容。

接下来让我们借助多模态模型,让AI助手能根据情景(图片)随兴作诗,并进一步通过流式输出让AI助手的输出更"流畅"。

2-动手实践

2.1-前置条件

作为本篇的准备工作,你需要先完成《1-Spring AI手把手教程-亲手创造我的AI助手

其它篇的内容,则不是必须。

2.2-准备多模态模型

之前用到的模型,都属于大语言模型嵌入模型,今天我们需要用到多模态大模型,通过搜索引擎、问AI助手或你能想到的任何方式,很容易找到支持图片的大模型。

作为示例,我们依然选择白嫖openrouter.ai,通过搜索支持图片的免费模型,我们可以找到一堆免费模型,找一个替换application.yaml配置文件中的模型:

yaml 复制代码
spring:
  ai:
    openai:
      base-url: "https://openrouter.ai/api"
      api-key: "" #都白嫖了,你自己去注册并申请一个api-key吧
      chat:
        options:
          # 千问图片理解模型
          model: "qwen/qwen2.5-vl-32b-instruct:free"

2.3-编写代码

在我们原来的控制器类中添加根据图片作诗的API入口和代码:

java 复制代码
@RestController
public class MyChatController {
    // Spring AI聊天客户端
    private final ChatClient chatClient;

    // 注入ChatClient生成器,Spring Boot已经根据上文的配置自动为我们创建了一个生成器实例
    public MyChatController(ChatClient.Builder chatClientBuilder) {
        this.chatClient = chatClientBuilder.build();
    }
    
    //...

    // 根据图片作诗的API入口
    @PostMapping(path = "multimodal/pic-to-poe")
    public String picToPoe(MultipartFile file) {
        return chatClient.prompt()
                .user(u -> u
                        .text("根据图片作一首诗")
                        .media(MimeTypeUtils.IMAGE_PNG, file.getResource()))
                .call()
                .content();
    }
}

2.4-测试效果

使用你喜欢的http客户端测试效果,本文还是以curl为例:

shell 复制代码
# 使用新添加的图片接口上传一张图片
curl -F "file=@ai-assistant/src/main/resources/multimodal.test.png" http://localhost:8080/multimodal/pic-to-poe
# AI助手的回复:
金篮静置果香浓,
黄蕉红果映晨风。
日常小景藏诗意,
清韵悠然入梦中。

可以看出AI助手根据我上传的图片内容,作出了一首七言律诗。诗意优雅,就是突然迸出四句诗,有点突兀。

接下来我们用流式输出的方式,让AI助手作诗的过程更"流畅":

2.5-编写流式输出代码

在原代码的基础上、再增加一种返回方式:

java 复制代码
@RestController
public class MyChatController {
    //...

    // (Spring)流式输出
    @PostMapping(path = "multimodal/pic-to-poe/stream")
    public Flux<String> picToPoeStream(MultipartFile file) {
        return chatClient.prompt()
                .user(u -> u
                        .text("根据图片作一首诗")
                        .media(MimeTypeUtils.IMAGE_PNG, file.getResource()))
                //与非流式输出的关键区别,是使用stream()代替call()
                .stream()
                .content();
    }
}

2.6-测试流式输出

再次用工具测试以上接口,会发现AI助手的输出是一个字接一个字,看起来流畅很多:

shell 复制代码
# 使用新添加的图片接口上传一张图片
curl -F "file=@ai-assistant/src/main/resources/multimodal.test.png" http://localhost:8080/multimodal/pic-to-poe/stream
# AI助手的回复:
金篮轻悬映晨光,
黄蕉红果溢芬芳。
静谧时光添雅趣,
一隅清欢入梦长。

纯文字展示不出实际效果,还希望各位亲自动手实践。

2.7-编写SSE输出代码

细心的小伙伴会发现,上面的流式输出格式,并不是我们编写前端聊天代码、或客户端工具时常用到的流式输出格式。

这是因为聊天工具中所谓的流式输出,实际上是指"服务器发送事件SSE"。

使用SSE的代码也不难:

java 复制代码
@RestController
public class MyChatController {
    //...

    // 使用SSE流式输出
    @PostMapping(path = "multimodal/pic-to-poe/sse")
    public SseEmitter picToPoeSse(MultipartFile file) {
        // 创建 SSE 发射器,设置超时时间(例如 1 分钟)
        SseEmitter emitter = new SseEmitter(60_000L);
        // 调用大模型
        chatClient.prompt()
                .user(u -> u
                        .text("根据图片作一首诗")
                        .media(MimeTypeUtils.IMAGE_PNG, file.getResource()))
                //与非流式输出的关键区别,是使用stream()代替call()
                .stream()
                .content()
                //与普通流式输出的区别,是每收到一次大模型的输出,都转换为服务器事件"发射"给客户端
                .doOnEach(s-> {
                    try {
                        emitter.send(SseEmitter.event()
                                .data(s)
                                .id(String.valueOf(System.currentTimeMillis()))
                                .build());
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                })
                // 注意大模型流结束时,也要告诉发射器结束了,否则客户端会继续等着你的输出
                .doOnTerminate(emitter::complete)
                .subscribe();
        return emitter;
    }
}

2.8-测试SSE输出

SSE主要用于代码集成,输出就没那么直观了:

shell 复制代码
# 使用新添加的图片接口上传一张图片
curl -F "file=@ai-assistant/src/main/resources/multimodal.test.png" http://localhost:8080/multimodal/pic-to-poe/sse
# AI助手的回复(篇幅有限,下面省略了很多输出):
data:金
id:1753341478512
...
data:时光
id:1753341479023
...
data:。
id:1753341479515

3-课外拓展

  1. 验证更多的模型,看看哪个效果好;
  2. 使用不同的temperature参数,让输出更随机/确定;
  3. 探索除图片外的更多多模型模型的输入输出,如:音视频输入、输出。

4-参考

相关推荐
寒秋丶1 天前
AutoGen多智能体协作、人机交互与终止条件
人工智能·python·microsoft·ai·人机交互·ai编程·ai写作
潘小安1 天前
跟着 AI 学(三)- spec-kit +claude code 从入门到出门
前端·ai编程·claude
DO_Community1 天前
AI 推理 GPU 选型指南:从 A100 到 L40S 再看 RTX 4000 Ada
人工智能·aigc·ai编程·ai推理
用户4099322502121 天前
快速入门Vue模板里的JS表达式有啥不能碰?计算属性为啥比方法更能打?
前端·ai编程·trae
飞哥数智坊1 天前
AI Coding 新手常见的3大误区
人工智能·ai编程
湘-枫叶情缘1 天前
敏捷开发遇阻?AI编程或让传统开发逢春
ai编程·敏捷流程
伟贤AI之路2 天前
开源!纯 HTML 实现支持 0.75~2× 变速、iOS 熄屏防中断的英语点读站
人工智能·ai编程
苦瓜汤补钙2 天前
论文阅读——Segment Anything(Meta AI)——SAM
论文阅读·图像处理·人工智能·nlp·ai编程
码间拾光・菲林斯2 天前
AI提示词:别再把提示词当 “聊天”—— 它是人机协作的 “接口定义”
人工智能·ai编程
深藏blue472 天前
OpenAI Codex & Codex CLI 国内使用教程:手把手教你安装与上手
openai·ai编程