一个多人格(multi-persona)的 AI 聊天智能体。每个人格(法律专家 / 情感大师 / 职业 prompt 工程师,……)都可以基于自己的 RAG 知识库回答问题,支持流式输出与引用来源。人格可直接在网页中创建、编辑、复制、归档与恢复,无需修改代码或重启服务。
RAG 对话工作流使用 LangGraph,HTTP 层使用 FastAPI,业务数据使用 SQLite,向量库使用 Qdrant,前端是 React + Vite。API 配置(对话模型 / 向量模型的 Base URL、密钥、模型名)可在 Web 页面运行时自定义,无需改代码或重启。
当前版本面向本地 / 小团队 MVP,尚未提供登录与权限隔离,请勿直接暴露到公网。业务数据使用 SQLite(可迁移到 PostgreSQL),向量数据使用 Qdrant。详见架构说明。
- 多身份 RAG:每个身份一个私有知识库 + 全员共享的通用知识库,检索时用加权 RRF(Reciprocal Rank Fusion) 融合两路结果。
- 可靠流式对话:SSE 逐 token 输出,支持停止、幂等 request_id、持久化部分回答与逐消息引用来源。
- API 配置可视化:内置「API 配置」页面,可填入任意 OpenAI 兼容服务(DeepSeek / Moonshot / OpenAI……)或 Anthropic,以及向量模型(本地 HuggingFace / OpenAI 兼容),并带「测试连接」按钮。
- 知识库管理:支持 CLI 批量导入 + Web 后台上传、失败重试、删除与非破坏式索引重建。
- 网页人格管理:名称、头像、system prompt、模型覆盖和检索参数持久化到 SQLite;
identities.yaml仅用于首次初始化默认人格。 - 独立会话空间:每个会话固定绑定一个人格,切换人格时恢复该人格最近使用的会话,避免上下文与私有知识库混用。
- ChatGPT 风格网页:可折叠会话侧边栏、移动端抽屉、会话自动命名/重命名/删除,以及位于聊天页顶部的人格选择器。
- LangGraph 工作流:
StateGraph显式编排人格加载、历史加载、知识检索和提示构建,服务层负责模型流式输出与消息持久化。
| 层 | 技术 |
|---|---|
| 后端 | Python 3.10+ · FastAPI · pydantic-settings · sse-starlette |
| AI 编排 | LangChain 1.x(langchain-openai / langchain-anthropic / langchain-qdrant / langchain-huggingface) |
| 向量库 | Qdrant(docker-compose,或 :memory: 用于测试) |
| 向量模型 | HuggingFace 本地(默认 BAAI/bge-small-zh-v1.5,512 维,免费无 key)/ OpenAI 兼容 |
| 对话模型 | OpenAI 兼容(默认 DeepSeek)/ Anthropic |
| 前端 | React 18 · TypeScript · Vite · @microsoft/fetch-event-source(POST + SSE) |
ChatMaster/
├── docker-compose.yml # Qdrant (6333 REST / 6334 gRPC)
├── .env.example
├── docs/architecture.md # 架构、持久化和数据流说明
├── backend/
│ ├── pyproject.toml
│ ├── .env # 本地配置(已 gitignore,不会上传)
│ ├── data/
│ │ └── sample_docs/ # 示例文档(每个人格一个子目录 + common/)
│ └── chatmaster/
│ ├── main.py # FastAPI app、迁移、种子与任务恢复
│ ├── config.py # 环境变量读取
│ ├── chat/ # LangGraph 状态图、SSE 与持久化
│ ├── identities/ # 人格 CRUD、归档/恢复、YAML 初始种子
│ ├── conversations/ # 严格按人格隔离的会话服务
│ ├── documents/ # 上传、后台导入任务和文档生命周期
│ ├── retrieval/ # Qdrant 索引、检索和加权 RRF
│ ├── providers/ # API 配置、Fernet 加密和地址校验
│ ├── ai/ # 模型、Embedding、加载器与提示词
│ ├── db/ # SQLAlchemy 模型、会话与种子
│ ├── routers/ # FastAPI 路由
│ └── cli/ # 导入、重建、清理和旧配置迁移命令
└── frontend/
├── package.json
├── vite.config.ts # 代理 /api → localhost:8000
└── src/
├── App.tsx # /chat、/knowledge、/settings 路由壳
├── api/ # HTTP、SSE 与 Query Client
├── features/chat/ # 消息、输入框、引用与聊天状态
├── features/knowledge/ # 上传、筛选、状态与向量删除
├── hooks/ # 人格、会话、上传与 Provider 状态
├── components/ # 侧边栏、人格管理、API 配置等
└── types/api.ts
需要 Python 3.10+、Node.js 18+、npm,以及可运行 Qdrant 的 Docker。推荐使用独立 Python 环境:
conda activate chatmaster
# 若无环境:conda create -n chatmaster python=3.12 -ydocker compose up -d qdrant
# 验证:curl http://localhost:6333/healthz → healthz check passedcp .env.example backend/.env
cd backend
pip install -e ".[dev]"PowerShell 可用 Copy-Item .env.example backend/.env 代替 cp。
编辑 backend/.env,至少填入一个对话模型的 API key,并生成一个用于加密网页保存凭据的 Fernet 密钥:
python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"DeepSeek 示例:
OPENAI_API_KEY=sk-你的key
OPENAI_BASE_URL=https://api.deepseek.com/v1
DEFAULT_GENERATION_MODEL=deepseek-v4-pro
DEFAULT_EMBEDDING_PROVIDER=huggingface
DEFAULT_EMBEDDING_MODEL=BAAI/bge-small-zh-v1.5
HUGGINGFACE_ENDPOINT=https://hf-mirror.com
PROVIDER_ENCRYPTION_KEY=粘贴上一步生成的Fernet密钥
.env是首次种子;在「API 配置」页面保存后写入 SQLiteprovider_configs,之后以数据库为准。请长期保管同一个PROVIDER_ENCRYPTION_KEY,更换或丢失后将无法解密已经保存的 API key。
uvicorn chatmaster.main:app --reload --host 127.0.0.1 --port 8000首次启动只执行数据库迁移、种子数据和身份配置校验。Embedding 模型与 Qdrant 会在首次导入或聊天时按需初始化,因此外部服务暂不可用时仍可进入 API 配置页。
python scripts/seed_sample_docs.py
# 或手动:python -m chatmaster.cli.ingest -i legal_expert -p ./data/sample_docs/legal_expertCLI 与网页上传均支持 .txt、.md、.pdf 和 .docx。原文件及任务状态写入本地持久化层,在「知识库」页可以按范围/状态筛选、重试失败任务,或通过行内确认删除文档及其向量。
cd ../frontend
npm install
npm run dev # http://localhost:5173打开页面 → 在顶部选择人格 → 在侧边栏点「API 配置」填好 base_url / key / 模型 → 保存(可点「测试连接」)→ 回到对话即可聊天。需要新增或修改人格时,使用侧边栏底部的「人格管理」。
curl -N -X POST http://localhost:8000/api/chat \
-H 'Content-Type: application/json' \
-d '{"request_id":"00000000-0000-4000-8000-000000000001","identity_id":"legal_expert","message":"你好,介绍一下你自己"}'SSE 事件流:sources → 多个 token → done。停止生成可调用
POST /api/chat/{request_id}/cancel;done.status 为 complete 或 stopped。
「API 配置」页面(GET/PUT /api/providers)让你不改代码、不重启即可切换模型与凭据:
- 对话模型:提供商(OpenAI 兼容 / Anthropic)、Base URL、API Key、模型名。
- 向量模型:提供商(HuggingFace 本地 / OpenAI 兼容)、Base URL、API Key、模型名、HF 镜像。
- 保存即生效:会清空已缓存的模型/向量/store 实例,下次请求用新配置重建。
- 安全:读取时 API key 脱敏(
sk-****fade);提交空值或脱敏值时保留原 key,不会被覆盖。 - 安全:凭据仅加密写入 SQLite;旧版
providers.json不再被读取。需要迁移时执行chatmaster-import-legacy-providers --path data/providers.json --confirm,确认后再手动删除明文文件。 - 地址限制:仅支持 HTTP(S);本机回环模型服务可直接使用,局域网地址需显式设置
ALLOW_PRIVATE_PROVIDER_URLS=true。此开关会放宽内网访问限制,只应在可信本地环境启用。 - 测试连接:
POST /api/providers/test分别试调对话模型与向量模型,失败详情仅写入服务端日志。
注意:切换向量模型会导致维度变化,已有知识库必须重建索引。
切换 embedding provider 或模型后,请使用非破坏式重建命令创建新索引版本;它会在全部文档重嵌入成功后才切换,旧版本会保留:
chatmaster-rebuild-index --identity legal_expert --confirm
# 重建共享知识库:chatmaster-rebuild-index --common --confirm
# 预览过期/失败索引:chatmaster-cleanup-indexes
# 确认删除:chatmaster-cleanup-indexes --confirm- 打开网页侧边栏底部的「人格管理」。
- 点击「创建新人格」,填写名称、简介、System Prompt;头像为可选项。
- 如有需要,展开高级设置配置聊天模型和私有/公共知识库检索权重。
- 保存后立即出现在聊天页顶部的人格选择器中,无需重启。
人格采用归档而非物理删除:归档后会话和私有知识库保持不变,恢复后可继续使用。系统级「通用助手」始终可用,只检索公共知识库;因此用户创建的人格可以全部归档。
- 对话模型:在「API 配置」页面改;或给单个身份在 YAML 里设
generation_model覆盖模型名。 - 向量模型:在「API 配置」页面改(切维度需重新导入)。
- 换向量库:只有
ai/vectorstore.py封装了QdrantVectorStore,换个 LangChainVectorStore实现即可。
chatmaster/chat/graph.py 中的 StateGraph 依次执行:
load_identity → load_history → retrieve_context → build_messages
随后 chatmaster/chat/service.py 流式调用模型、发送 SSE 事件,并在会话存在时持久化用户消息、部分/完整回答和引用来源。
| Method | Path | 说明 |
|---|---|---|
| GET | /api/health |
健康检查:当前 provider 配置、集合、身份 |
| GET | /api/identities |
身份列表(不含 system prompt) |
| GET | /api/identities/{id} |
单个身份详情 |
| POST | /api/identities |
创建人格 |
| PUT | /api/identities/{id} |
修改人格 |
| POST | /api/identities/{id}/archive |
归档人格 |
| POST | /api/identities/{id}/restore |
恢复人格 |
| POST | /api/identities/{id}/duplicate |
复制人格 |
| POST | /api/chat |
SSE 流式对话 |
| POST | /api/conversations |
创建会话 |
| GET | /api/conversations |
会话列表 |
| GET | /api/conversations/{id}/messages |
会话消息 |
| PUT | /api/conversations/{id} |
重命名会话 |
| DELETE | /api/conversations/{id} |
删除会话 |
| GET | /api/documents |
文档列表(支持范围/状态筛选) |
| GET | /api/documents/{id} |
文档详情 |
| POST | /api/documents/ingest |
提交后台文档导入任务(202) |
| GET | /api/ingest-jobs |
导入任务列表 |
| GET | /api/ingest-jobs/{id} |
导入任务详情 |
| POST | /api/ingest-jobs/{id}/retry |
重试失败导入 |
| DELETE | /api/documents/{id} |
删除文件、分块与向量 |
| GET | /api/indexes |
索引版本和新鲜度 |
| POST | /api/indexes/rebuild |
后台重建索引 |
| GET | /api/providers |
读取 API 配置(key 脱敏) |
| PUT | /api/providers |
保存 API 配置 |
| POST | /api/providers/test |
测试对话 + 向量连接 |
- 运行后端测试:在
backend/执行pytest,静态检查执行ruff check .。 - 运行前端检查:在
frontend/执行npm test和npm run build。 - 本地安全边界:当前
LOCAL_WORKSPACE_ID/LOCAL_USER_ID是未来认证的替换点,不是登录系统;只应在受信网络中运行。 - Qdrant 版本警告:客户端版本与 server 版本不一致会打印
UserWarning,功能不受影响。如需消除,把docker-compose.yml的 Qdrant 镜像升到与qdrant-clientminor 相近的版本。 - 代理 / TUN 的 Fake IP:如果公共 Provider 域名被代理解析到保留或私有地址,后端的出站地址校验会拒绝请求。优先让该域名走真实 DNS;仅在可信本地环境下考虑启用
ALLOW_PRIVATE_PROVIDER_URLS=true。 - Starlette 路由校验:
include_router产生的是惰性_IncludedRouter,不要靠遍历app.routes校验路由是否注册——直接用 httpxASGITransport打请求验证。 - Windows npm 缓存权限:若全局缓存目录 EPERM,用
npm install --cache /tmp/npm-cache。 - HuggingFace 镜像:必须在导入任何
huggingface_hub相关库之前设HF_ENDPOINT,否则镜像不生效(已在main.py/cli/ingest.py顶部默认设为https://hf-mirror.com)。
dev 附加项(pip install -e ".[dev]"):pytest、pytest-asyncio、httpx、ruff。
MIT