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-参考

相关推荐
kupeThinkPoem11 小时前
OpenAI最新大模型GPT-4o体验之Code Copilot AI编程大模型
人工智能·ai编程
AIBigModel16 小时前
开源Qwen凌晨暴击闭源Claude!刷新AI编程SOTA,支持1M上下文
开源·ai编程
量子位17 小时前
老黄自曝皮衣口袋藏“秘密期权池”!随时准备奖励员工,团队亿万富翁数量世界第一
ai编程
柒崽17 小时前
⁤⁤⁤‍‬⁣‌‍⁤‬‬⁤⁢⁣‍⁤⁢⁤‍⁢‬⁡⁡⁡‌⁣⁣⁡告别高额API!手把手教你在本地免费跑AI大模型,详细操作指南!
前端·ai编程
TGITCIC18 小时前
智能控制权回归:人机协创时代的极简主义编码革命
人工智能·copilot·ai编程·ai开发·ai ide·大模型辅助编程
扑克中的黑桃A18 小时前
从0开始部署个人博客——原以为又要烂尾,结果CodeBuddy带我嘎嘎乱杀
ai编程
倔强的石头_21 小时前
用 CodeBuddy 搭建我的个人博客:技术小白的零门槛建站实践指南
ai编程·腾讯
东哥说AI1 天前
守一晚上直播没抢到Trae SOLO邀请码,我反手用Builder模式一键开发出老师追着要的诗词抽背神器!
agent·ai编程·trae
cpp加油站1 天前
AI编程杀疯了!字节、腾讯、阿里一周三连击,程序员该何去何从
ai编程·trae
Baihai_IDP1 天前
实践出真知:构建编程智能体一年实践的经验教训分享
人工智能·llm·ai编程