跳到主要内容
基于 OpenClaw 构建企业知识库 RAG 问答系统 | 极客日志
Python AI 算法
基于 OpenClaw 构建企业知识库 RAG 问答系统 如何使用 OpenClaw 开源 AI 助手结合 RAG 技术构建企业级知识库问答系统。通过部署本地 Qdrant 向量数据库,利用 BGE 模型进行文档向量化与重排序,编写 Python 脚本实现检索逻辑,并将其封装为 OpenClaw Skill。方案支持在 Telegram 等聊天软件中直接查询内部文档,确保数据隐私安全,避免大模型幻觉问题,适合企业私有化部署场景。
城市逃兵 发布于 2026/4/6 更新于 2026/5/22 32 浏览
阅读时间 :约 20 分钟
难度 :中级 · 有一定 Python 基础即可上手
一、先聊聊 OpenClaw 到底是什么
如果你还没用过 OpenClaw,先花 60 秒理解它——因为它跟大多数人印象中的"AI 应用"完全不是同一种东西。
OpenClaw 是一个跑在你自己电脑上的开源个人 AI 助手。
不是云端 SaaS,不是聊天网页,不是某家大厂的 App。它就安装在你的 Mac / Windows / Linux 上,然后你可以通过 WhatsApp、Telegram、Discord、iMessage 等任何你已经在用的聊天软件来跟它对话。
一行命令,装完就能用:
curl -fsSL https://openclaw.ai/install.sh | bash openclaw onboard
它能做什么?
用社区里流传最广的一句描述来说:
"A smart model with eyes and hands at a desk with keyboard and mouse. You message it like a coworker and it does everything a person could do with that Mac mini."
不是夸张。OpenClaw 默认就具备:
持久记忆 :它记得你,每次对话都在积累上下文
浏览器控制 :它能打开网页、填表单、抓数据
完整系统权限 :读写文件、执行 Shell 命令、运行脚本
多模型后端 :Claude、GPT-4o、本地 Ollama,随意切换
Skills 扩展体系 :像装插件一样给它"教技能"
最后这一点,就是我们今天要深挖的核心。
Skills 是什么?
Skills 是 OpenClaw 的灵魂扩展机制。本质上是一个文件夹,里面有一个 SKILL.md,用 YAML frontmatter + Markdown 写清楚"这个技能是干嘛的、怎么用",OpenClaw 读完之后就会在系统提示词里注入这个能力。
~/.openclaw/s kills/ my -rag-skill/ SKILL.md ← 技能描述 + 调用说明 search.py ← 实际执行的检索脚本 requirements.txt
社区有个公开的技能市场 ClawHub ,装现成技能就像 pip install 一样简单。但今天我们要自己写一个——一个让 OpenClaw 能在你企业知识库里检索答案的 RAG Skill。
二、为什么是 RAG?企业知识库的真实痛点
在进入代码之前,我们必须先把问题说清楚。
大模型不知道你的内部知识
不管是 Claude、GPT-4o 还是 Qwen,它们的训练数据都是公开的互联网内容,截止日期固定。你公司的:
内部产品手册、操作规范
历史工单、客服记录
合规文件、法律条款
技术文档、API 说明
…这些,模型一无所知。如果你直接问它,它会结合通用知识"编"一个听起来合理但完全不准确的答案。这就是大名鼎鼎的**幻觉(Hallucination)**问题。
为什么不微调?
微调是一个选项,但对企业知识库场景来说代价太高:
知识更新 重新训练,周期以天计 更新向量库,分钟级生效 成本 GPU 算力 + 人力标注 只需向量化成本,几乎可忽略 可溯源性 模型黑盒,无法知道答案从哪来 每个答案都能追溯到原始文档片段 数据安全 数据进入训练过程 数据只存在本地向量库,不出内网
RAG(Retrieval-Augmented Generation) 的核心思想是:不要试图让模型"记住"知识,而是在回答问题时实时去检索知识,再基于检索结果来生成答案。
你在 Telegram 问 OpenClaw: "新员工入职流程第三步是什么?" │ ▼ OpenClaw 触发 RAG Skill │ ▼ 把问题向量化 → 去本地 Qdrant 向量库检索 │ ▼ 找到最相关的 3 段文档片段 │ ▼ 把原始问题 + 文档片段一起发给 Claude │ ▼ Claude 基于真实文档生成答案 │ ▼ OpenClaw 把答案 + 来源引用发回你的 Telegram
三、技术选型 组件 选型 理由 AI 助手 OpenClaw 本文主角,开源、本地部署、Skills 扩展 向量数据库 Qdrant Rust 编写,性能卓越,Docker 一键启动 Embedding 模型 BAAI/bge-large-zh-v1.5 中文语义最强开源模型,C-MTEB 长期领先 Reranker BAAI/bge-reranker-large Cross-Encoder 精排,提升检索精度 运行环境 Python 3.11 + uv 快速依赖管理,OpenClaw Skills 推荐
四、第一步:搭建向量知识库
4.1 启动 Qdrant docker run -d\
--name qdrant \
-p6333:6333 \
-v$(pwd )/qdrant_data:/qdrant/storage \
qdrant/qdrant
启动后访问 http://localhost:6333/dashboard,能看到 Web UI 就代表成功。
4.2 安装依赖 pip install qdrant-client sentence-transformers \
langchain-text-splitters pymupdf \
python-docx rich --break-system-packages
4.3 文档入库脚本 这个脚本负责读取你的文档、分割成合适的片段、向量化后存入 Qdrant。
import uuid
import sys
from pathlib import Path
from qdrant_client import QdrantClient
from qdrant_client.models import Distance, VectorParams, PointStruct
from sentence_transformers import SentenceTransformer
from langchain_text_splitters import RecursiveCharacterTextSplitter
import fitz
from rich.console import Console
from rich.progress import track
console = Console()
COLLECTION = "enterprise_kb"
EMBED_MODEL = "BAAI/bge-large-zh-v1.5"
VECTOR_SIZE = 1024
CHUNK_SIZE = 512
CHUNK_OVERLAP = 64
def load_pdf (path: str ) -> str :
doc = fitz.open (path)
return "\n" .join(page.get_text() for page in doc)
def load_text (path: str ) -> str :
return Path(path).read_text(encoding="utf-8" )
def main (docs_dir: str ):
client = QdrantClient(url="http://localhost:6333" )
embedder = SentenceTransformer(EMBED_MODEL)
splitter = RecursiveCharacterTextSplitter(
chunk_size=CHUNK_SIZE,
chunk_overlap=CHUNK_OVERLAP,
separators=["\n\n" , "\n" , "。" , "!" , "?" , " " ],
)
existing = {c.name for c in client.get_collections().collections}
if COLLECTION not in existing:
client.create_collection(
COLLECTION, vectors_config=VectorParams(size=VECTOR_SIZE, distance=Distance.COSINE),
)
console.print (f"[green]✓ 创建集合:{COLLECTION} [/green]" )
supported = {".pdf" , ".txt" , ".md" }
files = [f for f in Path(docs_dir).rglob("*" ) if f.suffix.lower() in supported]
console.print (f"[blue]发现 {len (files)} 个文档文件[/blue]" )
total_chunks = 0
for file in track(files, description="处理文档中..." ):
try :
raw = load_pdf(str (file)) if file.suffix == ".pdf" else load_text(str (file))
chunks = splitter.split_text(raw)
points = []
for i, chunk in enumerate (chunks):
vec = embedder.encode(f"为这个句子生成表示以用于检索相关文章:{chunk} " , normalize_embeddings=True ).tolist()
points.append(PointStruct(id =str (uuid.uuid4()), vector=vec, payload={"text" : chunk, "source" : file.name, "chunk_id" : i},))
client.upsert(COLLECTION, points=points)
total_chunks += len (points)
except Exception as e:
console.print (f"[yellow]⚠ 跳过 {file.name} : {e} [/yellow]" )
console.print (f"\n[bold green]✓ 入库完成!共处理 {len (files)} 个文件,{total_chunks} 个片段[/bold green]" )
if __name__ == "__main__" :
docs_dir = sys.argv[1 ] if len (sys.argv) > 1 else "./docs"
main(docs_dir)
把你的文档放到 ./docs/ 目录下,然后运行:
五、第二步:编写 RAG 检索脚本 这是核心检索逻辑,负责接收问题、返回最相关的文档片段:
import sys
import json
from qdrant_client import QdrantClient
from sentence_transformers import SentenceTransformer, CrossEncoder
COLLECTION = "enterprise_kb"
EMBED_MODEL = "BAAI/bge-large-zh-v1.5"
RERANK_MODEL = "BAAI/bge-reranker-large"
client = QdrantClient(url="http://localhost:6333" )
embedder = SentenceTransformer(EMBED_MODEL)
reranker = CrossEncoder(RERANK_MODEL)
def search (query: str , top_k_recall: int = 8 , top_k_final: int = 3 ) -> list [dict ]:
"""两阶段检索:向量召回 → Rerank 精排"""
query_vec = embedder.encode(f"为这个句子生成表示以用于检索相关文章:{query} " , normalize_embeddings=True ).tolist()
results = client.search(
collection_name=COLLECTION,
query_vector=query_vec,
limit=top_k_recall,
score_threshold=0.4 ,
)
if not results:
return []
docs = [r.payload["text" ] for r in results]
sources = [r.payload.get("source" , "未知" ) for r in results]
pairs = [(query, doc) for doc in docs]
scores = reranker.predict(pairs)
ranked = sorted (zip (scores, docs, sources), key=lambda x: x[0 ], reverse=True )
return [{"text" : doc, "source" : src, "score" : round (float (score), 4 )} for score, doc, src in ranked[:top_k_final]]
def format_output (query: str , docs: list [dict ] ) -> str :
"""格式化为易读的文本,供 OpenClaw 拼入 Prompt"""
if not docs:
return f"在知识库中未找到与「{query} 」相关的内容。"
parts = [f"以下是从企业知识库中检索到的相关内容,请基于这些内容回答问题:\n" ]
for i, doc in enumerate (docs, 1 ):
parts.append(f"【参考资料 {i} 】(来源:{doc['source' ]} )\n{doc['text' ]} \n" )
sources = list ({d["source" ] for d in docs})
parts.append(f"\n---\n📎 引用来源:{' | ' .join(sources)} " )
return "\n" .join(parts)
if __name__ == "__main__" :
query = " " .join(sys.argv[1 :]) if len (sys.argv) > 1 else ""
if not query:
print (json.dumps({"error" : "请提供查询词" }, ensure_ascii=False ))
sys.exit(1 )
docs = search(query)
print (format_output(query, docs))
python search.py "新员工入职需要准备哪些材料"
六、第三步:编写 OpenClaw Skill 现在是最关键的部分——把上面的检索能力"封装"成一个 OpenClaw Skill,让 OpenClaw 学会什么时候去用它。
6.1 创建 Skill 文件夹 mkdir -p ~/.openclaw/skills/enterprise-kb
6.2 编写 SKILL.md ---
name: enterprise-kb
description: 在企业内部知识库中检索信息。当用户询问公司内部政策、产品手册、操作规范、历史工单、合规文件等内部知识时,必须调用此技能获取准确信息,禁止凭空作答。
metadata: {"openclaw" : {"emoji" :"📚" ,"requires" : {"bins" : ["python3" ],"env" : []}}}
---
每当用户提问涉及以下类型内容时,你必须通过此技能检索,不得直接回答:
- 公司内部政策、人事制度、行政规定
- 产品操作手册、技术规范、API 文档
- 历史工单、解决方案、经验总结
- 法律合规文件、合同模板
- 任何你不确定是否有内部文档的专业问题
使用 bash 工具执行以下命令:
```bash
python3 {baseDir }/search.py <用户的完整问题>
将 <用户的完整问题> 替换为用户提问的自然语言表述,保持完整,不要简化。
结果处理规则
有检索结果时 :完全基于返回的参考资料回答问题,在答案末尾注明"📎 来源:xxx"
无检索结果时 :明确告知用户"企业知识库中暂无相关记录",不要用通用知识补充
永远不要 在没有调用此技能的情况下,自行回答内部知识相关的问题
示例 用户问:"报销需要提交哪些材料?"
→ 调用:python3 {baseDir}/search.py 报销需要提交哪些材料
→ 基于检索结果回答,注明来源文件名
```bash
cp search.py ~/.openclaw/skills/enterprise-kb/
cp requirements.txt ~/.openclaw/skills/enterprise-kb/
cd ~/.openclaw/skills/enterprise-kb
pip install -r requirements.txt
6.4 验证 Skill 加载 重启 OpenClaw 或开启新会话后,发送 /skills 命令,确认 enterprise-kb 出现在列表里。
七、实际体验:在 Telegram 里问公司文档 完成上面的步骤后,你就可以打开 Telegram,直接向 OpenClaw 提问了。
🧑 你:年假天数怎么算的,我工作 3 年了
🦞 OpenClaw:(调用 enterprise-kb 技能)
根据公司《员工手册》第 4.2 节,司龄满 1 年不足 3 年享有 5 天年假,满 3 年不足 10 年享有 10 天年假。您工作满 3 年,今年可享有 10 天带薪年假 。
📎 来源:员工手册_v2024.pdf
🧑 你:订单接口返回 ERROR_4023 是什么意思怎么处理
🦞 OpenClaw:(调用 enterprise-kb 技能)
根据订单系统 API 文档,ERROR_4023 表示"库存预占失败",通常原因是商品库存不足或并发扣减冲突。建议处理步骤:1. 检查商品实际库存;2. 重试间隔 500ms 后最多重试 3 次;3. 若持续失败,通知仓储团队检查 SKU 状态。
📎 来源:订单 API 文档_v3.2.md
🧑 你:竞争对手 XX 公司的产品定价是多少
🦞 OpenClaw:企业知识库中暂无该竞争对手定价的相关记录。如需了解,建议联系市场部门或通过官方渠道获取。
这第三个场景同样重要——OpenClaw 不会用通用知识或猜测来填充答案。这正是企业场景最需要的行为。
八、进阶优化:让检索更聪明 基础版跑起来了,但距离生产级还有几个值得做的优化点。
8.1 查询改写:理解用户真实意图 用户的提问往往不够精确,或者夹带了上下文缩写。可以在 SKILL.md 里增加一条指令:
如果用户的问题较短或含有缩写,先在心里将其扩展为完整的检索词。
例如:"报销流程" → "员工费用报销申请审批流程及所需材料"
这个"扩展"是由 Claude/GPT 在 Agent 推理阶段完成的,不需要额外代码。
8.2 多轮对话的 Query 改写
你:年假怎么算?
OpenClaw:司龄 3 年以下 5 天,3 年以上 10 天…
你:那病假呢?
"那病假呢"这个问题如果单独检索,效果会很差。可以在 SKILL.md 里加一条:
如果当前问题是一个代词性追问(如"那...呢" 、"它的...是什么" ),
请先根据对话历史补全问题的完整含义,再调用检索。
如上例应改写为"病假天数规定是什么" 再进行检索。
8.3 元数据过滤:检索范围精细化 如果你的知识库覆盖多个部门,可以在入库时打上部门标签,检索时只搜特定范围:
payload = {
"text" : chunk,
"source" : file.name,
"department" : "HR" ,
"year" : 2024 ,
}
from qdrant_client.models import Filter, FieldCondition, MatchValue
results = client.search(
collection_name=COLLECTION,
query_vector=query_vec,
query_filter=Filter(
must=[FieldCondition(key="department" , match =MatchValue(value="HR" ))],
),
limit=top_k_recall,
)
8.4 定期增量更新 企业文档是会更新的,你需要一个增量入库机制。最简单的方法是记录文件的最后修改时间:
import hashlib
def file_hash (path: str ) -> str :
return hashlib.md5(Path(path).read_bytes()).hexdigest()
或者更简单粗暴:每次重新入库,因为向量化速度够快,1000 页文档也就几分钟。
8.5 设置心跳提醒(Heartbeat) OpenClaw 支持定时心跳任务,你可以让它每天早上主动提醒你知识库状态:
# 在 openclaw.json 里配置
{
"heartbeat" : {
"enabled" : true ,
"cronExpression" : "0 9 * * 1-5" ,
"message" : "早上好!企业知识库今日状态:运行正常,共索引 XX 篇文档。有什么我能帮你查的吗?"
}
}
九、安全与隐私:这套方案为什么适合企业 这是企业用户最关心的问题,也是这套方案的核心优势。
数据从不离开你的机器。 Qdrant 跑在你本地,文档向量化在本地完成。唯一出去的是:你的问题(Query)和检索到的文档片段,打包成 Prompt 发给 LLM API。
ollama pull qwen2.5:32b
{
"model" : {
"provider" : "ollama" ,
"name" : "qwen2.5:32b"
}
}
这样整个链路 100% 本地,完全离线,企业数据安全级别拉满。
OpenClaw 官方的安全理念也与此一致,项目主页有一句话说得很好:
"Not enterprise. Not hosted. Infrastructure you control. This is what personal AI should feel like."
十、完整项目结构回顾 enterprise-kb-skill/
│ ├── SKILL.md
│ ├── search.py
│ ├── ingest.py
│ ├── requirements.txt
│ └── docs/
│ ├── 员工手册_v2024.pdf
│ ├── 产品 API 文档_v3.md
│ ├── 合规指引_2024Q4.docx
│ └── ...
docker run -d -p6333:6333 -v ./data:/qdrant/storage qdrant/qdrant
python ingest.py ./docs
cp -r enterprise-kb-skill ~/.openclaw/skills/enterprise-kb
openclaw restart
十一、总结与思考
搞清楚了 OpenClaw 的本质:跑在你电脑上的开源私人 AI 助手 ,通过 Skills 可以无限扩展
用 Qdrant + bge-large-zh + Cross-Encoder Reranker 搭了一套两阶段 RAG 检索链路
把检索能力封装成一个 OpenClaw Skill ,只需要一个 SKILL.md + 一个 Python 脚本
在 Telegram 里实现了"企业文档问答"的完整闭环
它把"企业知识库"从一个需要 IT 部门立项的大工程,变成了一个任何开发者下午就能跑起来的个人工具 。你可以先给自己用,积累效果后再推给团队,最终形成团队共享的知识助手——而整个过程,你对数据有完全的掌控权。
OpenClaw 还很年轻(2026 年初才出现),Skills 生态正在快速成长。现在入场,还能赶上最有趣的那段时间。
相关免费在线工具 加密/解密文本 使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
RSA密钥对生成器 生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
Mermaid 预览与可视化编辑 基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
随机西班牙地址生成器 随机生成西班牙地址(支持马德里、加泰罗尼亚、安达卢西亚、瓦伦西亚筛选),支持数量快捷选择、显示全部与下载。 在线工具,随机西班牙地址生成器在线工具,online
Gemini 图片去水印 基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online
curl 转代码 解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online