引言
为了应对不同的开发需求,本文我们将采用Java语言按照DeepSeek官方提供的关于DeepSeek-R1模型文件上传建议的操作方式进行一下实践。
准备工作
- 通过本地Ollama运行DeepSeek-R1模型,具体是运行7B、14B还是32B的模型,依据自己电脑的配置来选择模型的参数体量,当然是参数体量越大效果越好,但是无论使用什么参数体量的模型对本次实践不产生影响。【必不可少】
- Java:- 1.8。
- SpringBoot:- 2.X。
开始实践
第一步:Java&SpringBoot的安装与配置,此处忽略,可自行查询资料安装配置。【这不是本文重点】
第二步:采用工厂模式的Java设计模式来实现一个可以读取不同格式文件的读取器,示例如下:
- 创建文件读取内容的实体类,并且重写toString()方法,将返回的字符串按照DeepSeek官方提供的形式编写好,代码示例如下:
            
            
              java
              
              
            
          
          @Data
@Builder
public class FileReaderContent {
    /**
     * 文件名称
     */
    private String fileName;
    /**
     * 文件类型
     */
    private String fileType;
    /**
     * 文件内容
     */
    private String fileContent;
    /**
     * 转换成DeepSeek官方提供的形式
     *
     * @return String
     */
    @Override
    public String toString() {
        return "[file name]: " + fileName + '\n' +
                "[file content begin]\n" +
                fileContent + "\n" +
                "[file content end]\n";
    }
}- 创建文件读取器,代码示例如下:
            
            
              java
              
              
            
          
          public interface FileReader {
    /**
     * 读取文件内容
     *
     * @return FileReaderContent
     */
    FileReaderContent readerFileContent();
}- 按照不同的文件类型实现具体的文件读取器,下面以text和excel两个类型文件为例:
- 创建text文件读取器实现类,代码示例如下:
            
            
              java
              
              
            
          
          @Slf4j
public class TextFileReader implements FileReader {
    /**
     * 文件地址
     */
    private String filePath;
    public TextFileReader(String filePath) {
        this.filePath = filePath;
    }
    /**
     * 读取text文件内容
     *
     * @return FileReaderContent
     */
    @Override
    public FileReaderContent readerFileContent() {
        StringBuilder result = new StringBuilder();
        try {
            log.info("文件:{},内容读取开始======>", filePath);
            URL url = new URL(filePath);
            URLConnection urlConnection = url.openConnection();
            urlConnection.setConnectTimeout(30 * 1000);
            urlConnection.setReadTimeout(60 * 1000);
            urlConnection.setDoInput(true);
            InputStreamReader inputStreamReader = new InputStreamReader(urlConnection.getInputStream(), "GBK");
            BufferedReader br = new BufferedReader(inputStreamReader);//构造一个BufferedReader类来读取文件
            String s;
            while ((s = br.readLine()) != null) {//使用readLine方法,一次读一行
                result.append(System.lineSeparator()).append(s);
            }
            log.info("文件:{},读取内容:{}", filePath, result);
            br.close();
            log.info("文件:{},内容读取结束======>", filePath);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return FileReaderContent
                .builder()
                .fileName(filePath.substring(filePath.lastIndexOf("/"), filePath.lastIndexOf(".")))
                .fileType(filePath.substring(filePath.lastIndexOf(".")))
                .fileContent(result.toString())
                .build();
    }
}- 创建excel文件读取器实现类,代码示例如下:
            
            
              java
              
              
            
          
          @Slf4j
public class ExcelFileReader implements FileReader {
    /**
     * 文件地址
     */
    private String filePath;
    public ExcelFileReader(String filePath) {
        this.filePath = filePath;
    }
    /**
     * 读取excel文件内容
     *
     * @return FileReaderContent
     */
    @Override
    public FileReaderContent readerFileContent() {
        ExcelToStringListener excelToStringListener = new ExcelToStringListener();
        InputStream inputStream;
        try {
            log.info("文件:{},内容读取开始======>", filePath);
            URL url = new URL(filePath);
            URLConnection urlConnection = url.openConnection();
            urlConnection.setConnectTimeout(30 * 1000);
            urlConnection.setReadTimeout(60 * 1000);
            urlConnection.setDoInput(true);
            inputStream = urlConnection.getInputStream();
            EasyExcel.read(inputStream, excelToStringListener).sheet().doRead();
            log.info("文件:{},读取内容:{}", filePath, excelToStringListener.stringBuilder.toString());
            log.info("文件:{},内容读取结束======>", filePath);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return FileReaderContent
                .builder()
                .fileName(filePath.substring(filePath.lastIndexOf("/"), filePath.lastIndexOf(".")))
                .fileType(filePath.substring(filePath.lastIndexOf(".")))
                .fileContent(excelToStringListener.stringBuilder.toString())
                .build();
    }
}- 创建文件读取工厂类,代码示例如下:
            
            
              java
              
              
            
          
          public class FileReaderFactory {
    public static FileReader getFileReader(String filePath) {
        if (filePath.endsWith(".txt")) {
            return new TextFileReader(filePath);
        } else if (filePath.endsWith(".xlsx") || filePath.endsWith(".xls")) {
            return new ExcelFileReader(filePath);
        } else {
            throw new IllegalArgumentException("对不起,暂不支持读取此格式文件!");
        }
    }
}- 使用工厂类来获取并读取文件内容,代码示例如下:
            
            
              java
              
              
            
          
          FileReader fileReader = FileReaderFactory.getFileReader(tcmdiagQuestionSubmitForm.getTongueImgUrl());
FileReaderContent fileReaderContent = fileReader.readerFileContent();
String content = fileReaderContent.toString() + tcmdiagQuestionSubmitForm.getQuestion();注意注意注意:这里需要将转换好的文件内容fileReaderContent.toString()和用提问的问题拼接起来,然后再将整体内容传输给DeepSeek-R1进行提问。
- 这是完整的文件读取器的代码目录结构以供参考,如下图所示:
参考DeepSeek官方提供的上传文件的提示词,这是官方链接地址:DeepSeek官方链接地址
- 文档内容翻译如下:
            
            
              python
              
              
            
          
          官方提示
在官方的DeepSeek网络/应用程序中,我们不使用系统提示,而是为文件上传和网络搜索设计了两个特定的提示,以获得更好的用户体验。此外,web/app中的温度为0.6。
对于文件上传,请按照模板创建提示,其中{file_name}、{file_content}和{question}是参数。
file_template = \
"""[file name]: {file_name}
[file content begin]
{file_content}
[file content end]
{question}"""第三步:使用Java语言调用本地Ollama服务的DeepSeek-R1大模型。
- 配置文件中新增自定义配置,代码示例如下:
            
            
              yml
              
              
            
          
          # Ollama
    ollama:
        # 服务器地址
        serverUrl: http://192.168.110.45:11434
        # 问答接口地址
        chatUrl: /api/chat
        # 大语言模型
        llmModel: deepseek-r1:32b- 创建一个Ollama的自定义配置文件来读取相关配置,并初始化HttpURLConnectionBean,代码示例如下:
            
            
              java
              
              
            
          
          @Configuration
@Slf4j
public class OllamaLocalConfig {
    /**
     * Ollama服务器地址
     */
    @Value("${kczz.ollama.serverUrl}")
    private String kczzOllamaServerUrl;
    /**
     * Ollama问答接口地址
     */
    @Value("${kczz.ollama.chatUrl}")
    private String kczzOllamaChatUrl;
    /**
     * Ollama大语言模型
     */
    @Value("${kczz.ollama.llmModel}")
    private String kczzOllamaLLMModel;
    /**
     * Ollama视觉模型
     */
    @Value("${kczz.ollama.visionModel}")
    private String kczzOllamaVisionModel;
    @Bean
    public HttpURLConnection httpURLConnection() {
        try {
            log.info("HttpURLConnectionBean创建开始,Ollama接口地址:{} ======>", kczzOllamaServerUrl + kczzOllamaChatUrl);
            //1.设置URL
            URL url = new URL(kczzOllamaServerUrl + kczzOllamaChatUrl);
            //2.打开URL连接
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            //3.设置请求方式
            conn.setRequestMethod("POST");
            //4.设置Content-Type
            conn.setRequestProperty("Content-Type", "application/json;charset=utf-8");
            //5.设置Accept
            conn.setRequestProperty("Accept", "text/event-stream");
            //6.设置DoOutput
            conn.setDoOutput(true);
            //7.设置DoInput
            conn.setDoInput(true);
            log.info("<====== HttpURLConnectionBean创建结束,Ollama接口地址:{}", kczzOllamaServerUrl + kczzOllamaChatUrl);
            return conn;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
    @Autowired
    private HttpURLConnection httpURLConnection;
}- 参考Ollama的API文档实现初始化入参方法,代码示例如下:
            
            
              java
              
              
            
          
              private static String json = "";
    public void OllamaLocalCommonChat(HttpServletResponse response, String content) {
        // 普通聊天
        Map<String, Object> messageMap = new HashMap<>();
        messageMap.put("role", "user");
        messageMap.put("content", content);
        Map<String, Object> paramsMap = new HashMap<>();
        paramsMap.put("model", kczzOllamaLLMModel);
        paramsMap.put("messages", Collections.singletonList(messageMap));
        paramsMap.put("stream", true); // 是否流式输出
        json = JSON.toJSONString(paramsMap);
        this.chat(response);
    }- 参考Ollama的API文档实现处理Ollama返回的数据流方法,代码示例如下:
            
            
              java
              
              
            
          
              private void chat(HttpServletResponse response) {
        // 设置接口响应类型
        response.setContentType(MediaType.TEXT_EVENT_STREAM_VALUE);
        long start = System.currentTimeMillis();
        log.info("Ollama接口url:{},请求参数:{}", kczzOllamaServerUrl + kczzOllamaChatUrl, json);
        InputStream is = null;
        try {
            //8.获取输出流
            OutputStream os = httpURLConnection.getOutputStream();
            //9.写入参数(json格式)
            os.write(json.getBytes(StandardCharsets.UTF_8));
            os.flush();
            os.close();
            //10.获取输入流
            is = httpURLConnection.getInputStream();
            byte[] bytes = new byte[1024];
            int len;
            long end = System.currentTimeMillis();
            log.info("Ollama接口url:{},服务响应时间(毫秒):{}", kczzOllamaServerUrl + kczzOllamaChatUrl, end - start);
            log.info("Ollama接口url:{},开始输出流式内容。", kczzOllamaServerUrl + kczzOllamaChatUrl);
            StringBuilder lineSb = new StringBuilder();
            while ((len = is.read(bytes)) != -1) {
                String line = new String(bytes, 0, len, StandardCharsets.UTF_8);
                lineSb.append(line);
                try {
                    Map<String, Object> map = JSON.parseObject(lineSb.toString());
                    log.info("Ollama接口url:{},输出流式内容:{}", kczzOllamaServerUrl + kczzOllamaChatUrl, map);
                    String contentLLM = JSON.parseObject(JSON.toJSONString(map.get("message"))).getString("content");
                    log.info("Ollama接口url:{},输出流式内容:{}", kczzOllamaServerUrl + kczzOllamaChatUrl, contentLLM);
                    response.getOutputStream().write(contentLLM.getBytes(StandardCharsets.UTF_8));
                    response.getOutputStream().flush();
                    lineSb.delete(0, lineSb.length() - 1);
                } catch (JSONException ignored) {
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (!Objects.isNull(is)) {
                try {
                    //12.关闭输入流
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }- Ollama官方文档链接地址:官方文档链接地址
- 最后需要将第二步解析好的文件内容+问题内容传入写好的方法内,代码示例如下:
            
            
              java
              
              
            
          
          @Autowired
    private OllamaLocalConfig ollamaLocalConfig;
ollamaLocalConfig.OllamaLocalCommonChat(response, "文件内容+问题内容");好了,至此在Windows系统下Java代码方式的DeepSeek-R1文件上传提示词实践到此结束,如有问题或建议欢迎大家评论区交流,最后还是要Passion!