From cf499cb7b1cc61945e926b410979cf4f350b43db Mon Sep 17 00:00:00 2001 From: 0007 <0007@qq.com> Date: Wed, 27 Aug 2025 19:57:56 +0800 Subject: [PATCH] Add File --- .../store/aliyun/AliyunVectorStore.java | 218 ++++++++++++++++++ 1 file changed, 218 insertions(+) create mode 100644 agents-flex-store/agents-flex-store-aliyun/src/main/java/com/agentsflex/store/aliyun/AliyunVectorStore.java diff --git a/agents-flex-store/agents-flex-store-aliyun/src/main/java/com/agentsflex/store/aliyun/AliyunVectorStore.java b/agents-flex-store/agents-flex-store-aliyun/src/main/java/com/agentsflex/store/aliyun/AliyunVectorStore.java new file mode 100644 index 0000000..98c6719 --- /dev/null +++ b/agents-flex-store/agents-flex-store-aliyun/src/main/java/com/agentsflex/store/aliyun/AliyunVectorStore.java @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2023-2025, Agents-Flex (fuhai999@gmail.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.agentsflex.store.aliyun; + +import com.agentsflex.core.document.Document; +import com.agentsflex.core.llm.client.HttpClient; +import com.agentsflex.core.store.DocumentStore; +import com.agentsflex.core.store.SearchWrapper; +import com.agentsflex.core.store.StoreOptions; +import com.agentsflex.core.store.StoreResult; +import com.agentsflex.core.util.StringUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.*; + +/** + * 文档 https://help.aliyun.com/document_detail/2510317.html + */ +public class AliyunVectorStore extends DocumentStore { + private static final Logger LOG = LoggerFactory.getLogger(AliyunVectorStore.class); + + private AliyunVectorStoreConfig config; + + private final HttpClient httpUtil = new HttpClient(); + + public AliyunVectorStore(AliyunVectorStoreConfig config) { + this.config = config; + } + + @Override + public StoreResult storeInternal(List documents, StoreOptions options) { + if (documents == null || documents.isEmpty()) { + return StoreResult.success(); + } + Map headers = new HashMap<>(); + headers.put("Content-Type", "application/json"); + headers.put("dashvector-auth-token", config.getApiKey()); + + Map payloadMap = new HashMap<>(); + + List> payloadDocs = new ArrayList<>(); + for (Document vectorDocument : documents) { + Map document = new HashMap<>(); + if (vectorDocument.getMetadataMap() != null) { + document.put("fields", vectorDocument.getMetadataMap()); + } + document.put("vector", vectorDocument.getVector()); + document.put("id", vectorDocument.getId()); + payloadDocs.add(document); + } + + payloadMap.put("docs", payloadDocs); + + String payload = JSON.toJSONString(payloadMap); + String url = "https://" + config.getEndpoint() + "/v1/collections/" + + options.getCollectionNameOrDefault(config.getDefaultCollectionName()) + "/docs"; + String response = httpUtil.post(url, headers, payload); + + if (StringUtil.noText(response)) { + return StoreResult.fail(); + } + + JSONObject jsonObject = JSON.parseObject(response); + Integer code = jsonObject.getInteger("code"); + if (code != null && code == 0) { + return StoreResult.successWithIds(documents); + } else { + LOG.error("delete vector fail: " + response); + return StoreResult.fail(); + } + } + + + @Override + public StoreResult deleteInternal(Collection ids, StoreOptions options) { + Map headers = new HashMap<>(); + headers.put("Content-Type", "application/json"); + headers.put("dashvector-auth-token", config.getApiKey()); + + Map payloadMap = new HashMap<>(); + payloadMap.put("ids", ids); + String payload = JSON.toJSONString(payloadMap); + + String url = "https://" + config.getEndpoint() + "/v1/collections/" + + options.getCollectionNameOrDefault(config.getDefaultCollectionName()) + "/docs"; + String response = httpUtil.delete(url, headers, payload); + if (StringUtil.noText(response)) { + return StoreResult.fail(); + } + + JSONObject jsonObject = JSON.parseObject(response); + Integer code = jsonObject.getInteger("code"); + if (code != null && code == 0) { + return StoreResult.success(); + } else { + LOG.error("delete vector fail: " + response); + return StoreResult.fail(); + } + } + + + @Override + public StoreResult updateInternal(List documents, StoreOptions options) { + if (documents == null || documents.isEmpty()) { + return StoreResult.success(); + } + Map headers = new HashMap<>(); + headers.put("Content-Type", "application/json"); + headers.put("dashvector-auth-token", config.getApiKey()); + + Map payloadMap = new HashMap<>(); + + List> payloadDocs = new ArrayList<>(); + for (Document vectorDocument : documents) { + Map document = new HashMap<>(); + if (vectorDocument.getMetadataMap() != null) { + document.put("fields", vectorDocument.getMetadataMap()); + } + document.put("vector", vectorDocument.getVector()); + document.put("id", vectorDocument.getId()); + payloadDocs.add(document); + } + + payloadMap.put("docs", payloadDocs); + + String payload = JSON.toJSONString(payloadMap); + String url = "https://" + config.getEndpoint() + "/v1/collections/" + + options.getCollectionNameOrDefault(config.getDefaultCollectionName()) + "/docs"; + String response = httpUtil.put(url, headers, payload); + + if (StringUtil.noText(response)) { + return StoreResult.fail(); + } + + JSONObject jsonObject = JSON.parseObject(response); + Integer code = jsonObject.getInteger("code"); + if (code != null && code == 0) { + return StoreResult.successWithIds(documents); + } else { + LOG.error("delete vector fail: " + response); + return StoreResult.fail(); + } + + } + + + @Override + public List searchInternal(SearchWrapper wrapper, StoreOptions options) { + Map headers = new HashMap<>(); + headers.put("Content-Type", "application/json"); + headers.put("dashvector-auth-token", config.getApiKey()); + + Map payloadMap = new HashMap<>(); + payloadMap.put("vector", wrapper.getVector()); + payloadMap.put("topk", wrapper.getMaxResults()); + payloadMap.put("include_vector", wrapper.isWithVector()); + payloadMap.put("filter", wrapper.toFilterExpression()); + + String payload = JSON.toJSONString(payloadMap); + String url = "https://" + config.getEndpoint() + "/v1/collections/" + + options.getCollectionNameOrDefault(config.getDefaultCollectionName()) + "/query"; + String result = httpUtil.post(url, headers, payload); + + if (StringUtil.noText(result)) { + return null; + } + + //https://help.aliyun.com/document_detail/2510319.html + JSONObject rootObject = JSON.parseObject(result); + int code = rootObject.getIntValue("code"); + if (code != 0) { + //error + LoggerFactory.getLogger(AliyunVectorStore.class).error("can not search data AliyunVectorStore(code: " + code + "), message: " + rootObject.getString("message")); + return null; + } + + JSONArray output = rootObject.getJSONArray("output"); + List documents = new ArrayList<>(output.size()); + for (int i = 0; i < output.size(); i++) { + JSONObject jsonObject = output.getJSONObject(i); + Document document = new Document(); + document.setId(jsonObject.getString("id")); + document.setVector(jsonObject.getObject("vector", double[].class)); + // 阿里云数据采用余弦相似度计算 jsonObject.getDoubleValue("score") 表示余弦距离, + // 原始余弦距离范围是[0, 2],0表示最相似,2表示最不相似 + Double distance = jsonObject.getDouble("score"); + if (distance != null) { + double score = distance / 2.0; + document.setScore(1.0d - score); + } + + + JSONObject fields = jsonObject.getJSONObject("fields"); + document.addMetadata(fields); + + documents.add(document); + } + + return documents; + } +}