基于「交互式生成 + 实时检索」的轻量级知识图谱探索工具:输入关键词生成根节点与概念解释,点击节点沿不同方向扩展,通过 SSE 流式实时展示检索与 LLM 生成过程,逐步生长出个人知识图谱。
- 里程碑:M0–M6 已完成(项目启动 → UI 骨架 → 静态交互 → 后端基座 → 扩展引擎 → 端到端联调 → 测试优化),M7「上线部署」未开始。详细规划见 plan.md。
- 已实现功能:
- 用户注册 / 登录(JWT)
- 关键词创建图谱、多图谱列表与切换、重命名、删除
- 节点扩展(自动 / 子概念 / 相关应用;扩展数量由 LLM 在 2–5 之间智能决定),SSE 流式推送进度与 LLM 增量输出
- 在右侧「定义/要点」中选中文字 →「帮我解释」:LLM 分析该段应展开几个概念并复用扩展流水线生成解释性子节点
- 概念解释(定义 + 要点 + 来源 + 可信度),检索结果事实校验
- 图谱画布:缩放、平移、节点拖拽、小地图、选择/平移工具切换、画布全屏(隐藏两侧栏)、重排居中
- 节点删除(级联 / 重挂子树)、扩展撤销(快照回滚)
- 图谱内搜索定位(左侧「搜索」/ Ctrl+F 弹窗,命中后画布居中到节点)
- 导出 JSON / Markdown / SVG / PNG
- 近期改进:检索切换到 Serper(Google Search),429/5xx 自动重试 + 并发限流;扩展数量与「帮我解释」均由 LLM 决定;安全与正确性加固(事务原子化、JWT 启动校验、SSE 走
fetch+Authorization 头、节点按 id 着色);精简 UI(移除笔记/收藏/合集/状态/标签/专注模式/设置,新增全屏、选择/平移、搜索、选中解释)。 - 测试:后端 29 个单元测试通过,前端 19 个测试通过(另有需数据库的集成测试)。
FernGrow/
├── knowledge-graph-frontend/ # React 19 + TypeScript + Vite + Tailwind CSS v4
│ └── src/
│ ├── pages/ # LandingPage / Login / Register / GraphExplorer
│ ├── components/ # layout(三栏)、graph(画布/扩展)、modals、ui
│ ├── stores/ # Zustand:authStore / graphStore / uiStore
│ ├── services/ # axios API 客户端、llmStream(SSE 消费)
│ └── utils/ # 径向布局、导出、streamExtract(流式 JSON 解析)
├── knowledge-graph-backend/ # Python 3.12 + FastAPI + uv
│ └── app/
│ ├── api/v1/ # auth / graphs / nodes 路由
│ ├── core/ # 配置、JWT、鉴权依赖、异常
│ ├── db/ # SQLAlchemy 引擎与 Repository 层
│ ├── models/ # users / graphs / nodes / edges / snapshots / llm_cache
│ ├── services/ # 图谱、扩展引擎、导出、缓存、Prompt
│ └── external/ # LLM 客户端、Serper 检索、SSE 发布器
├── prd.md # 产品需求文档
├── ui.md # UI 设计描述
├── design.md # 系统架构设计
├── plan.md # 项目开发进度规划
├── api-contract.md # 前后端接口协议
├── docker-compose.yml # 全栈 Docker 编排
└── .env.example # 环境变量模板
- 前端:React 19 + TypeScript + Vite 8 + Tailwind CSS v4 + Zustand + axios + 原生 EventSource(SSE)+ Lucide Icons;测试用 Vitest,lint 用 oxlint
- 后端:Python 3.12 + FastAPI + SQLAlchemy 2.0(async)+ PostgreSQL + Redis(LLM 概念两级缓存:Redis 优先、PG 表降级)+ Alembic 迁移;依赖管理用 uv
- 外部服务:Serper(Google Search)检索 API + OpenAI 兼容格式的国产 LLM(支持流式输出;无密钥时自动进入 mock 模式,方便本地开发)
- 部署:Docker + Docker Compose + Nginx(含 SSE 透传配置)
docker run -d --name kg-db \
-e POSTGRES_USER=postgres \
-e POSTGRES_PASSWORD=password \
-e POSTGRES_DB=kgdb \
-p 5432:5432 \
-v $(pwd)/knowledge-graph-backend/init.sql:/docker-entrypoint-initdb.d/init.sql:ro \
postgres:16-alpine
docker run -d --name kg-redis -p 6379:6379 redis:7-alpinecp .env.example .env
# 编辑 .env:JWT_SECRET 必须改成一个随机字符串(占位符会被启动校验拒绝)
# SERPER_API_KEY / LLM_API_KEY 不填时,检索与 LLM 自动进入 mock 模式cd knowledge-graph-backend
uv sync
uv run dev # 等价于 uvicorn app.main:app --reload --port 8000交互式 API 文档:http://localhost:8000/docs
cd knowledge-graph-frontend
npm install
npm run dev # 端口 5173,/api 自动代理到 localhost:8000cd knowledge-graph-backend
uv run alembic upgrade head # 应用到最新
uv run alembic revision --autogenerate -m "..." # 模型变更后生成新迁移# 后端:单元测试(全部 mock,无需外部服务)
cd knowledge-graph-backend && uv run pytest -m unit
# 后端:集成测试(需要可连接的 PostgreSQL,连不上自动 skip)
uv run pytest -m integration
# 前端
cd knowledge-graph-frontend && npm test以「腾讯云香港 Ubuntu 24.04 + Godaddy 域名 geekbit.org,绑定子域 ferngrow.geekbit.org」为例。
架构:Docker Compose 跑 前端(Nginx, 仅 127.0.0.1:$FRONTEND_PORT) + 后端(127.0.0.1:$BACKEND_PORT) + Postgres + Redis;宿主机 Nginx 做 HTTPS 反代到 FRONTEND_PORT(默认 28080),certbot 申请并续期证书。对外只开放 80/443。
- 一台腾讯云服务器(Ubuntu 24.04),记下公网 IP。
- Godaddy 上的域名
geekbit.org,DNS 管理权限。
Godaddy 域名管理 → DNS Records:
- 类型
A,名称ferngrow(即ferngrow.geekbit.org),值填服务器公网 IP,TTL 默认。 - 生效后用
dig ferngrow.geekbit.org +short验证返回你的 IP。
腾讯云控制台 → 云服务器 → 安全组 → 入站规则,放行 TCP 80、443(以及 SSH 22)。
必做:腾讯云默认安全组通常只开 22;仅在服务器内
ufw放行不够,必须在控制台改安全组。
SSH 登录后:
sudo apt update && sudo apt install -y docker.io docker-compose-v2
sudo systemctl enable --now docker
docker --version && docker compose version # 验证
sudo usermod -aG docker $USER # 把当前用户加入 docker 组
⚠️ usermod需要重新登录才生效。加完组后必须exit重新 SSH 登录(或在当前 shell 执行newgrp docker),否则第 5 步docker compose ...会报permission denied ... docker.sock。临时绕过可在命令前加sudo。
git clone <你的仓库地址> FernGrow && cd FernGrow
cp .env.example .env编辑 .env(生产值):
DEBUG=falseJWT_SECRET=<openssl rand -hex 32 生成的随机串>(必须改,占位符会被启动校验拒绝)SERPER_API_KEY=<你的 Serper key>LLM_API_KEY/LLM_BASE_URL/LLM_MODEL:填你的 LLM 配置CORS_ORIGINS=["https://ferngrow.geekbit.org"]DATABASE_URL/REDIS_URL:保持.env.example即可(compose 会用容器网络地址覆盖)
docker compose up --build -d
docker compose ps # 4 个服务都应为 Up
docker compose logs -f backend # 含 alembic upgrade head 自动建表- 后端容器启动时自动
alembic upgrade head。 - 前端 Nginx 托管静态资源并把
/api反代到后端(已配 SSE 透传)。 - 此时前端仅在
127.0.0.1:28080(即$FRONTEND_PORT,可在.env改),尚未对外。
sudo apt install -y nginx certbot python3-certbot-nginx新建 /etc/nginx/sites-available/ferngrow.conf:
server {
listen 80;
server_name ferngrow.geekbit.org;
client_max_body_size 20m;
location / {
proxy_pass http://127.0.0.1:28080; # = FRONTEND_PORT(在 .env 里设置)
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# SSE:关闭缓冲 + 加长超时,避免流式扩展被 Nginx 截断
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
}启用并申请证书(自动加 443 配置 + 80→443 跳转):
sudo ln -s /etc/nginx/sites-available/ferngrow.conf /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d ferngrow.geekbit.orgsudo ufw allow 22/tcp && sudo ufw allow 80/tcp && sudo ufw allow 443/tcp
sudo ufw enable浏览器打开 https://ferngrow.geekbit.org → 注册登录 → 建图谱并扩展节点;SSE 应正常流式输出。
查日志:docker compose logs -f backend / sudo tail -f /var/log/nginx/error.log。
cd FernGrow && git pull && docker compose up --build -d # 重建并滚动更新
sudo certbot renew --dry-run # 验证证书自动续期(certbot 已装 systemd timer)- 容器均绑在
127.0.0.1(前端FRONTEND_PORT、后端BACKEND_PORT,默认 28080 / 28000),对外只经宿主机 Nginx 的 80/443;Postgres/Redis 不暴露端口。多项目服务器端口冲突时:在.env改FRONTEND_PORT/BACKEND_PORT(用sudo ss -ltnp | grep :端口号确认空闲),并同步把 Nginx 的proxy_pass端口改成新的FRONTEND_PORT,然后docker compose up -d。 .env含密钥,勿提交(已在.gitignore)。- 香港服务器访问 Serper / LLM 外网通常无碍;若用内地服务器需确认出网。
详见 api-contract.md。
核心接口(均挂在 /api/v1 下):
POST /auth/register、POST /auth/login、GET /auth/mePOST /graphs、GET /graphs、GET /graphs/{id}、PUT /graphs/{id}、DELETE /graphs/{id}POST /graphs/{id}/export(JSON / Markdown / SVG / PNG)GET /nodes/{id}/expand—— SSE 流式扩展(事件:progress/node_start/node_chunk/node_complete/edge_create/complete/error)POST /nodes/{id}/explain—— 选中文字「帮我解释」,SSE 扩展(复用扩展流水线)DELETE /nodes/{id}、POST /nodes/{id}/undo