Back to skills
extension
Category: OtherAPI key required

通用知识图谱构建器

通用知识图谱构建器。检查环境 → 分析文档 → 构建知识图谱 → 生成交互式HTML页面。支持任意目录的Markdown/代码/PDF/图片文档,自动适配LLM API配置。触发词:知识图谱、建知识库、知识库页面、graphify、生成知识库、文档图谱化、知识管理、知识库工具

personAuthor: user_19a6d235hubcommunity

knowledge-graph-builder — 通用知识图谱构建器

把任意文档目录变成可搜索、可交互的知识图谱页面。


触发条件

用户说以下内容时触发(适用于任何知识领域):

  • "把我的文档做成知识库"
  • "生成知识图谱"
  • "把这份文档做成可查询的知识图谱"
  • "建一个知识库页面"
  • "用 graphify 建知识库"
  • "文档图谱化"
  • "帮我整理知识库"

工作流程

Step 0: 收集用户输入

执行任何操作前,先问用户:

  1. 文档目录在哪?

    • 用户提供绝对路径
    • 如果没有现成目录,问用户是否先准备文档
  2. LLM API 配置是什么?(如果环境变量已配好则跳过)

    • API Base URL(如 https://api.deepseek.com/v1 / https://api.openai.com/v1
    • API Key
    • 模型名称(如 deepseek-v4-flash / gpt-4o-mini
  3. 知识库名称(可选)

    • 用于页面标题和文件名,例如"技术知识库"、"产品文档库"

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 端点 URL
    • ModuleNotFoundError → 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"

原因排查顺序:

  1. API Key 是否有效 → 检查 Step 1.6 的连通性验证
  2. Base URL 是否正确 → deepseek 用 https://api.deepseek.com/v1,openai 用 https://api.openai.com/v1
  3. 模型名是否正确 → 服务商返回的模型 ID
  4. 文档目录是否有支持的文件 → Graphify 支持 .md、代码文件、PDF、图片
  5. 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: 构建过程中途失败(网络/限流/超时)

处理步骤:

  1. 检查错误类型(见 Step 3.3 错误处理表)
  2. 如果是限流(429),降低并发 --max-concurrency 2 后重试
  3. 如果是网络问题(timeout),确认代理配置后重试
  4. 如果是 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 结构 |