[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-80166":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":9,"language":10,"languages":9,"totalLinesOfCode":9,"stars":11,"forks":12,"watchers":13,"openIssues":14,"contributorsCount":14,"subscribersCount":14,"size":14,"stars1d":14,"stars7d":14,"stars30d":15,"stars90d":14,"forks30d":14,"starsTrendScore":14,"compositeScore":16,"rankGlobal":9,"rankLanguage":9,"license":9,"archived":17,"fork":17,"defaultBranch":18,"hasWiki":19,"hasPages":17,"topics":20,"createdAt":9,"pushedAt":9,"updatedAt":21,"readmeContent":22,"aiSummary":23,"trendingCount":14,"starSnapshotCount":14,"syncStatus":24,"lastSyncTime":25,"discoverSource":26},80166,"anykb","GU-Cryptography\u002Fanykb","GU-Cryptography","AnyKB — 私有 RAG 知识库 + 透明 Agent",null,"Python",234,7,1,0,189,49.71,false,"main",true,[],"2026-06-12 04:01:26","# AnyKB — 私有 RAG 知识库 + 透明 Agent\n\n> 上传文档 \u002F 抓取网页 → 选中 KB → 用一句话问出来。\n> 30 秒内吐一份带原文引用的 markdown 报告，全过程思考链可视。\n\n**版本**：v3.1.0（2026-05-25 \u002F Docker + HTTPS）· **线上**：https:\u002F\u002Fanykb.cc.cd · **协议**：MIT\n\n---\n\n## 目录\n\n- [一、它能做什么](#一它能做什么)\n- [二、架构总览](#二架构总览)\n- [三、项目目录](#三项目目录)\n- [四、模块说明](#四模块说明)\n- [五、数据库现状与迁移路径](#五数据库现状与迁移路径)\n- [六、Docker 化方案](#六docker-化方案)\n- [七、本地开发](#七本地开发)\n- [八、服务器部署（已实战）](#八服务器部署已实战)\n- [九、配置项速查](#九配置项速查)\n- [十、常见运维](#十常见运维)\n- [十一、文档导航](#十一文档导航)\n\n---\n\n## 一、它能做什么\n\n| 能力 | 一句话 |\n|---|---|\n| **私有 RAG 知识库** | 上传 md \u002F txt \u002F pdf \u002F docx 或抓 URL → 后台异步 ingest → 对话顶部选这个 KB 提问 |\n| **多账户隔离** | 本地 JWT，每用户自建 KB；可邀请协作者 owner \u002F editor \u002F viewer 三档权限 |\n| **KB 协作 \u002F 分享** | 邮箱邀请已注册用户 + 匿名 token 分享链接（可设过期 + 次数 + 撤销） |\n| **真·通用聊天** | 不选 KB 时纯模型直答 + DuckDuckGo `web_search` 实时搜索兜底，引用必须带 URL |\n| **KB+Web 混合兜底** | KB 模式可选启用 web_search：KB 强相关（score ≥ 0.7）走 KB；KB 没找到（\u003C 0.4）兜底搜网，答案分【📚 KB】\u002F【🌐 Web】两段 |\n| **会话级模型切换** | 每个对话独立保存 LLM model，刷新 \u002F 切对话 \u002F 跨设备都记得 |\n| **Per-KB 检索配置** | 每个 KB 自带 embedding（必填）+ 可选 cross-encoder reranker —— 不同 KB 用不同向量模型互不干扰 |\n| **混合检索** | Milvus 服务端 BM25 全文检索 + 稠密向量 RRF 融合，关键词查询召回明显改善 |\n| **结构化报告** | 旅行 \u002F 通用两种 skill 模板，markdown 报告含 TL;DR + sections + citations |\n| **品牌图文分享卡** | 报告一键导出品牌框 PNG（html2canvas + 顶部品牌条 + 问题 chip + 水印），支持复制到剪贴板 |\n| **透明 Agent** | 前端实时展示思考链（每步工具调用 \u002F 耗时 \u002F 命中数），不再是黑盒 |\n| **流式输出** | SSE 推 token，\"正在思考 → 工具跑 → 正在撰写报告\" 三段过渡 |\n| **BYOK 强制 gate** | `BYOK_REQUIRED=true` 公网部署模式，禁止白嫖 owner 的 API key |\n| **Docker 化部署** | 4 容器 compose（postgres + backend + frontend + nginx），`.\u002Fscripts\u002Fdeploy.sh` 一行起 |\n| **完全解耦** | LLM \u002F embedding \u002F 向量库 \u002F App DB 全部 env 驱动或在 `\u002Fsettings` 改，代码零改动 |\n\n历史完整 changelog 见 [PROGRESS.md](PROGRESS.md)（M0 → v3-M8.2 共 30 个 milestone）。\n\n---\n\n## 二、架构总览\n\n```\n浏览器\n  │ HTTPS \u002F SSE\n  ▼\n┌────────────────────────────────┐\n│  Next.js 14 (App Router)       │  :3000\n│  - 聊天界面（流式 + 思考链）   │\n│  - KB 管理 \u002F 协作邀请          │\n│  - 系统设置 modal              │\n│  - 内置 API proxy 转发到后端   │\n└──────────────┬─────────────────┘\n               │ proxy\n               ▼\n┌────────────────────────────────┐\n│  FastAPI + LangGraph           │  :8000\n│  ├─ Auth (JWT, bcrypt)         │\n│  ├─ Conversations REST         │\n│  ├─ KB REST (含协作 \u002F 邀请)    │\n│  ├─ Settings REST              │\n│  └─ \u002Fapi\u002Fchat (SSE)            │\n│       └─ Agent 主循环          │\n│            plan → call_tools → skill_report\n└──┬─────────────────────┬───────┘\n   │                     │\n   ▼                     ▼\n┌──────────────┐    ┌─────────────────────────────┐\n│ App DB       │    │ Vector DB                   │\n│ SQLite       │    │ Milvus Lite (默认)          │\n│ (users \u002F kbs │    │   或 Milvus Standalone      │\n│  \u002Fdocs \u002Fmsgs)│    │   或 Qdrant                 │\n└──────────────┘    └─────────────────────────────┘\n\n外部服务（按 KB \u002F 用户配置）：\n  - LLM        : DeepSeek \u002F Claude \u002F OpenAI \u002F SiliconFlow \u002F Ollama\n  - Embedding  : SiliconFlow BGE-M3 \u002F OpenAI \u002F Ollama\n  - Reranker   : SiliconFlow \u002F Cohere \u002F 自托管 TEI (opt-in)\n  - Web Search : DuckDuckGo (ddgs, 免 key)\n```\n\nAgent 按所选 KB 切换工具集 — 用户 KB 只挂 `search_kb`；通用聊天挂 `web_search`；系统旅行示例 KB 挂旅行四件套（weather \u002F restaurant_kb \u002F amap \u002F travel_report）。\n\n详见 [docs\u002Farchitecture.md](docs\u002Farchitecture.md)。\n\n---\n\n## 三、项目目录\n\n```\nai-agent\u002F\n├── backend\u002F                       Python 3.11 \u002F FastAPI\n│   ├── src\u002F\n│   │   ├── app.py                 FastAPI 入口 + SSE chat 端点 + lifespan\n│   │   ├── settings.py            pydantic-settings（.env 驱动）\n│   │   ├── auth\u002F                  M1: JWT 认证（注册 \u002F 登录 \u002F me \u002F 改密 \u002F 删号）\n│   │   │   ├── models.py          User SQLAlchemy 模型（15 列：4 主 + 5 LLM + 5 embed + 1 reranker_enabled）\n│   │   │   ├── password.py        bcrypt cost=12\n│   │   │   ├── tokens.py          JWT HS256 编解码\n│   │   │   ├── middleware.py      CurrentUser FastAPI 依赖\n│   │   │   └── routes.py          \u002Fapi\u002Fauth\u002F{register,login,me,change-password}\n│   │   ├── kb\u002F                    M2\u002FM3: 知识库 + 文档 + ingest\n│   │   │   ├── models.py          KB \u002F Document \u002F KBMember \u002F KBInvitation\n│   │   │   ├── routes.py          \u002Fapi\u002Fkbs\u002F* + \u002Fapi\u002Finvitations\u002F*\n│   │   │   ├── ingest.py          后台异步 chunk + embed + upsert\n│   │   │   ├── chunker.py         段落→句子→字符递归切分\n│   │   │   ├── system_seed.py     启动注册 TravelGPT 演示库\n│   │   │   └── parsers\u002F           markdown \u002F pdf (pymupdf) \u002F docx \u002F webpage (trafilatura)\n│   │   ├── conversations\u002F         v2-M3: 跨设备会话历史\n│   │   │   ├── models.py          Conversation \u002F Message (FK CASCADE)\n│   │   │   └── routes.py          \u002Fapi\u002Fconversations\u002F* + bulk import + export\n│   │   ├── settings_user\u002F         v2-M1: 每用户自助配置\n│   │   │   ├── models.py          UserLLMConfig \u002F UserEmbeddingConfig \u002F UserRerankerConfig\n│   │   │   ├── kb_resolvers.py    v3-M7: KB 级 cfg 优先 → user 兜底\n│   │   │   ├── probe.py           列模型 \u002F 验 base_url+api_key\n│   │   │   ├── gate.py            v2-M2: BYOK 强制 gate\n│   │   │   └── routes.py          \u002Fapi\u002Fsettings\u002F*\n│   │   ├── agent\u002F                 LangGraph 主循环\n│   │   │   ├── graph.py           build_graph(kb, llm_cfg, embedding_cfg, reranker_cfg, ...)\n│   │   │   ├── nodes.py           plan \u002F call_tools \u002F skill_report\n│   │   │   ├── state.py           AgentState TypedDict\n│   │   │   └── prompts.py         3 套 system prompt (general \u002F travel \u002F kb)\n│   │   ├── tools\u002F                 Agent 工具实现\n│   │   │   ├── base.py            ToolRegistry 工厂（按 KB 模式三态切换工具集）\n│   │   │   ├── kb_search.py       通用 KB 检索（含 reranker over-fetch + rerank reorder）\n│   │   │   ├── web_search.py      DuckDuckGo \u002F ddgs\n│   │   │   ├── weather.py         高德天气（旅行示例）\n│   │   │   ├── restaurant_rag.py  旅行 KB 检索（MMR）\n│   │   │   └── amap_fallback.py   高德 POI 兜底\n│   │   ├── skills\u002F                结构化输出模板\n│   │   │   ├── loader.py          invoke_skill (LLM JSON 输出)\n│   │   │   ├── general_report\u002FSKILL.md   v2-M8: 通用 KB 报告模板\n│   │   │   └── travel_report\u002FSKILL.md    旅行报告模板\n│   │   ├── safety\u002F                输入清洗 \u002F 输出脱敏 \u002F 工具守卫\n│   │   └── infra\u002F                 基础设施抽象层（解耦核心）\n│   │       ├── database.py        SQLAlchemy async + init_db + 幂等 ALTER\n│   │       ├── vector_store.py    VectorStore Protocol + Qdrant\u002FMilvus\u002FLocal 三 adapter\n│   │       ├── local_vector.py    SQLite 兜底向量实现\n│   │       ├── embedding.py       OpenAI 兼容统一 embed 客户端 + provider 预设\n│   │       ├── reranker.py        v3-M4: cross-encoder rerank 客户端\n│   │       ├── llm.py             LLM 客户端（Anthropic \u002F OpenAI 兼容）+ pick_model\n│   │       ├── crypto.py          Fernet at-rest 加密（key 派自 JWT_SECRET）\n│   │       └── rate_limit.py      内存 sliding window\n│   ├── data\u002F                      运行时数据（gitignored）\n│   │   ├── app.db                 SQLite 应用库\n│   │   ├── milvus_local.db        Milvus Lite 向量库\n│   │   └── uploads\u002F{kb_id}\u002F       原始上传文件\n│   ├── tests\u002F                     11 个 smoke (test_reranker_smoke + test_milvus_smoke)\n│   ├── env.example                env 模板（每个字段含注释）\n│   └── pyproject.toml             依赖（核心 + dev\u002Follama\u002Fopenai\u002Fmonitoring\u002Fmilvus extras）\n│\n├── frontend\u002F                      Next.js 14 App Router + Tailwind\n│   ├── app\u002F\n│   │   ├── page.tsx               主聊天页（KB+Model selector + Sidebar + Chat）\n│   │   ├── welcome\u002F               未登录落地页\n│   │   ├── login\u002F register\u002F       双栏登录注册 + ?next= 回跳\n│   │   ├── kbs\u002F                   KB 列表 + 详情（含成员管理 \u002F 邀请 \u002F 高级设置）\n│   │   ├── settings\u002F              v3-M8 极简后只剩 LLM 凭据 + KB 选项\n│   │   ├── invite\u002F[token]\u002F        v2-M9: 匿名邀请落地页\n│   │   ├── api\u002F                   catch-all proxy → 后端 :8000\n│   │   └── layout.tsx             ThemeProvider + Toaster + Plausible 钩子\n│   ├── components\u002F\n│   │   ├── Sidebar.tsx            对话列表 + 重命名 + 底部 UserMenu\n│   │   ├── ChatBox.tsx            自适应高度 + Stop 按钮\n│   │   ├── MessageBubble.tsx      用户\u002F助手气泡 + 流式占位\n│   │   ├── ThinkingChain.tsx      工具调用思考链可视\n│   │   ├── ReportView.tsx         markdown 报告渲染（prose-tg）\n│   │   ├── ExportActions.tsx      复制 MD \u002F 导 PDF \u002F 图文分享\n│   │   ├── ShareCardDialog.tsx    品牌图文卡 portal 模态\n│   │   ├── SystemSettingsDialog.tsx  v3-M5: DeepSeek 风 4-tab 设置 modal\n│   │   ├── Select.tsx Dialog.tsx Brand.tsx ThemeToggle.tsx ...\n│   │   └── CreateKbDialog.tsx     v3-M7\u002F8.2: 含 Embedding+Reranker 配置 + 测试连接\n│   ├── lib\u002F\n│   │   ├── auth.ts                token + authFetch + per-user storage key\n│   │   ├── sseClient.ts           fetch+ReadableStream 手动 SSE 解析\n│   │   ├── conversations-api.ts   会话 CRUD wrapper\n│   │   ├── kb-api.ts              KB \u002F 文档 \u002F 成员 \u002F 邀请 wrapper\n│   │   ├── settings-api.ts        LLM \u002F Embedding \u002F Reranker probe + save\n│   │   ├── conversationStore.ts   Message 类型 + deriveTitle\n│   │   ├── byok-toast.ts          v2-M2: BYOK 422 → toast「去配置」\n│   │   ├── storage-migrate.ts     一次性 anykb:* 命名空间迁移\n│   │   ├── cn.ts                  clsx + tailwind-merge\n│   │   └── theme.ts               class 策略 dark mode\n│   ├── tailwind.config.ts         语义化 token + dark class 策略\n│   └── package.json               (next 14 \u002F react 18 \u002F tailwind 3.4)\n│\n├── data\u002F                          策展示例数据\n│   ├── seed\u002F{shanghai,beijing,chengdu,hangzhou}.json   20 家餐厅\n│   ├── schema.json                seed 数据 schema\n│   └── ingest.py                  独立 seed → vector store 入库脚本\n│\n├── docs\u002F                          完整文档\n│   ├── architecture.md            内部架构 \u002F 状态图 \u002F 解耦设计\n│   ├── deploy.md                  本地启动 + 服务器部署 + 换部件\n│   ├── rag-primer.md              小白入门：RAG \u002F Embedding \u002F BM25 \u002F Hybrid \u002F Rerank\n│   ├── milvus-guide.md            Milvus 向量库详解\n│   ├── curation-sop.md            策展 SOP\n│   └── tutorial.md                端到端流程教学\n│\n├── docker-compose.yml             4 服务编排（postgres + backend + frontend + nginx）\n├── env.docker.example             Docker 部署 env 模板\n├── nginx\u002Fanykb.conf               nginx 反代配置（SSE-safe）\n├── scripts\u002F                       运维脚本\n│   ├── deploy.sh                  build + up + 健康检查\n│   ├── backup.sh                  备份 PG + backend-data 卷\n│   └── logs.sh                    tail -f 服务日志\n├── start_local.bat \u002F start_local.sh   一键本地启动（非 Docker）\n├── start.py                       Python 启动器\n├── PROGRESS.md                    完整 changelog（M0 → v3-M8.2 + Docker 化）\n└── README.md                      本文档\n```\n\n---\n\n## 四、模块说明\n\n### 后端核心模块\n\n| 模块 | 职责 | 关键文件 |\n|---|---|---|\n| `auth\u002F` | 本地账户、JWT 签发、密码哈希、CurrentUser 依赖 | `routes.py`, `middleware.py` |\n| `kb\u002F` | 知识库 \u002F 文档 CRUD、4 种格式解析、后台 ingest、协作邀请 | `routes.py`, `ingest.py`, `parsers\u002F` |\n| `conversations\u002F` | 跨设备会话历史，含 bulk import + JSON export | `routes.py`, `models.py` |\n| `settings_user\u002F` | 每用户自助配 LLM \u002F Embedding \u002F Reranker，含 probe + Fernet 加密 | `routes.py`, `probe.py` |\n| `agent\u002F` | LangGraph 主循环 + 3 套 system prompt + 三态路由 | `graph.py`, `nodes.py` |\n| `tools\u002F` | KBSearchTool \u002F WebSearchTool \u002F 旅行四件套 + ToolRegistry 工厂 | `base.py`, `kb_search.py` |\n| `skills\u002F` | invoke_skill 二级 LLM 调用生成结构化报告 (JSON → markdown) | `loader.py`, `*\u002FSKILL.md` |\n| `safety\u002F` | 输入 prompt-injection 过滤 \u002F 输出 PII 脱敏 \u002F 工具白名单守卫 | `input_filter.py`, `output_filter.py` |\n| `infra\u002F` | DB \u002F 向量库 \u002F Embedding \u002F Reranker \u002F LLM \u002F 加密 \u002F 速率 — **解耦核心层** | `vector_store.py`, `embedding.py` |\n\n### 前端核心模块\n\n| 模块 | 职责 |\n|---|---|\n| `app\u002Fpage.tsx` | 主聊天页（含 KB selector + Model selector + Sidebar） |\n| `app\u002Fkbs\u002F` | KB 列表 \u002F 详情 \u002F 成员管理 \u002F 邀请 Dialog |\n| `app\u002Fsettings\u002F` | LLM 凭据卡 + KB 选项（v3-M8 精简后） |\n| `components\u002FSystemSettingsDialog.tsx` | DeepSeek 风 4-tab 系统设置 modal（通用 \u002F 账号 \u002F 数据 \u002F 关于） |\n| `components\u002FCreateKbDialog.tsx` | 含 embedding + reranker 配置 + 强制\"测试连接\"才能保存 |\n| `components\u002FThinkingChain.tsx` | 工具调用链实时可视化（运行中跳秒） |\n| `components\u002FShareCardDialog.tsx` | 品牌框图文分享卡（html2canvas → PNG） |\n| `lib\u002FsseClient.ts` | 手动解析 SSE（替代浏览器 EventSource，支持 POST + Bearer auth） |\n\n---\n\n## 五、数据库现状与迁移路径\n\n### 当前部署形态\n\n项目支持两种部署模式，**自动按 env 切换，业务代码 0 差异**：\n\n| 模式 | App DB | Vector DB | 适用 |\n|---|---|---|---|\n| **本地开发** | SQLite + aiosqlite（单文件 `backend\u002Fdata\u002Fapp.db`） | Milvus Lite（嵌入式 `backend\u002Fdata\u002Fmilvus_local.db`） | 单人调试、零依赖、Windows 原生跑 |\n| **生产 Docker**（线上实例采用） | **PostgreSQL 16** 容器（独立服务） | **Milvus Lite**（仍嵌在 backend 容器内，volume 持久化） | 多用户、商业级、可滚动升级 |\n\n> **线上为什么不直接上 Milvus Standalone？** 服务器内存只有 1.9GB，Standalone 需要 etcd + minio + milvus 三容器 ≥ 2GB。当前 Milvus Lite 完全够用（万级 chunk 性能稳定），未来上更大服务器再切（见下方\"切到 Milvus Standalone\"）。\n\n### 为什么 SQLite 不适合生产\n\n| 限制 | 影响 |\n|---|---|\n| SQLite 写锁是单线程 | 并发上传 \u002F 写聊天历史时容易触发 `database is locked` |\n| SQLite 文件不支持网络访问 | 多 worker \u002F 多容器无法共享同一份 DB |\n| 无副本 \u002F failover \u002F point-in-time recovery | 数据丢了就丢了 |\n\n所以**线上必须用 PostgreSQL**，本地开发 SQLite 够用。\n\n### 切换 \u002F 升级路径\n\n**强解耦设计早就为换 DB 留好了入口**：SQLAlchemy 抽象层 + VectorStore Protocol，**改 1~2 个 env 变量即可，业务代码 0 改动**。\n\n#### PostgreSQL（已在线上生效）\n\n```bash\n# Docker backend 已预装 asyncpg；本地需要：pip install asyncpg\nDATABASE_URL=postgresql+asyncpg:\u002F\u002Fanykb:strong_password@postgres:5432\u002Fanykb\n```\n\n所有表定义（User \u002F KB \u002F Document \u002F Conversation \u002F Message \u002F KBMember \u002F KBInvitation）都是 SQLAlchemy 2.x Mapped 风格，跨 dialect 通用。**注意**：`_migrate_additive_columns` 的 ALTER 是 SQLite 语法；PostgreSQL 上首次 `create_all` 一次建全所有最新列，老 ALTER 走不到 if 分支，不会报错。\n\n#### Milvus Standalone（向量 DB 升级，未做）\n\n服务器内存够时（≥ 4GB）值得切，可拿到更强的 index 调优 + 副本：\n\n```bash\n# 1. 在 docker-compose.yml 加 milvus \u002F etcd \u002F minio 三服务（参考官方 milvus-standalone-docker-compose.yml）\n# 2. backend service env：\nVECTOR_STORE=milvus\nMILVUS_URI=http:\u002F\u002Fmilvus:19530\nMILVUS_TOKEN=\n```\n\n`MilvusStore` adapter 对 Lite \u002F Standalone \u002F Zilliz Cloud 行为完全一致 — 这是 v3-M2 设计时刻意保留的兼容性。**数据迁移**：向量数据无法跨实例直接复制；最干净的做法是 KB 详情页点「重建索引」按钮（v3-M3 加的功能）→ 走 ingest pipeline 重新生成。\n\n#### Zilliz Cloud（托管 Milvus，0 运维）\n\n```bash\nVECTOR_STORE=milvus\nMILVUS_URI=https:\u002F\u002Fyour-cluster.api.gcp-us-west1.zillizcloud.com\nMILVUS_TOKEN=your_cluster_api_key\n```\n\n免费层 5 GB 够用很久。\n\n#### Qdrant（如果偏好）\n\n```bash\nVECTOR_STORE=qdrant\nQDRANT_URL=http:\u002F\u002Fqdrant:6333\nQDRANT_API_KEY=\n```\n\n**注意**：Qdrant adapter 没有 hybrid 检索（v3-M3 是 Milvus 专属服务端 BM25），切 Qdrant 后自动降级 dense-only。\n\n### 推荐生产组合\n\n```\nBackend (1~N worker) ──► PostgreSQL 15+   (App DB, 关系数据)\n                    ──► Milvus Standalone (Vector DB, chunk 向量)\n                    ──► SiliconFlow API   (Embedding + Reranker)\n                    ──► Anthropic \u002F DeepSeek API (LLM)\n```\n\nPG + Milvus Standalone 是被 1000+ 公司验证过的组合，资源占用合理（PG 内存 256MB 起、Milvus Standalone 2GB 起就能跑几十万向量）。\n\n---\n\n## 六、Docker 化方案\n\n### 当前实现：4 服务 compose\n\n仓库根目录已包含完整的 docker 化文件，**在你服务器上一行 `.\u002Fscripts\u002Fdeploy.sh` 就能起整套栈**。\n\n| 服务 | 镜像 | 内容 |\n|---|---|---|\n| `postgres` | `postgres:16-alpine` | App DB（用户 \u002F KB \u002F 文档 \u002F 会话）→ volume `anykb_postgres-data` |\n| `backend` | 自建（`.\u002Fbackend\u002FDockerfile`） | FastAPI + LangGraph + **Milvus Lite 嵌入式** → volume `anykb_backend-data`（`\u002Fapp\u002Fdata\u002Fmilvus_local.db` + uploads） |\n| `frontend` | 自建（`.\u002Ffrontend\u002FDockerfile`） | Next.js 14 standalone build（multi-stage，~150MB） |\n| `nginx` | `nginx:1.27-alpine` | 反代 :80，`\u002Fapi\u002Fchat` 关 buffering 透传 SSE |\n\n```\n┌────────────────────────────────────────────┐\n│           nginx:80 (host:80)               │\n│   \u002Fapi\u002Fchat → backend (SSE buffering off)  │\n│   \u002Fapi\u002F...  → backend                      │\n│   \u002F        → frontend                      │\n└──────────────────┬─────────────────────────┘\n                   │ docker network: anykb_default\n   ┌───────────────┼───────────────┐\n   ▼               ▼               ▼\npostgres        backend         frontend\n(:5432)         (:8000)         (:3000)\n   ▲                │\n   │                │ asyncpg\n   └────────────────┘\n```\n\n### 关键文件\n\n| 文件 | 作用 |\n|---|---|\n| `docker-compose.yml` | 4 服务编排 + volume + 健康检查 + env 注入 |\n| `backend\u002FDockerfile` | Python 3.11 slim + `pip install .[milvus] asyncpg` + healthcheck `\u002Fhealth` |\n| `frontend\u002FDockerfile` | 两阶段 build：builder npm ci + build \u002F runner 只装 `.next\u002Fstandalone`（~150MB） |\n| `nginx\u002Fanykb.conf` | upstream `backend:8000` + `frontend:3000` + SSE-safe 配置 |\n| `env.docker.example` | 模板：POSTGRES_PASSWORD \u002F JWT_SECRET \u002F PUBLIC_URL \u002F BYOK_REQUIRED |\n| `scripts\u002Fdeploy.sh` | build + up + healthcheck + 日志（**主入口**） |\n| `scripts\u002Fbackup.sh` | 备份 PG + backend-data volume 到 tarball |\n| `scripts\u002Flogs.sh` | tail -f 指定服务日志 |\n\n### 启动步骤\n\n```bash\n# 1. 准备 .env 文件\ncp env.docker.example .env\n# 编辑 .env：\n#   POSTGRES_PASSWORD=$(openssl rand -hex 16)\n#   JWT_SECRET=$(openssl rand -hex 32)\n#   PUBLIC_URL=http:\u002F\u002F你的IP或域名\n\n# 2. 一行起栈\n.\u002Fscripts\u002Fdeploy.sh\n\n# 3. 看健康检查 + 日志\ndocker compose ps\n.\u002Fscripts\u002Flogs.sh backend\n\n# 4. 公网验证\ncurl http:\u002F\u002Flocalhost\u002Fhealth\n```\n\n**`.\u002Fscripts\u002Fdeploy.sh` 做了什么**：\n1. 检查 `.env` 存在\n2. `docker compose build`（自动用 layer cache，未变的层秒过）\n3. `docker compose up -d --remove-orphans`\n4. 等 10s healthcheck\n5. 打印容器状态 + backend 日志\n\n### Docker 镜像生命周期（FAQ）\n\n> **\"镜像掉了或服务器重启了，要重新构建吗？\"**\n\n**不要**。镜像由 docker daemon 持久化在 `\u002Fvar\u002Flib\u002Fdocker\u002F`，独立于容器生命周期：\n\n| 操作 | 影响镜像？ | 影响容器？ |\n|---|---|---|\n| `docker compose up -d` | 不存在才 build，存在直接复用 | 创建并启动 |\n| `docker compose down` | ❌ 不删 | ✅ 删（数据 volume 保留） |\n| `docker compose restart` | ❌ 不删 | 重启 |\n| 服务器重启 | ❌ 不删 | docker daemon 按 restart policy 自动起 |\n| `docker rmi` \u002F `docker system prune` | ✅ 删 | — |\n\n所以正常运维不需要\"重建\"。只有以下场景需要：\n- **代码变了** → `.\u002Fscripts\u002Fdeploy.sh`（增量 build，秒级 cache）\n- **磁盘清空过** → `.\u002Fscripts\u002Fdeploy.sh`（全量 build，几分钟）\n- **想换 Python 依赖版本** → 同上\n\n### 数据持久化\n\n| Volume | 内容 | 备份命令 |\n|---|---|---|\n| `anykb_postgres-data` | PG 全部数据 | `.\u002Fscripts\u002Fbackup.sh` |\n| `anykb_backend-data` | Milvus Lite `.db` + 用户上传文件 | 同上 |\n\n`.\u002Fscripts\u002Fbackup.sh` 会同时打 PG + backend-data 两个 tarball 到 `.\u002Fbackups\u002F`。\n\n### 升级 \u002F 回滚 \u002F 滚动重启\n\n```bash\n# 改代码后部署\n.\u002Fscripts\u002Fdeploy.sh\n\n# 只重建 backend（前端没变）\n.\u002Fscripts\u002Fdeploy.sh backend\n\n# 滚动重启（不重建，只重启容器）\ndocker compose restart backend\n\n# 完全停止（数据 volume 保留）\ndocker compose down\n\n# 完全清掉（包括数据 volume，慎用）\ndocker compose down -v\n```\n\n---\n\n## 七、本地开发\n\n### 前置依赖\n\n- **Python 3.11+**\n- **Node.js 20+** + npm（或 pnpm）\n- 可选：Docker（如果走 Qdrant Docker）\n\n### 3 个进程启动\n\n```bash\n# 1. 后端\ncd backend\npython -m venv .venv\nsource .venv\u002Fbin\u002Factivate          # Windows: .venv\\Scripts\\activate\npip install -e '.[milvus]'         # 默认带 Milvus Lite\ncp env.example .env                # 编辑填关键 key\npython -m uvicorn src.app:app --host 0.0.0.0 --port 8000\n\n# 2. 前端（新窗口）\ncd frontend\nnpm install\nnpm run dev                        # :3000\n\n# 3. 浏览器进 http:\u002F\u002Flocalhost:3000\n#    注册 → 登录 → 选「TravelGPT 演示库」试问 \"5月13号上海，想找做酸菜鱼的店\"\n```\n\n### 一键启动（推荐：Docker）\n\n```bash\ncp env.docker.example .env\n# 编辑 .env 填 POSTGRES_PASSWORD \u002F JWT_SECRET \u002F PUBLIC_URL\n.\u002Fscripts\u002Fdeploy.sh\n```\n\n详见 [docs\u002Fdeploy.md](docs\u002Fdeploy.md)。\n\n---\n\n## 八、服务器部署（已实战）\n\n**线上实例**：https:\u002F\u002Fanykb.cc.cd （43.163.245.206 \u002F Ubuntu 24.04 \u002F 2 核 \u002F 1.9 GB \u002F 50 GB）\n\n### 当前部署架构（Docker compose）\n\n2026-05-25 已从 systemd 切换到 Docker。架构 = §6 的 4 服务全栈：\n\n```\n公网 :80\n  │\n  ▼\n┌──────────────────────────────────┐\n│ nginx (nginx:1.27-alpine)        │ host 端口 80\n│   \u002Fapi\u002Fchat → backend (SSE off)  │\n│   \u002Fapi      → backend            │\n│   \u002F         → frontend           │\n└──┬─────────────┬─────────────────┘\n   ▼             ▼\nbackend       frontend\n(FastAPI +    (Next.js\n Milvus       standalone)\n Lite 嵌入式)\n   │\n   ▼\npostgres (postgres:16-alpine)\n  → volume anykb_postgres-data\n\ndata volumes:\n  anykb_postgres-data   (PG 数据)\n  anykb_backend-data    (Milvus Lite + uploads)\n```\n\n### 部署清单\n\n| 项 | 状态 |\n|---|---|\n| ✅ docker compose 4 服务全栈 healthy | `restart: unless-stopped` 自启 |\n| ✅ PostgreSQL 16 替代 SQLite | volume 持久化 |\n| ✅ Milvus Lite 嵌入 backend 容器 | volume 持久化 |\n| ✅ nginx 反代 + SSE buffering off + 60M body limit | `nginx\u002Fanykb.conf` |\n| ✅ **HTTPS \u002F Let's Encrypt 证书**（2026-05-25 上线） | `\u002Fetc\u002Fletsencrypt\u002Flive\u002Fanykb.cc.cd\u002F`，TLSv1.2+1.3 + HSTS 1 年 |\n| ✅ **HTTP → HTTPS 301 自动跳** | nginx :80 block 全部 redirect |\n| ✅ **certbot 自动续期**（每 60 天自动） | systemd timer + renewal-hooks 自动停启 nginx 容器，downtime ~30s |\n| ✅ UFW 防火墙仅开 22 + 80 + 443 | 已启用 |\n| ✅ SSH key | `~\u002F.ssh\u002Fanykb_deploy` |\n| ✅ ubuntu 加入 docker 组 | 下次 SSH 登录免 sudo |\n| ✅ scripts\u002F 一键运维 | `deploy.sh` \u002F `backup.sh` \u002F `logs.sh` |\n| ⏳ 改 root \u002F ubuntu 密码 + 禁用密码登录 | TODO |\n| ⏳ 定时备份 cron（每日调 `backup.sh`） | TODO |\n| ⏳ Milvus Lite → Standalone | 等更大服务器或数据规模到瓶颈 |\n\n### 服务器初次部署步骤（如果是全新服务器）\n\n```bash\n# 1. 装 docker（Ubuntu）\nsudo apt update && sudo apt install -y docker.io docker-compose-plugin\nsudo systemctl enable --now docker\nsudo usermod -aG docker $USER  # 重新登录生效\n\n# 2. 拉代码（或 tar pipe 同步）\nmkdir -p ~\u002Fanykb && cd ~\u002Fanykb\n# scp \u002F rsync \u002F git clone 你的源码到这里\n\n# 3. 准备 .env\ncp env.docker.example .env\nvim .env  # 填 POSTGRES_PASSWORD \u002F JWT_SECRET \u002F PUBLIC_URL\n\n# 4. 启动\n.\u002Fscripts\u002Fdeploy.sh\n```\n\n### 升级后端代码\n\n本地改完代码 → tar pipe 同步 → 服务器跑 deploy.sh：\n\n```bash\n# 本地（在项目根目录）\ntar -cz backend\u002Fsrc | ssh user@server 'cd ~\u002Fanykb && tar -xz'\n\n# 服务器\nssh user@server\ncd ~\u002Fanykb && .\u002Fscripts\u002Fdeploy.sh backend\n```\n\n### 推荐后续动作\n\n1. **改密码 + 关 SSH 密码登录**（5 分钟） — `passwd ubuntu` + `sudo vi \u002Fetc\u002Fssh\u002Fsshd_config` 设 `PasswordAuthentication no`\n2. **定时备份** — `crontab -e` 加 `0 3 * * * cd \u002Fhome\u002Fubuntu\u002Fanykb && .\u002Fscripts\u002Fbackup.sh \u002Fhome\u002Fubuntu\u002Fanykb-backups`\n3. **多 worker** — `backend` Dockerfile CMD 加 `--workers 4`（PG 已经能支持，不再有 SQLite 写冲突）\n4. **Milvus Standalone** — 数据上百万级再考虑（见 §5 切换步骤）\n\n### HTTPS 部署细节（已生效）\n\n域名 `anykb.cc.cd` → Let's Encrypt 免费证书：\n\n```bash\n# 证书路径（容器内只读挂载）\n\u002Fetc\u002Fletsencrypt\u002Flive\u002Fanykb.cc.cd\u002Ffullchain.pem\n\u002Fetc\u002Fletsencrypt\u002Flive\u002Fanykb.cc.cd\u002Fprivkey.pem\n\n# 自动续期机制\nsystemd timer:  certbot.timer (每天 20:47 检查)\npre-hook:       \u002Fetc\u002Fletsencrypt\u002Frenewal-hooks\u002Fpre\u002Fanykb-stop-nginx.sh\npost-hook:      \u002Fetc\u002Fletsencrypt\u002Frenewal-hooks\u002Fpost\u002Fanykb-start-nginx.sh\n\n# 实际续期时间：到期 \u003C 30 天才会真续；约 60 天一次，每次 nginx ~30s downtime（凌晨）\n\n# 手动验证续期流程\nsudo certbot renew --dry-run\n\n# 手动续期（紧急用）\nsudo certbot renew\n```\n\n完整部署细节见服务器上 `\u002Fhome\u002Fubuntu\u002Fanykb-deploy-notes.md`。\n\n---\n\n## 九、配置项速查\n\n### 后端 env（`backend\u002F.env`）\n\n| 类别 | 字段 | 默认 \u002F 示例 | 说明 |\n|---|---|---|---|\n| **LLM** | `ANTHROPIC_API_KEY` | `sk-ant-...` | env fallback（用户可在 \u002Fsettings 覆盖） |\n| | `DEEPSEEK_API_KEY` | `sk-...` | 同上 |\n| | `LLM_DEFAULT_MODEL` | `claude-haiku-4-5-20251001` | 默认轻量模型 |\n| | `LLM_COMPLEX_MODEL` | `claude-sonnet-4-6` | 复杂任务用（plan_node 自动切） |\n| **向量库** | `VECTOR_STORE` | `milvus` | `milvus` \u002F `qdrant` \u002F `local` |\n| | `MILVUS_URI` | `.\u002Fdata\u002Fmilvus_local.db` | Lite 文件路径 \u002F Standalone HTTP |\n| | `MILVUS_TOKEN` | (空) | Zilliz Cloud \u002F 鉴权 |\n| | `QDRANT_URL` | `http:\u002F\u002Flocalhost:6333` | Qdrant 时用 |\n| **Embedding** | `EMBEDDING_PROVIDER` | `openai` | 默认 fallback |\n| | `EMBEDDING_MODEL` | `text-embedding-3-small` | 同上 |\n| **App DB** | `DATABASE_URL` | `sqlite+aiosqlite:\u002F\u002F\u002F.\u002Fdata\u002Fapp.db`（本地）\u002F `postgresql+asyncpg:\u002F\u002F...`（Docker） | Docker compose 自动注入 |\n| **Auth** | `JWT_SECRET` | (必填) | 32 字节随机十六进制 |\n| | `JWT_EXPIRE_MINUTES` | `10080` | 7 天 |\n| **BYOK gate** | `BYOK_REQUIRED` | `false` | **公网部署必设 true** |\n| **加密** | (派生自 `JWT_SECRET`) | — | Fernet at-rest 加密所有 api_key |\n| **CORS** | `CORS_ORIGINS` | `http:\u002F\u002Flocalhost:3000` | 多个用逗号分隔 |\n| **限流** | `RATE_LIMIT_PER_HOUR` | `20` | 每用户每小时 chat 上限 |\n| **监控** | `LOGFIRE_TOKEN` | (空) | 可选 Logfire backend tracing |\n\n### 前端 env（`frontend\u002F.env.local`）\n\n```ini\nBACKEND_URL=http:\u002F\u002F127.0.0.1:8000           # 后端地址（前端 proxy 转发）\nNEXT_PUBLIC_APP_NAME=AnyKB                  # 显示名\nNEXT_PUBLIC_PLAUSIBLE_DOMAIN=               # 可选 Plausible analytics\nNEXT_TELEMETRY_DISABLED=1                   # 关掉 next telemetry\n```\n\n---\n\n## 十、常见运维\n\n### 起 \u002F 停 \u002F 看日志（Docker 栈）\n\n```bash\n# 一键起 \u002F 重建（核心入口）\n.\u002Fscripts\u002Fdeploy.sh                # 全栈\n.\u002Fscripts\u002Fdeploy.sh backend        # 只 backend\n\n# 看日志\n.\u002Fscripts\u002Flogs.sh backend          # tail -f backend\n.\u002Fscripts\u002Flogs.sh all              # 全部服务\n\n# 重启容器（不重建镜像）\ndocker compose restart backend\n\n# 看状态\ndocker compose ps\n\n# 完全停（数据 volume 保留）\ndocker compose down\n\n# 完全清（包括数据，慎用）\ndocker compose down -v\n```\n\n### 起 \u002F 停 \u002F 看日志（本地非 Docker）\n\n```bash\n# Ctrl+C 停；删 backend\u002Fdata\u002Fapp.db 重启 → 全新数据库\ncd backend && python -m uvicorn src.app:app --port 8000\n```\n\n### 备份 \u002F 恢复\n\n```bash\n# Docker 模式：备份两个 volume 到 .\u002Fbackups\u002F\n.\u002Fscripts\u002Fbackup.sh\n# 输出：\n#   .\u002Fbackups\u002Fanykb-pg-2026-05-25-1430.tgz       (PG 全量)\n#   .\u002Fbackups\u002Fanykb-data-2026-05-25-1430.tgz     (Milvus Lite + uploads)\n\n# 恢复（停服 → 替换 volume → 起服）\ndocker compose down\ndocker run --rm -v anykb_postgres-data:\u002Fdst -v $(pwd)\u002Fbackups:\u002Fsrc alpine \\\n  tar xzf \u002Fsrc\u002Fanykb-pg-2026-05-25-1430.tgz -C \u002Fdst\ndocker run --rm -v anykb_backend-data:\u002Fdst -v $(pwd)\u002Fbackups:\u002Fsrc alpine \\\n  tar xzf \u002Fsrc\u002Fanykb-data-2026-05-25-1430.tgz -C \u002Fdst\n.\u002Fscripts\u002Fdeploy.sh\n```\n\n### 重置 DB（删数据 + 重建）\n\n```bash\ndocker compose down -v               # -v 关键：删 volume\n.\u002Fscripts\u002Fdeploy.sh                  # init_db() 自动建表 + seed TravelGPT 演示库\n```\n\n### 跑测试\n\n```bash\ncd backend\npytest                          # 11 测（test_reranker_smoke + test_milvus_smoke）\npytest -v -k milvus             # 跑 Milvus smoke\n```\n\n### 修密 \u002F 删账号 \u002F 导出对话\n\n全部在前端 Sidebar 底部 user card → 设置 modal → 账号 \u002F 数据 tab 完成（v3-M5 加的）。\n\n---\n\n## 十一、文档导航\n\n| 文档 | 用途 |\n|---|---|\n| [PROGRESS.md](PROGRESS.md) | 完整开发日志 + 30 个 milestone changelog |\n| [docs\u002Farchitecture.md](docs\u002Farchitecture.md) | 内部架构 \u002F Agent 状态图 \u002F 解耦设计 |\n| [docs\u002Fdeploy.md](docs\u002Fdeploy.md) | 详细部署（含 systemd \u002F nginx \u002F Linux 迁移） |\n| [docs\u002Frag-primer.md](docs\u002Frag-primer.md) | **入门**：RAG \u002F Embedding \u002F BM25 \u002F Hybrid \u002F Rerank 从零讲 |\n| [docs\u002Fmilvus-guide.md](docs\u002Fmilvus-guide.md) | Milvus 向量库定义 \u002F 部署形态 \u002F 集成 AnyKB |\n| [docs\u002Fcuration-sop.md](docs\u002Fcuration-sop.md) | 添加 \u002F 维护策展数据 |\n| [docs\u002Ftutorial.md](docs\u002Ftutorial.md) | 端到端流程教学（适合给团队培训用） |\n\n---\n\n## 致谢 \u002F 协议\n\n- Vibe project · MIT 协议\n- Powered by Claude \u002F DeepSeek \u002F FastAPI \u002F LangGraph \u002F Next.js \u002F Milvus \u002F SiliconFlow\n- 不隶属任何组织 \u002F 公司，独立维护\n\n如果这个项目对你有帮助，star 一下，欢迎 PR。\n","AnyKB 是一个私有 RAG（Retrieval-Augmented Generation）知识库和透明 Agent 系统，支持用户上传文档或抓取网页内容并进行高效检索。其核心功能包括多账户隔离、知识库协作与分享、以及基于混合检索技术的智能问答服务，能够生成结构化报告并展示思考链全过程。项目采用 Python 语言开发，利用 FastAPI 和 Next.js 构建前后端，并通过 Docker 化部署实现快速上线。适用于需要构建企业级内部知识管理系统或个人研究资料库的场景，特别适合对数据隐私有高要求且希望拥有透明可追溯查询过程的用户。",2,"2026-06-11 03:59:30","CREATED_QUERY"]