Skip to content

知识库 Agent

🎯 学习目标

  • 搭建文档上传 → 索引 → 对话检索全链路
  • 回答展示引用来源与「无法回答转人工」
  • 文档更新触发增量 re-index

引言

智能客服 = RAG + 会话管理 + 业务规则。本课按周交付:先 MVP 检索问答,再补缓存与增量索引。

章节正文

第 1 步:第 1 步:架构

上传 API → Loader/Splitter → Embedding → Qdrant/Chroma
用户问 → Query Embed → Top-K → Prompt + LLM → 回答 + citations
会话 → Redis/Postgres

多租户:collection 按 tenant_id 隔离。

第 2 步:第 2 步:索引管道

python
from langchain_openai import OpenAIEmbeddings
from langchain_qdrant import QdrantVectorStore
from qdrant_client import QdrantClient

emb = OpenAIEmbeddings(model="text-embedding-3-small")
client = QdrantClient(":memory:")  # 生产换持久化
vectorstore = QdrantVectorStore(client=client, collection_name="kb", embedding=emb)

def index_files(files):
    for f in files:
        docs = load_and_split(f)
        vectorstore.add_documents(docs)

第 3 步:第 3 步:带引用的 RAG 链

python
from langchain_core.runnables import RunnablePassthrough

def format_docs(docs):
    return "\n\n".join(
        f"[{i+1}] {d.page_content}\n来源: {d.metadata.get('source')}"
        for i, d in enumerate(docs)
    )

rag = (
    RunnablePassthrough.assign(docs=lambda x: vectorstore.similarity_search(x["question"], k=4))
    | RunnablePassthrough.assign(
        answer=lambda x: (prompt | llm | StrOutputParser()).invoke({
            "context": format_docs(x["docs"]),
            "question": x["question"],
        })
    )
)

Prompt 要求:「仅依据 context;无依据则说不知道」。

第 4 步:第 4 步:客服策略

  • 相似度低于阈值 → handoff_human: true
  • 相同 FAQ query 缓存 10min(Redis)
  • 文档 updated_at 变化 → 删旧 chunk id 再 embed
  • 会话结束生成摘要给人工坐席

动手练习

  1. 上传 3 份 Markdown FAQ,问 5 个问题并展示引用
  2. 实现低分拒答转人工按钮
  3. 更新一份文档后验证答案变化

本节小结

客服 RAG = 多租户索引 + 引用 + 拒答/转人工 + 缓存 + 增量索引。