feat(ai): 新增多模态与结构化输出功能支持

- 引入 Cassandra作为聊天记忆存储后端
- 配置 DashScope 多模态模型支持图文输入- 新增结构化输出控制器,支持 Bean、Map、List 等格式转换
- 添加文生图接口,集成阿里百炼图像生成能力
- 更新应用配置以支持多模态及持久化聊天记录
- 升级依赖项,引入 DashScope SDK 和 Cassandra 支持库
- 创建 ActorFilmography 和 Book 数据模型用于结构化响应
- 调整 ChatClient 配置以适配新的多模态与记忆逻辑
This commit is contained in:
2025-10-27 22:11:08 +08:00
parent d12334fe36
commit 594adcc48d
11 changed files with 331 additions and 10 deletions

View File

@@ -0,0 +1,127 @@
package com.hanserwei.airobot.controller;
import com.hanserwei.airobot.model.ActorFilmography;
import com.hanserwei.airobot.model.Book;
import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.ai.converter.BeanOutputConverter;
import org.springframework.ai.converter.ListOutputConverter;
import org.springframework.ai.converter.MapOutputConverter;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/v8/ai")
public class StructuredOutputController {
@Resource
private ChatClient chatClient;
/**
* 示例1: BeanOutputConverter - 获取演员电影作品集
*
* @param name
* @return
*/
@GetMapping("/actor/films")
public ActorFilmography generate(@RequestParam(value = "name") String name) {
// 一次性返回结果
return chatClient.prompt()
.user(u -> u.text("""
请为演员 {actor} 生成包含5部代表作的电影作品集,
只包含 {actor} 担任主演的电影,不要包含任何解释说明。
""")
.param("actor", name))
.call()
.entity(ActorFilmography.class);
}
/**
* 示例2: MapOutputConverter - 获取编程语言信息
* @param language
* @return
*/
@GetMapping("/language-info")
public Map<String, Object> getLanguageInfo(@RequestParam(value = "lang") String language) {
String userText = """
请提供关于编程语言 {language} 的结构化信息,包含以下字段:"
name (语言名称), "
popularity (流行度排名,整数), "
features (主要特性,字符串数组), "
releaseYear (首次发布年份). "
不要包含任何解释说明,直接输出 JSON 格式数据。
""";
return chatClient.prompt()
.user(u -> u.text(userText).param("language", language))
.call()
.entity(new MapOutputConverter());
}
/**
* 示例3: ListOutputConverter - 获取城市列表
* @param country
* @return
*/
@GetMapping("/city-list")
public List<String> getCityList(@RequestParam(value = "country") String country) {
return chatClient.prompt()
.user(u -> u.text(
"""
列出 {country} 的8个主要城市名称。
不要包含任何编号、解释或其他文本,直接输出城市名称列表。
""")
.param("country", country))
.call()
.entity(new ListOutputConverter(new DefaultConversionService()));
}
/**
* 使用低级 API 的 BeanOutputConverter - 获取书籍信息
* @param bookTitle
* @return
*/
@GetMapping("/book-info")
public Book getBookInfo(@RequestParam(value = "name") String bookTitle) {
// 使用 BeanOutputConverter 定义输出格式
BeanOutputConverter<Book> converter = new BeanOutputConverter<>(Book.class);
// 提示词模板
String template = """
请提供关于书籍《{bookTitle}》的详细信息:
1. 作者姓名
2. 出版年份
3. 主要类型(数组)
4. 书籍描述不少于50字
不要包含任何解释说明,直接按指定格式输出。
{format}
""";
// 创建 Prompt
PromptTemplate promptTemplate = new PromptTemplate(template);
Prompt prompt = promptTemplate.create(Map.of(
"bookTitle", bookTitle,
"format", converter.getFormat()
));
// 调用模型并转换结果
String result = chatClient.prompt(prompt)
.call()
.content();
// 结构化转换
return converter.convert(result);
}
}