08 - 知识库自动构建:从对话到结构化知识
学习目标
理解 CowAgent 的知识库系统如何实现自动写入触发、目录分类管理、索引同步与知识图谱构建。
前置知识
知识库自动构建的通用原理见 知识库自动构建。本章聚焦 CowAgent 的具体实现。
项目实践
知识写入触发
CowAgent 通过系统提示词中的强制写入规则引导 Agent 自动写入知识库:
以下场景必须在回复的同时写入知识库,直接写入,不要询问用户是否需要:
1. 用户分享了文章/链接/文档 → 阅读理解后,在同一轮回复中将要点写入 knowledge/sources/<slug>.md2. 深度讨论产生了结论/方案 → 整理为 knowledge/analysis/<slug>.md3. 对话涉及重要实体(人物/公司/项目)→ 创建或更新 knowledge/entities/<name>.md4. 讨论了技术概念/方法论 → 整理为 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 我知道了什么”)。分开设计使得:
- 记忆系统自动积累,无需分类
- 知识库主动整理,结构化程度更高
- 两者通过向量索引打通——
memory_search同时检索记忆和知识
为什么不使用外部 Wiki 工具(如 Notion/Obsidian)?
文件系统 Markdown 方案零依赖、零 API 调用、完全本地存储。对于个人/小团队使用场景,这比集成外部服务更可靠。代价是缺少富文本编辑和协作功能。