Java + Ollama + DeepSeek + Milvus:构建私有知识库问答系统

在 AI 技术蓬勃发展的当下,利用大语言模型和向量数据库构建智能问答系统已成为一种趋势。本文将介绍如何使用 Java 结合 Ollama、DeepSeek 和 Milvus,实现一个基于私有知识库的问答系统,让 DeepSeek 根据构建的私有知识库回答用户问题。

一、项目概述

我们将实现一个系统,该系统能够:

  1. 使用 Ollama 提供的文本嵌入功能,将文本转换为向量。

  2. 将这些向量存储在 Milvus 向量数据库中。

  3. 使用 DeepSeek 模型根据用户问题生成回答。

  4. 结合 Milvus 中存储的私有知识库,提供更准确的问答服务。

二、环境准备

  1. 安装并运行 Milvus 服务

  2. 安装并运行 Ollama 服务器

  3. 下载DeepSeek及nomic-embed-text

stranger@StrangerdeMacBook-Pro ~ % ollama list
NAME                       ID              SIZE      MODIFIED     
deepseek-r1:1.5b           a42b25d8c10a    1.1 GB    27 hours ago    
bge-m3:latest              790764642607    1.2 GB    2 days ago      
nomic-embed-text:latest    0a109f422b47    274 MB    2 days ago      
deepseek-r1:7b             0a8c26691023    4.7 GB    11 days ago     

三、代码实现

1. Milvus 配置

首先,我们需要配置 Milvus 客户端,连接到 Milvus 服务器:

java复制

@Configuration
public class MilvusConfig {
    @Value("${milvus.host}")
    private String milvusHost;

    @Value("${milvus.port}")
    private int milvusPort;

    @Bean
    public MilvusClientV2 milvusClientV2() {
        ConnectConfig connectConfig = ConnectConfig.builder()
                .uri(String.format("http://%s:%d", milvusHost, milvusPort))
                .build();
        return new MilvusClientV2(connectConfig);
    }
}

application.yml 中配置 Milvus 的主机和端口:

yaml复制

milvus:
  host: localhost
  port: 19530

2. Milvus 服务实现

创建一个服务类 MilvusEmbeddingService,用于与 Milvus 交互:

java复制

@Service
public class MilvusEmbeddingService {
    private static final String COLLECTION_NAME = "user_data";
    private static final int VECTOR_DIM = 768;

    private final MilvusClientV2 milvusClientV2;

    @Autowired
    public MilvusEmbeddingService(MilvusClientV2 milvusClientV2) {
        this.milvusClientV2 = milvusClientV2;
    }

    @PostConstruct
    public void init() {
        createCollection();
    }

    public void addDocument(String text) {
        float[] vector = getEmbedding(text);

        JsonObject document = new JsonObject();
        document.addProperty("id", UUID.randomUUID().toString());
        document.addProperty("text", text);
        document.add("vector", getVectorJsonArray(vector));

        List<JsonObject> data = new ArrayList<>();
        data.add(document);

        InsertReq insertReq = InsertReq.builder()
                .collectionName(COLLECTION_NAME)
                .data(data)
                .build();

        InsertResp insertResp = milvusClientV2.insert(insertReq);
        System.out.println("Insert response: " + insertResp);
    }

    public List<String> searchDocuments(String queryText, int topK) {
        float[] queryVector = getEmbedding(queryText);

        SearchReq searchReq = SearchReq.builder()
                .collectionName(COLLECTION_NAME)
                .data(Collections.singletonList(new FloatVec(queryVector)))
                .topK(topK)
                .outputFields(Collections.singletonList("text"))
                .build();

        SearchResp searchResp = milvusClientV2.search(searchReq);
        List<String> relatedDocuments = new ArrayList<>();
        if (searchResp != null && searchResp.getSearchResults() != null && !searchResp.getSearchResults().isEmpty()) {
            for (SearchResp.SearchResult result : searchResp.getSearchResults().get(0)) {
                relatedDocuments.add(result.getEntity().get("text").toString());
                if (relatedDocuments.size() >= topK) {
                    break;
                }
            }
        }
        return relatedDocuments;
    }

    private float[] getEmbedding(String text) {
        try {
            JsonObject requestBody = new JsonObject();
            requestBody.addProperty("model", "nomic-embed-text");
            requestBody.addProperty("prompt", text);

            RequestBody body = RequestBody.create(
                    MediaType.parse("application/json; charset=utf-8"),
                    requestBody.toString()
            );

            Request request = new Request.Builder()
                    .url("http://localhost:11434/api/embeddings")
                    .post(body)
                    .build();

            Response response = new OkHttpClient().newCall(request).execute();
            if (!response.isSuccessful()) {
                System.out.println("Request failed: " + response.code());
                return new float[VECTOR_DIM];
            }

            String responseBody = response.body().string();
            JsonObject jsonResponse = new com.google.gson.JsonParser().parse(responseBody).getAsJsonObject();
            JsonArray embeddingsArray = jsonResponse.getAsJsonArray("embedding");

            if (embeddingsArray == null || embeddingsArray.size() == 0) {
                System.out.println("Empty embedding array");
                return new float[VECTOR_DIM];
            }

            float[] vector = new float[embeddingsArray.size()];
            for (int i = 0; i < embeddingsArray.size(); i++) {
                vector[i] = embeddingsArray.get(i).getAsFloat();
            }

            return vector;
        } catch (IOException e) {
            e.printStackTrace();
            return new float[VECTOR_DIM];
        }
    }

    private JsonArray getVectorJsonArray(float[] vector) {
        JsonArray jsonArray = new JsonArray();
        for (float value : vector) {
            jsonArray.add(value);
        }
        return jsonArray;
    }

    public void createCollection() {
        CreateCollectionReq.CollectionSchema schema = milvusClientV2.createSchema();
        schema.addField(AddFieldReq.builder()
                .fieldName("id")
                .dataType(DataType.VarChar)
                .isPrimaryKey(true)
                .autoID(false)
                .build());
        schema.addField(AddFieldReq.builder()
                .fieldName("text")
                .dataType(DataType.VarChar)
                .maxLength(1000)
                .build());
        schema.addField(AddFieldReq.builder()
                .fieldName("vector")
                .dataType(DataType.FloatVector)
                .dimension(VECTOR_DIM)
                .build());

        IndexParam indexParam = IndexParam.builder()
                .fieldName("vector")
                .metricType(IndexParam.MetricType.COSINE)
                .build();

        CreateCollectionReq createCollectionReq = CreateCollectionReq.builder()
                .collectionName(COLLECTION_NAME)
                .collectionSchema(schema)
                .indexParams(Collections.singletonList(indexParam))
                .build();

        milvusClientV2.createCollection(createCollectionReq);
        System.out.println("Collection created successfully");
    }
}

3. Ollama 和 DeepSeek 集成

在 Spring Boot 项目中,配置 Ollama 和 DeepSeek 的相关参数:

yaml复制

spring:
  ai:
    ollama:
      base-url: http://localhost:11434
      chat:
        model: deepseek-r1:1.5b
      embedding:
        model: nomic-embed-text:latest

4. 控制器实现

创建一个控制器 OllamaController,用于处理用户请求:

java复制

@RestController
@RequestMapping("/api/ollama")
public class OllamaController {
    @Autowired
    private MilvusEmbeddingService milvusService;
    @Autowired
    private OllamaChatModel ollamaChatModel;

    @PostMapping(value = "/chat", consumes = MediaType.APPLICATION_JSON_VALUE)
    public Flux<String> chatWithContext(@RequestBody Map<String, String> input) {
        String queryText = input.get("msg");

        // 将自定义文本内容存入向量数据库
        String data = getBaseData();
        String[] documents = data.split("\n");
        for (String doc : documents) {
            if (!doc.trim().isEmpty()) {
                milvusService.addDocument(doc);
            }
        }

        // 检索相关文档
        List<String> relatedDocuments = milvusService.searchDocuments(queryText, 3);

        // 生成带有上下文的提示
        String prompt = generatePromptWithContext(queryText, relatedDocuments);

        // 调用 Ollama API
        Flux<String> stream = ollamaChatModel.stream(prompt);

        return stream;
    }

    private String generatePromptWithContext(String userInput, List<String> context) {
        StringBuilder promptBuilder = new StringBuilder("以下是与您的问题相关的内容:\n");
        for (String doc : context) {
            promptBuilder.append(doc).append("\n");
        }
        promptBuilder.append("现在,请根据上述内容回答:").append(userInput);
        return promptBuilder.toString();
    }

    private String getBaseData() {
        return "姓名 年龄 手机号 地址\n" +
                "张三 25 13800138000 北京市朝阳区望京SOHO\n" +
                "李四 30 13900139000 上海市浦东新区陆家嘴\n" +
                "王五 28 13700137000 广州市天河区珠江新城\n" +
                "赵六 35 13600136000 深圳市南山区科技园\n" +
                "孙七 22 13400134000 成都市武侯区桐梓林\n" +
                "周八 26 13500135000 杭州市西湖区文三路\n" +
                "吴九 29 13300133000 南京市玄武区珠江路\n" +
                "郑十 32 13200132000 武汉市洪山区光谷\n" +
                "钱十一 27 13100131000 西安市雁塔区小寨\n" +
                "陈十二 31 13000130000 长沙市岳麓区梅溪湖\n" +
                "杨十三 24 15900159000 重庆市渝中区解放碑\n" +
                "黄十四 23 15800158000 苏州市姑苏区观前街\n" +
                "徐十五 33 15700157000 天津市南开区鞍山西道\n" +
                "李四 21 15600156000 沈阳市和平区三好街\n" +
                "何十七 34 15500155000 大连市中山区人民路\n" +
                "郭十八 20 15400154000 青岛市市南区香港中路\n" +
                "林十九 36 15300153000 厦门市思明区莲坂\n" +
                "马二十 27 15200152000 福州市鼓楼区五四路\n" +
                "罗二十一 30 15100151000 合肥市庐阳区三孝口\n" +
                "宋二十二 28 15000150000 南昌市东湖区八一大道\n" +
                "唐二十三 32 18800188000 昆明市盘龙区北京路\n" +
                "刘二十四 26 18700187000 长沙市芙蓉区五一广场\n" +
                "白二十五 29 18600186000 贵阳市南明区花果园\n" +
                "杜二十六 35 18500185000 南宁市青秀区民族大道\n" +
                "冯二十七 23 18400184000 哈尔滨市南岗区学府路\n" +
                "李四 24 18300183000 长春市朝阳区红旗街\n" +
                "董二十九 33 18200182000 石家庄市长安区中山路\n" +
                "彭三十 31 18100181000 郑州市金水区花园路";
    }
}

5. 相关依赖

        <!-- 识别图片 -->
        <dependency>
			<groupId>ai.djl.tensorflow</groupId>
			<artifactId>tensorflow-model-zoo</artifactId>
			<version>0.20.0</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>

		<!-- HTTP客户端 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-webflux</artifactId> 
		</dependency>

		<dependency>
			<groupId>io.milvus</groupId>
			<artifactId>milvus-sdk-java</artifactId>
			<version>2.5.4</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.ai</groupId>
			<artifactId>spring-ai-ollama-spring-boot-starter</artifactId>
		</dependency>

6. 配置文件

server:
  port: 8080
  servlet:
    context-path: /deepseek
spring:
  application:
    name: deepseek
  ai:
    ollama:
      base-url: http://localhost:11434
      chat:
        model: deepseek-r1:1.5b
      embedding:
        model: nomic-embed-text:latest
milvus:
  host: localhost
  port: 19530
  collection:
    name: user_data
    dimension: 768  # Dimension of the embedding vector
    index-type: IVF_FLAT
    metric-type: L2

四、运行与测试

  1. 启动 Milvus 服务器和 Ollama 服务器。

  2. 启动 Spring Boot 应用程序。

  3. 使用thymeleaf简单做了个页面

  4. 效果如下:

从代码逻辑和设计来看,存在以下一些问题点, 本文只是简单demo,没做深究:

1. 固定数据的插入问题

  • 方法getBaseData() 返回固定文本数据,每次调用 /api/ollama/chat 接口时,都会调用 milvusService.addDocument(doc) 将这些固定数据存入 Milvus。

  • 问题

    • 每次请求都会插入相同的数据,导致 Milvus 数据库数据冗余。

    • 如果用户多次调用接口,数据库中会包含大量重复数据。

    • 数据的 ID 是通过 UUID.randomUUID().toString() 生成的,这不会重复,但 Milvus 集合中会出现大量重复的文本内容。

2. 上下文文档数量固定

  • 方法searchDocuments(queryText, 3) 调用时,总是返回最多 3 条相关文档。

  • 问题

    • 3 条文档的限制是硬编码,不能灵活调整。

    • 如果用户问题需要更多上下文,或者更少的上下文,这个数字可能不够。

    • 需要根据实际情况考虑是否动态调整检索结果的数量。

3. 提示字符串的格式问题

  • 方法generatePromptWithContext() 构造了一个固定的提示字符串格式,包含上下文内容和用户问题。

  • 问题

    • 提示的格式是否合理?是否符合 Ollama 模型的预期输入格式?

    • 如果上下文内容过长,提示字符串可能会超出模型的输入限制,导致生成失败。

4. 流式响应的处理问题

  • 方法ollamaChatModel.stream(prompt) 返回一个流式响应。

  • 问题

    • 流式响应的格式是否在接口文档中定义清楚?

    • 客户端是否能够正确处理流式响应?

    • 如果 Ollama 模型返回异常数据,流式响应如何处理?

5. 向量生成的效率问题

  • 方法getEmbedding() 每次都需要调用 Ollama API 获取文本的嵌入向量。

  • 问题

    • 如果每次处理请求都需要生成大量的向量,效率可能较低。

    • 缺少缓存机制,对相同的文本重复生成向量。

6. 错误处理和异常日志

  • 问题

    • 代码中对异常的处理较为简单,大部分地方直接用 e.printStackTrace() 输出错误。

    • 缺少详细的日志记录,不利于问题的排查和调试。

7. 资源释放问题

  • 问题

    • 使用了 OkHttp 客户端,但在 getEmbedding() 方法中没有明确关闭客户端。

    • OkHttp 的连接可能不会被及时释放,导致资源浪费。

8. 配置的硬编码问题

  • 问题

    • Milvus 的集合名称 user_data 是硬编码的,如果需要动态调整,需要修改代码。

    • 数据字段的定义(如 idtextvector)也硬编码在代码中,灵活性较差。

9. 数据一致性问题

  • 问题

    • 如果在并发环境下,多个请求同时插入或检索数据,是否会导致数据一致性问题?

    • Milvus 的插入和检索操作是否保证原子性?

10. 扩展

问答链(Chain)
LangChain框架中定义的结构化处理流程,通过多个组件的链式调用完成知识检索、上下文整合、答案生成等任务‌

典型链式操作类型(LangChain实现)

  1. MapReduceDocumentsChain
    将文档分块处理后分别分析,再汇总结果,适用于长文本处理‌。
  2. RefineDocumentsChain
    通过迭代优化逐步精炼答案,提升生成内容准确性‌。
  3. StuffDocumentsChain
    直接将所有相关文档片段拼接为上下文输入LLM,适合短文本场景‌。

本文只是简单直接将所有相关文档片段拼接为上下文输入LLM,类似于StuffDocumentsChain

方法 1:使用 Python 的 LangChain 并通过 REST API 调用

         2:在 Java 中手动实现问答链逻辑

        3:使用替代的 Java 库

              虽然 LangChain 没有 Java SDK,但可以使用其他 Java 库和框架:

  •         Deeplearning4j:用于深度学习和自然语言处理。

  •         Wikipedia Library:用于知识检索和问答。

五、总结

通过结合 Java、Ollama、DeepSeek 和 Milvus,我们成功构建了一个基于私有知识库的问答系统。Milvus 提供了高效的向量存储和检索功能,Ollama 提供了强大的文本嵌入服务,而 DeepSeek 则负责生成自然语言回答。这种组合为构建智能问答系统提供了一个强大的技术栈。

文章只是简单基于文本导入向量库,文件及图片等相关功能。。。。。。

三天后。。。

书接上回:

像DeepSeek、Kimi这样的网页端如何实现识别上传的文件和图片,尤其是它们作为基于文本的语言模型,提到识别Word、PDF等文档,涉及到文档解析技术,需要多模态模型将非文本信息转化为文本,再交给语言模型处理,这里简单扩展一下文件处理

添加依赖

<!-- PDF解析 -->
		<dependency>
			<groupId>org.apache.pdfbox</groupId>
			<artifactId>pdfbox</artifactId>
			<version>2.0.27</version>
		</dependency>

		<!-- 支持XML解析 -->
		<dependency>
			<groupId>org.apache.xmlbeans</groupId>
			<artifactId>xmlbeans</artifactId>
			<version>5.1.1</version>
		</dependency>

		<dependency>
			<groupId>commons-fileupload</groupId>
			<artifactId>commons-fileupload</artifactId>
			<version>1.4</version>
		</dependency>

		<!-- Apache Commons IO -->
		<dependency>
			<groupId>commons-io</groupId>
			<artifactId>commons-io</artifactId>
			<version>2.11.0</version>
		</dependency>

		<!-- Office文档解析 -->
		<dependency>
			<groupId>org.apache.poi</groupId>
			<artifactId>poi</artifactId>
			<version>5.2.3</version>
		</dependency>
		<dependency>
			<groupId>org.apache.poi</groupId>
			<artifactId>poi-ooxml</artifactId>
			<version>5.2.3</version>
		</dependency>
		<dependency>
			<groupId>org.apache.poi</groupId>
			<artifactId>poi-scratchpad</artifactId>
			<version>5.2.3</version>
		</dependency>
1. 文件解析器接口
import java.io.InputStream;

public interface FileParser {
    String parse(InputStream is) throws Exception;
}
2. 具体解析器实现

import cn.com.skyvis.milvus.service.FileParser;
import org.apache.poi.hwpf.HWPFDocument;

import java.io.InputStream;

public class DocParser implements FileParser {
    @Override
    public String parse(InputStream is) throws Exception {
        try (HWPFDocument doc = new HWPFDocument(is)) {
            return doc.getDocumentText().replaceAll("\u0007", "");
        }
    }
}



import cn.com.skyvis.milvus.service.FileParser;
import org.apache.poi.xwpf.extractor.XWPFWordExtractor;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import java.io.InputStream;

public class DocxParser implements FileParser {
    @Override
    public String parse(InputStream is) throws Exception {
        try (XWPFDocument doc = new XWPFDocument(is)) {
            return new XWPFWordExtractor(doc).getText();
        }
    }
}


import cn.com.skyvis.milvus.service.FileParser;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
import java.io.InputStream;

public class PdfParser implements FileParser {
    @Override
    public String parse(InputStream is) throws Exception {
        try (PDDocument doc = PDDocument.load(is)) {
            return new PDFTextStripper().getText(doc);
        }
    }
}



import cn.com.skyvis.milvus.service.FileParser;
import org.apache.poi.hslf.usermodel.HSLFSlideShow;
import org.apache.poi.sl.extractor.SlideShowExtractor;
import java.io.InputStream;

public class PptParser implements FileParser {
    @Override
    public String parse(InputStream is) throws Exception {
        try (HSLFSlideShow slideshow = new HSLFSlideShow(is)) {
            SlideShowExtractor extractor = new SlideShowExtractor(slideshow);
            return extractor.getText();
        }
    }
}


import cn.com.skyvis.milvus.service.FileParser;
import org.apache.poi.xslf.usermodel.XMLSlideShow;
import org.apache.poi.xslf.usermodel.XSLFSlide;
import org.apache.poi.xslf.usermodel.XSLFTextShape;
import java.io.InputStream;
import java.util.stream.Collectors;

public class PptxParser implements FileParser {
    @Override
    public String parse(InputStream is) throws Exception {
        try (XMLSlideShow slideshow = new XMLSlideShow(is)) {
            return slideshow.getSlides().stream()
                    .map(this::extractTextFromSlide)
                    .collect(Collectors.joining("\n"));
        }
    }

    private String extractTextFromSlide(XSLFSlide slide) {
        return slide.getShapes().stream()
                .filter(shape -> shape instanceof XSLFTextShape)
                .map(shape -> ((XSLFTextShape) shape).getText().trim())
                .filter(text -> !text.isEmpty())
                .collect(Collectors.joining("\n"));
    }
}


import cn.com.skyvis.milvus.service.FileParser;
import org.apache.commons.io.IOUtils;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;

public class TxtParser implements FileParser {
    @Override
    public String parse(InputStream is) throws IOException {
        return IOUtils.toString(is, String.valueOf(StandardCharsets.UTF_8));
    }
}


import cn.com.skyvis.milvus.service.FileParser;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Cell;
import java.io.InputStream;

public class XlsxParser implements FileParser {
    @Override
    public String parse(InputStream is) throws Exception {
        StringBuilder sb = new StringBuilder();
        try (XSSFWorkbook workbook = new XSSFWorkbook(is)) {
            for (Sheet sheet : workbook) {
                for (Row row : sheet) {
                    for (Cell cell : row) {
                        sb.append(cell.toString()).append("\t");
                    }
                    sb.append("\n");
                }
            }
        }
        return sb.toString();
    }
}
3. 解析器分发器
import cn.com.skyvis.milvus.service.impl.*;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.util.HashMap;
import java.util.Map;

@Service
public class FileParserDispatcher {
    private static final Map<String, FileParser> PARSERS = new HashMap<>();

    // 初始化解析器映射
    static {
        PARSERS.put("pdf", new PdfParser());
        PARSERS.put("doc", new DocParser());
        PARSERS.put("docx", new DocxParser());
        PARSERS.put("xlsx", new XlsxParser());
        PARSERS.put("ppt", new PptParser());
        PARSERS.put("pptx", new PptxParser());
        PARSERS.put("txt", new TxtParser());
    }

    public String parse(MultipartFile file) throws Exception {
        String ext = getFileExtension(file.getOriginalFilename());
        FileParser parser = PARSERS.getOrDefault(ext, new TxtParser());
        return parser.parse(file.getInputStream());
    }

    private static String getFileExtension(String filename) {
        return filename.substring(filename.lastIndexOf(".") + 1).toLowerCase();
    }

    public static void main(String[] args) {
        File file = new File("/Users/stranger/Desktop/未命名.pptx");
        String ext = getFileExtension(file.getName());
        FileParser parser = PARSERS.getOrDefault(ext, new TxtParser());
        try {
            InputStream inputStream = new FileInputStream(file);
            String parse = null;
            try {
                parse = parser.parse(inputStream);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            System.out.println("parse = " + parse);
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

}
4. SpringBoot控制器

import cn.com.skyvis.milvus.service.FileParserDispatcher;
import org.springframework.ai.ollama.OllamaChatModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.server.ResponseStatusException;
import reactor.core.publisher.Flux;


@RestController
@RequestMapping("/api/files")
public class FileController {

    @Autowired
    private FileParserDispatcher dispatcher;
    @Autowired
    private OllamaChatModel ollamaChatModel;

    @PostMapping(value = "/handleFileUpload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public Flux<String> handleFileUpload(
            @RequestPart("file") MultipartFile file,
            @RequestPart("msg") String queryText) {

        try {
            // 文件解析
            String textContent = dispatcher.parse(file);

            // 生成提示模板
            String prompt = generatePromptWithContext(queryText, textContent);

            // 流式响应
            return ollamaChatModel.stream(prompt)
                    .onErrorResume(e -> Flux.error(
                            new ResponseStatusException(
                                    HttpStatus.BAD_REQUEST,
                                    "模型服务异常: " + e.getMessage())
                    ));

        } catch (Exception e) {
            return Flux.error(new ResponseStatusException(
                    HttpStatus.INTERNAL_SERVER_ERROR,
                    "文件处理失败: " + e.getMessage()
            ));
        }
    }

    private String generatePromptWithContext(String userInput, String context) {
        return String.format("""
            以下是与您的问题相关的文件内容:
            %s
            现在,请根据上述内容回答:%s
            """, context, userInput);
    }
}

4. 运行结果

以上是解析常用文件;至于图片就比较复杂了,文字类图片还好说,通过OCR即可提取文字,但这只是基于纯文本模型,如果想要识别图片的色彩和构图等元素,即像人类一样感知色彩的明暗对比、形状的组合方式、构图的视觉重心等,则可能需集成视觉模型(如深度学习中的卷积神经网络,CNN)来处理图片的视觉信息。。。。。先鸽

### 使用 Deepseek Milvus 构建大规模知识库 #### 方案概述 为了构建一个671B规模的知识库,采用DeepSeekMilvus相结合的方式能够提供高效的数据处理能力强大的检索性能。通过利用DeepSeek提供的API接口以及Milvus向量数据库的功能特性,可以实现数据的有效存储与快速查询。 #### 数据准备阶段 在开始之前,需要准备好待入库的大规模文本或其他形式的信息资源。这些原始资料可能来自多个渠道,如网页抓取、文档扫描或是其他业务系统的导出文件等[^1]。 #### 预处理流程 针对收集来的海量非结构化或半结构化的源材料实施必要的预处理操作,包括但不限于清洗噪声、分词标注、去除停用词等一系列自然语言处理技术的应用,从而转换成适合后续索引入库的形式。 #### 向量化表示 借助于先进的语义理解模型(例如BERT),将经过初步加工后的每条记录转化为固定维度的稠密向量表达方式。此过程不仅保留了原文本的核心含义特征,同时也便于计算机理解计算相似度匹配关系。 ```python from transformers import BertTokenizer, BertModel import torch tokenizer = BertTokenizer.from_pretrained('bert-base-uncased') model = BertModel.from_pretrained('bert-base-uncased') def get_embedding(text): inputs = tokenizer(text, return_tensors="pt", padding=True, truncation=True) outputs = model(**inputs)[0][:, 0, :].detach().numpy() return outputs.flatten() text_example = "This is an example sentence." vector_representation = get_embedding(text_example) print(vector_representation) ``` #### 存储至 Milvus 完成上述准备工作之后,就可以把得到的所有实体对象及其对应的嵌入式编码批量导入到预先配置好的Milvus实例当中去了。这一步骤涉及到创建集合定义字段模式并指定距离度量标准等内容设置工作。 ```python from pymilvus import connections, FieldSchema, CollectionSchema, DataType, Collection connections.connect("default") fields = [ FieldSchema(name="id", dtype=DataType.INT64, is_primary=True), FieldSchema(name="embedding", dtype=DataType.FLOAT_VECTOR, dim=768) ] schema = CollectionSchema(fields) collection_name = 'large_scale_knowledge_base' milvus_collection = Collection(name=collection_name, schema=schema) data_to_insert = [[i for i in range(len(vectors))], vectors] milvus_collection.insert(data_to_insert) milvus_collection.load() # 加载新插入的数据以便立即可用 ``` #### 整合 DeepSeek API 最后,在应用程序层面调用DeepSeek所提供的RESTful风格的服务端点来发起请求命令,比如上传新的知识点条目或者执行复杂的组合条件过滤查找任务等等。由于其设计之初就考虑到了易于接入第三方平台这一点,因此整个对接过程相对较为简便快捷。 ```json POST /api/v1/search HTTP/1.1 Host: deepseek.example.com Content-Type: application/json { "query": "What are the main features of...", "topk": 5, "params": { "nprobe": 16 } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值