Spring AI RAG 体验项目中使用的是阿里云的模型,嵌入、推理和排序要调用阿里云的服务。本例尝试本地搭建ollama,跑一些开源的模型。
ollama号称大模型领域的docker。用来做验证或者跑一些经过蒸馏/量化的轻量模型、搭建问答机器人知识库,够用了。
整体步骤是:(1)安装ollama,相当于安装docker (2)ollama中拉取模型,相当于docker拉取镜像进行并运行 (3)验证ollama中的模型(4)写一个spring ai应用,该应用调用ollama中跑的模型。
本例代码 github.com/zhouruibest...
1. 安装ollama
ollama被设计用来单机运行的。可以用到Window上的显卡。

2. 拉取模型
windows powershell中
sh
ollama pull deepseek-r1:8b
ollama pull nomic-embed-text:latest
3. 验证
ollama默认监听本地11434端口,spring ai应用就是访问这个地址。用curl测一下:
sh
curl http://localhost:11434/api/generate -d '{
"model": "deepseek-r1:8b",
"prompt": "请介绍一下AI的发展历史"
}'
curl http://localhost:11434/api/embed -d '{
"model": "nomic-embed-text:latest",
"input": "这是一段需要转换为向量的文本"
}'
命令行中显示不友好。也可以用Open-WebUI(web 页面工具,镜像是 ghcr.io/open-webui/open-webui:main) ,在页面验证。 这里用了它的国内的镜像。推荐一个镜像同步网站 docker.aityp.com/。
bash
docker run -d -p 3000:8080 \
--add-host=host.docker.internal:host-gateway \
-e OLLAMA_BASE_URL=http://host.docker.internal:11434 \ # 指定了ollama的服务地址
swr.cn-north-4.myhuaweicloud.com/ddn-k8s/ghcr.io/open-webui/open-webui:main
4. spring ai 应用
本例是在 Spring AI RAG 体验项目 的基础上改的。
配置
yaml
spring:
application:
name: handbook
autoconfigure:
exclude:
- org.springframework.ai.autoconfigure.vectorstore.milvus.MilvusVectorStoreAutoConfiguration
ai:
dashscope:
api-key: ${MY_APP_API_KEY} # 项目中还有 spring-ai-alibaba-starter启动器,方便切回去
ollama:
base-url: http://localhost:11434 # Ollama 服务地址,默认是这个端口号
chat:
model: deepseek-r1:8b # 指定要使用的推理模型,这个3060Ti还能跑起来
embedding:
model: nomic-embed-text:latest # 文本嵌入模型,维度768
initKnowledge: false # 是否初始化到向量数据库。只运行回次就行
milvus:
host: mydevcvm
port: 19530
token: "root:Milvus"
database: "default" # 使用默认的
collection: "ollamarag1"
initializeSchema: false # milvus中的database要先创建好。collection可以不用创建;这个字段第一次为true,以后运行为false
向量数据库
nomic-embed-text:latest 的向量维度是768而非 1536
为了方便改回去,项目还引入了 spring-ai-alibaba-starter启动器 和相关的配置。因此会有两个Chat Model和Embedding Model,所以要用 @Qualifier("ollamaEmbeddingModel")和 @Qualifier("ollamaChatModel")指定一下。
java
// VectorStoreConfig.java
@Bean
public VectorStore vectorStore(MilvusServiceClient milvusClient, @Qualifier("ollamaEmbeddingModel") EmbeddingModel embeddingModel) {
return MilvusVectorStore.builder(milvusClient, embeddingModel) // 嵌入模型实例,用于将文本转换为向量表示
.collectionName(collection).databaseName(database).embeddingDimension(768)
.indexType(IndexType.IVF_FLAT) // 设置为IVF_FLAT(倒排文件Flat索引),是一种常用的近似最近邻搜索索引
.metricType(MetricType.COSINE).batchingStrategy(new TokenCountBatchingStrategy()).initializeSchema(initializeSchema).build();
}
嵌入并存储向量数据库
java
// KnowledgeInitializer.java
@Component
public class KnowledgeInitializer implements ApplicationRunner {
@Value("${initKnowledge}")
private Boolean initKnowledge;
@Value("classpath:/docs/handbook.pdf")
private Resource springAiResource;
@Autowired
private VectorStore vectorStore;
private final Logger logger = LoggerFactory.getLogger(KnowledgeInitializer.class);
public void run(ApplicationArguments args) throws Exception {
if (!initKnowledge) {
logger.info("initKnowledge is false, skip init");
return;
}
// 1. parse document
DocumentReader reader = new PagePdfDocumentReader(springAiResource);
List<Document> documents = reader.get();
logger.info("{} documents loaded", documents.size());
// 2. split trunks
List<Document> splitDocuments = new TokenTextSplitter().apply(documents);
logger.info("{} documents split", splitDocuments.size());
// 3. create embedding and store to vector store
logger.info("create embedding and save to vector store");
vectorStore.add(splitDocuments);
}
}
rag service
java
// AIRagService
public class AIRagService {
// 引入 system prompt tmpl
@Value("classpath:/docs/system-qa.st")
private Resource systemResource;
// 注入相关 bean 实例
@Qualifier("ollamaChatModel")
@Autowired
private ChatModel ragChatModel;
@Autowired
private VectorStore vectorStore;
private String getPromptTemplate(Resource systemResource) {
try {
return systemResource.getContentAsString(StandardCharsets.UTF_8);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
// 文本过滤,增强向量检索精度 private static final String textField = "content";
// ......
public Flux<String> retrieve(String prompt) {
// 加载 prompt tmpl
String promptTemplate = getPromptTemplate(systemResource);
// 启用混合搜索,包括嵌入和全文搜索
SearchRequest searchRequest = SearchRequest.builder().topK(4).similarityThresholdAll().build();
// build chatClient,发起大模型服务调用。
return ChatClient.builder(ragChatModel).build().prompt().advisors(new QuestionAnswerAdvisor(vectorStore, searchRequest, promptTemplate)).user(prompt).stream().content();
}
}
访问验证
浏览器地址栏输入:http://localhost:8080/rag/?message=现在我们身边一些老年人感染后病情就很重,那是不是老年人感染后一定会走向重症和危重症
markdown
这是一个非常关心的问题,但答案**并不简单,也不是绝对的"是"或"否"**。
老年人感染新冠病毒后,**发展为重症和危重症的风险确实比年轻健康人群要高得多**,但这**并不意味着所有**老年人感染后都会必然走向重症和危重症。感染后的严重程度受到多种因素的影响,主要包括:
1. **年龄**:年龄越大,风险通常越高。高龄老人(尤其是80岁以上)风险最高。
2. **基础健康状况**:**合并症**是最重要的风险因素。患有**心血管疾病、糖尿病、慢性肺病、免疫缺陷、癌症、慢性肾病、肥胖**等基础疾病,以及**免疫功能低下**的老年人,感染后发展为重症的风险显著增加。
3. **疫苗接种情况**:完成疫苗(包括加强针)接种,特别是有效疫苗,可以显著降低重症和死亡风险。
4. **感染的病毒毒株和变种**:不同毒株的致病力和传播性不同,感染不同毒株的风险和严重程度也会有所差异。
5. **感染时的身体状况和免疫反应**:个体对病毒的反应不同,有些老人即使有基础病,但感染时状态较好,免疫系统反应适当,也可能相对较轻。
6. **医疗支持**:及时的医疗干预、氧疗、抗病毒药物(如Paxlovid)和有效的支持治疗可以显著改善预后,避免走向危重症。
**总结来说:**
* 老年人是**感染后发展为重症的高危人群**,这是毋庸置疑的。
* **但是,个体差异很大**。并非所有老年人都会经历同样的严重程度。很多老年人在感染后可能只是出现类似感冒或流感的症状。
* **关键在于预防和早期干预**。通过接种疫苗、做好防护、管理基础疾病、以及在出现症状时及时就医,可以大大降低老年人走向重症和危重症的风险。
因此,与其说"老年人感染后一定会走向重症",不如说"**老年人感染新冠病毒后,有很高的风险发展为重症,特别是那些有基础疾病和免疫功能低下的老人**"。我们需要对老年人群给予特别的关注和保护。