deepseek私有化RAG思路 ollama 运行deepseek模型和向量化模型bge-m3,anything 实现喂数据到向量化数据库milvus,deepseek4j结合便可以实现RAG私有
【代码】deepseek私有化RAG思路 ollama 运行deepseek模型和向量化模型bge-m3,anything 实现喂数据到向量化数据库milvus,deepseek4j结合便可以实现RAG私有。
·
JDK:17
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>org.example</groupId>
<artifactId>deepseek4jDemo2</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>deepseek4jDemo2</name>
<description>deepseek4jDemo2</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-ollama-spring-boot-starter</artifactId>
<version>1.0.0.M2</version>
</dependency>
<dependency>
<groupId>io.github.pig-mesh.ai</groupId>
<artifactId>deepseek-spring-boot-starter</artifactId>
<version>1.4.5</version>
</dependency>
<!-- 链接 milvus SDK-->
<dependency>
<groupId>io.milvus</groupId>
<artifactId>milvus-sdk-java</artifactId>
<version>2.5.5</version>
</dependency>
<!--连接池-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.12.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<repositories>
<!-- spring ai -->
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<releases>
<enabled>false</enabled>
</releases>
</repository>
</repositories>
<profiles>
<profile>
<id>dev</id> <!-- 补充profile的id -->
<properties>
<username>zhangsan</username>
<profile.active>dev</profile.active>
</properties>
<repositories>
</repositories>
</profile>
</profiles>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
OllamaController
package org.example.deepseek4jdemo2.controller;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.protobuf.Field;
import io.milvus.client.MilvusClient;
import io.milvus.grpc.JSONArray;
import io.milvus.grpc.MutationResult;
import io.milvus.grpc.QueryResults;
import io.milvus.param.R;
import io.milvus.param.dml.DeleteParam;
import io.milvus.param.dml.InsertParam;
import io.milvus.param.dml.QueryParam;
import io.milvus.v2.service.vector.request.DeleteReq;
import io.milvus.v2.service.vector.response.DeleteResp;
import jakarta.annotation.Resource;
import org.example.deepseek4jdemo2.config.MilvusConnectPool;
import org.reactivestreams.Publisher;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.prompt.Prompt;
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.http.ResponseEntity;
import org.springframework.http.codec.ServerSentEvent;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
@RestController
@RequestMapping("/ollama")
public class OllamaController {
/**
* 用来记录上一次的对话, 实现历史对话
*/
List<Message> messageList = new CopyOnWriteArrayList<>();
@Resource
private OllamaChatModel ollamaChatModel;
@Autowired
private MilvusConnectPool milvusConnectPool;
/**
* 测试Milvus
* @param msg 消息
* @return Object
*/
@RequestMapping(value = "/ai/testMilvus")
public ResponseEntity<String> testMilvus(@RequestParam(value = "msg")String msg){
// return addEntity();
// return delEntity();
return selectEntity();
}
private ResponseEntity<String> addEntity(){
//申明
MilvusClient milvusClient= null;
try {
//获取
milvusClient = milvusConnectPool.getMilvusClient();
//使用相关逻辑...
//插入一条记录
// 定义集合名称
String collectionName = "test1";
// 准备插入的数据
String id = "4"; // 标量字段
// List<Float> vector = new ArrayList<>();
// vector.add(0.1f);
// vector.add(0.2f);
// vector.add(0.3f); // 假设这是一个 3 维向量
Gson gson = new Gson();
JsonObject jsonObject = gson.fromJson("{\"id\": "+id+", \"embedding\": [0.267653703392273,0.27469801090735446,0.7523448179995487,0.6182679052248916,0.5209705659673978,0.43325358160863026,0.734382551032327,0.14742111756050447,0.802413264681755,0.2462367824605045,0.8299054084772908,0.31264182713641087,0.04917484937162442,0.16127496484577897,0.2455484992892647,0.24276937996044468,0.8916419617467273,0.6665976572600019,0.9296905551538344,0.18167655642252423,0.4386186230899989,0.5849729529799343,0.2694948471406158,0.26614017277642743,0.8341706792424171,0.969470311659355,0.9121468980472549,0.2098657060788296,0.9883191740257584,0.06447802122144597,0.7118003795533774,0.736822974913709,0.9820312196361698,0.48581245336410794,0.1170811069106843,0.5134760820090933,0.34197137179044157,0.2299590027664138,0.8001682318319869,0.3621451457669662,0.6107828674109055,0.7362099273922231,0.18389225714694213,0.5170530188880735,0.06062360331149086,0.4416321491991757,0.13404612096779078,0.8124414219675331,0.13364241253820075,0.10089229984111814,0.37836102919784187,0.8199055981481149,0.4930100213035673,0.4249653532649711,0.46112697056926555,0.0598526101464365,0.2709764521575848,0.09098176531284685,0.6875809291691868,0.3400363848833554,0.9823577536488808,0.41755220600264176,0.9208623739588515,0.37271486138965537,0.5842775261352489,0.5320319248318137,0.9033098195782951,0.9313662654645027,0.9574831706937885,0.5366807664113107,0.33229305289457445,0.8023644765297424,0.4223566001331389,0.13775175913848448,0.413981932518503,0.5182451969350947,0.42693156008700117,0.8966189680658261,0.11518616496893519,0.9644146031198013,0.7385933802201408,0.602367975638975,0.7475218290878269,0.955402233631401,0.4117843073949887,0.04136438267797016,0.9065018351772771,0.5244012347993954,0.5604760504007029,0.39928012315669514,0.949872148410162,0.9905761967161661,0.695375291966563,0.24034338541509226,0.6975226686693514,0.9792128157460795,0.7821347117509823,0.5697587296888842,0.23299486125378555,0.41736112155934135,0.5689365569712348,0.4915866286703243,0.4972308367573499,0.7813830127027821,0.8844740787436474,0.44279424624744834,0.8528308018090132,0.5780228641541774,0.5523061476589788,0.3618334840206221,0.44096656196363937,0.0777927933524829,0.6717438441669625,0.16500861297209402,0.17021866624842397,0.029649856606401848,0.08754637577879376,0.8243210031521997,0.09135375267457002,0.7262079108275465,0.06143994684844811,0.2072436229208674,0.2146583852675905,0.03804672242289775,0.8336921828494217,0.38735637067305273,0.61624457478956,0.36513073908775495], \"name\": \"李四\"}", JsonObject.class);
List<JsonObject> rows = Arrays.asList(jsonObject);
// 构造插入参数
InsertParam insertParam = InsertParam.newBuilder()
.withCollectionName(collectionName)
.withRows(rows)
.build();
// 插入数据
R<MutationResult> response = milvusClient.insert(insertParam);
return ResponseEntity.ok("成功"+response);
}catch (Exception e){
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("获取失败");
}finally {
//归还
milvusConnectPool.releaseMilvusClient(milvusClient);
}
}
private ResponseEntity<String> delEntity(){
MilvusClient milvusClient= null;
try {
//获取
milvusClient = milvusConnectPool.getMilvusClient();
String collectionName = "test1";
String expr = "name == \"李四\"";
R<MutationResult> delete = milvusClient.delete(DeleteParam.newBuilder()
.withCollectionName(collectionName)
.withExpr(expr).build());
return ResponseEntity.ok("成功"+delete);
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage());
} finally {
//归还
milvusConnectPool.releaseMilvusClient(milvusClient);
}
}
private ResponseEntity<String> selectEntity(){
MilvusClient milvusClient= null;
try {
//获取
milvusClient = milvusConnectPool.getMilvusClient();
String collectionName = "test1";
String expr = "name == \"张三\"";
QueryParam queryParam = QueryParam.newBuilder().withCollectionName(collectionName).withExpr(expr).build();
R<QueryResults> queryResultsR = milvusClient.query(queryParam);
return ResponseEntity.ok("成功"+queryResultsR);
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage());
} finally {
//归还
milvusConnectPool.releaseMilvusClient(milvusClient);
}
}
/**
* 非流式
* @param msg 消息
* @return Object
*/
@RequestMapping(value = "/ai/ollama")
public Object ollama(@RequestParam(value = "msg")String msg){
return ollamaChatModel.call(msg);
}
/**
* 流式
* @param msg 消息
* @return Flux<ServerSentEvent<String>>
*/
@RequestMapping(value = "/ai/streamChat", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ServerSentEvent<String>> ollama1(@RequestParam(value = "msg") String msg) {
UserMessage userMessage = new UserMessage(msg);
if(messageList.size() == 2){
messageList.remove(0);
}
messageList.add(userMessage);
Prompt prompt = new Prompt(messageList);
System.out.println("准备开始进行推送...");
// 使用 Flux 流来进行异步处理,优化流的错误处理和结束操作
return ollamaChatModel.stream(prompt) // 获取 Flux<ChatResponse>,异步流
.doOnNext(chatResponse -> {
System.out.println("收到的数据: " + chatResponse);
})
.map(chatResponse -> {
String outputText = chatResponse.getResult().getOutput().getContent();
if (outputText == null || outputText.isEmpty()) {
// return ServerSentEvent.builder("没有返回有效数据").build();
return ServerSentEvent.builder("").event("complete").build();
}
System.out.println("推送 : " + outputText);
return ServerSentEvent.builder(outputText).build();
})
.onErrorResume(e -> {
// 错误处理,返回推送失败的信息
return Flux.just(ServerSentEvent.builder("推送发生错误,请重试。").build());
});
}
/**
*
* return ollamaChatModel.stream(prompt)
* .concatMap(chatResponse -> {
* String outputText = chatResponse.getResult().getOutput().getContent();
* // 移除空值检查,允许传递空字符串
* return Mono.just(ServerSentEvent.builder(outputText != null ? outputText : "").build());
* })
* .onErrorResume(e -> {
* return (Publisher<? extends ServerSentEvent<String>>) Flux.just(
* ServerSentEvent.builder("[ERROR] 请求失败").event("error").build(),
* ServerSentEvent.builder().event("complete").build() // 结束信号
* );
* })
* .concatWithValues( // 合并结束信号
* ServerSentEvent.<String>builder()
* .event("complete")
* .data("") // 必须包含至少一个有效字段
* .build() // 正常结束信号
* );
* // .concatWithValues(ServerSentEvent.builder().event("complete").build()); // 正常结束信号
* // .concatWith(Flux.just(ServerSentEvent.builder().event("complete").build())); // 正常结束信号
*/
}
前端访问跨域解决
package org.example.deepseek4jdemo2.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/*
* 跨域问题解决
* */
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") // 所有接口
// .allowedOrigins("*") // 允许所有源(生产环境建议指定具体域名)
// .addAllowedOrigin("http://example.com"); // 精确指定源
.allowedOriginPatterns("http://localhost:8889","http://localhost:5173")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") // 允许的方法
.allowedHeaders("*") // 允许所有请求头
.allowCredentials(true) // 允许携带凭证(如 cookies)
.maxAge(3600); // 预检请求缓存时间(秒)
}
}
Milvus连接池 MilvusConnectPool
package org.example.deepseek4jdemo2.config;
import io.milvus.client.MilvusClient;
import org.apache.commons.pool2.PooledObjectFactory;
import org.apache.commons.pool2.impl.AbandonedConfig;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
public class MilvusConnectPool extends GenericObjectPool<MilvusClient> {
/**
* Creates a new {@code GenericObjectPool} using defaults from
* {@link GenericObjectPoolConfig}.
*
* @param factory The object factory to be used to create object instances
* used by this pool
*/
public MilvusConnectPool(PooledObjectFactory<MilvusClient> factory) {
super(factory);
}
/**
* Creates a new {@code GenericObjectPool} using a specific
* configuration.
*
* @param factory The object factory to be used to create object instances
* used by this pool
* @param config The configuration to use for this pool instance. The
* configuration is used by value. Subsequent changes to
* the configuration object will not be reflected in the
* pool.
*/
public MilvusConnectPool(PooledObjectFactory<MilvusClient> factory, GenericObjectPoolConfig<MilvusClient> config) {
super(factory, config);
}
/**
* Creates a new {@code GenericObjectPool} that tracks and destroys
* objects that are checked out, but never returned to the pool.
*
* @param factory The object factory to be used to create object instances
* used by this pool
* @param config The base pool configuration to use for this pool instance.
* The configuration is used by value. Subsequent changes to
* the configuration object will not be reflected in the
* pool.
* @param abandonedConfig Configuration for abandoned object identification
* and removal. The configuration is used by value.
*/
public MilvusConnectPool(PooledObjectFactory<MilvusClient> factory, GenericObjectPoolConfig<MilvusClient> config, AbandonedConfig abandonedConfig) {
super(factory, config, abandonedConfig);
}
/**
* 获取MilvusClient实例
*/
public MilvusClient getMilvusClient() throws Exception {
return super.borrowObject();
}
/**
* 归还MilvusClient实例
*/
public void releaseMilvusClient(MilvusClient milvusClient) {
if (milvusClient!= null) {
super.returnObject(milvusClient);
}
}
}
package org.example.deepseek4jdemo2.config;
import io.milvus.client.MilvusClient;
import jakarta.annotation.PreDestroy;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.time.Duration;
@Slf4j
@Configuration
public class MilvusConnectPoolConfig {
private static MilvusConnectPool pool;
@Value("${spring.datasource.milvus-connect-pool.milvus.username}")
private String username;
@Value("${spring.datasource.milvus-connect-pool.milvus.password}")
private String password;
@Value("${spring.datasource.milvus-connect-pool.milvus.host}")
private String host;
@Value("${spring.datasource.milvus-connect-pool.milvus.port}")
private Integer port;
/** 最大空闲数 */
@Value("${spring.datasource.milvus-connect-pool.max-idle}")
private Integer maxIdle;
/** 最小空闲数 */
@Value("${spring.datasource.milvus-connect-pool.min-idle}")
private Integer minIdle;
/** 最大总数 */
@Value("${spring.datasource.milvus-connect-pool.max-total}")
private Integer maxTotal;
@Bean("milvusConnectPool")
public MilvusConnectPool milvusConnectPool(){
// 配置连接池的参数
GenericObjectPoolConfig<MilvusClient> config = new GenericObjectPoolConfig<>();
config.setMaxTotal(maxTotal); // 设置连接池的最大连接数
config.setMaxIdle(maxIdle); // 设置连接池的最大空闲连接数
config.setMinIdle(minIdle); // 设置连接池的最小空闲连接数
config.setMinEvictableIdleTime(Duration.ofMinutes(30));//逐出连接的最小空闲时间, 默认1800000毫秒(30分钟)
config.setTimeBetweenEvictionRuns(Duration.ofMinutes(30));// 多久执行一次对象扫描,将无用的对象销毁,默认-1不扫描
config.setTestOnBorrow(true);// 在获取对象的时候检查有效性, 默认false
config.setTestOnReturn(false);// 在归还对象的时候检查有效性, 默认false
config.setTestWhileIdle(false);// 在空闲时检查有效性, 默认false
config.setMaxWait(Duration.ofSeconds(1));// 最大等待时间, 默认的值为-1,表示无限等待。
config.setLifo(true);// 是否启用后进先出, 默认true
config.setBlockWhenExhausted(true);// 连接耗尽时是否阻塞, false立即抛异常,true阻塞直到超时, 默认true
config.setNumTestsPerEvictionRun(3);// 每次逐出检查时 逐出的最大数目 默认3
//此处建议关闭jmx或是设置config.setJmxNameBase(), 因为默认注册的jmx会与项目可能已经存在的其他基于池类的实现bean冲突
config.setJmxEnabled(false);
// 创建连接工厂
MilvusConnectPoolFactory factory = new MilvusConnectPoolFactory(username, password, host, port);
// 初始化连接池
pool = new MilvusConnectPool(factory, config);
// 以最小空闲数量为初始连接数, 添加初始连接
if(minIdle > 0){
for (int i = 0; i < minIdle; i++) {
try {
pool.addObject();
}catch (Exception e){
log.error("添加初始连接失败");
}
}
}
return pool;
}
/**
* 注销连接池
*/
@PreDestroy
public static void close() {
if (pool != null) {
pool.close();
}
}
}
工厂类
package org.example.deepseek4jdemo2.config;
import io.milvus.client.MilvusClient;
import io.milvus.client.MilvusServiceClient;
import io.milvus.grpc.CheckHealthResponse;
import io.milvus.param.ConnectParam;
import io.milvus.param.LogLevel;
import io.milvus.param.R;
import io.milvus.param.RetryParam;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.PooledObjectFactory;
import org.apache.commons.pool2.impl.DefaultPooledObject;
@Slf4j
public class MilvusConnectPoolFactory implements PooledObjectFactory<MilvusClient> {
private final String username;
private final String password;
private final String host;
private final Integer port;
public MilvusConnectPoolFactory(String username, String password, String host, Integer port) {
this.username = username;
this.password = password;
this.host = host;
this.port = port;
}
@Override
public void activateObject(PooledObject<MilvusClient> p) throws Exception {
log.info("每次获取MilvusClient实例时触发此方法");
}
@Override
public void destroyObject(PooledObject<MilvusClient> p) throws Exception {
log.info("注销MilvusClient实例时触发此方法, 可使用MilvusClient.close()关闭连接");
p.getObject().close();
}
@Override
public PooledObject<MilvusClient> makeObject() throws Exception {
log.info("创建MilvusClient实例");
try {
//连接参数
ConnectParam connectParam = ConnectParam.newBuilder()
.withHost(host)
.withPort(port)
.withAuthorization(username,password)
.build();
//重试参数
RetryParam retryParam = RetryParam.newBuilder()
.withMaxRetryTimes(3)
.build();
MilvusClient milvusClient = new MilvusServiceClient(connectParam).withRetry(retryParam);
milvusClient.setLogLevel(LogLevel.Error);
return new DefaultPooledObject<>(milvusClient);
} catch (Exception e) {
throw new RuntimeException("无法创建Milvus数据库连接", e);
}
}
@Override
public void passivateObject(PooledObject<MilvusClient> p) throws Exception {
log.info("归还MilvusClient实例时触发此方法");
}
@Override
public boolean validateObject(PooledObject<MilvusClient> p) {
log.info("判断MilvusClient实例状态, 借助MilvusClient.checkHealth()实现");
R<CheckHealthResponse> health = p.getObject().checkHealth();
if (health.getStatus() == R.Status.Success.getCode()) {
return true;
} else {
log.error("连接状态异常, 异常信息: {}", health.getMessage());
return false;
}
}
}
chat.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>AI 流式聊天</title>
<!-- 引入Quill富文本编辑器 -->
<!-- <link href="https://cdn.quilljs.com/1.3.6/quill.snow.css" rel="stylesheet">-->
<link href="./css/quill.snow.css" rel="stylesheet">
<style>
.container {
max-width: 800px;
margin: 20px auto;
padding: 20px;
}
#editor {
height: 300px;
margin-bottom: 20px;
border: 1px solid #ccc;
border-radius: 4px;
}
.button-group {
text-align: right;
}
button {
padding: 10px 20px;
background: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:disabled {
background: #6c757d;
cursor: not-allowed;
}
.loading {
display: none;
color: #6c757d;
margin-top: 10px;
}
</style>
</head>
<body>
<div class="container" id="app">
<!-- 富文本编辑器容器 -->
<div id="editor"></div>
<!-- <div>-->
<!-- <textarea id="area1" style="width: 798px;height: 298px;"></textarea>-->
<!-- </div>-->
<!-- 操作按钮 -->
<div class="button-group">
<button @click="sendMessage" :disabled="isSending">发送请求</button>
<div class="loading" :style="{display: isSending ? 'block' : 'none'}">
正在接收AI回复...
</div>
</div>
</div>
<!-- 引入Vue.js -->
<!--<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>-->
<script src="./js/vue.js"></script>
<!-- 引入Quill编辑器 -->
<!--<script src="https://cdn.quilljs.com/1.3.6/quill.js"></script>-->
<script src="./js/quill.js"></script>
<script>
// var elementArea = document.getElementById("area1");
// quill.setText('你好,你是谁?');
new Vue({
el: '#app',
data: {
isSending: false,
eventSource: null,
responseContent: '',
quill: null // 在 data 中声明
},
mounted(){
// 初始化Quill编辑器
this.quill = new Quill('#editor', {
theme: 'snow',
modules: {
toolbar: [
['bold', 'italic', 'underline'],
[{ 'list': 'ordered'}, { 'list': 'bullet' }],
['link', 'image']
]
}
});
},
methods: {
sendMessage() {
// var elementQuill = document.getElementById("editor");
if (this.isSending) return;
this.isSending = true;
this.responseContent = '';
console.log(this.quill.getText());
// 创建EventSource连接
this.eventSource = new EventSource(
`http://192.168.1.104:8889/ollama/ai/streamChat?msg=${encodeURIComponent(this.quill.getText())}`
);
// 消息监听
this.eventSource.addEventListener('message', event => {
console.log('消息监听',event)
if (event.data === '没有返回有效数据') {
this.closeConnection();
alert('未获取到有效响应');
return;
}
// 获取当前编辑器内容长度
const length = this.quill.getLength();
console.log("quill:"+length);
// 去除数据中的换行符(如果存在)
const cleanText = event.data.replace(/\n/g, '');
console.log("cleanText:"+cleanText);
// 将新内容插入到末尾(流式输出)
// this.quill.insertText(length, cleanText, 'user');
this.quill.insertText(length, event.data, { 'inline': true }, 'user');
this.responseContent = this.quill.getText().replace(/\n/g, '')
console.log('Quill content:', this.responseContent);
this.quill.setText(this.responseContent);
// 移动光标到新位置(可选)
this.quill.setSelection(length + cleanText.length, 0);
// 替换 Quill 的整个内容
console.log('Quill formats:', this.quill.getFormat());
// this.responseContent += event.data;
// console.log(this.responseContent);
//
// this.quill.updateContents([
// { insert: event.data }
// ]);
// this.quill.setSelection(length + event.data.length, 0);
// var elementArea = document.getElementById("area1");
// elementArea.innerText = this.responseContent;
});
// this.eventSource.addEventListener('message', event => {
// if (event.data.startsWith('[ERROR]')) {
// this.closeConnection();
// alert(event.data.replace('[ERROR] ', ''));
// return;
// }
//
// // 累积有效内容(过滤空值)
// if (event.data && event.data.trim() !== '') {
// this.responseContent += event.data;
// quill.updateContents([{ insert: this.responseContent }]);
// }
// });
// 添加流结束监听
this.eventSource.addEventListener('complete', () => {
this.closeConnection();
// if (this.responseContent === '') {
// alert('未获取到有效响应');
// }
});
// 错误处理
this.eventSource.addEventListener('error', error => {
this.closeConnection();
console.error('连接错误:', error);
alert('连接发生异常');
});
},
closeConnection() {
if (this.eventSource) {
this.eventSource.close();
this.eventSource = null;
}
this.isSending = false;
}
},
// 销毁前关闭连接
beforeDestroy() {
this.closeConnection();
}
});
// 添加回车发送功能
document.addEventListener('keydown', (e) => {
if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) {
vueApp.sendMessage();
}
});
</script>
</body>
</html>
application.yml
server:
port: 8889
spring:
#Milvus 连接
datasource:
milvus-connect-pool:
max-idle: 5
min-idle: 2
max-total: 10
milvus:
username: root
password:
host: 192.168.1.44
port: 19530
application:
name: Cleaner-AI
ai:
ollama:
# ollama API Server 地址默认的他就是11434
base-url: http://192.168.1.44:11434
chat:
enabled: true
# 使用的模型名称
model: deepseek-r1:14b
# deepseek-v2:16b
options:
temperature: 0.7
## 推理模型链接信息
#deepseek:
# base-url: http://127.0.0.1:11434/v1
# model: deepseek-r1:14b
# api-key: ollama-local
# # 向量模型链接信息
#embedding:
# api-key: ${deepseek.api-key}
# base-url: ${deepseek.base-url}
# model: bge-m3:latest
更多推荐
所有评论(0)