人工智能图像文本模型处理之SPring Ai
RAG(Retrieval-Augmented Generation,检索增强生成)是一种结合检索和生成的技术,用于提升生成式AI模型(如GPT等)的能力和准确性。RAG将外部知识检索与生成模型相结合,使模型能够基于实时或动态的数据生成更相关的答案
RAG的核心流程
检索阶段(Retrieval) :
从外部知识库(如文档数据库、向量数据库、搜索引擎)中检索与用户问题相关的上下文或信息。
检索到的内容可以是文本片段、表格数据等,通常通过语义搜索技术获取。
生成阶段(Generation) :
将检索到的上下文与用户问题结合,输入到生成式模型中。
模型利用这些上下文生成更准确、相关的回答。
RAG的优势
实时性:通过外部知识检索,RAG能够回答基于最新数据的问题,而不是依赖模型训练时的静态知识。
准确性:减少模型的“幻觉”问题(生成虚假或不相关内容),提高回答的相关性和可信度。
可控性:可以通过限制检索范围(如某一领域的知识库)来控制生成结果的质量和内容。
实现
文本向量化
RAG是基于本地知识库的检索增强问答功能,首先第一步是需要对本地知识库的文档进行向量化存储,之后用于对问题的检索。
- 嵌入模型
向量化存储需要用到向量数据库和嵌入模型。嵌入模型也就是embedding model,可以将文档解析成一个多维向量,具体生成多少维的向量基于你选择的嵌入模型。
嵌入模型可以选在在线API,很多模型厂商都提供嵌入模型的调用。我这里使用的是Ollama本地部署的嵌入模型,进入Ollama官网,搜索embedding,我选择了第一个进行下载。
进入模型详情页,有介绍如何拉取模型,值得注意的是,一般嵌入式模型是不需要通过ollama运行的,只需要拉取就行了。
详情页有介绍:
"num_ctx": 8192: 表示嵌入模型一次最多可以处理 8192 Tokens。这是模型的上下文窗口大小,用来限制输入文本的长度。
Spring AI 关于Ollama Embedding model中也有对这个参数的设置:
PropertyDescriptionDefaultspring.ai.ollama.embedding.options.num-ctxSets the size of the context window used to generate the next token.2048
Spring AI中设置的默认值是2048,所以,如果通过Spring AI调用Ollama Embedding model会限制模型输入最多2048个tokens。所以当输入的文本较长时,我们需要对文本进行分割处理。
当我们将嵌入模型拉取到我们本地安装的Ollama之后,我们就可以在Spring AI中集成文档向量化功能了。
2. Spring AI集成Ollama嵌入模型
首先引入相关的依赖pom.xml:
xml 体验AI代码助手 代码解读复制代码
这里把对话模型openai的依赖也一并引入了,值得注意的是,Spring AI是支持zhipu AI大模型的,zhipu ai模型支持openapi 规范,所以我们可以使用openai的依赖来调用智普提供的api。
通过配置application.yml就可以实现,代码如下:
yaml 体验AI代码助手 代码解读复制代码ai:
openai:
api-key: ${ZHIPU_AI_API_KEY}
base-url: https://open.bigmodel.cn/api/paas/
chat:
api-key: ${ZHIPU_AI_API_KEY}
base-url: https://open.bigmodel.cn/api/paas/
completions-path: /v4/chat/completions
options:
model: GLM-4-flash
如果用的智普的api 可以使用zhipu ai配置
zhipuai:
api-key: $
ollama:
base-url: http://localhost:11434
# ollama版本更新,api路径修改,spring ollama内置的api跟老版本ollama对应不上,导致报错:404。措施:升级ollama至0.45
embedding:
options:
num-batch: 512
model: nomic-embed-text
相关的API Key可以去官网申请,我用的智普提供的免费模型GLM-4-flash。
相关配置完成后,接着就开始实现文本嵌入功能,主要流程就是获取上传的文档内容,然后进行向量化,将向量化的数据保存到向量数据库,同时保存一些元数据信息,用于检索时使用。
主要代码如下:
typescript 体验AI代码助手 代码解读复制代码@Component
@Slf4j
public class Embedding {
private final VectorStore vectorStore;
public Embedding(VectorStore vectorStore) {
this.vectorStore = vectorStore;
}
@Async
public void save(String content, Map<String, Object> meta) {
log.info("正在向量化...");
Document document = new Document(content, meta);
vectorStore.add(List.of(document));
log.info("完成向量化!");
}
}
这里的vectorStore就是我们引入的ES向量数据库,使用add命令就同步完成了文档向量化操作和文档的保存。
我们也可以对vectorStore做一下自定义配置:
java 体验AI代码助手 代码解读复制代码@Bean
public ElasticsearchVectorStore vectorStore(OllamaEmbeddingModel embeddingModel, RestClient restClient) {
ElasticsearchVectorStoreOptions options = new ElasticsearchVectorStoreOptions();
options.setIndexName("ai-knowledge"); // 设置es索引名称
options.setDimensions(768); // 设置向量化的维度大小
return new ElasticsearchVectorStore(options, restClient, embeddingModel, true);
}
-
检索问答
当然在我们进行检索问答前,你自己需要实现文档上传功能,上传功能中调用文档向量化组件用来将文档保存到向量数据库中。
当用户提问时,问题需要先向量化处理后去向量数据库中检索相似度最高的几段文本返回,这几段文本作为问题的上下文结合问题一起发送给大模型,大模型就基于这个上下文进行回答。这就是RAG的主要思想。非常简单,就是提供更加丰富的信息给大模型,让它回答的更准确。
下面是流式回答的实现代码:
less 体验AI代码助手 代码解读复制代码@RestController
@RequestMapping("/api/v1/rag")
@Slf4j
public class RagChatController {
private final ChatClient chatClient;
public RagChatController(ElasticsearchVectorStore vectorStore, OpenAiChatModel chatModel) {
this.chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(new MessageChatMemoryAdvisor(new InMemoryChatMemory()),
new QuestionAnswerAdvisor(vectorStore)
)
.build();
}@GetMapping("/chat")
public Map<String, String> generate(@RequestParam(value = "message", defaultValue = "Tell me a joke") String message) {
String response = this.chatClient.prompt()
.advisors(advisor -> advisor.param("chat_memory_conversation_id", "678")
.param("chat_memory_response_size", 100))
.user(message)
.call()
.content();
return Map.of("generation", response);
}
}
MessageChatMemoryAdvisor是用于实现对话记忆。
QuestionAnswerAdvisor是用于实现RAG的。
浙公网安备 33010602011771号