跳转到内容

08 - 知识库自动构建:从对话到结构化知识

学习目标

理解 CowAgent 的知识库系统如何实现自动写入触发、目录分类管理、索引同步与知识图谱构建。

前置知识

知识库自动构建的通用原理见 知识库自动构建。本章聚焦 CowAgent 的具体实现。

项目实践

知识写入触发

CowAgent 通过系统提示词中的强制写入规则引导 Agent 自动写入知识库:

以下场景必须在回复的同时写入知识库,直接写入,不要询问用户是否需要:
1. 用户分享了文章/链接/文档 → 阅读理解后,在同一轮回复中将要点写入 knowledge/sources/<slug>.md
2. 深度讨论产生了结论/方案 → 整理为 knowledge/analysis/<slug>.md
3. 对话涉及重要实体(人物/公司/项目)→ 创建或更新 knowledge/entities/<name>.md
4. 讨论了技术概念/方法论 → 整理为 knowledge/concepts/<topic>.md
每次写入知识页面后,必须同步更新 knowledge/index.md 添加一行索引。

⚠️ 关键约束:不要问”要不要存到知识库”——符合场景就直接写入。

知识服务(KnowledgeService)

KnowledgeService 提供三个核心操作:

操作说明用途
list_tree()递归扫描目录树,返回分类结构Web 控制台展示知识库概览
read_file(rel_path)读取指定知识页面内容查看单条知识详情
build_graph()解析 Markdown 链接,构建知识图谱可视化知识关联

知识图谱构建算法

# 伪代码 — 知识图谱构建核心逻辑
def build_graph(self) -> dict:
nodes = {}
links = []
link_re = re.compile(r'\[([^\]]*)\]\(([^)]+\.md)\)')
for md_file in knowledge_path.rglob("*.md"):
if md_file.name in ("index.md", "log.md"):
continue
rel = str(md_file.relative_to(knowledge_path))
category = rel.split("/")[0] # 第一级目录作为分类
title = extract_first_heading(md_file)
# 提取所有 Markdown 链接
for _, link_target in link_re.findall(content):
resolved = (md_file.parent / link_target).resolve()
target_rel = str(resolved.relative_to(knowledge_path))
if target_rel != rel and target_rel in valid_ids:
links.append({"source": rel, "target": target_rel})
nodes[rel] = {"id": rel, "label": title, "category": category}
# 无向边去重
links = dedup_undirected_links(links)
return {"nodes": list(nodes.values()), "links": links}

安全约束

知识读取实施路径穿越检查:

if ".." in rel_path:
raise ValueError("invalid path")
full_path = os.path.normpath(os.path.join(self.knowledge_dir, rel_path))
if not full_path.startswith(allowed + os.sep) and full_path != allowed:
raise ValueError("path outside knowledge dir")

这防止了通过相对路径 ../../etc/passwd 访问知识目录之外的文件。

问题与规避

问题CowAgent 的规避策略
知识页面过度碎片化按主题分类(concepts/entities/sources/analysis)归入子目录
索引与页面不同步prompt 强制要求”每次写入后必须同步更新 index.md”
路径穿越攻击_read_file() 中验证路径不超出 knowledge/ 根目录
重复知识页面写入前检查同名文件是否已存在
知识图谱无效链接构建时验证 target 是否在 valid_ids 中,过滤无效边

设计取舍

为什么知识库与记忆系统分开?

记忆系统按时间组织(适合”昨天讨论了什么”),知识库按主题组织(适合”关于 RAG 我知道了什么”)。分开设计使得:

  1. 记忆系统自动积累,无需分类
  2. 知识库主动整理,结构化程度更高
  3. 两者通过向量索引打通——memory_search 同时检索记忆和知识

为什么不使用外部 Wiki 工具(如 Notion/Obsidian)?

文件系统 Markdown 方案零依赖、零 API 调用、完全本地存储。对于个人/小团队使用场景,这比集成外部服务更可靠。代价是缺少富文本编辑和协作功能。

参考来源