8.2. 知识库问答机器人可以零代码定制
知识库问答机器人可以零代码定制
2026 年 4 月的一个周三下午,运营团队的小林第 5 次在钉钉群里问:“谁能告诉我海外仓的发货流程文档在哪儿?”产品经理、技术负责人、HR,每个人都在不同的 Confluence 空间里翻找。这是许多团队的真实困境——文档写了但找不到,找到了但看不懂,看懂了又不敢确认是不是最新版本。
上一章我们用 Skill 帮代码审查提速,这一章我们要解决一个更普遍的问题:让团队里任何一个成员都能直接向文档“提问”,得到带有出处的准确回答。 而实现它,不需要写一行后端代码——你只需要一个 Markdown 文件和一个想法。
我们会在 45 分钟内完成:把 Confluence 上的散落页面变成可检索的向量索引,编写一个 Skill 让 Claude 能接收问题、检索上下文、生成带引用的答案,最后加上“不确定就不瞎说”的拒绝机制。
你需要什么
| 条件 | 说明 |
|---|---|
| 一个 Confluence 空间 | 需要有一些已有的文档页面,建议至少 10 个以上 |
| Confluence API Token | 在你的 Atlassian 账号设置中生成,用于拉取页面内容 |
| Claude Code 环境 | 已在本地或服务器上安装并配好 API Key |
| 预计时间 | 45 分钟(首次搭建约 60 分钟) |
如果你的团队不用 Confluence 也没关系——只要能把文档导出为纯文本或 Markdown,这套流程完全适用于任何文档源。
最终成果
你将得到一个放在 .claude/skills/ 目录下的 Skill 文件夹,包含三个核心文件:
.claude/skills/doc-qa/
├── SKILL.md # 主配置:告诉 Claude 什么时候用它、怎么用
├── index.py # 工具脚本:拉取文档、分块、构建索引
└── constants.py # 参数配置:分块大小、检索数量、相似度阈值
通过这个 Skill,任何人在 Claude Code 会话中问“海外仓发货流程是什么”,都会得到类似这样的回答:
## 回答
海外仓发货分为以下步骤:
1. 系统收到订单后,自动匹配就近仓库
2. 仓库人员在 WMS 中确认拣货
3. ...
## 参考来源
- [海外仓发货流程 v2.3](https://confluence.company.com/pages/12345) - 更新时间 2026-03-15
- [异常处理手册 第4节](https://confluence.company.com/pages/67890) - 更新时间 2026-02-28
为什么要做这个? 因为知识库的真正价值不在于“存了多少文档”,而在于“需要的时候能不能找到对的那一段”。这恰好是 RAG(检索增强生成)擅长的——先检索、后生成,每一步都有据可查。
步骤一:文档处理与向量索引
1.1 拉取 Confluence 页面
首先要让文档内容“可检索”。我们写一个工具脚本,Claude 在构建索引时会自动调用它。
# index.py
import requests, json, hashlib, os
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import Chroma
from constants import *
def get_all_pages(space_key: str, base_url: str, auth_token: str):
"""递归获取 Confluence 空间下所有页面内容"""
headers = {"Authorization": f"Bearer {auth_token}"}
url = f"{base_url}/wiki/rest/api/content"
params = {"spaceKey": space_key, "limit": 50, "expand": "body.storage"}
pages = []
while url:
resp = requests.get(url, headers=headers, params=params)
data = resp.json()
# 把 HTML 标签简单剥离,只保留文本
for page in data.get("results", []):
html = page["body"]["storage"]["value"]
text = re.sub(r"<[^>]+>", "", html)
pages.append({
"id": page["id"],
"title": page["title"],
"text": text,
"url": f"{base_url}/wiki/spaces/{space_key}/pages/{page['id']}"
})
url = data.get("_links", {}).get("next")
return pages
1.2 分块策略
文档拉下来后,需要切分成适合检索的“块”。这直接影响后续的问答质量:
- 太大(如 2000 字符):一块里混入多个主题,召回精度低
- 太小(如 200 字符):缺乏上下文,LLM 看不懂片段意思
- 合适(800-1200 字符):保持语义完整,便于精确匹配
# constants.py
CHUNK_SIZE = 1000 # 每块约 250 个中文字
CHUNK_OVERLAP = 200 # 块间重叠,避免关键信息被切断
TOP_K_RETRIEVAL = 5 # 每次问答检索的最相关块数
SIMILARITY_THRESHOLD = 0.75 # 相似度低于此值视为“找不到相关内容”
# index.py 续
def build_index(pages: list):
"""对文档分块并构建 Chroma 向量索引"""
splitter = RecursiveCharacterTextSplitter(
chunk_size=CHUNK_SIZE, chunk_overlap=CHUNK_OVERLAP
)
docs = []
for page in pages:
chunks = splitter.split_text(page["text"])
for i, chunk in enumerate(chunks):
docs.append({
"content": chunk,
"metadata": {
"source_url": page["url"],
"title": page["title"],
"chunk_index": i
}
})
embedding = OpenAIEmbeddings()
vectorstore = Chroma.from_texts(
texts=[d["content"] for d in docs],
embedding=embedding,
metadatas=[d["metadata"] for d in docs],
persist_directory="./doc_index"
)
return vectorstore
注意:踩坑经验
Confluence 的页面可能包含大量表格、代码块、Jira 宏。这些内容在转纯文本时往往会丢失结构或变成乱码。解决方式:用
atlassian-python-api库代替自己拼 REST 请求,它内置了对这些特殊内容的处理,能节省至少 2 小时的调试时间。
1.3 预期结果
执行 python index.py 后,终端输出类似:
拉取页面: 完成,共 87 个页面
文本分块: 完成,生成 312 个块
向量索引: 完成,持久化到 ./doc_index
此时 ./doc_index 目录下会出现 Chroma 的索引文件,后续问答就靠它来“找到相关段落”。
步骤二:问答 Skill 实现
2.1 编写 SKILL.md
Skill 的核心是告诉 Claude 在什么情况下启用,以及执行的步骤。
# 知识库问答 (doc-qa)
## 触发条件
当用户提出的问题涉及公司内部流程、制度、产品文档时,使用此 Skill。
## 执行步骤
1. 加载位于 `./doc_index` 的 Chroma 向量索引
2. 将用户问题向量化,检索 Top-K 个最相似的文档块
3. 判断检索结果的相关性:
- 若最高相似度 < 0.75:回复“抱歉,我在现有文档中没有找到与您问题相关的信息”
- 若最高相似度 ≥ 0.75:将检索到的块作为上下文,生成回答
4. 回答必须包含:
- 结构化的答案(分点、步骤)
- 每条引用后标注来源页面标题和链接
- 最后注明文档的更新时间(若有)
## 注意事项
- 不要编造文档中没有的信息
- 如果文档之间有矛盾,指出矛盾并列出两边说法
- 回答末尾可以询问用户是否需要查看原始页面
2.2 为什么用 Markdown 而不是 Python?
你可能会问:检索和生成难道不需要写代码?事实上,当 Skill 定义在 SKILL.md 中时,Claude Code 的运行时环境会自动处理“工具调用”和“上下文注入”。你只需要告诉它:
- 去哪里加载索引(
./doc_index) - 检索多少条(Top 5)
- 相似度阈值是多少(0.75)
- 回答时要注意什么(引用来源、不瞎编)
Claude 会在内部完成向量检索、提示词拼接、生成回答这一整条链路。这就是“零代码”的含义:编排逻辑用自然语言写,执行细节交给运行时。
2.3 预期结果
启动 Claude Code,加载 Skill 后,对话效果如下:
用户:海外仓退货流程是什么?
Claude:根据现有文档,海外仓退货流程如下:
1. 客户发起退货申请,系统生成 RMA 编号
2. 仓库收到退货商品后,质检人员检查外观和配件
3. 合格的商品重新入库,不合格的移入“待处理区”
...
参考来源:
- 海外仓发货流程 v2.3 — 覆盖步骤1-2
- 异常处理手册 第4节 — 覆盖步骤3
步骤三:性能调优与幻觉控制
3.1 调整检索参数
部署后的第一周,你可能发现两种典型问题:
问题 A:回答太“泛”
用户问“报销流程”,回答里却夹杂了“采购流程”的内容。这是检索精度不够。
解决:提高 TOP_K_RETRIEVAL 但降低 SIMILARITY_THRESHOLD。
TOP_K_RETRIEVAL = 3 # 减少噪音,只取最相关的3条
SIMILARITY_THRESHOLD = 0.80 # 提高门槛,过滤弱相关块
问题 B:答不上来但硬编
用户问“新员工入职体检要去哪家医院”,文档里根本没有这条信息。但 Claude 可能会基于“一般常识”编一个答案。
解决:加入显式的拒绝机制。
3.2 拒绝机制
在 SKILL.md 的注意事项中,加入一条硬性规则:
## 拒绝回答规则(最高优先级)
如果检索到的文档块中,没有任何一块的最高相似度达到阈值(0.75),
必须严格回复:
“抱歉,我在现有文档中没有找到与您问题相关的信息。建议您联系 [部门名称] 获取最新资料。”
禁止在此情况下:
- 基于常识进行推测
- 说“根据我的理解”
- 提供任何可能误导用户的片面信息
这个规则之所以放在“最高优先级”,是因为在实际使用中,LLM 的“讨好型人格”会导致它倾向于给出一个看起来合理的答案——哪怕这个答案是错的。明确禁止它的“创造性发挥”,是把回答质量从“70分”提到“90分”的关键一步。
3.3 文档更新策略
内部文档不是一成不变的。建议在 Skill 中加入定期重建索引的说明:
## 维护说明
- 每周一凌晨由 CI 任务触发 `python index.py --rebuild`
- 重大政策更新后,手动运行一次重建
- 索引重建期间(约30秒),旧索引仍可用,不影响问答
踩坑经验
首次部署后,你可能发现检索结果中有大量过时的页面(2023 年的流程文档)。解决方式:在
get_all_pages中加一个version过滤——只拉取标注为“当前有效”的页面,或者在 metadata 中加入last_updated字段,让 Claude 在生成回答时优先引用最新的文档。
回顾
你刚刚完成的是:
- 文档拉取与清洗(约 20 行 Python):从 Confluence 抽取页面、剥离 HTML
- 分块与索引构建(约 30 行 Python):把长文档切成语义完整的“块”,嵌入向量数据库
- Skill 编排(一个 SKILL.md 文件):用自然语言定义触发条件、检索逻辑、回答要求
- 幻觉控制(一行阈值 + 一个拒绝规则):确保“不知道就说不知道”
实际动手时间约 45 分钟,核心代码不到 100 行,但解决了一个长期让团队头疼的问题。
接下来的行动清单
- 选一个文档源:Confluence、飞书、Notion 都行,先拿一个小空间(20-30 页)试试
- 跑通索引脚本:调整
CHUNK_SIZE直到检索结果让你满意 - 写 10 个测试问题:覆盖精确匹配(“海外仓退货流程”)和模糊搜索(“海外仓有问题的货怎么处理”)
- 让非技术同事试用:收集 3 天内的真实提问,观察拒绝率是否合理
- 迁移到下一章——“自动化数据分析报告解放分析师双手”,你将看到 Skill 不仅能处理“语言”,还能编排“工具链”:从数据库查询到可视化图表,一次性自动化。
Skill 的真正力量不在于“多复杂的代码”,而在于把别人半天的查找压缩到 30 秒的对话里。当运营、产品、HR 都开始自己向知识库提问时,你会发现:AI 落地的最大障碍从来不是技术,而是“他们不知道可以这么简单”。
agent skills 入门到精通
关于 LearnKu