引言
为了应对不同的开发需求,本文我们将采用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
!