上一篇 下一篇 分享链接 返回 返回顶部

用 Claude 搭一套企业知识库:架构思路、RAG 流程与完整源码

发布人:慈云数据-客服中心 发布时间:12小时前 阅读量:7

Claude 企业知识库搭建|附源码

在企业数字化转型过程中,“知识库”已经从一个简单的文档存储系统,逐渐演变为企业智能化运营的重要基础设施。无论是客服问答、内部制度查询、研发文档检索,还是销售资料辅助、合同条款解释,企业都希望员工能够像与专家对话一样,快速、准确地获取内部知识。

随着大语言模型的发展,基于 Claude、GPT 等模型构建企业知识库,已经成为非常实用的方案。本文将围绕 Claude 企业知识库搭建 展开,介绍整体架构、核心原理、技术选型、实现流程,并附上可运行的示例源码,帮助你快速搭建一个基于 Claude 的企业知识库问答系统。


一、为什么选择 Claude 搭建企业知识库?

Claude 是 Anthropic 推出的大语言模型,具备较强的长文本理解能力、推理能力和安全性。在企业知识库场景中,Claude 有几个明显优势。

1. 长上下文能力强

企业文档通常不是简单的短文本,而是大量制度文件、产品手册、技术文档、合同模板、会议纪要等。这些内容往往篇幅较长,结构复杂。

Claude 的长上下文能力适合处理:

  • 长篇 PDF 文档
  • 多章节制度文件
  • 复杂技术文档
  • 多轮业务问答
  • 多文档综合分析

当企业希望模型不仅能“检索到内容”,还要“理解上下文并进行总结”时,Claude 的优势会更加明显。

2. 输出风格稳定

企业知识库问答不同于普通聊天机器人,它需要回答准确、专业、克制,不能随意编造。Claude 在回答风格上通常比较稳定,适合用于:

  • 企业内部助手
  • 客服辅助系统
  • 合规知识查询
  • 法务/人事制度解释
  • 技术支持知识库

3. 适合 RAG 架构

目前企业知识库最常见的方案是 RAG,即 Retrieval-Augmented Generation,中文通常称为“检索增强生成”。

简单来说,RAG 的流程是:

  1. 将企业文档切分成小片段;
  2. 使用 Embedding 模型将文本转为向量;
  3. 存入向量数据库;
  4. 用户提问时,先检索相关文档片段;
  5. 将检索结果和问题一起交给 Claude;
  6. Claude 基于已检索内容生成答案。

这种方式比直接把所有文档塞给模型更加高效,也更适合企业实际使用。


二、企业知识库整体架构

一个完整的 Claude 企业知识库系统,通常由以下几个模块组成:

企业文档
  ↓
文档解析
  ↓
文本切分
  ↓
向量化 Embedding
  ↓
向量数据库
  ↓
用户提问
  ↓
相似度检索
  ↓
Claude 生成答案
  ↓
返回结果

如果做成 Web 系统,架构可以进一步扩展为:

前端页面
  ↓
后端 API 服务
  ↓
知识库检索模块
  ↓
向量数据库
  ↓
Claude API
  ↓
答案返回

常见技术选型如下:

模块 可选方案
大语言模型 Claude 3.5 Sonnet、Claude 3 Opus、Claude Haiku
后端语言 Python、Node.js
Web 框架 FastAPI、Flask、Express
文档解析 PyPDF、pdfplumber、python-docx
文本切分 LangChain TextSplitter、自定义切分
Embedding OpenAI Embedding、Voyage AI、BGE、text-embedding 模型
向量库 Chroma、FAISS、Milvus、Pinecone、Qdrant
数据库 PostgreSQL、MySQL、MongoDB
前端 Vue、React、Next.js

本文为了便于演示,采用较轻量的方案:

  • 后端:Python + FastAPI
  • 向量库:ChromaDB
  • LLM:Claude API
  • Embedding:OpenAI Embedding 或兼容 Embedding API
  • 文档类型:TXT / Markdown 示例,可扩展 PDF、Word

三、RAG 企业知识库核心原理

在真正写代码之前,我们先理解一下关键步骤。

1. 文档切分

企业文档不能直接整个放入向量数据库,因为太长的文本不利于检索,也会影响相似度匹配效果。

例如一份 5 万字的员工手册,如果整体作为一个向量存储,用户问“年假如何计算”,检索效果会非常差。因此需要将文档切分为多个 chunk。

常见切分方式:

  • 按固定字数切分;
  • 按段落切分;
  • 按标题层级切分;
  • 按语义切分;
  • 加入 overlap 重叠窗口。

例如:

chunk_size = 800
chunk_overlap = 100

意思是每个文本块约 800 字,相邻文本块之间保留 100 字重叠内容,避免上下文断裂。

2. 文本向量化

Embedding 是将自然语言转成数字向量的过程。

例如:

“员工年假如何计算?”

会被转换成类似:

[0.012, -0.087, 0.234, ...]

语义相近的文本,在向量空间中的距离也会更近。

用户提问时,也会先被转成向量,再到向量数据库中查找最相似的文档片段。

3. 相似度检索

向量数据库会返回与用户问题最相关的前 N 个文档片段,例如 top_k = 5。

如果用户问:

入职满一年可以休几天年假?

系统可能检索到:

  • 员工手册中关于年假的章节;
  • 人事制度中关于休假规则的片段;
  • 公司补充福利政策中的相关描述。

4. Claude 生成答案

最后,将检索到的上下文与用户问题组合成 Prompt,发送给 Claude。

示例 Prompt:

你是企业内部知识库助手。
请严格根据以下资料回答问题。
如果资料中没有答案,请回答“根据现有资料无法确定”。

资料:
{context}

用户问题:
{question}

这种方式可以有效降低模型幻觉,让 Claude 尽量基于企业知识库内容回答。


四、项目目录结构

下面是一个简单但完整的项目结构:

claude-enterprise-kb/
├── app.py
├── config.py
├── requirements.txt
├── documents/
│   └── employee_handbook.md
├── services/
│   ├── document_loader.py
│   ├── text_splitter.py
│   ├── vector_store.py
│   └── claude_client.py
└── README.md

各文件作用如下:

文件 说明
app.py FastAPI 主程序
config.py 配置文件
requirements.txt 依赖包
documents 企业知识文档目录
document_loader.py 文档加载模块
text_splitter.py 文本切分模块
vector_store.py 向量库管理模块
claude_client.py Claude 调用模块

五、安装依赖

创建 requirements.txt

fastapi==0.115.0
uvicorn==0.30.6
chromadb==0.5.5
anthropic==0.34.2
openai==1.40.0
python-dotenv==1.0.1
pydantic==2.8.2

安装依赖:

pip install -r requirements.txt

六、环境变量配置

建议不要把 API Key 直接写到代码里,可以使用 .env 文件。

创建 .env

ANTHROPIC_API_KEY=你的_Claude_API_Key
OPENAI_API_KEY=你的_OpenAI_API_Key
CHROMA_PERSIST_DIR=./chroma_db

如果你使用的是其他 Embedding 服务,也可以在后续代码中替换。


七、配置文件源码

创建 config.py

import os
from dotenv import load_dotenv

load_dotenv()

ANTHROPIC_API_KEY = os.getenv("ANTHROPIC_API_KEY")
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
CHROMA_PERSIST_DIR = os.getenv("CHROMA_PERSIST_DIR", "./chroma_db")

CLAUDE_MODEL = "claude-3-5-sonnet-20240620"
EMBEDDING_MODEL = "text-embedding-3-small"

COLLECTION_NAME = "enterprise_knowledge_base"

CHUNK_SIZE = 800
CHUNK_OVERLAP = 120
TOP_K = 5

这里定义了 Claude 模型、Embedding 模型、向量库保存目录、文本切分长度等参数。


八、文档加载模块

创建 services/document_loader.py

import os
from typing import List, Dict


def load_documents(directory: str) -> List[Dict]:
    """
    加载指定目录下的 txt 和 md 文档。
    返回格式:
    [
        {
            "source": "documents/xxx.md",
            "content": "文档内容"
        }
    ]
    """
    docs = []

    for root, _, files in os.walk(directory):
        for file in files:
            if not file.endswith((".txt", ".md")):
                continue

            path = os.path.join(root, file)

            with open(path, "r", encoding="utf-8") as f:
                content = f.read()

            docs.append({
                "source": path,
                "content": content
            })

    return docs

如果你要支持 PDF,可以加入 pypdfpdfplumber。如果要支持 Word,可以加入 python-docx


九、文本切分模块

创建 services/text_splitter.py

from typing import List, Dict


def split_text(text: str, chunk_size: int = 800, chunk_overlap: int = 120) -> List[str]:
    """
    简单滑动窗口切分文本。
    """
    chunks = []
    start = 0
    text_length = len(text)

    while start < text_length:
        end = start + chunk_size
        chunk = text[start:end]

        if chunk.strip():
            chunks.append(chunk.strip())

        start = end - chunk_overlap

        if start < 0:
            start = 0

        if start >= text_length:
            break

    return chunks


def split_documents(
    docs: List[Dict],
    chunk_size: int = 800,
    chunk_overlap: int = 120
) -> List[Dict]:
    """
    将文档列表切分为 chunk 列表。
    """
    results = []

    for doc in docs:
        chunks = split_text(
            doc["content"],
            chunk_size=chunk_size,
            chunk_overlap=chunk_overlap
        )

        for idx, chunk in enumerate(chunks):
            results.append({
                "id": f"{doc['source']}_{idx}",
                "source": doc["source"],
                "chunk_index": idx,
                "content": chunk
            })

    return results

这是一种基础切分方案。在生产环境中,更推荐按标题、段落、Markdown 结构进行切分。例如保留标题路径:

员工手册 > 第三章 假期管理 > 年假规定

这样可以提升检索准确率。


十、向量库模块源码

创建 services/vector_store.py

from typing import List, Dict
import chromadb
from openai import OpenAI

from config import (
    OPENAI_API_KEY,
    CHROMA_PERSIST_DIR,
    COLLECTION_NAME,
    EMBEDDING_MODEL
)


class VectorStore:
    def __init__(self):
        self.embedding_client = OpenAI(api_key=OPENAI_API_KEY)

        self.chroma_client = chromadb.PersistentClient(
            path=CHROMA_PERSIST_DIR
        )

        self.collection = self.chroma_client.get_or_create_collection(
            name=COLLECTION_NAME,
            metadata={"description": "enterprise knowledge base"}
        )

    def embed_texts(self, texts: List[str]) -> List[List[float]]:
        """
        批量生成文本向量。
        """
        response = self.embedding_client.embeddings.create(
            model=EMBEDDING_MODEL,
            input=texts
        )

        return [item.embedding for item in response.data]

    def add_chunks(self, chunks: List[Dict]):
        """
        将切分后的文档片段写入 Chroma。
        """
        if not chunks:
            return

        ids = [chunk["id"] for chunk in chunks]
        documents = [chunk["content"] for chunk in chunks]
        metadatas = [
            {
                "source": chunk["source"],
                "chunk_index": chunk["chunk_index"]
            }
            for chunk in chunks
        ]

        embeddings = self.embed_texts(documents)

        self.collection.upsert(
            ids=ids,
            documents=documents,
            metadatas=metadatas,
            embeddings=embeddings
        )

    def search(self, query: str, top_k: int = 5) -> List[Dict]:
        """
        根据用户问题检索最相关的知识片段。
        """
        query_embedding = self.embed_texts([query])[0]

        result = self.collection.query(
            query_embeddings=[query_embedding],
            n_results=top_k,
            include=["documents", "metadatas", "distances"]
        )

        docs = []

        if not result["documents"]:
            return docs

        for i, content in enumerate(result["documents"][0]):
            docs.append({
                "content": content,
                "metadata": result["metadatas"][0][i],
                "distance": result["distances"][0][i]
            })

        return docs

这个模块完成了三个关键功能:

  1. 将文本转成向量;
  2. 将文档片段存入 Chroma;
  3. 根据用户问题进行相似度检索。

十一、Claude 调用模块源码

创建 services/claude_client.py

from typing import List, Dict
from anthropic import Anthropic

from config import ANTHROPIC_API_KEY, CLAUDE_MODEL


class ClaudeClient:
    def __init__(self):
        self.client = Anthropic(api_key=ANTHROPIC_API_KEY)

    def build_prompt(self, question: str, contexts: List[Dict]) -> str:
        """
        构造发送给 Claude 的提示词。
        """
        context_text = ""

        for idx, item in enumerate(contexts, start=1):
            source = item["metadata"].get("source", "unknown")
            chunk_index = item["metadata"].get("chunk_index", -1)

            context_text += f"\n【资料 {idx}】\n"
            context_text += f"来源:{source},片段:{chunk_index}\n"
            context_text += item["content"]
            context_text += "\n"

        prompt = f"""
你是企业内部知识库助手,负责根据企业内部资料回答员工问题。

请严格遵守以下规则:
1. 只能根据提供的资料回答,不要编造资料中不存在的信息;
2. 如果资料中没有明确答案,请回答“根据现有资料无法确定”;
3. 回答要清晰、准确、适合企业内部沟通;
4. 如果涉及制度、流程、权限、金额、日期等信息,请尽量引用原文依据;
5. 回答末尾请列出参考资料来源。

以下是可参考的企业资料:

{context_text}

用户问题:
{question}

请给出答案:
"""
        return prompt

    def ask(self, question: str, contexts: List[Dict]) -> str:
        prompt = self.build_prompt(question, contexts)

        response = self.client.messages.create(
            model=CLAUDE_MODEL,
            max_tokens=1200,
            temperature=0.2,
            messages=[
                {
                    "role": "user",
                    "content": prompt
                }
            ]
        )

        return response.content[0].text

在企业知识库场景中,temperature 建议设置较低,例如 0.10.3,这样回答会更稳定,减少自由发挥。


十二、FastAPI 主程序源码

创建 app.py

from fastapi import FastAPI
from pydantic import BaseModel
from typing import List, Dict

from config import CHUNK_SIZE, CHUNK_OVERLAP, TOP_K
from services.document_loader import load_documents
from services.text_splitter import split_documents
from services.vector_store import VectorStore
from services.claude_client import ClaudeClient


app = FastAPI(title="Claude 企业知识库系统")

vector_store = VectorStore()
claude_client = ClaudeClient()


class IndexRequest(BaseModel):
    directory: str = "documents"


class AskRequest(BaseModel):
    question: str
    top_k: int = TOP_K


@app.get("/")
def root():
    return {
        "message": "Claude Enterprise Knowledge Base API",
        "endpoints": [
            "POST /index",
            "POST /ask"
        ]
    }


@app.post("/index")
def index_documents(req: IndexRequest):
    """
    加载文档、切分文本、写入向量数据库。
    """
    docs = load_documents(req.directory)

    chunks = split_documents(
        docs,
        chunk_size=CHUNK_SIZE,
        chunk_overlap=CHUNK_OVERLAP
    )

    vector_store.add_chunks(chunks)

    return {
        "status": "success",
        "documents": len(docs),
        "chunks": len(chunks)
    }


@app.post("/ask")
def ask(req: AskRequest):
    """
    用户提问接口。
    """
    contexts = vector_store.search(
        query=req.question,
        top_k=req.top_k
    )

    if not contexts:
        return {
            "question": req.question,
            "answer": "根据现有资料无法确定。",
            "references": []
        }

    answer = claude_client.ask(
        question=req.question,
        contexts=contexts
    )

    references = [
        {
            "source": item["metadata"].get("source"),
            "chunk_index": item["metadata"].get("chunk_index"),
            "distance": item.get("distance")
        }
        for item in contexts
    ]

    return {
        "question": req.question,
        "answer": answer,
        "references": references
    }

启动服务:

uvicorn app:app --reload --port 8000

访问:

http://localhost:8000/docs

即可打开 FastAPI 自动生成的接口文档。


十三、准备一份企业文档示例

创建 documents/employee_handbook.md

# 员工手册

## 年假规定

员工连续工作满一年后,可享受带薪年休假。累计工作已满一年不满十年的,年休假为五天;已满十年不满二十年的,年休假为十天;已满二十年的,年休假为十五天。

员工申请年假时,应至少提前三个工作日在系统中提交申请,并由直属上级审批。连续休假超过五个工作日的,还需部门负责人审批。

## 病假规定

员工因病不能正常工作的,应在当天上班前向直属上级请假,并在返岗后三个工作日内提交医院证明。病假期间薪资按照公司所在地相关规定及公司制度执行。

## 报销制度

员工发生与工作相关的合理费用,可以申请报销。报销申请应在费用发生后三十日内提交,需提供真实、完整、合规的发票或付款凭证。

单笔金额超过五千元的报销,需要部门负责人和财务负责人共同审批。

十四、接口调用示例

1. 建立索引

请求:

curl -X POST "http://localhost:8000/index" \
  -H "Content-Type: application/json" \
  -d '{"directory":"documents"}'

返回:

{
  "status": "success",
  "documents": 1,
  "chunks": 1
}

2. 提问

请求:

curl -X POST "http://localhost:8000/ask" \
  -H "Content-Type: application/json" \
  -d '{"question":"员工入职满一年后可以休几天年假?","top_k":3}'

可能返回:

{
  "question": "员工入职满一年后可以休几天年假?",
  "answer": "根据员工手册中的年假规定,员工连续工作满一年后可以享受带薪年休假。累计工作已满一年不满十年的,年休假为五天;已满十年不满二十年的,年休假为十天;已满二十年的,年休假为十五天。\n\n参考资料来源:documents/employee_handbook.md",
  "references": [
    {
      "source": "documents/employee_handbook.md",
      "chunk_index": 0,
      "distance": 0.23
    }
  ]
}

十五、如何提升企业知识库效果?

上面的代码可以完成一个基础版 Claude 企业知识库,但生产环境中还需要进一步优化。

1. 优化文档切分策略

简单按字数切分容易破坏文档结构。更好的方式是按标题、段落、表格和语义进行切分。

例如 Markdown 文档可以按标题层级处理:

一级标题 > 二级标题 > 三级标题 > 正文片段

这样用户问某个制度时,系统可以保留更完整的章节上下文。

2. 增加文档元数据

企业知识库不只是文本,还需要元数据,例如:

  • 文档名称
  • 部门
  • 发布时间
  • 生效时间
  • 版本号
  • 适用范围
  • 权限等级
  • 文档负责人

这些信息可以存入向量数据库的 metadata 中,便于过滤和追溯。

例如:

{
  "source": "hr/employee_handbook.md",
  "department": "HR",
  "version": "2024.08",
  "effective_date": "2024-09-01",
  "permission": "internal"
}

3. 增加权限控制

企业知识库必须考虑权限问题。并不是所有员工都能查看所有文档。

例如:

  • 普通员工只能查询员工手册;
  • 销售人员可以查询销售话术和报价政策;
  • 财务人员可以查询财务制度;
  • 管理层可以查看经营分析文档。

权限控制一般需要在检索阶段完成。用户提问时,系统根据用户身份、部门、角色过滤可访问文档,再进行向量检索。

4. 加入重排序 Rerank

向量检索返回的 top_k 结果不一定总是最准确。可以在向量检索后加入 Rerank 模型,对候选文档重新排序。

流程如下:

用户问题
  ↓
向量检索 top 20
  ↓
Rerank 重新排序
  ↓
选择 top 5
  ↓
Claude 生成答案

这种方式可以明显提升复杂问题的回答质量。

5. 增加引用来源

企业场景中,“答案是否可追溯”非常重要。因此建议每次回答都返回引用来源,包括:

  • 文档名称;
  • 章节标题;
  • 段落编号;
  • 文档链接;
  • 生效日期;
  • 匹配分数。

这样员工不仅可以看到答案,也能知道答案依据来自哪里。


十六、生产环境注意事项

1. 防止模型幻觉

即使使用 RAG,也不能完全避免模型幻觉。建议在 Prompt 中明确要求:

  • 不允许编造;
  • 没有依据就回答无法确定;
  • 涉及金额、日期、审批流程必须引用原文;
  • 不输出与资料无关的推测。

此外,可以在后端增加置信度判断。如果检索结果相似度过低,直接返回无法确定,而不是继续调用 Claude。

2. 数据安全与合规

企业知识库通常包含敏感数据,必须注意:

  • API Key 安全存储;
  • 文档访问权限控制;
  • 日志脱敏;
  • 传输加密;
  • 私有化部署或专有云部署;
  • 敏感问题审计;
  • 员工访问记录留痕。

如果涉及高度敏感数据,需要评估是否适合调用外部模型 API,或者采用私有部署的大模型方案。

3. 文档更新机制

企业制度和资料会不断更新。知识库需要支持:

  • 增量更新;
  • 删除旧版本;
  • 文档版本管理;
  • 定时同步;
  • 索引重建;
  • 失效文档下线。

否则员工可能拿到过期答案,造成业务风险。

4. 用户反馈闭环

一个成熟的知识库系统应该允许用户对答案进行反馈,例如:

  • 有帮助;
  • 没帮助;
  • 答案错误;
  • 资料过期;
  • 需要人工处理。

这些反馈可以帮助运营人员持续优化文档质量和检索效果。


十七、可扩展功能建议

如果你希望将这个项目升级为企业级系统,可以继续增加以下功能:

功能 说明
Web 前端 提供聊天式界面
多知识库 HR、财务、研发、销售分别管理
用户登录 对接企业微信、飞书、钉钉、LDAP
权限系统 按部门、角色、文档等级控制访问
文档上传 支持后台上传 PDF、Word、Excel
OCR 解析 解析扫描件和图片文档
表格问答 针对 Excel、财务表格做特殊处理
引用高亮 回答中展示命中的原文片段
对话历史 支持多轮上下文问答
审计日志 记录用户查询行为
反馈系统 收集回答质量反馈
定时同步 从企业网盘或知识平台自动同步

十八、总结

本文介绍了如何基于 Claude 搭建企业知识库系统,并提供了一套 Python + FastAPI + ChromaDB 的示例源码。

核心思路是使用 RAG 架构:

  1. 将企业文档加载进系统;
  2. 对文档进行切分;
  3. 使用 Embedding 模型生成向量;
  4. 存入向量数据库;
  5. 用户提问时先检索相关资料;
  6. 将资料和问题一起交给 Claude;
  7. Claude 根据企业资料生成准确回答。

这种方案相比传统关键词搜索更加智能,也比单纯依赖大模型更加可靠。对于企业来说,Claude 企业知识库不仅可以提升员工查询效率,还可以沉淀组织知识、降低培训成本、提升客服和业务支持质量。

不过,真正落地到生产环境时,还需要重点关注权限控制、数据安全、文档更新、引用溯源和反馈闭环。只有把模型能力、知识管理和企业治理结合起来,才能构建一个稳定、可信、可持续运营的智能知识库系统。

目录结构
全文