knowledge-graph-builder — 通用知识图谱构建器
把任意文档目录变成可搜索、可交互的知识图谱页面。
触发条件
用户说以下内容时触发(适用于任何知识领域):
- "把我的文档做成知识库"
- "生成知识图谱"
- "把这份文档做成可查询的知识图谱"
- "建一个知识库页面"
- "用 graphify 建知识库"
- "文档图谱化"
- "帮我整理知识库"
工作流程
Step 0: 收集用户输入
执行任何操作前,先问用户:
-
文档目录在哪?
- 用户提供绝对路径
- 如果没有现成目录,问用户是否先准备文档
-
LLM API 配置是什么?(如果环境变量已配好则跳过)
- API Base URL(如
https://api.deepseek.com/v1/https://api.openai.com/v1) - API Key
- 模型名称(如
deepseek-v4-flash/gpt-4o-mini)
- API Base URL(如
-
知识库名称(可选)
- 用于页面标题和文件名,例如"技术知识库"、"产品文档库"
Step 1: 环境检查
1.1 检查 Python 环境
# 检查 Python 3 版本
python3 --version 2>&1
判据: 需要 Python 3.10+。如果版本过低或不存在,执行对应平台的安装指引:
| 系统 | 安装命令 |
|:-----|:---------|
| macOS (Homebrew) | brew install python@3.12 |
| Ubuntu/Debian | sudo apt update && sudo apt install python3.12 python3.12-venv |
| Windows (winget) | winget install Python.Python.3.12 |
| 通用 | 前往 https://www.python.org/downloads/ 下载 3.10+ 版本 |
1.2 检测网络代理
执行以下检测:
# 检查环境变量代理(跨平台)
echo "HTTP_PROXY=$HTTP_PROXY"
echo "HTTPS_PROXY=$HTTPS_PROXY"
echo "http_proxy=$http_proxy"
echo "https_proxy=$https_proxy"
echo "NO_PROXY=$NO_PROXY"
# macOS 额外检测系统代理
if [[ "$(uname)" == "Darwin" ]]; then
networksetup -getwebproxy Wi-Fi 2>/dev/null
networksetup -getsecurewebproxy Wi-Fi 2>/dev/null
networksetup -getsocksfirewallproxy Wi-Fi 2>/dev/null
fi
# Windows 额外检测系统代理
if [[ "$(uname -s)" == MINGW* || "$(uname -s)" == CYGWIN* ]]; then
reg query "HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings" /v ProxyEnable 2>/dev/null
reg query "HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings" /v ProxyServer 2>/dev/null
fi
如果检测到代理: 记录代理地址,后续执行 graphify 时自动添加 NO_PROXY="*" 环境变量。
如果检测不到代理: 后续不需要 NO_PROXY,但保留在出错时提示加上的能力。
1.3 准备 Graphify 运行环境
方案 A:WorkBuddy 托管 Python 环境(检测 ~/.workbuddy/binaries/python/ 是否存在)
if [ -d ~/.workbuddy/binaries/python ]; then
# 使用托管环境
PYTHON_DIR=~/.workbuddy/binaries/python
# 找可用的 3.10+ 版本
PYTHON_BIN=$(ls -d $PYTHON_DIR/versions/3.1* 2>/dev/null | sort -V | tail -1)/bin/python3
fi
方案 B:系统 Python(托管环境不存在时)
PYTHON_BIN=$(which python3)
然后创建/使用 venv:
# 检查已有 venv
if [ -f ~/.knowledge-graph-builder/venv/bin/python3 ]; then
VENV_PYTHON=~/.knowledge-graph-builder/venv/bin/python3
echo "venv 已存在,跳过创建"
else
mkdir -p ~/.knowledge-graph-builder
$PYTHON_BIN -m venv ~/.knowledge-graph-builder/venv
VENV_PYTHON=~/.knowledge-graph-builder/venv/bin/python3
fi
1.4 安装 graphifyy + openai SDK
# 检查是否已安装
$VENV_PYTHON -c "import graphify" 2>/dev/null && echo "graphifyy 已安装" || echo "需要安装"
# 安装(版本固定,详情可Review)
# graphifyy 0.7.13 — 知识图谱引擎
# openai 2.36.0 — OpenAI-compatible API SDK
$VENV_PYTHON -m pip install graphifyy==0.7.13 openai==2.36.0
如需确认包完整性,可在安装前访问 PyPI 检查对应版本 hash:
- graphifyy: https://pypi.org/project/graphifyy/0.7.13/
- openai: https://pypi.org/project/openai/2.36.0/
1.5 配置 LLM API
读取已有配置:
# 检查配置文件
if [ -f ~/.knowledge-graph-builder/env.sh ]; then
source ~/.knowledge-graph-builder/env.sh
echo "已加载 LLM 配置:$LLM_BASE_URL / $LLM_MODEL"
fi
# 或从环境变量检测
echo "OPENAI_API_KEY=${OPENAI_API_KEY:+已设置}"
echo "ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:+已设置}"
如果缺失信息,询问用户:
| 信息 | 示例 | 说明 |
|:-----|:-----|:-----|
| Base URL | https://api.deepseek.com/v1 | OpenAI-compatible API 地址,必须以 /v1 结尾 |
| API Key | sk-xxx... | 你的 API Key |
| 模型名称 | deepseek-v4-flash | 模型 ID,需与 base_url 对应的服务商匹配 |
异常处理:
- 如果
env.sh存在但source失败(格式错误、变量缺失) → 提示用户配置文件损坏,询问"修复配置 / 重新配置 / 退出" - 如果
env.sh存在但LLM_API_KEY为空或格式不对 → 提示 Key 可能无效,询问"继续使用 / 重新配置"
保存配置:
cat > ~/.knowledge-graph-builder/env.sh << 'ENVEOF'
# LLM API 配置 —— 由 knowledge-graph-builder 工具维护
LLM_BASE_URL="<用户提供的 base_url>"
LLM_API_KEY="<用户提供的 api_key>"
LLM_MODEL="<用户提供的 model>"
ENVEOF
chmod 600 ~/.knowledge-graph-builder/env.sh
1.6 验证连通性
# 使用正确的 proxy 设置发送测试请求
# 如果有代理:NO_PROXY="*"
# 如果没有代理:不加
source ~/.knowledge-graph-builder/env.sh
curl -s $NO_PROXY_VAR \
-H "Authorization: Bearer $LLM_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"'"$LLM_MODEL"'","messages":[{"role":"user","content":"hi"}],"max_tokens":10}' \
"$LLM_BASE_URL/chat/completions" | head -c 200
如果验证失败,给出明确的错误提示(Key 无效 / URL 不可达 / 模型不存在等)。
Step 2: 分析知识内容
2.1 扫描文档目录
# 统计文件类型和数量
echo "=== 文档统计 ==="
find "$TARGET_DIR" -type f | while read f; do
case "${f##*.}" in
md|MD) echo "markdown: $f" ;;
py|js|ts|go|rs|java|c|cpp|h|hpp) echo "code: $f" ;;
pdf|PDF) echo "pdf: $f" ;;
png|jpg|jpeg|gif|svg|webp) echo "image: $f" ;;
txt|TXT) echo "text: $f" ;;
*) echo "other: $f" ;;
esac
done | sort | uniq -c | sort -rn
echo
echo "文件总数:$(find "$TARGET_DIR" -type f | wc -l)"
2.2 生成分析报告
向用户报告以下信息:
📊 知识内容分析报告
━━━━━━━━━━━━━━━━━━━━━━━━━
文档目录:<路径>
文件总数:<N> 个
文档类型:<类型分布>
预估总量:约 <N> tokens(按 4 字符/token 估算)
推荐方案:
- 后端:openai(OpenAI-compatible API)
- 模型:<用户配置的模型>
- Token budget:<根据文件大小计算>
- 聚类:<N 个节点以上推荐开启,以下跳过>
Token budget 计算规则:
- 逐个文件估算,
<文件字节数 / 4 + 每文件 80 字符开销> - 文件超过 20000 字符时截断
- 默认
--token-budget 60000,如果总 tokens < 60000 则设为总 tokens + 2000
聚类判断规则:
- 节点数 < 20:跳过聚类(
--no-cluster),节省 token - 节点数 ≥ 20:建议开启聚类,询问用户是否开启
2.3 ⏸️ 用户确认(检查点)
将分析报告展示给用户,并增加费用预估:
📊 知识内容分析报告
━━━━━━━━━━━━━━━━━━━━━━━━━
文档目录:<路径>
文件总数:<N> 个
文档类型:<类型分布>
预估总量:约 <N> tokens(按 4 字符/token 估算)
推荐方案:
- 后端:openai(OpenAI-compatible API)
- 模型:<用户配置的模型>
- Token budget:<根据文件大小计算>
- 聚类:<N 个节点以上推荐开启,以下跳过>
💰 预估费用(仅供参考):
输入:约 <N> tokens × $<价格>/1M = ~$<N>
输出:约 预估值的 3-5 倍 × $<价格>/1M = ~$<N>
总计:~$<N>
(API 计费请以各服务商实际价格为准)
⏸️ 检查点:必须等待用户明确确认后才能继续执行后续步骤。
⚠️ 如果用户对费用敏感,此时提供"跳过构建,仅生成空页面"选项。 ⚠️ 在此检查点停留时,不允许自动调用工具或发出 API 请求。
用户可以说:
- "继续" → 进入 Step 3 构建知识图谱
- "调整参数" → 修改 token budget 或聚类选项后重新预览
- "取消" → 终止流程
Step 3: 构建知识图谱
3.1 准备构建脚本
构造 Python 内联脚本(关键:monkey-patch base_url):
import sys, os, json
# 加载 LLM 配置
with open(os.path.expanduser('~/.knowledge-graph-builder/env.sh')) as f:
for line in f:
if line.startswith('LLM_'):
k, v = line.strip().split('=', 1)
os.environ[k] = v
# 设置 API Key(graphify openai 后端读取的变量)
os.environ['OPENAI_API_KEY'] = os.environ['LLM_API_KEY']
# Monkey-patch Graphify 的 openai backend base_url
import graphify.llm
graphify.llm.BACKENDS['openai']['base_url'] = os.environ['LLM_BASE_URL']
# 设置模型
os.environ['GRAPHIFY_OPENAI_MODEL'] = os.environ['LLM_MODEL']
from graphify.__main__ import main
sys.argv = ['graphify', 'extract', '<文档目录>', '--backend', 'openai', '--no-cluster']
main()
3.2 ⏸️ 构建前确认(检查点)
执行构建前,向用户展示完整配置摘要并请求最终确认:
═══ 构建配置确认 ═══
文档目录: <路径>
API 端点: <base_url>
模型: <model>
Token 预算:<budget> tokens
聚类: <是/否>
Proxy 模式:<NO_PROXY / 系统代理>
输入:<N> 个文档(约 <N> tokens)
估计费用:~$<N>
════════════════════
输入 "y" 开始构建,输入 "n" 取消:
⏸️ 检查点:必须等待用户输入确认后才执行构建。 如果用户取消,回到 Step 0 或退出。 ⚠️ 在此检查点停留时,不允许自动调用工具或发送 API 请求。
3.3 执行构建
cd <文档目录的父目录>
# 如果有代理
NO_PROXY="*" $VENV_PYTHON <构建脚本>
# 如果没有代理
$VENV_PYTHON <构建脚本>
错误处理:
- 如果命令返回非零退出码 → 检查 stderr 输出
401 Unauthorized/403 Forbidden→ API Key 无效或过期,回到 Step 1.5 重新配置429 Too Many Requests→ API 限流,等待 30 秒后重试(建议加--max-concurrency 2降低并发)Connection refused/Timeout→ 网络不可达,检查代理设置和 API 端点 URLModuleNotFoundError→ openai SDK 未安装,回到 Step 1.4 重新安装- 其他错误 → 显示错误信息给用户,提供重试选项
- 如果命令成功但 graph.json 中 nodes === 0 → 在 Step 3.4 中处理
重试策略: 构建失败时询问用户"是否重试 / 调整参数后重试 / 退出",不要静默跳过。
3.4 验证产出
# 检查 graph.json
$VENV_PYTHON -c "
import json
with open('<文档目录>/graphify-out/graph.json') as f:
d = json.load(f)
nodes = len(d.get('nodes', []))
edges = len(d.get('edges', []))
print(f'✅ 知识图谱构建成功:{nodes} 个节点,{edges} 条关系')
print(f' 输入 tokens:{d.get(\"input_tokens\", 0)}')
print(f' 输出 tokens:{d.get(\"output_tokens\", 0)}')
# 节点列表
for n in d['nodes']:
print(f' - {n[\"label\"]} ({n[\"source_file\"]})')
"
如果 nodes === 0,给出排查建议(API 配置 / 文档格式 / 网络连通性)。
Step 4: 生成静态 HTML 页面
4.1 读取图谱数据
import json
with open('<文档目录>/graphify-out/graph.json') as f:
graph_data = json.load(f)
4.2 确定页面标题
- 使用用户指定的"知识库名称"
- 如果未指定,用目录名
4.3 生成自包含的 HTML
读取 template.html(skill 同级目录),替换两个占位符后写出:
import json, os, sys
# 读取图谱数据
with open('<文档目录>/graphify-out/graph.json') as f:
graph_data = json.load(f)
# 定位模板文件
template_paths = [
'template.html',
os.path.expanduser('~/.workbuddy/skills/knowledge-graph-builder/template.html'),
]
template = None
for p in template_paths:
if os.path.exists(p):
with open(p) as f:
template = f.read()
break
if template is None:
print('❌ template.html 未找到。请确认 skill 已正确安装。')
sys.exit(1)
# 替换占位符
html = template.replace('{{TITLE}}', '<知识库名称>') \
.replace('{{GRAPH_JSON}}', json.dumps(graph_data))
# 写出最终文件
with open('<知识库名称>-知识图谱.html', 'w') as f:
f.write(html)
模板说明:
- 模板位于
~/.workbuddy/skills/knowledge-graph-builder/template.html - D3.js v7 已内联嵌入模板,无需 CDN 加载,离线可运行
- 字体使用 Google Fonts(Syne + IBM Plex Mono),通过
display=swap优雅降级,无网络时回退到系统等宽字体 - 生成的
.html是完全自包含的单文件,发给任何人双击即可打开
Step 5: 提供访问方式与注意事项
5.1 访问方式
| 方式 | 命令 | 适用场景 |
|:-----|:------|:---------|
| 双击 HTML 文件 | 直接打开 /path/to/页面.html | 本机使用,无需网络(推荐) |
| HTTP server | cd 目录 && python3 -m http.server <端口> | 团队内网共享 |
5.2 输出报告模板
✅ 知识库构建完成!
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
知识库名称:<名称>
源文档目录:<路径>
知识图谱:<N> 个概念,<M> 条关系
API 花费:约 $<N>(<in> in / <out> out tokens)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📄 页面文件:<输出路径>
🔗 双击即可打开
注意事项:
- D3.js 已内联嵌入 HTML,双击即开,无需联网
- 字体依赖 Google Fonts(Syne + IBM Plex Mono),首次打开建议联网加载字体;无网络时自动回退等宽体,不影响使用
- 更新文档后重新运行此流程,知识图谱会自动增量更新
5.3 后续更新说明
后续更新文档后,重新触发本 skill 即可。参见 Step 6。
Step 6: 增量更新知识图谱(已有构建时自动触发)
当用户说"更新/刷新知识库"、"新加了几个文档"、"补充文档"时,本流程自动替代 Step 0-2。
6.1 检测已有构建
# 检测之前构建的目录结构和配置
BUILD_DIR="${SOURCE_DIR}/graphify-out"
HISTORY_DIR="${SOURCE_DIR}/.knowledge-graph-history"
if [ -f "$HISTORY_DIR/last_build.json" ]; then
echo "✅ 检测到历史构建记录"
cat "$HISTORY_DIR/last_build.json"
elif [ -f "$BUILD_DIR/graph.json" ]; then
echo "✅ 检测到已有 graph.json(无历史记录)"
else
echo "❌ 未找到已有构建,请先执行首次构建(Step 0-5)"
exit 1
fi
从已有的 env.sh 自动恢复配置(无需再次询问 API 信息):
if [ -f ~/.knowledge-graph-builder/env.sh ]; then
source ~/.knowledge-graph-builder/env.sh
echo "已从 env.sh 恢复 LLM 配置:$LLM_BASE_URL / $LLM_MODEL"
fi
6.2 扫描新增/变更/删除的文件
# 如果存在历史记录,对比变更
if [ -f "$HISTORY_DIR/last_build.json" ]; then
# 读取上次构建的文件列表
PREV_FILES=$(python3 -c "import json; d=json.load(open('$HISTORY_DIR/last_build.json')); print('\n'.join(d.get('files',[])))")
# 扫描当前文件列表
CURRENT_FILES=$(find "$SOURCE_DIR" -type f \( -name "*.md" -o -name "*.py" -o -name "*.js" -o -name "*.ts" -o -name "*.pdf" -o -name "*.txt" \) | sort)
# 对比差异
echo "=== 变更检测 ==="
comm -13 <(echo "$PREV_FILES" | sort) <(echo "$CURRENT_FILES") | head -20 | while read f; do echo " + 新增: $f"; done
comm -23 <(echo "$PREV_FILES" | sort) <(echo "$CURRENT_FILES") | head -20 | while read f; do echo " - 删除: $f"; done
echo "文件总数:$(echo "$CURRENT_FILES" | wc -l)(上次:$(echo "$PREV_FILES" | wc -l))"
else
echo "无历史记录,跳过文件变更检测"
echo "文件总数:$(find "$SOURCE_DIR" -type f | wc -l)"
fi
异常处理:
- 如果历史记录中的文件路径在当前目录不存在 → 提示用户"上次的文件结构已变更,建议全量重建"
- 如果新增文件超过总文件的 50% → 建议全量重建而非增量更新
6.3 ⏸️ 变更报告与确认(检查点)
向用户展示变更摘要并请求确认:
📊 增量更新报告
━━━━━━━━━━━━━━━━━━━━━━━━━
知识库名称:<名称>
文档目录:<路径>
新增文件:<N> 个
删除文件:<M> 个(将影响已有节点)
基础配置:<API端点> / <模型>(来自历史记录)
操作:
1. 增量更新(仅处理变更文件,推荐)
2. 全量重建(重新处理所有文件)
3. 取消
⏸️ 检查点:必须等待用户选择操作方式后才能继续。 ⚠️ 在此检查点停留时,不允许自动调用工具或发送 API 请求。
6.4 执行重建
方式 A:增量更新(默认,仅重新处理变更文件)
import json, os
# 加载历史记录
history = {}
history_path = os.path.expanduser(f'{SOURCE_DIR}/.knowledge-graph-history/last_build.json')
if os.path.exists(history_path):
with open(history_path) as f:
history = json.load(f)
# 计算变更文件
prev_files = set(history.get('files', []))
current_files = set(...)
new_files = current_files - prev_files
deleted_files = prev_files - current_files
# 只处理新增+变更文件(graphify 会自动增量)
# 复用上次构建参数,只更新文件列表
方式 B:全量重建(用户要求时)
# 删除已有构建缓存,重新构建
import shutil
shutil.rmtree(f'{SOURCE_DIR}/graphify-out', ignore_errors=True)
# 然后走 Step 3 的完整构建流程
注意: 无论哪种方式,都确保使用上次成功构建的 env.sh 中的配置,避免需要用户重新输入 API 信息。
6.5 更新历史记录
# 构建完成后,同步更新历史记录
mkdir -p "$HISTORY_DIR"
python3 -c "
import json, os, glob
source_dir = '$SOURCE_DIR'
history = {}
if os.path.exists(os.path.join(source_dir, 'graphify-out/graph.json')):
with open(os.path.join(source_dir, 'graphify-out/graph.json')) as f:
g = json.load(f)
history['build_time'] = '$TIMESTAMP'
history['files'] = sorted(glob.glob(os.path.join(source_dir, '**/*'), recursive=True))
history['node_count'] = len(g.get('nodes', []))
history['edge_count'] = len(g.get('edges', []))
history['model'] = '$LLM_MODEL'
with open(os.path.join(source_dir, '.knowledge-graph-history/last_build.json'), 'w') as f:
json.dump(history, f, indent=2)
print(f'历史记录已更新:{history[\"node_count\"]} 节点 / {history[\"edge_count\"]} 关系')
"
6.6 重新生成 HTML 页面
直接复用 Step 4 的生成逻辑,HTML 文件会覆盖重建。
6.7 输出报告
✅ 知识库增量更新完成!
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
知识库名称:<名称>
源文档目录:<路径>
更新方式:<增量/全量>
新增文件:<N> 个
删除文件:<M> 个
知识图谱:<N> → <M> 个节点(+<Δ>)
API 花费:约 $<N>
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📄 页面文件:<输出路径>(已覆盖更新)
🔗 双击即可打开(无需联网)
提示:再次更新文档后,重新触发本 skill 即可继续增量更新。
常见问题
Q: httpx 报 "Invalid port: ':1'"
原因: macOS 系统代理设置与 httpx 库冲突。
解决: 在执行命令前加 NO_PROXY="*",Step 1.2 中已检测代理时会自动处理。
Q: extract 报 "0 nodes, 0 edges"
原因排查顺序:
- API Key 是否有效 → 检查 Step 1.6 的连通性验证
- Base URL 是否正确 → deepseek 用
https://api.deepseek.com/v1,openai 用https://api.openai.com/v1 - 模型名是否正确 → 服务商返回的模型 ID
- 文档目录是否有支持的文件 → Graphify 支持 .md、代码文件、PDF、图片
- Base URL 是否以
/v1结尾 → 有些服务商不加会 404
Q: graph.json 在 HTML 中内联后页面空白
原因: JSON 数据中包含特殊字符(如 </script>)导致 HTML 解析错误。
解决: 在 Python 中生成时用 json.dumps(graph_data) 自动转义,JS 中用 JSON.parse() 解析。
Q: D3.js 离线兼容
D3.js v7 已内联嵌入模板(template.html),生成后的 HTML 文件不依赖 CDN,完全离线可用。
Q: 想要多个知识库并存
操作: 每次构建时指定不同的"知识库名称",会生成不同名称的 HTML 文件,互不干扰。
Q: 构建过程中途失败(网络/限流/超时)
处理步骤:
- 检查错误类型(见 Step 3.3 错误处理表)
- 如果是限流(429),降低并发
--max-concurrency 2后重试 - 如果是网络问题(timeout),确认代理配置后重试
- 如果是 API Key 问题(401),回到 Step 1.5 重新配置
Q: template.html 找不到
原因: skill 安装不完整或工作目录不对。
解决: 将模板路径显式指定为 ~/.workbuddy/skills/knowledge-graph-builder/template.html,参见 Step 4.3 的模板定位代码。
产出物
| 文件 | 用途 |
|:-----|:------|
| ~/.knowledge-graph-builder/venv/ | Python 虚拟环境(所有项目共享) |
| ~/.knowledge-graph-builder/env.sh | LLM API 配置(仅首次需要,后续自动复用) |
| <知识库名称>-知识图谱.html | 自包含的交互式知识图谱页面 |
| <文档目录>/graphify-out/graph.json | 原始图谱数据 |
设计决策说明
| 决策 | 理由 |
|:-----|:------|
| 配置文件用 env.sh 而非 JSON | 跨平台兼容,可被 bash 直接 source |
| D3.js 内联嵌入模板 | 生成后的 HTML 不依赖 CDN,离线可用,单文件可分发 |
| HTML 内联 graph.json | 双击即可打开,不依赖 HTTP server |
| 用 ~/.knowledge-graph-builder/ 而非 ~/.workbuddy | 跨机器可运行,不依赖 WorkBuddy 目录结构 |
| 优先检测代理环境并加 NO_PROXY | httpx 在 macOS 系统代理下会因 URL 解析 bug 崩溃 |
| 用 Python 生成 HTML 而非手动编辑 | 确保 JSON 数据正确转义,不破坏 HTML 结构 |
Scan to contact