本地部署大模型完全指南③:私有知识库搭建(Ollama + Dify)
本地部署大模型完全指南③:私有知识库搭建(Ollama + Dify)
光有模型还不够——它不知道你的业务文档。本文教你用Dify搭建私有知识库,让AI能读懂你的文件、回答你专属领域的问题。
前言:为什么本地模型需要知识库?
大模型的训练数据截止到某个时间点,它不知道:
- 你公司的内部文档写了什么
- 你项目的最新代码变更
- 某个特定领域的专业知识
RAG(检索增强生成) 就是解决这个问题的:先在你的文档里搜索相关信息,再把搜索结果作为上下文传给大模型,让它基于这些资料来回答。
传统的RAG方案依赖云端API,但2026年一切都可以本地化:
用户提问
↓
① 向量检索 → 在本地文档库中搜索相关内容
↓
② 拼接上下文 → 问题 + 检索到的文档片段
↓
③ 本地模型推理 → Ollama上的DeepSeek/Qwen回答
↓
④ 返回结果 → 基于你的文档给出精准答案
一、Dify是什么?为什么选它?
Dify是一个开源的LLM应用开发平台,核心能力包括:
| 功能 | 说明 |
|---|---|
| 知识库管理 | 上传文档→自动分段→向量化→检索 |
| 工作流编排 | 可视化拖拽构建AI应用流程 |
| 模型接入 | 支持Ollama/OpenAI/Claude等多种模型 |
| Agent模式 | 支持工具调用、函数调用 |
| 对话API | 提供REST API供外部系统调用 |
二、Dify本地部署
2.1 Docker Compose一键部署
# 克隆Dify仓库
git clone https://github.com/langgenius/dify.git
cd dify/docker
# 复制环境配置
cp .env.example .env
# 启动所有服务
docker compose up -d
# 查看启动状态
docker compose ps
# 等待所有服务变成 healthy 状态
启动后访问 http://localhost:48080,首次进入需要注册管理员账号。
2.2 配置Ollama模型接入
登录Dify后台后:
设置 → 模型供应商 → Ollama
填写以下信息:
| 配置项 | 值 |
|---|---|
| 模型名称 | deepseek-r1:7b(或其他已有模型) |
| 基础URL | http://host.docker.internal:11434 |
| 模型类型 | LLM |
| 上下文长度 | 8192 |
注意:Docker容器内的Dify访问宿主机Ollama需要使用 host.docker.internal 而不是 localhost。
2.3 配置Embedding模型(向量化用)
知识库需要把文档转换成向量,需要配置一个Embedding模型:
# 推荐方案1:用Ollama自带的Embedding模型
ollama pull nomic-embed-text
# 推荐方案2:bge-m3(中文效果更好)
ollama pull bge-m3
然后在Dify后台设置:
设置 → 模型供应商 → Ollama
添加Embedding模型 → 选择 nomic-embed-text 或 bge-m3
三、创建第一个知识库
3.1 准备文档
Dify支持多种文档格式:
支持的格式:TXT、PDF、Markdown、DOCX、HTML、CSV、JSON
文件大小限制:单文件不超过15MB
文档准备建议:
my-knowledge-base/
├── 01_产品手册.md # 产品介绍文档
├── 02_API文档.pdf # 接口文档
├── 03_常见问题.txt # FAQ
├── 04_技术规范.docx # 技术规格
└── 05_最佳实践.md # 经验总结
3.2 上传与分段
在Dify中操作:
知识库 → 创建知识库 → 上传文件 → 选择分段策略
分段策略选择:
| 策略 | 适用场景 | 说明 |
|---|---|---|
| 自动分段 | 通用文档 | 按段落自然切分,每段约200-500字 |
| 自定义分段 | 结构化文档 | 手动设置分段大小和重叠 |
| Q&A对 | FAQ类 | 一问一答格式,精确匹配 |
推荐配置(中文文档):
分段模式:自动分段
分段长度:500 tokens
分段重叠:50 tokens
检索策略:混合检索(关键词+向量)
召回数量:3-5条
3.3 索引与测试
上传完成后,Dify会自动进行:
① 分段 → ② 向量化 → ③ 建立索引 → ④ 完成
完成后可以在界面中测试:
测试问题:公司的产品支持哪些导出格式?
预期结果:Dify会检索知识库中相关文档片段,
并给出基于文档内容的精准回答。
四、搭建知识库问答应用
4.1 创建对话型应用
工作室 → 创建应用 → 对话型应用 → 填写应用名称
4.2 配置应用参数
提示词设置:
你是公司内部知识助手,基于以下资料回答用户问题。
如果资料中没有相关信息,请明确告知"我没有找到相关资料"。
请用中文回答,语言简洁清晰。
参考资料:
{{#context#}}
知识库关联:
在应用配置中 → 添加知识库 → 选择刚才创建的知识库
参数调优:
| 参数 | 推荐值 | 说明 |
|---|---|---|
| 温度 temperature | 0.3 | 知识问答要准确,低温度 |
| 检索召回数 | 3 | 每次检索返回Top-3片段 |
| 相似度阈值 | 0.6 | 低于此值的片段不纳入上下文 |
| 引用标注 | 开启 | 让模型标注信息来源段落 |
4.3 测试效果
应用配置完成后,可以直接在界面上测试问答:
用户:公司的API接口支持哪些认证方式?
助手:根据公司API文档,支持的认证方式包括:
1. API Key认证(推荐):在Header中传入X-API-Key
2. OAuth 2.0认证:支持授权码模式
3. JWT Token认证:适用于服务间调用
> 信息来源:[02_API文档.pdf - 第3节]
五、本地RAG工作流进阶
5.1 用Python直接调用本地RAG
不依赖Dify界面,直接用Python实现RAG:
# local_rag.py — 本地RAG问答工具
import requests
from typing import List, Dict
class LocalRAG:
"""本地RAG问答系统"""
def __init__(self,
ollama_base: str = "http://localhost:11434",
embed_model: str = "bge-m3",
llm_model: str = "deepseek-r1:7b"):
self.ollama_base = ollama_base
self.embed_model = embed_model
self.llm_model = llm_model
self.documents = [] # 文档存储:[{"content": "...", "metadata": {...}}]
def add_documents(self, docs: List[Dict]):
"""添加文档到知识库"""
for doc in docs:
# 向量化文档
embedding = self._get_embedding(doc["content"])
self.documents.append({
"content": doc["content"],
"metadata": doc.get("metadata", {}),
"embedding": embedding
})
print(f"已添加 {len(docs)} 个文档")
def _get_embedding(self, text: str) -> List[float]:
"""获取文本向量"""
response = requests.post(f"{self.ollama_base}/api/embeddings", json={
"model": self.embed_model,
"prompt": text
})
return response.json()["embedding"]
def search(self, query: str, top_k: int = 3) -> List[Dict]:
"""向量检索最相关的文档"""
query_embedding = self._get_embedding(query)
# 计算余弦相似度
def cosine_sim(a, b):
dot = sum(x*y for x, y in zip(a, b))
na = sum(x*x for x in a) ** 0.5
nb = sum(x*x for x in b) ** 0.5
return dot / (na * nb) if na * nb > 0 else 0
scored = []
for doc in self.documents:
score = cosine_sim(query_embedding, doc["embedding"])
scored.append((score, doc))
scored.sort(key=lambda x: x[0], reverse=True)
return scored[:top_k]
def ask(self, question: str) -> str:
"""基于知识库回答问题"""
# 1. 检索相关文档
results = self.search(question)
# 2. 构建上下文
context = "\n\n".join([
f"[来源{r['metadata'].get('source', '未知')}]\n{r['content']}"
for score, r in results if score > 0.5
])
if not context:
return "抱歉,我的知识库中没有找到相关信息。"
# 3. 构造提示词
prompt = f"""基于以下资料回答问题。
如果资料中没有相关信息,请直接说不知道。
资料:
{context}
问题:{question}
回答:"""
# 4. 调用本地模型
response = requests.post(f"{self.ollama_base}/api/generate", json={
"model": self.llm_model,
"prompt": prompt,
"stream": False,
"options": {"temperature": 0.3}
})
return response.json()["response"]
# 使用示例
rag = LocalRAG()
# 添加文档
rag.add_documents([
{
"content": "Python是一种解释型、面向对象的高级编程语言。",
"metadata": {"source": "python_intro.txt"}
},
{
"content": "Python的列表推导式语法:[expression for item in iterable if condition]",
"metadata": {"source": "python_syntax.txt"}
},
{
"content": "Python函数使用def关键字定义,支持默认参数和关键字参数。",
"metadata": {"source": "python_functions.txt"}
}
])
# 问答
result = rag.ask("Python的列表推导式怎么写?")
print(result)
5.2 批量导入文档
# batch_import.py — 批量导入文档到RAG
import os
from pathlib import Path
def import_documents(rag_instance, folder_path: str):
"""导入文件夹中所有文档"""
supported_ext = {'.txt', '.md', '.csv', '.json'}
for file_path in Path(folder_path).rglob('*'):
if file_path.suffix.lower() in supported_ext:
try:
content = file_path.read_text(encoding='utf-8')
relative_path = str(file_path.relative_to(folder_path))
# 分段处理长文档
chunks = split_text(content, max_chars=500)
for i, chunk in enumerate(chunks):
rag_instance.add_documents([{
"content": chunk,
"metadata": {
"source": relative_path,
"chunk": i + 1
}
}])
print(f"已导入: {relative_path} ({len(chunks)}段)")
except Exception as e:
print(f"导入失败 {file_path}: {e}")
def split_text(text: str, max_chars: int = 500, overlap: int = 50) -> list:
"""将文本按长度分割,保留段落结构"""
paragraphs = text.split('\n\n')
chunks = []
current = ""
for para in paragraphs:
if len(current) + len(para) < max_chars:
current += para + "\n\n"
else:
if current:
chunks.append(current.strip())
current = para[-overlap:] + "\n\n" + para + "\n\n" if overlap > 0 else para + "\n\n"
if current:
chunks.append(current.strip())
return chunks
# 使用
rag = LocalRAG()
import_documents(rag, "./my_documents")
rag.ask("我们公司的技术栈是什么?")
六、Dify高级功能
6.1 工作流模式
Dify支持可视化工作流编排,比单纯问答更强大:
用户输入 → 问题分类器
├── 技术问题 → 检索知识库 → 代码助手模型回答
├── 业务咨询 → 检索知识库 → 业务模型回答
└── 闲聊 → 通用模型回答 → 无需检索
6.2 API集成
Dify应用可对外提供API接口:
# 调用Dify应用API
curl -X POST http://localhost:48080/api/workflows/run \
-H "Authorization: Bearer your-api-key" \
-H "Content-Type: application/json" \
-d '{
"inputs": {"query": "公司的API文档在哪里?"},
"response_mode": "blocking"
}'
6.3 多知识库切换
# 根据业务场景使用不同知识库
def route_query(query: str) -> str:
"""根据查询内容切换到合适知识库"""
if any(kw in query for kw in ["代码", "接口", "开发"]):
return "tech_docs" # 技术文档知识库
elif any(kw in query for kw in ["价格", "产品", "购买"]):
return "product_docs" # 产品知识库
else:
return "general_docs" # 通用知识库
七、性能优化
7.1 检索速度优化
| 问题 | 方案 |
|---|---|
| 文档太多检索慢 | 分库分表,按领域划分 |
| 中文检索不准 | 使用中文优化的Embedding模型(bge-m3) |
| 响应太慢 | 减少召回数量(3→2),缩短上下文 |
7.2 回答质量优化
# 提示词优化技巧
❌ 差:根据资料回答问题
✅ 好:你是严谨的技术文档助手。基于以下资料,用结构化方式回答。
如果资料中找不到相关信息,请明确告知"未找到相关信息"。
回答时标注信息来源(文档名称和章节)。
资料:[检索结果]
问题:[用户问题]
总结
通过Ollama + Dify的组合,你已经在本地搭建了一套完整的企业级知识库系统。从文档上传、向量化存储、语义检索到AI回答,全流程本地化运行,数据不出门。
下一篇预告:第④篇《API服务化与远程访问》—— 把本地模型变成可远程调用的API服务,团队协作更高效。
需要完整脚本和配置文件的同学,可以看我主页的付费资源专栏。
有问题欢迎评论区留言,大家一起讨论!
更多推荐



所有评论(0)