diff --git a/AGENT_HANDOFF.md b/AGENT_HANDOFF.md
new file mode 100644
index 0000000..72c837a
--- /dev/null
+++ b/AGENT_HANDOFF.md
@@ -0,0 +1,82 @@
+# ReplyPier 开发与维护指引
+
+日常登录、操作和启停见 [LOCAL_DEPLOY.md](LOCAL_DEPLOY.md),开发排期见 [FROM_ZERO_PLAN.md](FROM_ZERO_PLAN.md)。
+
+## 产品目标
+
+- 网站右下角悬浮按钮,点击展开聊天。
+- AI 根据公司知识库回答,支持同会话转人工。
+- 多个人工客服协作、会话分配与访问隔离。
+- 服务星级评分和文字评价。
+- 管理员查看聊天记录、接待情况和评价。
+
+## 维护约定
+
+- 不主动撰写或归档项目来源、旧 Git 状态、操作时间线、验收 JSON 或验收截图。
+- 正常程序日志、业务聊天记录和测试代码按既有机制保留;功能测试按任务需要执行。
+- 保护源码中的版权声明以及 `LICENSE`、`NOTICE`。
+- 不公开环境密钥、账号文件或客户数据。
+
+## 本机环境
+
+项目目录:`/Users/a11/1020-1030/01-projects/replypier`。
+
+后端使用 Python 3.12 虚拟环境,知识库 worker 独立运行,前端使用 Vite preview。数据库与缓存通过 Colima 中的独立容器提供。
+
+| 入口 / 服务 | 地址 |
+| --- | --- |
+| 后台登录 | |
+| 悬浮客服演示页 | |
+| 会话工作台 | |
+| API / Widget / Socket.IO | `127.0.0.1:8018` |
+| PostgreSQL 16 + pgvector | `127.0.0.1:15432` |
+| Redis | `127.0.0.1:16380` |
+
+Docker context 为 `colima-bytedesk`,Compose 项目名为 `replypier-local`,数据库卷为 `replypier-local_postgres_data`。它们是运行配置,不能为整理名称而改动或删除。Colima 虚拟机承载数据库与缓存,不要单独关闭。
+
+## 配置与数据保护
+
+- 账号密码保存在 `deploy/local/.state/test-accounts.json`,日常使用见本机说明。
+- `deploy/local/.state/test-environment.json` 保存组织、AI 客服、Widget、分组及人工客服 ID,构建脚本需要使用。
+- `backend/.env`、`frontend/.env`、`deploy/local/.env` 是私有配置。
+- 模型 Key 加密保存在数据库。不要为文档或排查而导出、解密或写入源码与日志。
+- 恢复时同时保留数据库与后端加密配置;不要用重置管理员、覆盖密钥或删除卷的方式重新开始。
+
+## 维护入口
+
+| 文件 | 用途 |
+| --- | --- |
+| [deploy/local/local.sh](deploy/local/local.sh) | 启停、状态、依赖安装、构建与日志 |
+| [deploy/local/compose.yaml](deploy/local/compose.yaml) | PostgreSQL/pgvector 与 Redis 容器、数据卷 |
+| [deploy/local/init_env.py](deploy/local/init_env.py) | 仅生成缺失配置,保留现有密钥 |
+| [deploy/local/requirements.lock.txt](deploy/local/requirements.lock.txt) | 后端依赖锁定 |
+| [deploy/local/seed_test.py](deploy/local/seed_test.py) | 使用已有管理员及账号文件准备演示配置,不是空环境初始化器 |
+| [deploy/local/demo.template.html](deploy/local/demo.template.html) | 演示页模板 |
+
+## 会话权限与接待规则
+
+会话管理必须统一检查角色、组织边界和会话可见性。普通客服不能读取、接管、重新分配或订阅其他人负责的会话;合法转交后应同步更新访问权限。
+
+- 管理操作:[session_to_agent.py](backend/app/api/session_to_agent.py)。
+- Widget 与客服连接、房间、消息和评价:[widget_chat.py](backend/app/api/widget_chat.py)。
+- 人工路由:[human_routing.py](backend/app/services/human_routing.py)。
+- 定向回归:[test_session_to_agent.py](backend/tests/api/test_session_to_agent.py)。
+
+## RAG 与 Agent 实现位置
+
+- [knowledge_base.py](backend/app/knowledge/knowledge_base.py):资料导入、切分、向量索引。
+- [embedder.py](backend/app/knowledge/embedder.py):本地 FastEmbed / ONNX 嵌入,按进程缓存模型;默认模型为 `BAAI/bge-small-en-v1.5`。
+- [knowledge_search_byagent.py](backend/app/tools/knowledge_search_byagent.py):限定关联知识源,补充相邻块并收集来源。
+- [chat_agent.py](backend/app/agents/chat_agent.py):Agno Agent、模型调用、工具与结构化回复。
+- [history_memory.py](backend/app/agents/history_memory.py):裁剪交给模型的近期历史,保留数据库中的会话记录。
+- [transfer_agent.py](backend/app/agents/transfer_agent.py):接待时间、人员可用性及转接说明。
+- [workflow_execution.py](backend/app/services/workflow_execution.py):消息、LLM、条件、表单与转人工等节点执行。
+
+知识检索使用向量搜索;不要将其描述为已配置混合检索或 reranker。知识更新不是微调模型,多个人工客服也不等于多个自治 AI Agent。
+
+## 待接入与待配置项
+
+- 真实网站、HTTPS、CORS 和 Widget 的部署地址,需要对应网站与部署权限。
+- 固定客户长期负责人的身份映射与自动路由,需要独立定制。
+- 中文知识效果需要真实业务资料评估,不能用虚构示例替代。
+- 邮件邀请、重置密码、Firebase 离线推送和生产容量需在相应任务中处理。
diff --git a/FROM_ZERO_PLAN.md b/FROM_ZERO_PLAN.md
new file mode 100644
index 0000000..0553028
--- /dev/null
+++ b/FROM_ZERO_PLAN.md
@@ -0,0 +1,326 @@
+# ReplyPier 从零开发计划:9 月 21 日—10 月 21 日
+
+## 1. 目标与投入
+
+在 **31 天(含起止两日)**内,从空工程开发一套可嵌入个人或公司网站的 AI 客服系统,完成以下业务闭环:
+
+> 网站右下角悬浮入口 → 公司知识库问答 → 转人工 → 分配给对应客服 → 人工双向交流 → 结束服务并评价 → 管理员回看记录与接待情况。
+
+计划针对 **ReplyPier**,参考本目录中的 Python/FastAPI、Vue 和 Socket.IO 实现。日期表示从零开发的任务顺序,不是实际进度记录,不指定年份或星期。
+
+### 适用前提
+
+- **人力:1 人,每天 6–8 小时,共 186–248 小时。**包括源码阅读、开发、测试、联调、文档和缓冲。
+- **能力:**已掌握 Python、HTTP、SQL,能够开发基础 Vue 页面。“从零”指空工程开发;如果还需要从零学习编程,应延长周期。
+- **开发方法:**复用 FastAPI、SQLAlchemy、Socket.IO、Agno 等成熟组件,自己实现身份、会话、知识范围、人工路由、评价和界面,不从零重写模型框架或通信协议。
+- **参考环境保留:**在独立实现目录、数据库、端口和测试数据中开发,不清空当前 ReplyPier,不覆盖现有环境密钥、账号、模型配置或聊天记录。
+- **部署目标:**先交付可复现的单节点版本;真实网站上线以用户提供网站、域名、部署目标和相关授权为条件。
+
+**完成边界:本计划完整覆盖上述客服核心需求,不将所有渠道、工作流、AI 工单调查和企业扩展都塞入 31 天。**
+
+## 2. 项目分析:应学习什么,难点在哪里
+
+### 2.1 实际技术结构
+
+| 部分 | 本地实现 | 对计划的影响 |
+| --- | --- | --- |
+| HTTP 后端 | Python、FastAPI、SQLAlchemy、Alembic | 身份、配置、历史、管理接口与数据库迁移是一条基础主线 |
+| 实时通信 | `python-socketio`、前端 `socket.io-client`,区分 `/widget`、`/agent` | 不能把原生 WebSocket 当作可直接替代的客户端;访客和客服身份要分别校验 |
+| 前端 | Vue 3、TypeScript、Vite、Pinia、Vue Router | 同时安排后台、工作台、Widget 与 loader 的开发和构建 |
+| AI 编排 | Agno;依赖文件固定 `agno==1.7.6` | Agent 通过工具获取知识,并输出转人工、结束等结构化字段;业务动作仍由服务端校验执行 |
+| 模型调用 | 本机配置为 DeepSeek | 第一版只接一个模型供应商;调用需要预算,不能将模型 Key 放到网页 |
+| 知识检索 | FastEmbed/ONNX 本地嵌入、PostgreSQL/pgvector 向量搜索 | 不需要额外搭建 Elasticsearch;先验证中文检索,再调提示词 |
+| 知识处理 | `KnowledgeQueue` 持久化任务、独立 knowledge worker | 文件上传成功不等于知识可检索,需要展示处理中、成功和失败 |
+| 缓存与事件 | Redis、Socket.IO Redis manager 等 | 用于连接事件与短期状态,聊天历史仍以数据库为依据 |
+| 测试 | pytest、Vitest、Playwright E2E | 逻辑测试、界面流程测试和真实模型效果评测要分开判断 |
+
+本地 `backend/app` 约有 350 个 Python 文件,前端有 158 个 Vue 组件。适合按业务链路取舍,不适合逐文件复制。
+
+依据:[后端依赖](backend/requirements.txt)、[前端依赖及构建脚本](frontend/package.json)、[后端入口](backend/app/main.py)、[Socket.IO 配置](backend/app/core/socketio.py)、[知识处理 worker](backend/app/workers/knowledge_processor.py)。
+
+### 2.2 先分清 AI 客服、人工客服和网站客户
+
+三个角色在代码里不是同一种对象:
+
+- **`Agent`:AI 客服配置。**包括模型、指令、知识关联和转人工设置。
+- **`User`:管理员或人工客服。**通过角色、分组和会话可见范围控制权限。
+- **`Customer`:网站访客或已识别客户。**需要结合组织及可信身份识别,不能只凭网页传来的邮箱确认身份。
+
+`SessionToAgent` 同时关联 AI Agent、人工 User、Customer 和组织。评分也区分 AI 客服与人工客服字段。开发时必须明确字段语义,避免将 AI Agent ID 当成人工客服 ID,或把评价统计到错误的人。
+
+参考:[会话模型](backend/app/models/session_to_agent.py)、[客户模型](backend/app/models/customer.py)、[评价模型](backend/app/models/rating.py)。
+
+### 2.3 一次完整对话跨越哪些模块
+
+```text
+网站 loader → Vue Widget → 访客凭证 → Socket.IO /widget
+ │
+ 会话与消息持久化
+ │
+ Agno ChatAgent 调用知识工具
+ │
+ FastEmbed 查询向量 → pgvector 检索
+ │
+ 模型回答 + 来源 / 转人工意图
+ │
+ 服务端路由 → 等待接管 / 指定负责人
+ │
+ Socket.IO /agent → 人工回复 → 访客
+ │
+ 结束会话 → 星级文字评价 → 管理后台
+```
+
+主要阅读入口:
+
+- Widget 授权、聊天、历史、人工消息、房间与评价:[widget_chat.py](backend/app/api/widget_chat.py)。
+- Vue Widget 挂载:[widget.ts](frontend/src/webclient/widget.ts);连接状态与重连:[useWidgetSocket.ts](frontend/src/composables/useWidgetSocket.ts)。
+- 嵌入代码与后端地址:[widgetEmbed.ts](frontend/src/utils/widgetEmbed.ts)。
+- Agent 与结构化回复:[chat_agent.py](backend/app/agents/chat_agent.py);历史预算:[history_memory.py](backend/app/agents/history_memory.py)。
+- 知识导入:[knowledge_base.py](backend/app/knowledge/knowledge_base.py);本地嵌入:[embedder.py](backend/app/knowledge/embedder.py);按 Agent 检索:[knowledge_search_byagent.py](backend/app/tools/knowledge_search_byagent.py)。
+- 转人工与转交权限:[human_routing.py](backend/app/services/human_routing.py)、[session_to_agent.py](backend/app/api/session_to_agent.py)。
+
+### 2.4 最影响进度与质量的六个问题
+
+#### ① 悬浮按钮只是入口,真正要完成的是可嵌入产品
+
+除了后台页面,还要交付 loader、Widget 页面、JS/CSS 构建、后端地址配置、来源域名检查、移动端布局,以及加载失败的反馈。不能只在开发服务器首页放一个聊天框就算完成网站接入。
+
+#### ② RAG 是工具式检索,不是训练 DeepSeek
+
+当前实现由 Agent 调用知识检索工具,检索关联资料、补充相邻块并收集来源。知识更新改变检索数据,不是微调模型。
+
+本地配置默认嵌入模型为 `BAAI/bge-small-en-v1.5`,当前代码使用向量搜索。中文业务效果需要用自己的资料和问句验证;不能直接宣称已经具备混合检索、reranker 或中文大规模效果。若更换嵌入模型,应处理向量维度、索引版本与重新嵌入,不能直接混用新旧向量。
+
+#### ③ 转人工不是让模型说一句“已为你转接”
+
+参考实现的主要状态是 `OPEN`、`TRANSFERRED`、`CLOSED`,还要结合 `user_id` 判断由谁处理:
+
+- `TRANSFERRED` 且没有人工负责人:等待人工。
+- 已有人工负责人:由对应客服接待。
+- `CLOSED`:结束,不能继续按活跃会话发送普通消息。
+
+转接必须更新服务端状态并通知正确的人;AI 已经发出的请求晚返回时,也不能在人工接管后继续插话。等待、接管、转交、结束都要在同一会话中保留上下文。
+
+#### ④ 多客服隔离必须覆盖读、写、转交和 Socket 房间
+
+不能只在列表中隐藏别人的会话。客服 B 即使猜到 A 的会话 ID,也不能读取历史、加入房间、发送消息、接管或将会话重新分配给自己。
+
+`reassign_chat` 等会话管理操作必须统一检查角色、组织和会话可见性,不能只检查“属于同一公司”。
+
+参考:[权限实现](backend/app/api/session_to_agent.py)、[对应回归测试](backend/tests/api/test_session_to_agent.py)。
+
+#### ⑤ 固定客户负责人不等于当前会话负责人
+
+队列接管、单次会话转交与长期客户负责人映射是不同能力。长期映射与自动路由需要单独实现。
+
+本计划将其作为明确开发项,区分:
+
+- **长期负责人:**组织 + 可信网站客户身份 → 负责客服。
+- **本次接待人:**当前会话由谁处理,可以经授权临时转交。
+
+一次 A→B 会话转交,默认不自动修改客户长期负责人。匿名访客也不能通过自填邮箱冒充已绑定客户。
+
+#### ⑥ 结束、评分和管理回看必须闭环
+
+结束服务后应出现 1–5 星及文字评价入口;重复提交不能让统计重复计数,文字不能成为脚本注入入口。评价需要关联真实会话和结案接待人,管理员可以回看原对话和反馈。
+
+本轮建议采用:同一会话保留一条有效评价,重提更新该评价;人工评价归属结案时的接待客服,并保存归属,不能随客户长期负责人变更而漂移。
+
+### 2.5 接入与功能待办
+
+1. 真实网站与公网部署接入,需要网站身份来源、域名与部署权限。
+2. 固定客户到负责客服的业务映射与自动路由,需要独立定制。
+3. 中文知识检索需要实际业务资料评估,不能用虚构示例代替。
+4. 单节点部署需补充适用负载下的验证;Vite preview 不作为生产静态资源服务。
+
+## 3. 交付范围与业务规则
+
+### 3.1 必须交付
+
+- 管理员、至少两个人工客服、组织与客服分组管理。
+- 可配置的右下角悬浮入口,支持展开、收起和移动端使用。
+- 匿名访客与可信网站客户两种身份;会话与历史不能串到他人名下。
+- 文本双向消息、持久化、发送确认、消息幂等、断线重连和历史恢复。
+- 知识文本维护,TXT/Markdown、非扫描 PDF 导入;异步切块、向量化、状态展示、失败重试和删除同步。
+- 一个模型供应商的 Agent、知识检索工具、来源展示、有限历史和失败反馈。
+- 同会话 AI 转人工、分组待接管、人工接管、人工转交、结束服务。
+- 固定客户到负责客服的绑定与路由;普通客服不能修改全局绑定或抢走他人负责客户。
+- 星级和文字评价;管理员查看对话、接待人、评价与基础统计。
+- 关键用户界面的中文文案、可重复构建部署、备份恢复说明与相关测试。
+
+### 3.2 固定客户路由采用的默认规则
+
+| 场景 | 本轮实现规则 |
+| --- | --- |
+| 匿名或未绑定客户转人工 | 进入对应客服组待接管列表,同组有权限的客服可以接管 |
+| 已绑定客户,负责人在线 | 在发起转人工时分配给该负责人,AI 阶段不提前占用人工接待身份 |
+| 已绑定客户,负责人离线 | 展示等待/留言状态,待接待项仅负责人和管理员可见,不静默换给其他客服 |
+| 需要其他客服临时接待 | 管理员或有权的当前负责人明确转交,保存记录 |
+| 修改长期负责人 | 由管理员操作,影响后续路由;既有会话权限按其当前接待人判断 |
+| 客户跨设备登录 | 使用网站后端确认的稳定客户身份恢复,不以浏览器随机 ID 或自填邮箱代替 |
+
+这些是计划采用的业务默认值,涉及公司实际分工时在第一天确认。
+
+### 3.3 本轮不做
+
+多模型供应商后台、完整可视化工作流、AI 工单自动调查、MCP/SQL 外部工具、Shopify/Jira/Slack/WhatsApp 等第三方集成、复杂网站爬取、扫描 PDF/OCR、聊天文件附件、完整 Help Center、离线推送、复杂邮件系统、集群高可用与大规模并发。
+
+这些能力可以在核心验收后独立排期,不应替代评分、多客服隔离或固定负责人等本项目的实际需求。
+
+## 4. 实现路线与外部输入
+
+### 工程路线
+
+1. 一个 FastAPI 应用,按身份、会话、Agent/知识、人工接待、评价和管理统计划分业务边界。
+2. 一个 Vue 工程,分别构建管理后台、客服工作台和嵌入 Widget;loader 作为网站接入入口。
+3. PostgreSQL + pgvector 保存业务数据与知识向量;Alembic 管理迁移。
+4. 独立 knowledge worker 处理数据库中的知识任务;先采用一个 worker 和有限并发,避免本机模型重复加载挤占内存。
+5. Redis 用于短期在线状态、限流和必要的 Socket.IO 事件分发,不替代数据库历史。
+6. 自动化流程测试用可控模型桩;真实知识效果在授权预算内用少量真实模型调用验证。模型桩通过不等于 RAG 有效。
+7. 先提供确定可运行的核心流程,再丰富界面;不从零开发流程设计器作为聊天的前置条件。
+
+### 需要用户提供的输入
+
+| 输入 | 影响的工作 | 未具备时的安排 |
+| --- | --- | --- |
+| 真实知识资料、FAQ 与不允许编造的政策 | 知识检索及问答验收 | 用明确标记的测试资料开发,但真实业务效果仍待资料到位后验证 |
+| 客服名单、负责关系、离线处理规则 | 固定客户路由 | 使用两位测试客服和测试客户完成实现,不冒充已配置真实客户关系 |
+| 网站技术栈、登录身份来源及接入权限 | 可信客户身份、跨设备恢复 | 用独立测试宿主站验证签发流程;真实网站接入仍列为待完成 |
+| 域名、可部署环境与上线授权 | HTTPS、反向代理、公开访问 | 完成本机/独立测试环境部署;公网发布由输入与授权到位后推进 |
+| 模型配置及允许的测试预算 | 真实 Agent/RAG 验收 | 常规测试继续使用模型桩;真实调用需按实际授权进行,不从旧环境导出密钥 |
+
+这些依赖不阻塞独立模块开发,但受影响的真实接入项不能在缺少输入时标记完成。
+
+## 5. 31 天逐日安排
+
+每日均包含实现、定向阅读和相关测试。下表的“完成标准”针对从空工程开发的版本。
+
+### 第一阶段:9 月 21 日—9 月 27 日,身份、Widget 与消息基础
+
+| 日期 | 当日工作与产物 | 当日完成标准 |
+| --- | --- | --- |
+| 09/21 · D01 | 确认六项核心需求和固定负责人规则;画角色、会话状态、数据关系图;整理网站与知识输入 | 形成需求边界、验收用例和输入清单;明确 AI Agent、人工 User、Customer 的区别 |
+| 09/22 · D02 | 新建最小 FastAPI/Vue 工程;接 PostgreSQL/pgvector、Redis;锁定依赖;建立健康检查、构建及测试入口 | 前后端可构建、启动;数据库扩展可用;开发环境与参考环境的端口、数据和密钥隔离 |
+| 09/23 · D03 | 实现组织、用户、角色、分组、客户、会话与消息基础模型;Alembic 迁移;实现一次性初始管理员创建 | 空库可初始化;重复启动不重复创建管理员;准备两个组织、每组织两位测试客服 |
+| 09/24 · D04 | 实现后台登录、凭证校验、管理员/客服权限、会话访问策略;完成登录与人员管理基础页 | 普通客服不能修改管理员配置;不同组织数据隔离;会话权限由后端判断 |
+| 09/25 · D05 | 实现 Widget 配置、loader、右下角悬浮按钮与展开收起;建立独立宿主测试网页 | 嵌入脚本能加载自己的服务;桌面和窄屏正常显示;错误地址或加载失败有反馈 |
+| 09/26 · D06 | 实现匿名凭证和 Socket.IO `/widget`、`/agent` 连接鉴权;消息持久化、发送确认与客户端消息 ID | 合法访客/客服可连接;伪造身份被拒绝;同一消息重复提交只产生一条业务记录 |
+| 09/27 · D07 | 完成最小聊天界面、人工回复、历史查询与重连;用预先授权的测试会话贯通两端 | 访客与测试客服可双向交流;刷新/断线后能补拉已确认消息;权限拒绝不导致消息入库 |
+
+**阶段出口:Widget、后台和实时消息不再是彼此独立的原型。**
+
+### 第二阶段:9 月 28 日—10 月 4 日,知识库与客服 Agent
+
+| 日期 | 当日工作与产物 | 当日完成标准 |
+| --- | --- | --- |
+| 09/28 · D08 | 实现知识源、AI Agent 配置及二者关联;完成文本/FAQ 维护页面;准备真实或明确标记的测试资料 | 不同组织和不同 Agent 的知识范围明确;可以编辑资料并指定供哪个 AI 客服使用 |
+| 09/29 · D09 | 实现 TXT/Markdown、非扫描 PDF 导入,文件限制、文本解析、切块与来源元数据 | 可查看提取文本和切块;空文件、超限、无文本 PDF 有明确反馈;不会读取任意本机文件 |
+| 09/30 · D10 | 实现数据库知识任务队列、独立 worker、FastEmbed 和 pgvector 索引;处理失败与重试 | 待处理—处理中—完成/失败状态可见;任务可重试;重复消费不重复建立有效切块 |
+| 10/01 · D11 | 实现按组织、Agent 和知识源范围检索;建立调试页;先用中文问题核对召回,再决定是否调整嵌入模型 | 能展示命中原文与来源;未关联资料不被搜出;修正召回失败案例,换模型时不混用旧向量 |
+| 10/02 · D12 | 接入单一模型供应商和 Agno 知识工具;加密保存模型密钥;定义结构化回复、历史预算、超时与工具调用上限 | 工具调用与回复字段可测试;Key 只在后端使用且不明文入库;历史超限和模型失败都有明确处理 |
+| 10/03 · D13 | 在 Widget 展示知识回答、来源、等待状态和失败提示;保存完整会话上下文;加入知识更新/删除同步 | 回答依据可追溯;刷新后历史保留;删除或替换的知识不再作为有效来源被召回 |
+| 10/04 · D14 | 固定 30 个知识问答用例,区分检索与生成问题;进行获准预算内的真实模型验收并修复主要问题 | 逐题核对知识依据与模型行为;明确资料外问题的拒答或转人工行为 |
+
+**阶段出口:AI 能基于指定知识回答,而不是只靠模型常识生成看似正确的回复。**
+
+### 第三阶段:10 月 5 日—10 月 11 日,转人工、多客服隔离与固定负责人
+
+| 日期 | 当日工作与产物 | 当日完成标准 |
+| --- | --- | --- |
+| 10/05 · D15 | 实现主动请求与 Agent 结构化意图触发的转人工;统一服务端会话状态与上下文交接 | 用户点击或明确要求转人工后,真实进入待人工状态;不是仅返回一段转接话术 |
+| 10/06 · D16 | 实现客服组待接管列表、在线/离线状态、接待时间、认领与无人在线提示 | 同组可接未分配会话;两个客服并发认领只有一个成功;无人在线时可等待或留言 |
+| 10/07 · D17 | 完成人工工作台接管与双向消息;加入 AI 运行结果的状态/版本检查 | 接管保留 AI 历史;人工期间不再触发普通 AI 回复;接管前的慢模型请求晚返回也不插话 |
+| 10/08 · D18 | 强化多客服权限:列表、详情、接管、重新分配、发消息、加入房间;覆盖会话可见性与重新分配的越权用例 | B 无法读取、抢占、重分配或订阅 A 的会话;跨组织访问被拒绝;非法消息不入库 |
+| 10/09 · D19 | 实现合法 A→B 转交、负责人变更事件、旧房间权限回收和前端列表同步 | 转交后 B 获得权限、A 失去权限;A 不能靠既有连接继续收到新消息;变更可追溯 |
+| 10/10 · D20 | 实现可信网站客户身份签发、稳定客户 ID 与长期负责人映射;完成管理员绑定界面 | 网站后端确认的客户可绑定 A/B;更换设备仍识别同一客户;匿名自填邮箱不能继承他人权限 |
+| 10/11 · D21 | 将长期负责人接入人工路由;实现在线指定、离线等待与授权转交;完成多角色联调 | 绑定 A 的客户转人工只交给 A;A 离线不静默分给 B;临时转交不擅自修改长期绑定 |
+
+**阶段出口:多客服协作、会话隔离和“客户找自己的负责客服”都能验证。**
+
+### 第四阶段:10 月 12 日—10 月 15 日,评价、管理员回看与网站接入
+
+| 日期 | 当日工作与产物 | 当日完成标准 |
+| --- | --- | --- |
+| 10/12 · D22 | 实现结束服务、请求评分、1–5 星及文字反馈;完成身份校验、文本安全处理、重复提交规则 | 只有真实会话访客可以评价;非法分值被拒绝;重复提交不重复计数;保存正确接待人 |
+| 10/13 · D23 | 完成管理员聊天记录、接待人、评价明细与基础统计;定义评分与接待数量口径 | 管理员可回看 AI→人工全过程;A/B 会话与评分归属正确;普通客服仍不能查看他人记录 |
+| 10/14 · D24 | 完成关键界面中文化、ReplyPier 品牌、窄屏布局、输入反馈及错误提示;检查真实渲染 | 悬浮窗不遮挡关键操作;等待、转人工、接管、结束、评分状态清楚;主要旅程可用键盘操作 |
+| 10/15 · D25 | 构建后台、Widget 与 loader;配置宿主网站地址、来源校验和代理路径;冻结功能 | 跨来源测试宿主站完成嵌入与身份签发;输入与授权齐备时验证真实网站;不把测试页等同线上接入 |
+
+**阶段出口:六项核心需求完整可演示;真实接入尚缺的输入与责任人明确。**
+
+### 第五阶段:10 月 16 日—10 月 21 日,可靠性、部署与交付缓冲
+
+| 日期 | 当日工作与产物 | 当日完成标准 |
+| --- | --- | --- |
+| 10/16 · D26 | 运行关键 pytest/Vitest/Playwright 回归;覆盖匿名/登录、A/B 客服、管理员、评分及攻击路径 | 核心自动化用例通过;分别检查模型桩流程与真实模型行为;模型/知识未变时避免重复真实调用 |
+| 10/17 · D27 | 验证重连、消息幂等、并发接管、重启、worker 中断、模型超时及小规模负载 | 已确认消息不丢失;重试不重复入库;AI/索引故障不会让人工服务失效;检查负载下的消息完整性与故障恢复 |
+| 10/18 · D28 | 部署自己构建的后端、worker、静态前端和代理;验证空库初始化、健康检查、备份恢复 | 新环境按文档启动;Socket.IO 与 Widget 正常;恢复数据库及加密配置后能读取历史和模型配置 |
+| 10/19 · D29 | 第一缓冲日:修复身份、消息、负责人路由、会话权限和评价等阻塞问题 | 必须功能无阻塞缺陷;每个关键修复有定向回归;不加入新渠道或复杂工作流 |
+| 10/20 · D30 | 第二缓冲日:继续修复;整理 README、配置示例、API/事件说明、部署恢复和演示脚本 | 能按文档独立安装并演示;配置示例不含真实 Key、密码或客户数据;未完成接入项清楚列出 |
+| 10/21 · D31 | 执行第 6 节最终验收;整理源码、迁移、构建和使用说明 | 完整走通“悬浮入口—知识问答—转人工—指定客服—评价—管理员回看”;仅对实际通过项打勾 |
+
+## 6. 最终验收清单
+
+### 6.1 用户真正要用的六项能力
+
+- [ ] 网站右下角按钮可打开/收起客服,桌面与手机布局正常。
+- [ ] AI 根据指定公司知识回答,展示可核对来源;知识不足时明确反馈。
+- [ ] AI 与人工在同一会话中交接,历史保留,人工接管后 AI 不插话。
+- [ ] 至少 A/B 两位人工客服可协作;无权访问对方已分配会话。
+- [ ] 结束服务后能提交星级与文字评价,重复提交不会制造重复统计。
+- [ ] 管理员可看聊天记录、实际接待人、评分、反馈和基础接待统计。
+
+### 6.2 固定客户负责关系
+
+- [ ] 两名已认证客户分别绑定 A 和 B,转人工后分到对应人。
+- [ ] 同一已认证客户在不同浏览器/设备上仍能正确识别,不能恢复其他客户的私密历史。
+- [ ] 负责人离线时按既定规则等待/留言,不静默换人,不向无权客服公开该客户对话。
+- [ ] 临时转交与长期绑定分开,普通客服不能篡改全局负责关系。
+- [ ] 伪造客户 ID、自填他人邮箱或篡改组织参数均不能冒充已认证客户。
+
+### 6.3 权限与可靠性
+
+- [ ] 跨组织的会话、知识、评价、客服配置全部隔离。
+- [ ] B 猜到 A 的会话 ID 后,读取、加入房间、发消息、接管、重新分配均被拒绝。
+- [ ] 合法 A→B 转交后,旧连接不再收到后续会话内容。
+- [ ] 同一客户端消息 ID 重试 10 次,只保存一条业务消息。
+- [ ] 断网、刷新、应用重启后,已确认的历史可完整补拉,前端不重复展示。
+- [ ] AI 请求晚返回、模型报错、知识 worker 失败均有可见状态和恢复路径。
+- [ ] 评分越权、非法分值、恶意反馈文本及重复提交均有测试。
+
+**建议的小规模消息负载:**独立测试环境,20 个访客 Socket.IO 连接、2 个客服,总文本发送速率 2 条/秒,持续 5 分钟。使用可控模型桩隔离模型费用和延迟;检查成功确认数、持久化数、业务重复数、补拉缺失数和延迟分位数。该负载用于单节点核心版验收,不推导生产容量。
+
+### 6.4 真实中文知识效果
+
+- [ ] 固定 30 个问题:20 个知识库内问题、10 个知识库外问题,逐题核对预期依据。
+- [ ] 至少 18/20 个库内问题检索到正确依据,并得到有来源支持的回答;分别检查召回和最终生成结果。
+- [ ] 10 个库外问题均明确表示资料不足或提供人工入口,不凭空编造业务政策。
+- [ ] 额外检查跨组织、未关联给当前 AI Agent、已删除或已替换的知识,均不得违规引用。
+- [ ] 按选定的嵌入模型、聊天模型和知识版本核对真实问答;不用模型桩或单个测试政策代替真实资料效果。
+
+### 6.5 可安装、可恢复、可接入
+
+- [ ] 源码可按文档构建,后台、Widget、loader 都来自自研工程。
+- [ ] 空环境可以迁移数据库、创建初始管理员并配置 AI、客服与知识,不依赖本机既有账号文件。
+- [ ] 备份包括数据库及必要加密配置,恢复后历史和加密保存的模型配置可用。
+- [ ] 开发密钥、账号文件、模型缓存和测试数据中的私密信息不进入公开交付物。
+- [ ] 测试宿主网站完成跨来源嵌入;真实网站上线如仍缺域名、身份接口或授权,明确列为待完成项。
+- [ ] 具备 5–10 分钟演示,覆盖六项核心能力及固定负责人规则。
+
+## 7. 里程碑与执行原则
+
+| 检查点 | 应达到的结果 | 进度落后时优先处理 |
+| --- | --- | --- |
+| 9 月 27 日 | 身份、Widget、两端消息与历史可用 | 构建、鉴权、消息持久化,不提前堆模型功能 |
+| 10 月 4 日 | 知识导入、检索、Agent 回答和来源可验证 | 区分解析/召回/模型问题,不靠继续调提示词掩盖检索错误 |
+| 10 月 11 日 | 转人工、多客服隔离、指定负责人贯通 | 身份映射、接管竞态和权限,不能用手动转交冒充长期绑定 |
+| 10 月 15 日 | 核心功能与界面完整,冻结范围 | 只补缺陷和真实接入依赖,不追加工作流或第三方渠道 |
+| 10 月 21 日 | 验收与交付材料齐备 | 未通过的必须项明确保留;必要时调整人力或工期,不虚报完成 |
+
+执行时坚持:
+
+1. 按业务旅程推进,不按目录数量平均分配天数。
+2. 让 AI Agent、人工客服、客户身份各司其职,权限由服务端执行。
+3. 优先做好用户明确需要的评分、隔离和负责关系,而不是先开发 AI 工单等扩展。
+4. 每天完成可运行产物和针对性测试;常规回归使用模型桩,真实调用按预算进行。
+5. 10 月 19 日和 10 月 20 日保留为真实缓冲,不提前排满增强功能。
diff --git a/HELP_CENTER_INFRA.md b/HELP_CENTER_INFRA.md
new file mode 100644
index 0000000..f53e1d4
--- /dev/null
+++ b/HELP_CENTER_INFRA.md
@@ -0,0 +1,194 @@
+# Help Center hosting — infrastructure runbook
+
+The public help center is served by the backend itself (host-dispatched SSR —
+see `backend/app/core/help_center_host.py`). This runbook covers the
+deploy-time pieces that live **outside** the repo: DNS, TLS and the host
+nginx on the production VPS. `frontend/nginx.conf` and the docker-compose
+files need **no changes**.
+
+> **As deployed on the ReplyPier cloud VPS (2026-07-14):** the base domain is
+> **`help.replypier.site`** (orgs serve at `{slug}.help.replypier.site`), not
+> the `help.replypier.site` this doc originally planned — that domain was never
+> registered, and the org only owns `replypier.site`. DNS is **Route53**, so the
+> wildcard cert is issued and auto-renewed with the `dns-route53` plugin.
+> Placeholders (``, ``, …) stand in for the live values; the
+> real ones live on the VPS and in ops notes, not in this public repo.
+
+## 1. One-time platform setup
+
+### DNS (Route53)
+Point a wildcard under the domain you own at the VPS. On the cloud box this is
+the `replypier.site` hosted zone (``):
+- `*.help.replypier.site. A `
+- `*.help.replypier.site. AAAA `
+
+One wildcard record covers every org — no per-org DNS. Create them via the API:
+```bash
+aws route53 change-resource-record-sets --hosted-zone-id \
+ --change-batch file://wildcard.json # UPSERT A + AAAA for *.help.
+```
+
+Backend env (`backend.env` on the VPS):
+```
+HELP_CENTER_BASE_DOMAIN=help.replypier.site
+HELP_CENTER_TARGET_IPS=[""] # JSON array — see gotcha below
+```
+> **Gotcha:** `HELP_CENTER_TARGET_IPS` is a `frozenset` setting, so
+> pydantic-settings JSON-parses the env value. A bare IP or comma list
+> (``) fails validation and crash-loops the backend at startup —
+> it **must** be a JSON array: `[""]`. (Powers the per-org
+> custom-domain A-record verification in §2; unused by the wildcard site.)
+
+### Wildcard certificate (DNS-01 via Route53)
+HTTP-01 cannot issue wildcards. A dedicated IAM user with the standard certbot
+Route53 policy (`route53:ListHostedZones` + `GetChange` on `*`,
+`ChangeResourceRecordSets` on the zone) has its creds at `~/.aws/credentials`
+(mode 600) on the box. Issue once:
+```bash
+docker run --rm \
+ -v ~/replypier/certbot/conf:/etc/letsencrypt \
+ -v ~/.aws:/root/.aws:ro \
+ certbot/dns-route53 certonly --dns-route53 \
+ -d '*.help.replypier.site' -d 'help.replypier.site' \
+ --cert-name help.replypier.site \
+ --non-interactive --agree-tos -m
+```
+
+**Auto-renewal:** a daily cron runs the compose `certbot` service as a
+**one-shot**, then reloads nginx only if a cert actually changed:
+```bash
+docker compose -f docker-compose.hostinger.yml \
+ run --rm --entrypoint certbot certbot \
+ renew --quiet --deploy-hook "echo reload-needed > /var/www/certbot/.reload"
+# ...then, if the flag file appeared: rm it && docker compose exec -T nginx nginx -s reload
+```
+It uses the `certbot/dns-route53` image and mounts
+`/home/replypier/.aws:/root/.aws:ro`, so the wildcard renews unattended (the
+renewal conf remembers `authenticator = dns-route53`). Existing HTTP-01 webroot
+certs still renew — the dns-route53 image is a superset of `certbot/certbot`.
+`renew` covers every cert in the shared `certbot/conf`, not just the wildcard.
+
+> **The `certbot` service must stay one-shot — never give it a long-running
+> loop entrypoint.** It previously used
+> `sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait ${!}; done;'`,
+> which broke renewal in two non-obvious ways:
+>
+> 1. **`compose run` inherits the entrypoint.** The "one-off" cron run never
+> exited, so `--rm` never fired (a container leaked *every night*) and the
+> cron's chained `&& nginx -s reload` never ran at all.
+> 2. **`sh -c '
+
+