项目目标
本项目的核心目标是打造一套企业级AI知识库系统,覆盖从多源数据接入、文档解析、智能检索到安全权限控制的完整流程。该系统支持用户通过自然语言提问,自动检索相关知识并生成精准答案,旨在解决企业知识管理中普遍存在的“找不到、不信任、不安全”三大痛点。
最终效果
完成本系统部署后,你将获得一个功能完备的企业知识库应用,具备以下核心能力:
- 用户可通过Web界面或IM机器人提问,系统自动返回基于内部文档的精准答案
- 支持混合检索,同时匹配关键词和语义相似度,提升检索准确率
- 知识图谱支持关联查询,例如“查找所有依赖微服务架构且由后端团队维护的项目文档”
- 文档级权限控制,不同安全等级的数据实现物理隔离,保障信息安全
技术方案
系统采用经典的四层架构设计:
┌─────────────────────────────────────┐
│ 应用层(Web/API/IM Bot/Widget) │
├─────────────────────────────────────┤
│ 智能层(RAG/知识图谱/问答引擎) │
├─────────────────────────────────────┤
│ 安全层(权限控制/数据隔离/审计) │
├─────────────────────────────────────┤
│ 数据层(多源接入/文档解析/索引) │
└─────────────────────────────────────┘
核心技术栈选型如下:
- 文档解析:Apache Tika + PaddleOCR
- 全文检索:Elasticsearch
- 向量数据库:Milvus
- 知识图谱:Neo4j Community
- LLM:Qwen2
- Embedding:BGE-large-zh
- RAG框架:LlamaIndex
环境与依赖
在部署前,需准备好以下运行环境:
- 操作系统:Linux(推荐Ubuntu 22.04)
- Python 3.10+
- Docker和Docker Compose
- NVIDIA GPU(可选,用于LLM推理加速)
- Node.js 18+(用于前端应用)
主要依赖库及版本要求如下:
- Apache Tika 2.9+
- PaddleOCR 2.7+
- Elasticsearch 8.x
- Milvus 2.3+
- Neo4j Community 5.x
- LlamaIndex 0.9+
- Qwen2 7B
- BGE-large-zh
项目结构
ai-knowledge-base/
├── docker-compose.yml
├── config/
│ ├── elasticsearch.yml
│ ├── milvus.yml
│ └── neo4j.yml
├── src/
│ ├── data_layer/
│ │ ├── connectors/
│ │ │ ├── s3_connector.py
│ │ │ ├── nfs_connector.py
│ │ │ ├── webda v_connector.py
│ │ │ └── local_connector.py
│ │ ├── parsers/
│ │ │ ├── pdf_parser.py
│ │ │ ├── doc_parser.py
│ │ │ └── ocr_parser.py
│ │ └── indexer.py
│ ├── intelligence_layer/
│ │ ├── retriever.py
│ │ ├── reranker.py
│ │ ├── knowledge_graph.py
│ │ └── llm_engine.py
│ ├── security_layer/
│ │ ├── rbac.py
│ │ ├── sanitizer.py
│ │ └── auditor.py
│ └── api_layer/
│ ├── main.py
│ └── routes/
├── tests/
└── scripts/
核心实现
数据层实现
首先实现统一的数据源接入接口。以下代码创建 src/data_layer/connectors/base_connector.py:
from abc import ABC, abstractmethod
from typing import List, Dict
import os
class BaseConnector(ABC):
@abstractmethod
def list_files(self, path: str) -> List[str]:
pass
@abstractmethod
def read_file(self, path: str) -> bytes:
pass
@abstractmethod
def get_metadata(self, path: str) -> Dict:
pass
以S3连接器为例,实现具体连接器,创建 src/data_layer/connectors/s3_connector.py:
import boto3
from botocore.exceptions import ClientError
from .base_connector import BaseConnector
class S3Connector(BaseConnector):
def __init__(self, bucket: str, endpoint_url: str = None, access_key: str = None, secret_key: str = None):
self.bucket = bucket
self.client = boto3.client(
's3',
endpoint_url=endpoint_url,
aws_access_key_id=access_key,
aws_secret_access_key=secret_key
)
def list_files(self, prefix: str) -> List[str]:
response = self.client.list_objects_v2(Bucket=self.bucket, Prefix=prefix)
return [obj['Key'] for obj in response.get('Contents', [])]
def read_file(self, path: str) -> bytes:
response = self.client.get_object(Bucket=self.bucket, Key=path)
return response['Body'].read()
def get_metadata(self, path: str) -> Dict:
response = self.client.head_object(Bucket=self.bucket, Key=path)
return {
'size': response['ContentLength'],
'last_modified': response['LastModified'].isoformat(),
'etag': response['ETag']
}
文档解析管线采用两级解析架构。创建 src/data_layer/parsers/ocr_parser.py:
from paddleocr import PaddleOCR
import pdfplumber
class OCRParser:
def __init__(self):
self.ocr = PaddleOCR(use_angle_cls=True, lang='ch')
def parse_pdf(self, pdf_path: str) -> str:
text = ""
with pdfplumber.open(pdf_path) as pdf:
for page in pdf.pages:
# 提取可解析文本
page_text = page.extract_text()
if page_text and page_text.strip():
text += page_text + "\n"
else:
# 扫描件使用OCR
for img in page.images:
result = self.ocr.ocr(img['stream'])
for line in result:
text += line[1][0] + " "
return text
接着创建索引器,负责将解析后的文档写入Elasticsearch和Milvus,创建 src/data_layer/indexer.py:
from elasticsearch import Elasticsearch
from pymilvus import connections, Collection, CollectionSchema, FieldSchema, DataType
import numpy as np
class Indexer:
def __init__(self, es_host: str, milvus_host: str):
self.es = Elasticsearch([es_host])
connections.connect(host=milvus_host, port='19530')
self._init_milvus_collection()
def _init_milvus_collection(self):
fields = [
FieldSchema(name="id", dtype=DataType.INT64, is_primary=True),
FieldSchema(name="embedding", dtype=DataType.FLOAT_VECTOR, dim=1024),
FieldSchema(name="metadata", dtype=DataType.JSON)
]
schema = CollectionSchema(fields, "Document embeddings")
self.collection = Collection("documents", schema)
def index_document(self, doc_id: str, text: str, embedding: List[float], metadata: Dict):
# 写入Elasticsearch
self.es.index(index="documents", id=doc_id, body={
"text": text,
"metadata": metadata
})
# 写入Milvus
self.collection.insert([
[int(doc_id)], [embedding], [metadata]
])
智能层实现
实现混合检索功能。创建 src/intelligence_layer/retriever.py:
from elasticsearch import Elasticsearch
from pymilvus import Collection
from sentence_transformers import SentenceTransformer
from typing import List, Tuple
class HybridRetriever:
def __init__(self, es_host: str, milvus_host: str, model_name: str = "BAAI/bge-large-zh"):
self.es = Elasticsearch([es_host])
connections.connect(host=milvus_host, port='19530')
self.collection = Collection("documents")
self.encoder = SentenceTransformer(model_name)
def retrieve(self, query: str, top_k: int = 10) -> List[Tuple[str, float]]:
# 语义检索
query_embedding = self.encoder.encode(query).tolist()
semantic_results = self.collection.search(
data=[query_embedding],
anns_field="embedding",
param={"metric_type": "IP", "params": {"nprobe": 10}},
limit=top_k
)
# 关键词检索
keyword_results = self.es.search(
index="documents",
body={
"query": {"match": {"text": query}},
"size": top_k
}
)
# RRF融合
combined = self._rrf_merge(semantic_results, keyword_results, k=60)
return combined[:top_k]
def _rrf_merge(self, semantic_results, keyword_results, k: int = 60) -> List[Tuple[str, float]]:
scores = {}
for rank, hit in enumerate(semantic_results[0]):
doc_id = hit.id
scores[doc_id] = scores.get(doc_id, 0) + 1.0 / (k + rank + 1)
for rank, hit in enumerate(keyword_results['hits']['hits']):
doc_id = hit['_id']
scores[doc_id] = scores.get(doc_id, 0) + 1.0 / (k + rank + 1)
sorted_scores = sorted(scores.items(), key=lambda x: x[1], reverse=True)
return sorted_scores
知识图谱构建通过LLM抽取实体和关系。创建 src/intelligence_layer/knowledge_graph.py:
from neo4j import GraphDatabase
import json
class KnowledgeGraphBuilder:
def __init__(self, uri: str, user: str, password: str):
self.driver = GraphDatabase.driver(uri, auth=(user, password))
def extract_entities_and_relations(self, text: str, llm_pipeline) -> Dict:
# 使用LLM抽取实体和关系
prompt = f"""从以下文本中抽取实体和关系,输出JSON格式:
文本:{text}
输出格式:{{"entities": [{{"name": "...", "type": "..."}}], "relations": [{{"source": "...", "target": "...", "relation": "..."}}]}}"""
response = llm_pipeline(prompt, max_new_tokens=500)
return json.loads(response[0]['generated_text'])
def add_to_graph(self, data: Dict):
with self.driver.session() as session:
for entity in data['entities']:
session.run(
"MERGE (e:Entity {name: $name, type: $type})",
name=entity['name'], type=entity['type']
)
for relation in data['relations']:
session.run(
"MATCH (s:Entity {name: $source}), (t:Entity {name: $target}) "
"MERGE (s)-[r:RELATES {type: $relation}]->(t)",
source=relation['source'], target=relation['target'], relation=relation['relation']
)
安全层实现
实现基于角色的访问控制(RBAC)。创建 src/security_layer/rbac.py:
from typing import List, Dict
class RBACController:
def __init__(self):
self.roles = {
'admin': ['read', 'write', 'delete', 'manage'],
'editor': ['read', 'write'],
'viewer': ['read']
}
self.document_permissions: Dict[str, Dict] = {}
def set_document_permission(self, doc_id: str, role: str, allow: bool = True):
if doc_id not in self.document_permissions:
self.document_permissions[doc_id] = {}
self.document_permissions[doc_id][role] = allow
def check_access(self, user_roles: List[str], doc_id: str, action: str) -> bool:
if doc_id not in self.document_permissions:
return True # 无权限配置时默认允许
for role in user_roles:
if role in self.document_permissions[doc_id] and self.document_permissions[doc_id][role]:
if action in self.roles.get(role, []):
return True
return False
配置说明
创建 docker-compose.yml 文件以启动基础服务:
version: '3.8'
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.10.0
environment:
- discovery.type=single-node
- xpack.security.enabled=false
ports:
- "9200:9200"
volumes:
- es_data:/usr/share/elasticsearch/data
milvus:
image: milvusdb/milvus:v2.3.0
ports:
- "19530:19530"
environment:
- ETCD_EMBEDDED=true
- MINIO_ACCESS_KEY=minioadmin
- MINIO_SECRET_KEY=minioadmin
volumes:
- milvus_data:/var/lib/milvus
neo4j:
image: neo4j:5.15.0-community
environment:
- NEO4J_AUTH=neo4j/password
ports:
- "7474:7474"
- "7687:7687"
volumes:
- neo4j_data:/data
volumes:
es_data:
milvus_data:
neo4j_data:
创建 config/milvus.yml 配置文件,设置向量索引参数:
index:
metric_type: IP
index_type: IVF_FLAT
params:
nlist: 4096
运行方法
- 启动基础服务(Docker容器):
docker-compose up -d
- 安装Python依赖包:
pip install -r requirements.txt
requirements.txt 内容:
langchain==0.1.0
llama-index==0.9.0
sentence-transformers==2.2.2
pymilvus==2.3.0
elasticsearch==8.10.0
neo4j==5.15.0
paddleocr==2.7.0
pdfplumber==0.10.3
boto3==1.28.0
- 初始化数据库和索引:
python scripts/init_db.py
- 启动API服务:
cd src/api_layer
python main.py
- 在浏览器中访问Web界面:
https://localhost:8000
结果验证
部署完成后,请按照以下步骤验证系统功能是否正常:
- 上传测试文档(如PDF或Word文件)到指定数据源目录
- 通过Web界面输入问题,检查返回结果是否包含文档中的相关内容
- 验证混合检索:分别输入精确关键词和语义相似词,对比返回结果的差异
- 测试知识图谱查询:输入“查找所有依赖微服务架构的文档”,确认是否能返回关联结果
- 验证权限控制:创建不同角色的用户,尝试访问受限文档,确认权限策略生效
常见问题
- 文档解析失败:请检查文档格式是否在支持范围内,PDF扫描件需依赖OCR模块进行解析
- 检索结果为空:确认文档已成功索引,并检查Elasticsearch和Milvus中是否存在数据
- LLM返回错误:请检查GPU显存是否充足,Qwen2 7B模型大约需要16GB显存
- 权限控制不生效:确认RBAC配置已正确加载,且用户角色已正确分配
后续优化
- Phase 1:部署基础组件(Elasticsearch + Milvus + 开源LLM),接入2-3个核心文档源,实现基础RAG检索和Web界面功能
- Phase 2:上线混合检索与Reranking精排,构建知识图谱,接入IM Bot和API接口
- Phase 3:实现物理级数据隔离,支持多终端接入,进行性能优化与安全加固
查询验证清单:
- 所有Docker容器均处于正常运行状态
- Elasticsearch索引已创建且包含文档数据
- Milvus集合已存在且包含向量数据
- Neo4j图数据库中已包含实体和关系
- API服务能够正常响应查询请求
- Web界面可以正常交互使用
