指定返回实体类型
示例
java
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(chatMemoryAdvisor).build();
List<Book> books = chatClient.prompt().user("生成四大名著和对应的作者").call()
.entity(new ParameterizedTypeReference<List<Book>>() {
});
for (Book book : books) {
System.out.println(book);
// ColorLogger.info(book.toString());
}
转换后提示词
- org.springframework.ai.converter.BeanOutputConverter#getFormat
生成四大名著和对应的作者
Your response should be in JSON format.
Do not include any explanations, only provide a RFC8259 compliant JSON response following this format without deviation.
Do not include markdown code blocks in your response.
Remove the ```json markdown from the output.
Here is the JSON Schema instance your output must adhere to:
```{
"$schema" : "https://json-schema.org/draft/2020-12/schema",
"type" : "array",
"items" : {
"type" : "object",
"properties" : {
"actor" : {
"type" : "string"
},
"bookName" : {
"type" : "string"
}
},
"additionalProperties" : false
}
}```
通过将提示词发送给 ai,可以看到如下返回结构
[{"actor":"罗贯中","bookName":"三国演义"},{"actor":"施耐庵","bookName":"水浒传"},{"actor":"吴承恩","bookName":"西游记"},{"actor":"曹雪芹","bookName":"红楼梦"}]
顾问
CompressionQueryTransformer 压缩查询转换器
使用大型语言模型将对话历史和后续查询压缩成一个独立的查询,该查询能够捕捉对话的本质。
java
Query query = Query.builder()
.text("And what is its second largest city?")
.history(new UserMessage("What is the capital of Denmark?"),
new AssistantMessage("Copenhagen is the capital of Denmark."))
.build();
QueryTransformer queryTransformer = CompressionQueryTransformer.builder()
.chatClientBuilder(chatClientBuilder)
.build();
Query transformedQuery = queryTransformer.transform(query);
RewriteQueryTransformer 重写查询转换器
java
Query query = new Query("I'm studying machine learning. What is an LLM?");
QueryTransformer queryTransformer = RewriteQueryTransformer.builder()
.chatClientBuilder(chatClientBuilder)
.build();
Query transformedQuery = queryTransformer.transform(query);
MultiQueryExpander 多查询扩展器
使用大型语言模型将查询扩展为多个语义不同的变体,以捕捉不同的视角,这有助于检索额外的上下文信息并增加找到相关结果的机会。
java
MultiQueryExpander queryExpander = MultiQueryExpander.builder()
.chatClientBuilder(chatClientBuilder)
.numberOfQueries(3)
.build();
List<Query> queries = queryExpander.expand(new Query("How to run a Spring Boot app?"));
检索
完整的Demo
java
public static void main(String[] args) {
ConfigurableApplicationContext app = SpringApplication.run(SpringAiApplication.class, "--spring.profiles.active=mi");
ChatModel chatModel = app.getBean(ChatModel.class);
// 工具回调提供者
MethodToolCallbackProvider toolCallbackProvider = MethodToolCallbackProvider.builder().
toolObjects(new DateTimeTools()).build();
// 工具回调解析器
StaticToolCallbackResolver staticToolCallbackResolver = new StaticToolCallbackResolver(Arrays.stream(toolCallbackProvider.getToolCallbacks()).toList());
// 工具调用管理器
DefaultToolCallingManager toolCallingManager = DefaultToolCallingManager.builder()
.toolCallbackResolver(staticToolCallbackResolver)
.toolExecutionExceptionProcessor(new ToolExecutionExceptionProcessor() {
// 异常处理器
@Override
public String process(ToolExecutionException exception) {
ToolDefinition toolDefinition = exception.getToolDefinition();
System.err.println("Tool Error: " + toolDefinition);
return exception.getMessage();
}
})
.build();
// 会话上下文
ChatOptions chatOptions = ToolCallingChatOptions.builder()
.toolCallbacks(toolCallbackProvider.getToolCallbacks())
// 决定函数是内部自动调用还是外部调用 (这里保持 false 以使用手动循环)
.internalToolExecutionEnabled(false)
.build();
// 会话记忆
ChatMemory chatMemory = MessageWindowChatMemory.builder().maxMessages(20).build();
String conversationId = UUID.randomUUID().toString();
// 会话客户端
ChatClient chatClient = ChatClient.builder(chatModel).defaultOptions(chatOptions).build();
Scanner scanner = new Scanner(System.in);
System.out.println("=== AI 聊天助手启动 (输入 'exit' 退出) ===");
while (true) {
System.out.print("\n锦衣卫·智问: ");
String input = scanner.nextLine();
if ("exit".equalsIgnoreCase(input)) {
break;
}
// 1. 构建 Prompt 并存入记忆
Prompt prompt = new Prompt(input, chatOptions);
chatMemory.add(conversationId, prompt.getInstructions());
// 2. 从记忆中获取完整的上下文
Prompt promptWithMemory = new Prompt(chatMemory.get(conversationId), chatOptions);
// 3. 发起调用
ChatResponse chatResponse = chatClient.prompt(promptWithMemory).call().chatResponse();
chatMemory.add(conversationId, chatResponse.getResult().getOutput());
// 4. 处理工具调用循环
while (chatResponse.hasToolCalls()) {
System.out.println("[System] 正在调用工具...");
ToolExecutionResult toolExecutionResult = toolCallingManager.executeToolCalls(promptWithMemory, chatResponse);
// 使用工具执行结果更新 Prompt
promptWithMemory = new Prompt(toolExecutionResult.conversationHistory(), chatOptions);
// 再次调用模型
chatResponse = chatModel.call(promptWithMemory);
chatMemory.add(conversationId, chatResponse.getResult().getOutput());
}
System.out.println("AI: " + chatResponse.getResult().getOutput().getText());
}
}
static class DateTimeTools {
@Tool(description = "负责查询天气")
public String query(
@ToolParam(description = "要查询天气的城市") String cityName) {
String[] weathers = {"晴天", "多云", "小雨", "中雨", "大雪", "雷阵雨"};
int temp = ThreadLocalRandom.current().nextInt(-5, 35);
int humidity = ThreadLocalRandom.current().nextInt(30, 90);
int wind = ThreadLocalRandom.current().nextInt(1, 6);
String weather = weathers[ThreadLocalRandom.current().nextInt(weathers.length)];
String format = String.format(
"%s 当前天气:%s,温度 %d°C,湿度 %d%%,风力 %d 级",
cityName, weather, temp, humidity, wind
);
// System.out.println(format);
return format;
}
}
想法或问题?在 GitHub Issue 下方参与讨论
去评论