您的位置:首页 > 财经 > 产业 > 马蹄室内设计网论坛_常州百度seo_合肥网站推广_上海seo网站排名优化公司

马蹄室内设计网论坛_常州百度seo_合肥网站推广_上海seo网站排名优化公司

2025/4/18 21:35:19 来源:https://blog.csdn.net/weixin_43828090/article/details/147200952  浏览:    关键词:马蹄室内设计网论坛_常州百度seo_合肥网站推广_上海seo网站排名优化公司
马蹄室内设计网论坛_常州百度seo_合肥网站推广_上海seo网站排名优化公司

SpringAi主要依赖

在这里插入图片描述
System Prompt :设置提示词 用来预设角色
ConversationMemory: 对话集
RAG: 检索增强生成 将业务数据存储在向量数据库中(做相似性检索)通过RAG进行链接
Function Calling 用来调用自己的api

 <dependencyManagement><dependencies><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-bom</artifactId><version>1.0.0-M6</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement>
 <dependencies><!--Springai依赖--><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-ollama-spring-boot-starter</artifactId></dependency><!--pgVector向量数据库--><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-pgvector-store-spring-boot-starter</artifactId></dependency><!--doc解析器--><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-tika-document-reader</artifactId></dependency><!--分词器--><dependency><groupId>com.knuddels</groupId><artifactId>jtokkit</artifactId><version>1.1.0</version></dependency></dependencies>

yml配置

  ai:ollama:language: zhresponse-language: zhchat:options:model: qwen2:7bembedding:enabled: truemodel: bge-m3base-url: http://172.21.198.208:11434/vectorstore:pgvector:index-type: HNSWdistance-type: COSINE_DISTANCEdimension: 1024batching-strategy: TOKEN_COUNTmax-document-batch-size: 10000

chat.options.model: qwen2:7b 这个是默认的模型(但是后期不需要了我们从数据库中拿)
embedding.model: bge-m3 这个模型是用来进行向量化的模型
base-url: http://172.21.198.208:11434/ 这个是ollama模型访问地址
我这里用的是pgvector来作为向量库 vectorstore.pgvector:
dimension: 1024 这个至关重要 这里写多少创建向量库的时候就要写多少 如果两边不一致的话向向量库向量会失败

创建向量库

-- 增加扩展
create extension if not exists vector;
create extension if not exists hstore;
create extension if not exists "uuid-ossp";create table if not exists vector_store
(id        uuid primary key default uuid_generate_v4(),content   text,metadata  json,embedding vector(1024)  //这个的参数要和配置文件的一样,要不插入不进去 这个就是向量数据库的维度
);create index on vector_store using hnsw(embedding vector_cosine_ops);

ollama对话模型

/*** Ollama模型配置类* 负责配置和管理不同的Ollama模型实例*/
@Slf4j
@Configuration
public class OllamaModelConfig {/*** 创建OllamaChatModel的通用方法* @param baseUrl Ollama服务地址* @param modelName 模型名称* @param temperature 温度参数* @return 配置好的OllamaChatModel实例*/public OllamaChatModel createOllamaModel(String baseUrl, String modelName, double temperature) {Assert.hasText(baseUrl, "Ollama服务地址不能为空");Assert.hasText(modelName, "模型名称不能为空");Assert.isTrue(temperature >= 0 && temperature <= 1, "温度参数必须在0-1之间");try {log.debug("开始创建Ollama模型: {}, 温度参数: {}, 服务地址: {}", modelName, temperature, baseUrl);OllamaApi ollamaApi = new OllamaApi(baseUrl);OllamaOptions options = OllamaOptions.builder().model(modelName).temperature(temperature).build();OllamaChatModel model = OllamaChatModel.builder().ollamaApi(ollamaApi).defaultOptions(options).observationRegistry(ObservationRegistry.NOOP).build();log.debug("Ollama模型创建成功: {}", modelName);return model;} catch (Exception e) {log.error("创建Ollama模型失败: {}", modelName, e);throw new RuntimeException("创建Ollama模型失败: " + modelName, e);}}
}

temperature温度的值越小回答的越准确

调用实例

 	    // 使用模型配置创建OllamaChatModelOllamaChatModel chatModel = ollamaModelConfig.createOllamaModel(aiModel.getAimodelAccessAddress(),aiModel.getAimodelName(),aiModel.getAimodelTemperature());ChatClient chatClient = ChatClient.builder(chatModel).build();// 使用响应式流处理对话return chatClient.prompt().user(message).stream().content().onErrorResume(e -> {log.error("聊天过程发生错误: ", e);return Flux.just("抱歉,处理您的请求时出现了错误,请稍后重试。");});

接下来介绍上面提到的主要的四个属性

1.System Prompt :设置提示词 用来预设角色

 systemPrompt="你是一位专业的室内设计顾问,精通各种装修风格、材料选择和空间布局。请基于提供的参考资料,为用户提供专业、详细且实用的建议。在回答时,请注意:\n" +"1. 准确理解用户的具体需求\n" +"2. 结合参考资料中的实际案例\n" +"3. 提供专业的设计理念和原理解释\n" +"4. 考虑实用性、美观性和成本效益\n" +"5. 如有需要,可以提供替代方案";// 创建聊天客户端ChatClient chatClient = ChatClient.builder(ollamaChatModel).defaultSystem(systemPrompt).build();

将预设词放入defaultSystem中之后ai就会根据我们的提示词进行工作

2.ConversationMemory: 对话集

package com.system.ai.config;import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.List;//持久化对话
@Slf4j
@Configuration
public class AiChatMemory implements ChatMemory {//这个是存储在内存中Map<String, List<Message>> conversationHistory = new ConcurrentHashMap<>();//新增持久化 (现在没有做持久化所以都存在了内存中 重启后会丢失对话记忆)@Overridepublic void add(String conversationId, List<Message> messages) {log.error("对话id: {}", conversationId);this.conversationHistory.putIfAbsent(conversationId, new ArrayList<>());this.conversationHistory.get(conversationId).addAll(messages);}//获取@Overridepublic List<Message> get(String conversationId, int lastN) {List<Message> all = this.conversationHistory.get(conversationId);return all != null ? all.stream().skip(Math.max(0, all.size() - lastN)).toList() : List.of();}//清除@Overridepublic void clear(String conversationId) {this.conversationHistory.remove(conversationId);}
}

这个是我们创建的自己的AiChatMemory他继承了ChatMemory类,可以看到他其实就是将消息存储到List内 保存到内存中 当我们程序重启之后就会从内存删除

使用我们自己的AiChatMemory

 	@Autowiredprivate AiChatMemory aiChatMemory ;ChatClient chatClient = ChatClient.builder(ollamaChatModel).defaultAdvisors(new PromptChatMemoryAdvisor(aiChatMemory))//设置上下文              .build();Flux<String> content = chatClient.prompt().user(message).advisors(advisorSpec -> //设置持久化对话的查询的联通上下文//设置聊天记忆检索的大小为100advisorSpec.param(AbstractChatMemoryAdvisor.CHAT_MEMORY_RETRIEVE_SIZE_KEY, 100)// 设置对话ID 在聊天应用中,不同用户或不同会话的对话历史应该是相互独立的。// 通过设置对话ID,可以让聊天记忆顾问根据不同的对话ID来管理和检索相应的对话历史记录。// 这样,当用户发起新的对话时,系统可以根据对话ID准确地获取该用户之前的对话内容,从而提供更连贯和个性化的服务。.param(AbstractChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID_KEY, "测试固定id")).stream().content();//聊天交互的场景中, content 流会不断地发出聊天响应的文本片段,// 而 [complete] 这个字符串可以作为一个特殊的标识,用来表示聊天响应已经结束。// 当客户端接收到这个 "[complete]" 标识时,就可以知道整个聊天响应已经全部接收完毕,// 从而可以进行相应的处理,例如关闭连接、更新 UI 状态等。

设置上下文有两个点
第一个点就是创建ChatClient的时候的:

.defaultAdvisors(new PromptChatMemoryAdvisor(aiChatMemory))//设置上下文 

这里指定的是在哪里取上下文

                .advisors(advisorSpec -> //设置持久化对话的查询的联通上下文//设置聊天记忆检索的大小为100advisorSpec.param(AbstractChatMemoryAdvisor.CHAT_MEMORY_RETRIEVE_SIZE_KEY, 100)// 设置对话ID 在聊天应用中,不同用户或不同会话的对话历史应该是相互独立的。// 通过设置对话ID,可以让聊天记忆顾问根据不同的对话ID来管理和检索相应的对话历史记录。// 这样,当用户发起新的对话时,系统可以根据对话ID准确地获取该用户之前的对话内容,从而提供更连贯和个性化的服务。.param(AbstractChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID_KEY, "测试固定id"))

这里指定的是会话的key和取出的上下文条数
AbstractChatMemoryAdvisor.CHAT_MEMORY_RETRIEVE_SIZE_KEY= 取出的上下文条数
AbstractChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID_KEY = 会话id

也可以通过向量库存储对话上下文上下文

  	 //对话历史向量库AbstractChatMemoryAdvisor chatMemoryAdvisor = VectorStoreChatMemoryAdvisor.builder(历史对话向量库).protectFromBlocking(true).chatMemoryRetrieveSize(取出多少条).conversationId(会话id).build();ChatClient chatClient = ChatClient.builder(chatModel).defaultAdvisors(chatMemoryAdvisor) //向量库版的历史对话.build();

3.RAG: 检索增强生成 将业务数据存储在向量数据库中(做相似性检索)通过RAG进行链接

在这里插入图片描述

对文件进行向量化
    @Autowired@Qualifier("secondaryJdbcTemplate")private JdbcTemplate jdbcTemplate;//引入嵌入模型@Autowired@Qualifier("ollamaEmbeddingModel")private EmbeddingModel embeddingModel;/*** 创建向量存储的通用方法*/private PgVectorStore.Builder createVectorStoreBuilder(JdbcTemplate jdbcTemplate, EmbeddingModel embeddingModel,String tableName) {Assert.notNull(jdbcTemplate, "JdbcTemplate 不能为空");Assert.notNull(embeddingModel, "EmbeddingModel 不能为空");Assert.hasText(tableName, "表名不能为空");return PgVectorStore.builder(jdbcTemplate, embeddingModel).dimensions(dimensions).vectorTableName(tableName);}public Result saveVectorStoreByType(String filePath, String dfileFid) {try {// 检查文件是否存在File file = new File(filePath);if (!file.exists()) {return Result.error("文件不存在: " + filePath);}Resource resource = new FileSystemResource(filePath);TikaDocumentReader reader = new TikaDocumentReader(resource);List<Document> documentList = reader.read();// 创建文本分割器并配置智能参数TextSplitter textSplitter = TokenTextSplitter.builder().withChunkSize(300)           // 适中的块大小.withMinChunkSizeChars(100)   // 最小块字符数.withKeepSeparator(true)      // 保留分隔符.withMaxNumChunks(1000)       // 最大块数量限制.build();// 应用智能分块List<Document> splitDocuments = textSplitter.apply(documentList);// 对分块结果进行后处理,确保质量List<Document> processedDocuments = new ArrayList<>();for (Document doc : splitDocuments) {String text = doc.getText().trim();// 跳过过短的块/*if (text.length() < 50) {continue;}*/// 处理过长的块if (text.length() > 1000) {// 在适当的位置进行二次分割List<String> subChunks = splitLongText(text);for (String subChunk : subChunks) {if (subChunk.trim().length() >= 50) {Map<String, Object> metadata = new HashMap<>(doc.getMetadata());metadata.put("knowledgeId", dfileFid);//知识库idprocessedDocuments.add(new Document(subChunk.trim(), metadata));}}} else {// 添加元数据Map<String, Object> metadata = new HashMap<>(doc.getMetadata());metadata.put("knowledgeId", dfileFid);//知识库idprocessedDocuments.add(new Document(text, metadata));}}VectorStore vectorStore = vectorStoreFactory.createVectorStoreByTableName(jdbcTemplate, embeddingModel, "vector_store");vectorStore.add(processedDocuments);log.info("成功将文件加载到向量数据库,共 {} 个文档片段", processedDocuments.size());return Result.ok("成功将文件 " + file.getName() + " 加载到向量数据库,共 " + processedDocuments.size() + " 个文档片段");} catch (Exception e) {log.error("保存向量存储失败", e);throw new RuntimeException("保存向量存储失败: " + e.getMessage());}}

ollamaEmbeddingModel 注入的是 ai.ollama.embedding.model: bge-m3 模型

当将数据向量化后,创建获取

   /*** 创建向量存储的通用方法*/private PgVectorStore.Builder createVectorStoreBuilder(JdbcTemplate jdbcTemplate, EmbeddingModel embeddingModel,String tableName) {Assert.notNull(jdbcTemplate, "JdbcTemplate 不能为空");Assert.notNull(embeddingModel, "EmbeddingModel 不能为空");Assert.hasText(tableName, "表名不能为空");`在这里插入代码片`return PgVectorStore.builder(jdbcTemplate, embeddingModel).dimensions(dimensions).vectorTableName(tableName);}@Bean@Qualifier("VectorStore ")public VectorStore enterpriseVectorStore(@Qualifier("secondaryJdbcTemplate") JdbcTemplate jdbcTemplate,@Qualifier("ollamaEmbeddingModel") EmbeddingModel embeddingModel) {try {return createVectorStoreBuilder(jdbcTemplate, embeddingModel, "vector_store").build();} catch (Exception e) {throw new RuntimeException("创建向量库失败", e);}}
  1. 从向量库中查询相关信息 转为提示词给ai
    // 从向量库中查询相关信息private String getVectorStoreStr(String message) {List<String> relevantInfo = new ArrayList<>();try {List<Document> similarDocs = enterpriseVectorStore.similaritySearch(message);for (Document doc : similarDocs) {relevantInfo.add(doc.getText());}log.error("从向量库获取到{}条相关信息", relevantInfo.size());} catch (Exception e) {log.error("向量库查询失败,将继续使用原始对话: {}", e.getMessage());}// 如果找到相关信息,添加到系统提示中StringBuilder context = new StringBuilder();String systemPrompt="";if (!relevantInfo.isEmpty()) {context.append("以下是一些相关的背景信息,请参考这些信息来回答问题:\n\n");for (String info : relevantInfo) {context.append("- ").append(info).append("\n");}context.append("\n请基于以上信息,结合自己的知识来回答用户的问题。如果上述信息不足以完整回答问题,可以使用自己的知识进行补充。");systemPrompt=context.toString();}return systemPrompt;}//放入预设角色中ChatClient chatClient = ChatClient.builder(ollamaChatModel).defaultSystem(getVectorStoreStr(message)).build();
  1. 创建检索增强顾问
    private Advisor getSearchEnhancementConsultant(VectorStore vectorStore,List<String> knowledge) {// 1. 构建复杂的文档过滤条件var b = new FilterExpressionBuilder();Object[] array = knowledge.toArray();// 使用in条件,匹配指定的知识库ID列表var filterExpression = b.in("knowledgeId", array);// 2. 配置文档检索器// 设置相似度阈值和返回文档数量,同时应用过滤条件DocumentRetriever retriever = VectorStoreDocumentRetriever.builder().vectorStore(vectorStore).similarityThreshold(0.5)    // 设置相似度阈值,大于0.5的文档才会被返回.topK(3)                     // 返回相似度最高的前3个文档.filterExpression(filterExpression.build()).build();// 3. 创建并配置检索增强顾问// allowEmptyContext设置为true,确保在没有找到相关文档时也能回答问题Advisor advisor = RetrievalAugmentationAdvisor.builder().queryAugmenter(ContextualQueryAugmenter.builder().allowEmptyContext(true)        // 允许空上下文,当没有找到相关文档时也会回答.build()).documentRetriever(retriever).build();return advisor;}chatClient.prompt().user(message).advisors(advisor)  // 只有在开启向量库时才添加检索增强顾问  // 添加检索增强顾问     .stream().content().onErrorResume(e -> {log.error("聊天过程发生错误: ", e);return Flux.just("抱歉,处理您的请求时出现了错误,请稍后重试。");});
  1. 使用ai重写查询后再去向量库查询
    private Advisor getSearchEnhancementConsultant2(ChatClient chatClient, VectorStore vectorStore) {Advisor retrievalAdvisor = RetrievalAugmentationAdvisor.builder().queryTransformers(RewriteQueryTransformer.builder().chatClientBuilder(chatClient.mutate())//使用ai优化查询.build()).documentRetriever(VectorStoreDocumentRetriever.builder().similarityThreshold(0.50).vectorStore(vectorStore).build()).build();return retrievalAdvisor;}chatClient.prompt().user(message).advisors(advisor)  // 只有在开启向量库时才添加检索增强顾问  // 添加检索增强顾问     .stream().content().onErrorResume(e -> {log.error("聊天过程发生错误: ", e);return Flux.just("抱歉,处理您的请求时出现了错误,请稍后重试。");});

4.Function Calling 用来调用自己的api (这个官网上淘汰了)

    @Bean@Description("处理机票退订")//告诉ai什么时候调用这个方法public Function<CancelBookingRequest, String> cancelBooking() {return cancelBookingRequest -> {//在这调用方法flightBookingService.cancelBooking(cancelBookingRequest.bookingId(),cancelBookingRequest.name() );return "退订成功";};}ChatClient chatClient = ChatClient.builder(chatModel).defaultFunctions("cancelBooking") //这指定的是调用那些方法的bean的名称 多个用逗号分割.build();

5.Tools (这个有个问题调用工具后返回就不是流式的了)

	public class ToolsFactory {@Tool(description = "获取用户的数量")void getUserCount() {System.out.println("ai获取用户的数量");}}chatClient.prompt().user(message).tools(new ToolsFactory()) 加上这个工具后 流式返回就失效了.stream().content().onErrorResume(e -> {log.error("聊天过程发生错误: ", e);return Flux.just("抱歉,处理您的请求时出现了错误,请稍后重试。");});

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com