本地部署大模型完全指南③:私有知识库搭建(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服务,团队协作更高效。


需要完整脚本和配置文件的同学,可以看我主页的付费资源专栏。

有问题欢迎评论区留言,大家一起讨论!

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐