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

用 Claude 搭一个企业知识库问答助手:从文档检索到源码实现

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

Claude 实战案例分享|附源码

在过去一年里,大模型应用从“聊天问答”快速走向“真实业务落地”。很多团队已经不再满足于让 AI 简单回答问题,而是希望它能够参与到客服、运营、研发、数据分析、知识库问答、代码生成、文档总结等具体流程中。Claude 作为 Anthropic 推出的高性能大语言模型,凭借较强的长文本理解能力、稳定的指令遵循能力以及较好的代码生成能力,成为许多开发者构建 AI 应用时的重要选择。

本文将通过一个完整的实战案例,分享如何使用 Claude 构建一个“企业知识库智能问答助手”。这个案例非常适合中小团队落地:它可以读取公司内部文档、产品说明、FAQ、接口文档等内容,并基于用户问题给出准确回答,同时附带参考来源,减少胡编乱造的风险。

文章会从需求分析、技术架构、实现流程、核心源码、优化思路等方面展开。你可以直接参考本文代码搭建一个基础版本,再根据自己的业务场景进行扩展。


一、案例背景:为什么要做企业知识库问答助手?

很多公司在发展过程中都会积累大量文档,例如:

  • 产品使用手册;
  • 售后客服 FAQ;
  • 内部培训资料;
  • API 接口文档;
  • 销售话术文档;
  • 项目交付文档;
  • 规章制度说明;
  • 技术排障记录。

这些文档虽然很有价值,但实际使用时常常面临几个问题:

  1. 文档太多,查找困难
    员工需要在不同系统、不同文件夹中查找资料,效率很低。

  2. 客服和运营重复回答同类问题
    大量用户问题其实答案已经在 FAQ 中,但人工仍然需要反复处理。

  3. 新人学习成本高
    新员工入职后,需要阅读大量资料,很难快速掌握核心信息。

  4. 传统搜索不够智能
    关键词搜索只能匹配字面内容,如果用户换一种问法,可能就搜不到结果。

  5. 大模型直接回答容易幻觉
    如果直接把问题丢给 Claude,它可能根据通用知识回答,但不一定符合企业内部资料。

因此,一个更可靠的方案是:将企业文档转成知识库,通过向量检索找到相关内容,再交给 Claude 基于检索结果生成答案。这类方案通常被称为 RAG,即 Retrieval-Augmented Generation,中文可以理解为“检索增强生成”。


二、最终效果展示

假设我们有一份产品 FAQ 文档,里面包含如下内容:

Q:系统支持哪些支付方式?
A:当前系统支持微信支付、支付宝支付、银行卡快捷支付。企业版客户还可以申请对公转账功能。

Q:用户忘记密码怎么办?
A:用户可以在登录页面点击“忘记密码”,通过绑定手机号或邮箱完成身份验证后重置密码。

Q:企业版和个人版有什么区别?
A:企业版支持成员管理、权限分级、数据看板、自定义审批流程和专属技术支持;个人版主要面向单用户使用。

用户提问:

企业客户可以用对公转账吗?

智能助手回答:

可以。根据知识库内容,企业版客户可以申请对公转账功能。

参考来源:
- faq.txt,第 1 段

用户再提问:

企业版比个人版多了哪些能力?

智能助手回答:

企业版相比个人版,主要多了以下能力:

1. 成员管理;
2. 权限分级;
3. 数据看板;
4. 自定义审批流程;
5. 专属技术支持。

个人版主要面向单用户使用。

参考来源:
- faq.txt,第 3 段

这个效果看似简单,但已经具备企业知识库问答系统的基本能力:理解问题、检索资料、生成回答、引用来源


三、技术架构设计

本案例采用 Node.js 实现,核心技术栈如下:

模块 技术
服务端运行环境 Node.js
Web 框架 Express
大模型 Claude API
向量生成 可使用 OpenAI Embeddings、Voyage AI 或本地 Embedding 模型
向量数据库 简化版本使用内存存储
文档处理 fs 文件读取 + 文本切分
接口调用 fetch / axios

为了让案例更容易理解,本文不会一开始就引入复杂的向量数据库,如 Pinecone、Weaviate、Milvus、Qdrant 等,而是先实现一个简化版:把文档切分成多个片段,再为每个片段生成向量,并保存在内存中。用户提问时,同样生成问题向量,然后计算问题向量与文档片段向量的余弦相似度,找出最相关的几个片段,最后交给 Claude 生成答案。

整体流程如下:

文档读取
   ↓
文本切分
   ↓
生成 Embedding 向量
   ↓
保存到知识库
   ↓
用户提问
   ↓
问题生成 Embedding
   ↓
相似度检索
   ↓
取出相关文档片段
   ↓
Claude 基于上下文生成回答
   ↓
返回答案和引用来源

四、项目目录结构

我们先创建一个项目目录:

mkdir claude-rag-demo
cd claude-rag-demo
npm init -y

安装依赖:

npm install express dotenv node-fetch

项目目录建议如下:

claude-rag-demo
├── docs
│   └── faq.txt
├── src
│   ├── server.js
│   ├── claude.js
│   ├── embedding.js
│   ├── vectorStore.js
│   └── documentLoader.js
├── .env
└── package.json

其中:

  • docs/faq.txt:企业知识库文档;
  • server.js:服务入口;
  • claude.js:调用 Claude API;
  • embedding.js:生成文本向量;
  • vectorStore.js:保存和检索向量;
  • documentLoader.js:加载和切分文档;
  • .env:保存 API Key 等配置。

五、准备 Claude API Key

.env 文件中加入如下内容:

ANTHROPIC_API_KEY=你的_Claude_API_Key
EMBEDDING_API_KEY=你的_Embedding_API_Key
PORT=3000

Claude API 主要负责最终回答生成。Embedding 模型用于把文本转换为向量。实际项目中,你可以选择:

  1. OpenAI Embeddings;
  2. Voyage AI Embeddings;
  3. Cohere Embeddings;
  4. BGE、M3E 等本地开源 Embedding 模型;
  5. 云厂商提供的 Embedding 服务。

为了突出 Claude 实战逻辑,本文将 Embedding 接口写成可替换形式,你可以根据自己的服务商修改。


六、准备知识库文档

docs/faq.txt 中写入示例内容:

Q:系统支持哪些支付方式?
A:当前系统支持微信支付、支付宝支付、银行卡快捷支付。企业版客户还可以申请对公转账功能。

Q:用户忘记密码怎么办?
A:用户可以在登录页面点击“忘记密码”,通过绑定手机号或邮箱完成身份验证后重置密码。

Q:企业版和个人版有什么区别?
A:企业版支持成员管理、权限分级、数据看板、自定义审批流程和专属技术支持;个人版主要面向单用户使用。

Q:系统是否支持发票?
A:系统支持电子发票和纸质发票。用户可以在订单中心提交开票申请,企业客户可以批量申请发票。

Q:数据是否安全?
A:系统采用多层安全机制,包括数据加密传输、访问权限控制、操作日志审计和定期安全巡检。

真实业务中,你可以把更多文档放入 docs 目录,例如:

docs
├── product.txt
├── faq.txt
├── api.md
├── sales.md
└── policy.md

后续只需要扩展文档加载逻辑即可。


七、核心源码实现

下面进入代码部分。


1. 文档加载与切分:documentLoader.js

文档切分非常重要。切分太短,信息不完整;切分太长,检索不精准,也会浪费上下文窗口。实际项目中常见的切分方式包括:

  • 按段落切分;
  • 按标题切分;
  • 按固定字符数切分;
  • 滑动窗口切分;
  • 结合 Markdown 结构切分。

本文为了简单,使用“按空行分段”的方式。

// src/documentLoader.js
const fs = require("fs");
const path = require("path");

function loadDocuments(docsDir) {
  const files = fs.readdirSync(docsDir);
  const chunks = [];

  for (const file of files) {
    const filePath = path.join(docsDir, file);
    const stat = fs.statSync(filePath);

    if (!stat.isFile()) continue;

    const content = fs.readFileSync(filePath, "utf-8");

    const paragraphs = content
      .split(/\n\s*\n/g)
      .map((text) => text.trim())
      .filter(Boolean);

    paragraphs.forEach((paragraph, index) => {
      chunks.push({
        id: `${file}-${index + 1}`,
        source: file,
        index: index + 1,
        text: paragraph,
      });
    });
  }

  return chunks;
}

module.exports = {
  loadDocuments,
};

这个函数会把每个文档切成多个片段,每个片段包含:

  • id:唯一标识;
  • source:来源文件;
  • index:第几段;
  • text:文本内容。

2. Embedding 生成:embedding.js

Embedding 的作用是把文本转成向量。这里给出一个通用实现示例。为了方便演示,假设你使用的是 OpenAI Embeddings 接口。如果你使用其他 Embedding 服务,只需要替换这个文件即可。

// src/embedding.js
require("dotenv").config();

async function createEmbedding(text) {
  const response = await fetch("https://api.openai.com/v1/embeddings", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${process.env.EMBEDDING_API_KEY}`,
    },
    body: JSON.stringify({
      model: "text-embedding-3-small",
      input: text,
    }),
  });

  if (!response.ok) {
    const errorText = await response.text();
    throw new Error(`Embedding API 调用失败:${errorText}`);
  }

  const data = await response.json();
  return data.data[0].embedding;
}

module.exports = {
  createEmbedding,
};

如果你想使用本地 Embedding 模型,也可以把 createEmbedding 改成调用本地服务。例如你使用 Python 起了一个 Embedding 服务,Node.js 只需要请求本地接口即可。


3. 向量存储与检索:vectorStore.js

为了便于学习,这里我们实现一个最简单的内存向量库。它有三个核心功能:

  1. 添加文档向量;
  2. 计算余弦相似度;
  3. 根据问题向量返回最相关的文档片段。
// src/vectorStore.js
class VectorStore {
  constructor() {
    this.items = [];
  }

  add(item) {
    this.items.push(item);
  }

  addMany(items) {
    this.items.push(...items);
  }

  cosineSimilarity(vecA, vecB) {
    if (!vecA || !vecB || vecA.length !== vecB.length) {
      return 0;
    }

    let dot = 0;
    let normA = 0;
    let normB = 0;

    for (let i = 0; i < vecA.length; i++) {
      dot += vecA[i] * vecB[i];
      normA += vecA[i] * vecA[i];
      normB += vecB[i] * vecB[i];
    }

    if (normA === 0 || normB === 0) return 0;

    return dot / (Math.sqrt(normA) * Math.sqrt(normB));
  }

  search(queryEmbedding, topK = 3) {
    return this.items
      .map((item) => ({
        ...item,
        score: this.cosineSimilarity(queryEmbedding, item.embedding),
      }))
      .sort((a, b) => b.score - a.score)
      .slice(0, topK);
  }
}

module.exports = {
  VectorStore,
};

在真实项目中,如果文档数量较大,不建议使用内存数组。因为内存检索的复杂度较高,而且服务重启后数据会丢失。可以替换为:

  • Qdrant;
  • Milvus;
  • Weaviate;
  • Pinecone;
  • Elasticsearch dense vector;
  • pgvector。

不过无论底层向量库怎么换,核心思想都是一样的:文本向量化,相似度召回,再让大模型组织答案


4. 调用 Claude:claude.js

接下来是调用 Claude 的核心代码。这里使用 Anthropic Messages API。

// src/claude.js
require("dotenv").config();

async function askClaude({ question, contexts }) {
  const contextText = contexts
    .map((ctx, idx) => {
      return `【资料${idx + 1}】
来源:${ctx.source},第 ${ctx.index} 段
内容:${ctx.text}`;
    })
    .join("\n\n");

  const systemPrompt = `
你是一个企业知识库智能问答助手。
你必须严格基于提供的资料回答用户问题。
如果资料中没有相关信息,请明确说明“根据当前知识库资料,无法确认该问题”。
不要编造资料中不存在的内容。
回答要简洁、准确、结构清晰。
如果能回答,请在最后列出参考来源。
`;

  const userPrompt = `
以下是从企业知识库中检索到的资料:

${contextText}

用户问题:
${question}

请基于以上资料回答。
`;

  const response = await fetch("https://api.anthropic.com/v1/messages", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "x-api-key": process.env.ANTHROPIC_API_KEY,
      "anthropic-version": "2023-06-01",
    },
    body: JSON.stringify({
      model: "claude-3-5-sonnet-20241022",
      max_tokens: 1000,
      temperature: 0.2,
      system: systemPrompt,
      messages: [
        {
          role: "user",
          content: userPrompt,
        },
      ],
    }),
  });

  if (!response.ok) {
    const errorText = await response.text();
    throw new Error(`Claude API 调用失败:${errorText}`);
  }

  const data = await response.json();

  return data.content
    .map((part) => part.text || "")
    .join("\n")
    .trim();
}

module.exports = {
  askClaude,
};

这里有几个关键点。

第一,systemPrompt 明确要求 Claude 只能基于资料回答,这可以降低幻觉概率。

第二,temperature 设置为 0.2,让回答更加稳定,不要过度发散。

第三,在上下文中提供来源信息,例如文件名和段落编号,这样 Claude 可以在最终回答中引用来源。

第四,如果检索资料不相关,Claude 应该拒绝强行回答,而是说明无法确认。


5. 服务入口:server.js

最后实现服务启动、知识库初始化和问答接口。

// src/server.js
require("dotenv").config();

const express = require("express");
const path = require("path");

const { loadDocuments } = require("./documentLoader");
const { createEmbedding } = require("./embedding");
const { VectorStore } = require("./vectorStore");
const { askClaude } = require("./claude");

const app = express();
app.use(express.json());

const vectorStore = new VectorStore();

async function initKnowledgeBase() {
  console.log("正在加载知识库文档...");

  const docsDir = path.join(__dirname, "../docs");
  const chunks = loadDocuments(docsDir);

  console.log(`共加载 ${chunks.length} 个文档片段,开始生成向量...`);

  for (const chunk of chunks) {
    const embedding = await createEmbedding(chunk.text);

    vectorStore.add({
      ...chunk,
      embedding,
    });

    console.log(`已处理:${chunk.id}`);
  }

  console.log("知识库初始化完成。");
}

app.post("/ask", async (req, res) => {
  try {
    const { question } = req.body;

    if (!question || typeof question !== "string") {
      return res.status(400).json({
        error: "question 字段不能为空",
      });
    }

    const queryEmbedding = await createEmbedding(question);
    const contexts = vectorStore.search(queryEmbedding, 3);

    const answer = await askClaude({
      question,
      contexts,
    });

    res.json({
      question,
      answer,
      references: contexts.map((ctx) => ({
        source: ctx.source,
        index: ctx.index,
        score: Number(ctx.score.toFixed(4)),
        text: ctx.text,
      })),
    });
  } catch (error) {
    console.error(error);
    res.status(500).json({
      error: error.message,
    });
  }
});

async function main() {
  await initKnowledgeBase();

  const port = process.env.PORT || 3000;
  app.listen(port, () => {
    console.log(`服务已启动:http://localhost:${port}`);
  });
}

main();

修改 package.json

{
  "name": "claude-rag-demo",
  "version": "1.0.0",
  "main": "src/server.js",
  "scripts": {
    "start": "node src/server.js"
  },
  "dependencies": {
    "dotenv": "^16.4.5",
    "express": "^4.18.2",
    "node-fetch": "^3.3.2"
  }
}

需要注意的是,如果你的 Node.js 版本低于 18,原生可能不支持 fetch。建议使用 Node.js 18 以上版本,或者额外配置 node-fetch 的导入方式。


八、启动项目并测试

启动服务:

npm start

控制台会输出类似内容:

正在加载知识库文档...
共加载 5 个文档片段,开始生成向量...
已处理:faq.txt-1
已处理:faq.txt-2
已处理:faq.txt-3
已处理:faq.txt-4
已处理:faq.txt-5
知识库初始化完成。
服务已启动:http://localhost:3000

使用 curl 测试:

curl -X POST http://localhost:3000/ask \
  -H "Content-Type: application/json" \
  -d '{"question":"企业客户可以申请对公转账吗?"}'

可能得到如下结果:

{
  "question": "企业客户可以申请对公转账吗?",
  "answer": "可以。根据知识库资料,企业版客户可以申请对公转账功能。\n\n参考来源:\n- faq.txt,第 1 段",
  "references": [
    {
      "source": "faq.txt",
      "index": 1,
      "score": 0.7821,
      "text": "Q:系统支持哪些支付方式?\nA:当前系统支持微信支付、支付宝支付、银行卡快捷支付。企业版客户还可以申请对公转账功能。"
    }
  ]
}

再测试一个问题:

curl -X POST http://localhost:3000/ask \
  -H "Content-Type: application/json" \
  -d '{"question":"企业版有什么功能?"}'

返回结果可能是:

{
  "question": "企业版有什么功能?",
  "answer": "企业版支持以下功能:\n\n1. 成员管理;\n2. 权限分级;\n3. 数据看板;\n4. 自定义审批流程;\n5. 专属技术支持。\n\n此外,企业版客户还可以申请对公转账功能,并且可以批量申请发票。\n\n参考来源:\n- faq.txt,第 3 段\n- faq.txt,第 1 段\n- faq.txt,第 4 段"
}

可以看到,RAG 系统不只是机械匹配关键词,而是可以从多个文档片段中提取相关信息,再由 Claude 组织成自然语言答案。


九、关键设计点解析

1. 为什么不能直接把全部文档塞给 Claude?

Claude 有较强的长上下文能力,但直接把所有文档塞进去并不是最佳实践,原因包括:

  • 文档越多,调用成本越高;
  • 响应速度会变慢;
  • 无关信息会干扰模型判断;
  • 文档规模持续增长后不可控;
  • 每次请求都传全部资料没有必要。

RAG 的优势在于:先检索,再生成。也就是每次只把与用户问题最相关的片段传给 Claude,从而提高准确性、降低成本。


2. 为什么需要 Embedding,而不是关键词搜索?

关键词搜索适合精确匹配,但面对语义表达时效果有限。例如文档写的是:

企业版客户还可以申请对公转账功能。

用户可能会问:

公司客户能不能走银行转账?

这里“公司客户”对应“企业版客户”,“银行转账”可能对应“对公转账”。传统关键词搜索不一定能匹配,但 Embedding 可以捕捉语义相似度,从而召回正确内容。


3. 为什么要让 Claude 引用来源?

引用来源有三个好处:

  1. 增强可信度
    用户可以知道答案来自哪份文档。

  2. 方便人工复核
    如果答案有误,可以快速定位原文。

  3. 降低幻觉风险
    当模型被要求引用来源时,它更倾向于基于上下文回答,而不是自由发挥。

不过需要注意,模型生成的引用也可能不完全可靠。因此更稳妥的做法是:后端直接返回检索到的 references,前端展示这些来源,而不是完全依赖模型自己生成。


4. 如何处理“知识库没有答案”的情况?

这是 RAG 系统非常重要的一点。如果用户问:

系统是否支持海外 PayPal 支付?

但文档中没有相关说明,那么系统不应该编造一个答案。

可以从两方面控制:

第一,在 Prompt 中明确要求:

如果资料中没有相关信息,请明确说明“根据当前知识库资料,无法确认该问题”。

第二,在检索阶段设置相似度阈值。例如最高相似度低于 0.55,就认为没有召回有效资料。

可以修改 /ask 接口:

const contexts = vectorStore.search(queryEmbedding, 3);
const validContexts = contexts.filter((ctx) => ctx.score >= 0.55);

if (validContexts.length === 0) {
  return res.json({
    question,
    answer: "根据当前知识库资料,无法确认该问题。",
    references: [],
  });
}

这样可以进一步减少无依据回答。


十、进一步优化方向

上面的代码可以跑通一个基础版,但如果要在真实企业环境中使用,还需要继续优化。


1. 使用专业向量数据库

内存向量库只适合 Demo。如果文档达到几千、几万甚至几十万段,需要使用专业向量数据库。比如 Qdrant,它支持:

  • 高性能向量检索;
  • 元数据过滤;
  • 持久化存储;
  • 分集合管理;
  • 相似度排序;
  • 批量写入。

典型应用场景包括:

  • 按部门过滤知识库;
  • 按用户权限过滤文档;
  • 按文档类型过滤;
  • 按更新时间过滤;
  • 多租户知识库隔离。

2. 加入权限控制

企业知识库往往不是所有人都能看全部内容。例如:

  • 普通员工不能查看财务资料;
  • 客服只能查看对外 FAQ;
  • 销售可以查看销售话术;
  • 技术支持可以查看排障文档;
  • 管理层可以查看经营报表。

因此,文档入库时应该带上权限字段:

{
  source: "finance-policy.md",
  department: "finance",
  roles: ["admin", "finance_manager"],
  text: "..."
}

检索时根据当前用户身份过滤:

const contexts = vectorStore.search(queryEmbedding, 5, {
  role: currentUser.role,
});

如果不做权限控制,AI 应用可能会把敏感信息回答给无权限用户,这是企业落地时必须避免的问题。


3. 加入重排序 Rerank

向量召回有时会召回语义相关但不够精确的内容。为了提升准确率,可以在向量召回后加入 Rerank 模型。

常见流程是:

问题向量召回 Top 20
   ↓
Rerank 模型重新排序
   ↓
取 Top 3 给 Claude

Rerank 对知识库问答非常有用,尤其当文档内容相似、术语较多时,可以显著提升最终回答质量。


4. 文档增量更新

真实系统中,文档会经常变化。如果每次启动都重新计算所有文档向量,成本和时间都很高。

更合理的做法是:

  1. 计算文档内容 hash;
  2. 判断文档是否变化;
  3. 只对新增或变更片段重新生成向量;
  4. 删除已经不存在的旧片段;
  5. 保存文档版本号和更新时间。

示例:

const crypto = require("crypto");

function hashText(text) {
  return crypto.createHash("sha256").update(text).digest("hex");
}

每个 chunk 入库时保存 hash:

{
  id: "faq.txt-1",
  text: "...",
  hash: "xxxx",
  embedding: [...]
}

这样可以避免重复处理。


5. 增加对话记忆

如果用户连续提问:

用户:企业版有哪些功能?
助手:企业版支持成员管理、权限分级、数据看板等。
用户:那它支持对公转账吗?

第二个问题中的“它”指的是“企业版”。如果每次都只处理单轮问题,系统可能理解不准确。因此可以加入对话历史,把最近几轮对话传给 Claude,让它改写成独立问题。

流程如下:

用户追问
   ↓
Claude 根据历史对话改写问题
   ↓
基于改写后的问题进行检索
   ↓
Claude 生成最终回答

例如改写为:

企业版是否支持对公转账?

这样检索会更准确。


6. 前端交互优化

如果要做成完整产品,前端可以加入:

  • 聊天窗口;
  • 流式输出;
  • 参考来源卡片;
  • 点赞 / 点踩反馈;
  • 人工客服转接;
  • 问题推荐;
  • 历史会话;
  • 文档来源跳转;
  • 答案复制;
  • Markdown 渲染。

其中“流式输出”可以显著提升用户体验,因为用户不需要等待完整答案生成后才能看到内容。


十一、Claude Prompt 设计经验

在 Claude 应用开发中,Prompt 质量非常关键。下面是一些实战经验。


1. 明确角色

不要只写:

请回答问题。

更好的方式是:

你是一个企业知识库智能问答助手,负责基于公司内部资料回答员工和客户的问题。

角色越明确,模型输出越稳定。


2. 明确边界

如果希望模型不要编造,一定要写清楚:

你只能基于提供的资料回答。
如果资料中没有答案,请明确说明无法确认。
不要使用资料之外的信息进行推测。

这类约束对 RAG 系统很重要。


3. 明确输出格式

例如:

请按以下格式回答:

结论:
说明:
参考来源:

这样可以让前端更容易解析,也能提升用户阅读体验。


4. 控制语气和长度

客服场景可以要求:

语气要礼貌、自然、面向普通用户。

内部技术支持场景可以要求:

回答要准确、专业,可以使用技术术语。

管理层汇报场景可以要求:

回答要简洁,优先给结论,再给依据。

Claude 的指令遵循能力较强,只要 Prompt 写清楚,输出通常比较稳定。


十二、完整调用流程回顾

用户发起问题后,系统实际做了以下事情:

  1. 接收用户问题;
  2. 调用 Embedding 服务,将问题转成向量;
  3. 在向量库中计算相似度;
  4. 取出最相关的文档片段;
  5. 把用户问题和相关片段拼接成 Prompt;
  6. 调用 Claude;
  7. Claude 基于资料生成答案;
  8. 后端返回答案和参考来源;
  9. 前端展示给用户。

这个流程看起来简单,但已经是当前很多 AI 知识库产品的核心架构。


十三、常见问题与排查

1. Claude 回答不准确怎么办?

可以从以下几个方面排查:

  • 检索出来的文档片段是否正确;
  • 文档切分是否合理;
  • topK 是否太小;
  • Prompt 是否明确限制了回答边界;
  • Embedding 模型是否适合中文;
  • 是否需要加入 Rerank;
  • 原始知识库文档是否写得清楚。

很多时候,问题不是 Claude 本身,而是检索阶段没有召回正确资料。


2. 回答速度慢怎么办?

可以优化:

  • 缓存常见问题的 Embedding;
  • 使用更快的 Claude 模型;
  • 减少传入上下文长度;
  • 使用流式输出;
  • 文档向量提前生成;
  • 向量检索使用专业数据库;
  • 对接口设置超时和重试机制。

3. 成本太高怎么办?

可以优化:

  • 缩短 Prompt;
  • 降低 topK;
  • 对重复问题做缓存;
  • 根据问题复杂度选择不同模型;
  • 简单问题直接返回 FAQ;
  • 只把必要文档片段传给 Claude;
  • 用较便宜模型做问题改写和分类,用强模型做复杂回答。

4. 如何减少幻觉?

建议同时采取多种措施:

  • Prompt 中明确禁止编造;
  • 设置相似度阈值;
  • 返回参考来源;
  • 使用 Rerank 提高上下文质量;
  • 对关键业务答案进行规则校验;
  • 高风险问题转人工;
  • 对回答做后置审核。

十四、适合扩展的业务场景

本文案例虽然是企业 FAQ 问答,但同样可以扩展到很多场景。

1. 智能客服

将产品说明、售后政策、物流规则、退换货规则导入知识库,用户提问后自动回答。无法回答时转人工客服。

2. 内部知识助手

员工可以询问公司制度、报销流程、请假规则、IT 支持流程、新人培训内容等。

3. 技术文档助手

开发者可以询问 API 参数、错误码含义、SDK 使用方式、接口调用示例等。

4. 销售助手

销售人员可以查询产品卖点、竞品对比、报价规则、行业案例和常见客户异议处理话术。

5. 数据分析助手

结合数据库查询能力,Claude 可以帮助生成 SQL、解释数据指标、总结经营报表。

6. 代码审查助手

将项目规范、代码风格、架构文档和历史问题导入知识库,让 Claude 辅助进行代码审查和问题定位。


十五、实战总结

通过本文案例,我们实现了一个基于 Claude 的企业知识库智能问答助手。它虽然是一个简化版 Demo,但已经覆盖了大模型应用落地中的几个核心环节:

  • 文档加载;
  • 文本切分;
  • Embedding 向量化;
  • 向量相似度检索;
  • Claude 生成答案;
  • 返回参考来源;
  • 控制模型幻觉;
  • 支持后续扩展。

Claude 在这类场景中的优势主要体现在:

  1. 长文本理解能力较强
    可以处理较长的文档上下文,并从中提炼出结构化答案。

  2. 回答风格自然
    相比传统搜索结果,Claude 能把零散内容组织成更容易理解的表达。

  3. 指令遵循能力好
    通过合理 Prompt,可以让它按指定格式、语气和边界回答。

  4. 适合复杂业务问答
    不只是回答 FAQ,还可以结合多段资料进行归纳、对比和总结。

当然,真正的企业级 AI 应用不能只依赖模型本身。文档质量、检索质量、权限控制、系统稳定性、成本控制、人工反馈机制同样重要。一个成熟的 Claude 应用,往往是“大模型能力 + 工程架构 + 业务规则 + 数据治理”的结合。

如果你正在探索 AI 应用落地,可以从本文这个 RAG 案例开始。先用少量文档搭建一个可运行版本,再逐步加入向量数据库、权限管理、流式输出、反馈闭环和运营后台。这样既能快速验证价值,也能为后续规模化建设打好基础。

目录结构
全文