安裝milvus向量數據庫
官方網址 https://milvus.io/zh
使用docker安裝milvus
mkdir -p /data/docker/milvus
cd /data/docker/milvus
wget https://raw.githubusercontent.com/milvus-io/milvus/master/scripts/standalone_embed.sh#在docker中啟動milvus
sh standalone_embed.sh start
docker ps -a#停止、刪除
sh standalone_embed.sh stop
sh standalone_embed.sh delete
瀏覽器訪問
http://192.168.2.205:9091/webui/
在langchain中使用milvus
- 在
pom.xml
中引入依賴
<dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-milvus</artifactId><version>${langchain4j.version}</version></dependency>
- 設置配置信息
milvus:host: 192.168.1.131port: 19530langchain4j:community:dashscope:chat-model:api-key: ${dashscope.key}model-name: qwen-maxembedding-model:api-key: ${dashscope.key}model-name: text-embedding-v3streaming-chat-model:api-key: ${dashscope.key}model-name: qwen-plus
- 配置向量庫
@Configuration
@Slf4j
public class EmbeddingStoreConfig {@Autowiredprivate EmbeddingModel embeddingModel;@Value("${milvus.host}")private String host;@Value("${milvus.port}")private Integer port;@Beanpublic EmbeddingStore embeddingStore() {log.info("==========開始創建Milvus的Collection");MilvusEmbeddingStore store = MilvusEmbeddingStore.builder().host(host).port(port).collectionName("langchain_01").dimension(1024).indexType(IndexType.FLAT).metricType(MetricType.COSINE)
// .username("username")
// .password("password").consistencyLevel(ConsistencyLevelEnum.EVENTUALLY).autoFlushOnInsert(true).idFieldName("id").textFieldName("text").metadataFieldName("metadata").vectorFieldName("vector").build();log.info("==========創建Milvus的Collection完成");return store;}}
- 使用向量庫存儲數據
@SpringBootTest
public class EmbeddingTest {@Autowiredprivate EmbeddingModel embeddingModel;@Autowiredprivate EmbeddingStore embeddingStore;@Testpublic void testEmbeddingModel() {Response<Embedding> embed = embeddingModel.embed("你好");System.out.println("向量維度:" + embed.content().vector().length);System.out.println("向量輸出:" + embed.toString());}/*** 將文本轉換成向量,然后存儲到pinecone中* <p>* 參考:* https://docs.langchain4j.dev/tutorials/embedding-stores*/@Testpublic void testPineconeEmbeded() {//將文本轉換成向量TextSegment segment1 = TextSegment.from("我喜歡羽毛球");Embedding embedding1 = embeddingModel.embed(segment1).content();//存入向量數據庫embeddingStore.add(embedding1, segment1);TextSegment segment2 = TextSegment.from("今天天氣很好");Embedding embedding2 = embeddingModel.embed(segment2).content();embeddingStore.add(embedding2, segment2);}/*** 相似度匹配*/@Testpublic void embeddingSearch() {//提問,并將問題轉成向量數據Embedding queryEmbedding = embeddingModel.embed("你最喜歡的運動是什么?").content();//創建搜索請求對象EmbeddingSearchRequest searchRequest = EmbeddingSearchRequest.builder().queryEmbedding(queryEmbedding).maxResults(1) //匹配最相似的一條記錄//.minScore(0.8).build();//根據搜索請求 searchRequest 在向量存儲中進行相似度搜索EmbeddingSearchResult<TextSegment> searchResult =embeddingStore.search(searchRequest);//searchResult.matches():獲取搜索結果中的匹配項列表。//.get(0):從匹配項列表中獲取第一個匹配項EmbeddingMatch<TextSegment> embeddingMatch = searchResult.matches().get(0);//獲取匹配項的相似度得分System.out.println(embeddingMatch.score()); // 0.8144288515898701//返回文本結果System.out.println(embeddingMatch.embedded().text());}@Testpublic void testUploadKnowledgeLibrary() {//使用FileSystemDocumentLoader讀取指定目錄下的知識庫文檔//并使用默認的文檔解析器對文檔進行解析Document document1 = FileSystemDocumentLoader.loadDocument("D:/knowledge/文檔1.md");Document document2 = FileSystemDocumentLoader.loadDocument("D:/knowledge/文檔2.md");Document document3 = FileSystemDocumentLoader.loadDocument("D:/knowledge/文檔3.md");List<Document> documents = Arrays.asList(document1, document2, document3);//文本向量化并存入向量數據庫:將每個片段進行向量化,得到一個嵌入向量EmbeddingStoreIngestor.builder().embeddingStore(embeddingStore).embeddingModel(embeddingModel).build().ingest(documents);}
}
- 配置Agent屬性
@Configuration
public class AgentConfig {@Autowiredprivate MongoChatMemoryStore mongoChatMemoryStore;@Autowiredprivate EmbeddingStore embeddingStore;@Autowiredprivate EmbeddingModel embeddingModel;@Beanpublic ChatMemoryProvider chatMemoryProviderXiaozhi() {return memoryId -> MessageWindowChatMemory.builder().id(memoryId).maxMessages(20).chatMemoryStore(mongoChatMemoryStore).build();}@BeanContentRetriever contentRetriever() {// 創建一個 EmbeddingStoreContentRetriever 對象,用于從嵌入存儲中檢索內容return EmbeddingStoreContentRetriever.builder()// 設置用于生成嵌入向量的嵌入模型.embeddingModel(embeddingModel)// 指定要使用的嵌入存儲.embeddingStore(embeddingStore)// 設置最大檢索結果數量,這里表示最多返回 1 條匹配結果.maxResults(1)// 設置最小得分閾值,只有得分大于等于 0.8 的結果才會被返回.minScore(0.8)// 構建最終的 EmbeddingStoreContentRetriever 實例.build();}
}
- 配置AIService
@AiService(wiringMode = EXPLICIT,
// chatModel = "qwenChatModel",streamingChatModel = "qwenStreamingChatModel",chatMemoryProvider = "chatMemoryProviderXiaozhi",tools = "appointmentTools",contentRetriever = "contentRetriever")
public interface Agent {@SystemMessage(fromResource = "zhaozhi-prompt-template.txt")Flux<String> chat(@MemoryId Long memoryId, @UserMessage String userMessage);
}
- Controller類
@RestController
@RequestMapping("/agent")
public class AgentController {@Autowiredprivate Agent agent;@Operation(summary = "對話")@PostMapping(value = "/chat", produces = "text/stream;charset=utf-8")public Flux<String> chat(@RequestBody ChatForm chatForm) {return agent.chat(chatForm.getMemoryId(), chatForm.getMessage());}
}
源碼地址:
https://gitee.com/galen.zhang/langchain-ai-demo/java-ai-langchain4j
注意事項
-
pom.xml
文件中的langchain4j-pinecone
與langchain4j-milvus
依賴了不同版本的gRPC,需要注釋掉其中一種向量庫 -
milvus
默認有速率限制,寫向量庫會出現錯誤
2025-05-13T14:34:50.404+08:00 ERROR 26656 --- [java-ai-langchain4j] [ main] i.m.client.AbstractMilvusGrpcClient : FlushRequest failed, error code: 8, reason: request is rejected by grpc RateLimiter middleware, please retry later: rate limit exceeded[rate=0.1]
2025-05-13T14:34:50.405+08:00 ERROR 26656 --- [java-ai-langchain4j] [ main] i.m.client.AbstractMilvusGrpcClient : FlushRequest failed! Exception:{}
io.milvus.exception.ServerException: request is rejected by grpc RateLimiter middleware, please retry later: rate limit exceeded[rate=0.1]
解決方法:
需要修改配置文件 /milvus/configs/milvus.yaml
quotaAndLimits.flushRate.collection.max
默認值是0.1,需要調高一些
quotaAndLimits.flushRate.collection.max = 10
下載配置文件
wget https://raw.githubusercontent.com/milvus-io/milvus/v2.5.11/configs/milvus.yaml
修改docker啟動配置,掛載外部配置文件
vi standalone_embed.sh
增加一行 -v $(pwd)/milvus.yaml:/milvus/configs/milvus.yaml \
sudo docker run -d \--name milvus-standalone \--security-opt seccomp:unconfined \-e ETCD_USE_EMBED=true \-e ETCD_DATA_DIR=/var/lib/milvus/etcd \-e ETCD_CONFIG_PATH=/milvus/configs/embedEtcd.yaml \-e COMMON_STORAGETYPE=local \-v $(pwd)/milvus.yaml:/milvus/configs/milvus.yaml \-v $(pwd)/volumes/milvus:/var/lib/milvus \-v $(pwd)/embedEtcd.yaml:/milvus/configs/embedEtcd.yaml \-v $(pwd)/user.yaml:/milvus/configs/user.yaml \-p 19530:19530 \-p 9091:9091 \-p 2379:2379 \--health-cmd="curl -f http://localhost:9091/healthz" \--health-interval=30s \--health-start-period=90s \--health-timeout=20s \--health-retries=3 \milvusdb/milvus:v2.5.11 \milvus run standalone 1> /dev/null
刪除之前的容器,重新啟動
sh standalone_embed.sh stop
sh standalone_embed.sh deletesh standalone_embed.sh start