当前位置: 首页 > news >正文

Spring AI:RAG函数调用

1. 为什么需要RAG?

标准的大语言模型(LLM)的知识仅限于其训练数据,这意味着:

  • 知识陈旧:它们不知道训练日期之后发生的任何事情。

  • 缺乏私有知识:它们不了解你公司的内部文档、产品手册或数据库内容。

  • 可能产生幻觉:在不确定的领域,模型可能会编造听起来合理但实际上是错误的答案。

RAG通过将外部的、实时的、私有的知识注入到LLM的提示中,完美地解决了这些问题。它将LLM从一个“封闭的知识库”转变为一个能够利用特定数据进行推理的“开放式推理引擎”。

2. RAG在Spring AI中的工作流程

整个流程可以分解为两个阶段:

数据准备阶段(离线处理):

  1. 加载文档(Load):从各种来源(如PDF, MarkDown, JSON, 网站)加载你的私有数据。

  2. 分割(Split):将长文档分割成更小的、语义完整的文本块(Chunks)。

  3. 嵌入(Embed):使用EmbeddingClient将每个文本块转换成一个向量(一长串数字),这个向量代表了文本的语义含义。

  4. 存储(Store):将文本块和其对应的向量存储在一个专门的向量数据库中。

查询阶段(实时处理):

  1. 用户提问:用户提出一个问题。

  2. 嵌入问题:使用相同的EmbeddingClient将用户的问题也转换成一个向量。

  3. 相似性搜索(Search):在向量数据库中,使用问题的向量去搜索最相似的文本块向量(即内容最相关的知识片段)。

  4. 增强提示(Augment):将搜索到的相关知识片段与用户的原始问题结合,形成一个内容丰富的“增强提示”。

  5. 调用模型(Generate):将这个增强后的提示发送给ChatClient。由于模型现在有了相关的上下文,它能够生成一个基于你私有数据的、更准确、更具事实性的回答。

3. 代码示例:构建一个简单的RAG应用

假设我们要构建一个基于Spring官方文档的问答机器人。

依赖 (pom.xml): 你需要添加一个向量数据库的starter。为了简单起见,我们使用一个内存中的向量数据库。

<dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>
<dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-vector-store-simple</artifactId>
</dependency>

实现步骤:

import org.springframework.ai.reader.pdf.PagePdfDocumentReader;
import org.springframework.ai.transformer.splitter.TokenTextSplitter;
import org.springframework.ai.vectorstore.SimpleVectorStore;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;@Service
public class RagService {@Value("classpath:/docs/spring-documentation.pdf") // 假设你有一个PDF文档private Resource pdfResource;@Autowiredprivate VectorStore vectorStore; // Spring AI会自动注入配置好的VectorStore// 1. 数据准备:在应用启动时加载PDF并存入向量数据库@PostConstructpublic void init() {// 使用Tika解析PDFvar pdfReader = new PagePdfDocumentReader(pdfResource);var textSplitter = new TokenTextSplitter();// 将文档分割、嵌入并存储vectorStore.accept(textSplitter.apply(pdfReader.get()));}// 2. 查询阶段public String answer(String question) {// 在向量数据库中搜索相关文档List<Document> similarDocuments = vectorStore.similaritySearch(question);// 将文档内容组装成上下文String context = similarDocuments.stream().map(Document::getContent).collect(Collectors.joining(System.lineSeparator()));// 创建提示模板String promptTemplate = """根据以下信息回答问题:---{context}---问题: {question}""";PromptTemplate template = new PromptTemplate(promptTemplate);Prompt prompt = template.create(Map.of("context", context, "question", question));// 调用AI模型return chatClient.call(prompt).getResult().getOutput().getContent();}
}

通过这个服务,你的应用现在可以回答关于spring-documentation.pdf的具体问题了,而不是依赖LLM的通用知识。


深度解析:函数调用 (Function Calling)

1. 为什么需要函数调用?

函数调用赋予了LLM“执行动作”的能力。它允许模型请求调用你的Java代码来完成特定任务,例如:

  • 获取实时信息:查询天气、股票价格、航班信息。

  • 与外部系统交互:在数据库中下单、发送邮件、调用另一个微服务。

  • 执行复杂计算:执行精确的数学运算。

这极大地扩展了LLM的应用范围,使其从一个聊天机器人转变为一个智能的“调度中心”或“代理”(Agent)。

2. 函数调用在Spring AI中的工作流程

  1. 定义函数:在你的Java代码中创建一个Bean,它是一个标准的java.util.function.Function。这个函数的输入和输出最好是结构化的POJO。

  2. 描述函数:在调用ChatClient时,通过ChatOptions将函数的名称描述传递给模型。这个描述至关重要,模型会根据这个描述来决定何时以及如何使用这个函数。

  3. 模型决策:当你向模型提问时(例如“上海今天的天气怎么样?”),模型会分析你的问题和它所知道的函数。如果它认为getWeather函数可以回答这个问题,它不会直接回答,而是返回一个特殊的指令,告诉Spring AI:“请调用名为getWeather的函数,参数是{ "location": "上海" }”。

  4. Spring AI执行:Spring AI框架会自动拦截这个指令,查找名为getWeather的Bean,然后用模型提供的参数去执行这个Java函数。

  5. 返回结果:函数执行的结果(例如,天气数据对象)会被发送回给模型。

  6. 最终回答:模型接收到函数的返回结果后,会用自然语言组织并生成最终的用户答案(例如“上海今天晴天,气温25摄氏度。”)。

3. 代码示例:查询天气

定义一个Function Bean:

import java.util.function.Function;@Configuration
public class AppConfiguration {// 定义函数的输入和输出类型public record Request(String location) {}public record Response(double temp, String unit) {}@Bean@Description("获取指定城市的天气信息") // 这个描述非常重要!模型会读取它public Function<Request, Response> getWeather() {return request -> {// 这里应该是调用真实天气API的逻辑// 为简化示例,我们返回一个固定的值System.out.println("正在调用天气API获取 " + request.location() + " 的天气...");return new Response(25.0, "摄氏度");};}
}

在Service中调用:

@Service
public class WeatherService {@Autowiredprivate ChatClient chatClient;public String getWeather(String message) {ChatResponse response = chatClient.call(new Prompt(message,OpenAiChatOptions.builder().withFunction("getWeather") // 告诉模型可以使用这个函数.build()));// 检查模型是否请求调用函数if (response.getResult().getOutput().getToolCalls() != null) {// Spring AI会自动处理函数的调用和结果返回,对于开发者来说通常是透明的// 最终会返回一个综合了函数结果的自然语言回答}return response.getResult().getOutput().getContent();}
}

现在,当你调用weatherService.getWeather("帮我查一下伦敦的天气")时,getWeather这个Java Bean就会被自动触发,并将结果用于生成最终的回答。

总结

通过掌握RAG和函数调用,你可以利用Spring AI构建出远超简单问答机器人的复杂应用。

  • RAG 让你的应用变得博学,能够基于特定领域的私有数据提供精准回答。

  • 函数调用让你的应用变得能干,能够与外部世界交互并执行实际任务。

将这两者结合起来,你就可以构建出真正能够解决复杂业务问题的、高度智能化的企业级Java应用。

http://www.dtcms.com/a/405915.html

相关文章:

  • 基于Amazon S3设置AWS Transfer Family Web 应用程序
  • 稳石氢能出席AEM电解水学术与产业化论坛,大标方AEM制氢设备批量化生产荣获技术卓越奖。
  • 渲染 Python 中用 LaTeX 语法定义的数学公式 - 例子
  • 菊风金融智能双录:为金融业务合规与信任保驾护航
  • Debian安装PVE
  • 云计算实验2——CentOS中zookeeper的安装
  • 网络管理实验1:ASN.1软件应用
  • 前端开发用什么工具?前端开发工具推荐清单、实用对比与我的使用心得
  • 做亚马逊有什么网站可以借鉴大连城市建设档案馆官方网站
  • 协议不通,数据何通?耐达讯自动化Modbus TCP与Profibus网关技术破解建筑自动化最大瓶颈
  • 新零售模式下仓储变化与发展趋势
  • Stable Video Diffusion:将潜在视频扩散模型扩展到大规模数据集——论文阅读
  • [linux仓库]解剖ELF:从文件头到进程地址空间的完美映射
  • Lisp 与 C# 交互中,类型码(TypeCode)的映射关系
  • Java基础(十四):枚举类详解
  • python+springboot+uniapp基于微信小程序的任务打卡系统
  • 【多线程】计算机领域中的各种锁
  • python+uniapp基于微信小程序的医院陪诊预约系统
  • 免费html网页模板 html5网站模板 静态网页模板
  • 网站怎么做区域性优化公司网站建设会计上怎么处理
  • 专业商城网站建设价格定制家具网
  • ABCTorrents官网入口 – 磁力搜索引擎网站
  • python+django/flask+uniapp基于微信小程序的瑜伽体验课预约系统
  • 设计模式(C++)详解——解释器模式(1)
  • iOS 26 系统流畅度深度剖析,Liquid Glass 视效与界面滑动的实际测评
  • mysql asp网站2001国产卡一卡二新区
  • Docker容器化部署简要指南
  • 一文掌握 Apache SeaTunnel 构建系统与分发基础架构
  • 【数据结构】常用数据结构深度剖析
  • 适合小型网络公司的建站方式可以为网络黄页推广大全4