Files
ai-kefu-admin/CLAUDE.md
T
浅念 6af8c186d8 fix: Phase3 终审修复(env入库/通用配置组面板/锁屏通知摘除/记档修正)
- I-2: admin/.env(被根 .gitignore 误伤)删除,内容并入入库的
  .env.development/.env.production(公共块两份都写,PORT 仅 dev)
- I-1: 配置中心补 contact/engine/misc 三组通用 JSON 编辑面板
  (JsonGroupPane:等宽 textarea 整组编辑 + JSON.parse 预检 +
  PUT 整组 + 重置出厂 + 顶部字段说明;挂进 GROUP_NAV 三入口);
  修组键缺失租户的禁存 bug(父层快照加载后挂载,空组可建组)
- I-3: config_routes.py docstring 改「接线归 Phase 4」;
  CLAUDE.md Phase 4 清理单补 resolver 回退链与 email/push test 限流
- M-1: 锁屏摘除入口(用户菜单项删 + 全局组件 enabled:false)
- M-2: 顶栏通知铃铛隐藏(headerBar notification.enabled:false)
- M-5/M-8: admin/README 换三行指针(删模板 README.zh-CN.md);
  quickLinks 删「登录」项
- M-3/M-4 不做记档入 Phase 4 清理单
- 测试基线:后端 pytest 595 / 前端 vitest 156(+3 通用 JSON 组)

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-14 00:05:40 +08:00

20 KiB
Raw Blame History

CLAUDE.md — all-kefu 工作约定

项目定位

本项目 all-kefu 是统一多租户 AI 客服平台:一套引擎 + 租户配置包,消除"每客户复制一套代码"的旧模式。项目由 ai-kefu / ai-kefu-jc 两个复制分叉项目合并而来,jc 为主线(代码以 ai-kefu-jc 为基础迁移,再做租户化改造)。

硬约束

  • uvicorn 必须以 --workers 1 运行。内存中的 token 缓存、限流状态、配置缓存、每租户 engine 缓存均假设单进程,多 worker 会导致状态割裂。任何启动脚本/部署配置不得改动此项。
  • Windows 环境:跑 python / pytest 前设置 PYTHONUTF8=1(GBK 控制台遇 emoji / 中文 payload 会直接崩,血泪教训)。

本地开发访问方式

  • 路由按 Host 解析租户:/ 与 /static 也需要已知租户域名(未知 Host 一律 404,唯一豁免 /health,供部署健康检查 curl 用)。
  • 本地访问二选一:改 hosts 把 chat.agent.hnxinyouhe.com / chat2.agent.hnxinyouhe.com 指到 127.0.0.1;或 curl 显式带 -H "Host: chat2.agent.hnxinyouhe.com"。
  • uvicorn 起在 8018:PYTHONUTF8=1 .venv/Scripts/python -m uvicorn app.main:app --port 8018 --workers 1。

开发流程

  • 改动 seed_profiles 后必须跑全量 pytest(出厂配置影响面大)。
  • 全量回归命令:PYTHONUTF8=1 .venv/Scripts/python -m pytest -q(根目录与 tests/ 子目录均可跑,pytest.ini 已锚定根路径)。

Phase 1 完成状态(2026-10-12 收尾核对)

测试基线:459 passed(根目录与 tests/ 子目录各跑一遍,数字一致;唯一 warning 为 starlette TestClient 对 httpx 的弃用提示,无害)。此为 Phase 1 时点数字;现行回归基线以 Phase 2 完成状态段为准(566)。

零同步 IO 审计(grep -rn "requests\.\|urllib\|time\.sleep" app/):

  • 命中数 0——app/ 内无 requests 调用、无 urllib、无 time.sleep。
  • 全库唯一 HTTP 出站点为 app/engine/services/lead_push_service.py _do_post,使用 httpx.AsyncClient(async with 上下文),异步路径成立。
  • app/engine/providers/glm.py 的同步 token 生成与兜底同步调用分别经 loop.run_in_executor / asyncio.to_thread 下卸,事件循环不被阻塞。
  • app/engine/services/rate_limit.py 的 import time 仅用 time.time() 读时钟(纯 CPU,非 IO);app/ 源码内无 open() 文件读取。

spec §6.2 合并清单核对(逐项,全部在位):

  • 取自 jc:cc_completion 完整判定树+状态机+extractor cc_reply+e164 出站渲染(app/engine/services/cc_completion.py,conversation.py 双门控消费);多语言墙(€/£、西/葡/俄/阿词表在 seed_profiles/jiancai/wall.json、ES 迂回池 boundary_filter+wall.json、建材防误杀白名单为 pattern 级 comment+后顾断言 DN800/SDR11/PN16/"euro pallets" 排除);dual-contact 分步双要(cc_settled 串行门控邮箱轻补)+_email_asked;CC 强制墙(conversation.py cc_wall 裁决段)+零号码墙回显豁免收窄(boundary_filter.py:旧豁免移除,改 allow_number 回显确认轮白名单+防自锁线);XFF 服务端优先(chat_rest _server_ip/chat.py 握手期提取,均取第一段)、阿语数字归一(contact_pipeline 捕获源头 translate)、resolve_region_code(ip_service.py)。
  • 上游吸收:input-placeholder 可配置化(品牌配置组,public.py 下发,widget/chat data-placeholder 管道);7 个测试文件(test_channel_hygiene / test_chat_rest_ip / test_lead_push_cc / test_cc_integration / test_ip_service_region / test_prompt_static / test_placeholder_static,commit d1aed8e)。
  • 三不带:blog.html(static/ 无此文件)、Redis(app+scripts 零命中)、旧版 whatsapp-cc-completion(零命中,jc 版 cc_completion.py 取代)。

当前能力一览:多租户引擎(TenantRuntime 隔离边界,engine 不感知 platform);站点模型(tenant_config.Site,一租户多站点覆盖);词表/提示词/品牌全配置化(seed_profiles 出厂包 + 文件级 registry_reload 热加载,Phase 2 升级为 platform.db);Host 路由(host_middleware,未知 Host 404);WS 防串库(租户化 client key + 会话站点标);WS 每 IP 连接上限(conn_limiter,租户级 ws_conn_limit 配置)。

Phase 2 完成状态(2026-10-13 收尾核对)

测试基线:566 passed(根目录与 tests/ 子目录各跑一遍,数字一致;唯一 warning 同 Phase 1——starlette TestClient 对 httpx 的弃用提示,无害)。后续任务回归以此为准。

零同步 IO 审计复查(grep requests / urllib / time.sleep / open 于 app/):命中数 0——无 requests/urllib 导入、无 time.sleep、无 open() 调用;import time 仅 platform/auth.py 与 engine/services/rate_limit.py 两处,均只读 time.time() 时钟。scrypt 登录路径 CPU 开销注记在案(auth.py 模块 docstring:单次 ~50ms,只在登录/改密路径,不在每请求热路径——依赖只查内存 token 表)。

安全自查(读码 + 定向测试 28 条全绿:test_auth / test_admin_mounting / test_avatar_upload / test_host_routing):

  • 密码/token 不进日志:app/platform/ 全层零 logger 调用(干净由构造保证);main.py 启动日志仅静态串 + slug/db 路径;引擎层(Phase 1 既有)日志参数为会话内容摘录/session_id/notify_emails,无凭据字段。
  • gitignore 覆盖复核(git check-ignore 实证全命中):tenants/*.db{,-wal,-shm}(租户库实际落 tenants/<slug>.db)、platform.db{,-wal,-shm}(仓库根)、data/(含 data/uploads/ 上传卷)。
  • 登录限流在 /api/platform 豁免路径仍生效:host_middleware 对 /api/platform/ 前缀的豁免只跳过租户解析,认证守护在路由依赖;登录限流先于密码验证(锁定期内正确密码也 429)。测试钉位:test_auth.py::test_login_lockout_after_5_fails + test_admin_mounting.py::test_platform_api_bypasses_tenant_resolution / test_platform_prefix_not_open / test_prefix_match_precise。
  • 上传目录不可执行:上传只认魔数(png/jpg/webp)+ uuid 重命名落盘——.py 文件无进无出;avatar 下发(public.py)取 PurePosixPath.name 并显式拒绝 / \ 与目录点(防 Windows 反斜杠穿透),FileResponse content-type 由扩展名映射(只可能 image/*)。

平台层能力一览(Phase 2 交付;engine 仍不感知 platform):

  • 认证:scrypt 版本化哈希(stdlib 零新依赖,偏离 spec bcrypt/argon2 有注记)、内存 token 8h TTL、登录限流(username+IP 滑动窗 5 次/15 分钟)、改密(must_change_password 引导)、角色两级(超管 / 租户管理员)+ 409 语义。
  • 租户管理:创建向导(消费出厂档案,blank=jc 墙提示词)、站点与域名子资源 CRUD、停用(聊天面 503、后台仍可见)、slug 不可变。
  • 配置版本管线:分组校验 → 整份版本快照(MAX+1)→ 回滚 / 重置出厂 / 试金石(wall/test)→ registry_reload 热生效。
  • 上传:头像 multipart(魔数校验 / 2MB 上限 / uuid 重命名),写配置版本 + 三级解析链下发(uploads → seed 资产 → 静态兜底)。
  • 数据视图:会话列表(筛选/分页)/ 详情 / 删除 + 推送日志——租户 scope 强制(跨租户 404)。
  • 统计:卡片 / 漏斗 / 趋势 / 分布 / 推送健康——逐租户库查询 + 应用层合并(禁 ATTACH,spec §7.6);指标口径表定死在 stats.py docstring。
  • DB 源生产装配:TENANT_SOURCE=db 默认(DbTenantSource),FileTenantSource 保留为测试/档案/冷备形态;bootstrap_platform.py 一键引导。

admin API 速览(全部 /api/platform 前缀;Host 中间件豁免该前缀的租户解析,认证由路由依赖守护——require_super 超管 / _tenant_guard 租户管理员):

  • 认证:POST /auth/login、POST /auth/logout、POST /auth/change-password
  • 租户:GET|POST /tenants、GET|PATCH /tenants/{slug}、POST /tenants/{slug}/sites、PATCH /tenants/{slug}/sites/{site_slug}、POST|DELETE /tenants/{slug}/sites/{site_slug}/domains[/{domain}]
  • 配置:GET /tenants/{slug}/config、PUT /tenants/{slug}/config/{group}、POST /tenants/{slug}/config/{group}/reset、GET /tenants/{slug}/config/versions[/{version}]、POST /tenants/{slug}/config/rollback、POST /tenants/{slug}/wall/test
  • 上传:POST /tenants/{slug}/brand/avatar
  • 数据:GET /tenants/{slug}/conversations[/{conv_id}]、DELETE /tenants/{slug}/conversations/{conv_id}、GET /tenants/{slug}/push-logs
  • 统计:GET /stats(超管,?tenant= 单租户下钻)、GET /tenants/{slug}/stats
  • 平台设置:GET|PUT /settings

Phase 2 沉淀的取舍与 Phase 3 清理单

实现取舍(API 冻结 / Phase 3 前端对接前须知):

  1. PUT config 整组替换语义——前端必须整组读-改-写(Task 6;做部分更新需先 GET 全组再改再 PUT)。
  2. rollback 仍绕过 prefix 跨租户查重(终审 I-3 已收口 PUT/创建向导/reset 三口;rollback 单独保留豁免 = 历史不可变语义优先——回滚到含已占前缀的历史版本属数据修复场景;如仍需收口,在 rollback 前补跑查重)。
  3. 并发保存同租户撞 UNIQUE → 500(单管理员场景可接受;收口 = 捕 IntegrityError 转 409)。
  4. 统计 API 无 ?site= 筛选(spec §7.6 提及;Phase 3 前端需要时补)。
  5. 登录限流反代形态退化为按用户名(限流键 username + request.client.host,无 XFF 解析;可信代理解析归部署批次)。
  6. avatar 旧文件不清理(换头像后旧文件留盘;孤儿累积无害)。
  7. 停用租户管理员存量 token 最长存活 8h(TTL 限窗)——即时锁死需在 validate 时回查租户状态。
  8. 400(预检撞车)vs 409(竞态 IntegrityError)并存——API 冻结前统一。
  9. limiter 全量清扫(token/登录限流的内存键永不回访,长跑进程缓慢累积;收口 = 定期清扫)。
  10. lead_push_service 的 settings re-export 锚(app.engine.config 的 settings 在 lead_push_service 顶部 re-export,22 处测试 monkeypatch 依赖此锚——改动前先迁测试)。
  11. 密钥掩码未实现(Phase 3 收口,commit 0c25287:GET config/settings 四处响应密钥值掩码为哨兵 __SET__,PUT 收哨兵保留库值;前端 admin/src/api/mask.ts secretToSubmit/secretPlaceholder 同款契约)。
  12. 站点管理权限现为仅超管(Phase 3 收口,commit 0c25287:放开租户管理员同权,与 spec §7.3 原文一致)。

Phase 3 清理单(Phase 1/2 审查挂账滚动合并;删除线 项已结,结项时点见各条注记;未结项滚入 Phase 4 清理单增补):

  1. FakeAI 三份拷贝收敛进 conftest(Task 10 完成:conftest.py 单一定义,tests/ 零拷贝)。
  2. rate_limit 与 ws_conn_limit 的 0 语义统一 + 配置类型校验(Task 10 完成)。
  3. name_to_region_extras / context_seed_map 等散装 resolver 收拢为 config_views 单入口(Task 10 完成:app/platform/config_views.py)。
  4. avatar 从 seed_profiles 迁 uploads 卷(Task 7 完成:上传 + 三级解析链)。
  5. AIService.from_runtime 类方法——组装正典化(出处:引擎改造审查)。
  6. boundary_filter 视图常量退役——admin 重写后旧测试迁移时(出处:引擎改造审查)。
  7. push enabled 布尔空槽语义(Phase 3 收口:凭据页显式布尔开关 ElSwitch,normalize enabled === true 空槽归 false,提交永为显式布尔)。
  8. contact.template_languages 运行期接线——后台批次(出处:配置 schema 审查)。
  9. 话术含引号/换行的转义——编辑器批次(_format_deflection_pool docstring 有注)。
  10. 产品决策项:国名纠正 e164 变化时是否补发更正邮件(上游行为更优);REST/WS 服务端 IP 口径是否统一(出处:上游吸收审查)。
  11. /health 分级健康——Phase 4 部署批次(进程存活之外按依赖分级上报;出处:终审审查 M-3)。
  12. PromptProvider.from_dir 的 FileNotFoundError 带路径上下文——提示词目录缺失时异常信息含实际路径(出处:终审审查 M-4)。

Phase 3 完成状态(2026-10-13 收尾核对)

测试基线:后端 pytest 595 passed / 前端 vitest 156 passed(Phase 3 起步 139,Task 9 平台设置页 +14、终审修复通用 JSON 组 +3;两端数字均已实跑核对,后端唯一 warning 仍为 starlette TestClient 弃用提示,无害)。pnpm build(vue-tsc --noEmit && vite build)零错误。后续回归以此为准。

交付形态:admin/ 子目录的 Vue3 + TS + Vite + Element Plus SPA(art-design-pro 模板,pnpm 管理),与后端同仓。本地起法:

  • 后端:PYTHONUTF8=1 .venv/Scripts/python -m uvicorn app.main:app --port 8018 --workers 1(不变)。
  • 前端:cd admin && pnpm install && pnpm dev(默认 3006,被占自动顺延);Vite 代理 /api → 8018,/api/public 与 /static 走 Host 改写代理(VITE_TENANT_PREVIEW_HOST,见 admin/.env.development 注释)。pnpm test 跑 vitest;pnpm build 出 dist。
  • 登录:超管 / 租户管理员同一登录页(滑块验证为纯前端交互校验);must_change_password 由后端 409 门强制,前端跳 #/auth/force-change-password。

admin 能力一览(Phase 2 平台 API 的全量对接;菜单按角色过滤,后端 require_super / _tenant_guard 兜底):

  • 认证流:登录 / 强制改密 / 退出;pinia + 内存 token,401 统一跳登录,改密 409 契约锁定。
  • 仪表盘:平台总览 + 租户卡片墙(超管)/ 我的指标(租户管理员),时间窗切换。
  • 租户管理(仅超管):列表 / 创建向导(消费出厂档案)/ 详情编辑 / 站点与域名子资源 / 停用复启。
  • 会话与线索:列表(筛选 / 分页 / 租户切换)/ 详情消息流 / 推送日志 / 删除。
  • 配置中心:品牌(表单 / 本地简化预览 / 嵌入代码 / 头像上传)、提示词四件套与结构化词表、试金石面板、凭据(邮件 / 推送 / AI 表单 + SMTP 测试 + 推送探测)、contact/engine/misc 三组通用 JSON 编辑(终审 I-1 补齐 spec §7.3 全组覆盖)、版本历史(两版对比 / 回滚 / 组级重置出厂)。
  • 统计分析:卡片 / 转化漏斗 / 按日趋势 / 意向·来源·站点·国家分布(ECharts)/ 推送健康表;时间与租户筛选(口径同 stats.py docstring 口径表)。
  • 平台设置(仅超管):自由 KV 表的推荐键表单(ai.* / smtp.*,键名是前端约定)+ 存储总览表格;保存 = 逐键 PUT(只提交脏键)。当前仅存储与展示——resolver 的平台默认回退链未接线(页头注记),引擎不读取这些值;密钥键掩码哨兵交互与凭据页同款(清除 / 恢复保持)。

前端实现约定(后续维护须知):

  • 视图逻辑沉淀在各 views/**/model.ts 纯函数(掩码判定 / 归一 / 脏检测 / 提交值构造 / 校验),vitest 直接覆盖;.vue 只做装配。auto-import 生效(ref/reactive/ElMessage 等无需手写 import)。
  • 密钥键判定前端镜像后端 _SETTING_SECRET_MARKERS 子串匹配('monkey' 含 'key' 也命中——与后端一致是契约,不"修正")。
  • PUT config 整组替换语义:前端封装读-改-写(GET 全组 → 改 → PUT);平台设置不同,为逐键 upsert。
  • components.d.ts 构建漂移:pnpm build/dev 会重写入库的 dts(仅行尾差异)——git diff --ignore-cr-at-eol 为空即 git checkout -- 还原入库版(超集原则,勿提交行尾噪音)。

滚动纪律(自 Phase 3 计划注意延续):conftest 三坑(NullPool / portal 桥接 / 禁 asyncio.run)、平台层测试用 db_source_app / super_client 夹具、引擎读配置走 config_views 单入口、统计展示照 stats.py 口径表——均照抄勿发明。

Phase 4 部署记档(提前锚定,部署批次展开成 docs/deploy-production.md)

  • pip install -r requirements.txt 必须执行:python-multipart(>=0.0.20)为 Phase 2 新依赖——Task 7 实证缺失即头像上传 500(FastAPI multipart 解析依赖它)。
  • bootstrap_platform.py 用法:PYTHONUTF8=1 python scripts/bootstrap_platform.py [--db PATH] [--profiles SLUG ...] [--seed DIR]。两步全幂等:init(schema + 超管引导,凭据取 env/.env 的 ADMIN_USERNAME/ADMIN_PASSWORD)+ 导入出厂租户(默认 auto jiancai 两租户,已有 slug 跳过);不建租户管理员(走后台创建向导补)。
  • TENANT_SOURCE=db 默认(生产装配形态);file 为冷备/本地调试回退。
  • admin 域名反代区分:/api/platform/* 前缀豁免租户 Host 解析——平台后台 API 可与租户聊天域名同源反代(按路径分流)或独立域名,两形态皆可;租户聊天面(/、/static、WS、/api/public、/api/chat*)仍严格按 Host 解析租户,未知 Host 404。
  • ADMIN_PASSWORD 默认值警示(终审 I-4):lifespan 启动检测到 ADMIN_PASSWORD 仍是出厂默认(admin123)时打 WARNING 日志——生产部署必须设 ADMIN_PASSWORD 环境变量换掉,且超管首登立即改密(must_change_password 门强制)。
  • Phase 4 清理单增补(Phase 3 冒烟沉淀 + 终审记档;Phase 3 清理单未结项 5/6/8/9/10 一并滚入):
    1. components.d.ts 构建行尾漂移(长期收口 = 生成物出库或 EOL 规范钩子,现阶段靠手工还原)。
    2. 推送探测 env 回退真址:租户 push URL 空槽时 resolve_push_settings 回退 env PUSH_API_URL(默认即真 UniService 生产址)——后台点「探测」会对真实服务发空批量 POST({"messages": []},按契约文档应为无操作,但属真实出站)。收口 = 部署设 stub 域或探测端点拒绝 env 回退 URL。
    3. 生产反代需规划 admin 域的头像/品牌公开端点:/api/public/* 按租户 Host 解析,admin 域直挂必 404——开发期靠 Vite Host 改写代理,生产要么同源反代改 Host(单租户形态)要么前端改用租户域名绝对 URL。
    4. VITE_TENANT_PREVIEW_HOST 单一预览域名:开发代理只指向一个租户 Host,切换租户时头像等公开资源预览不准(.env.development 已注记)。
    5. resolver 平台默认回退链接入 platform_settings(终审 I-3 记档):平台设置当前仅存储与展示(admin 页头有注记),引擎不读取——回退链接线是 Phase 4 的功能项。
    6. email/test 与 push/test 速率限制(终审 I-3 记档):两测试端点无限流,复用登录限流器模式收口。
    7. 锁屏功能已摘除入口(终审 M-1):用户菜单项删除 + 全局组件 enabled:false 不再挂载(AES 存 localStorage + 公开模板默认密钥,防御价值为零);代码待 Phase 4 决定删否。
    8. 租户切换器 composable 收拢(终审 M-3 不做记档):仪表盘/会话/配置三页各自内联实现同一模式,收拢归后续。
    9. ECharts 按需收窄(终审 M-4 不做记档):stats 的 charts.ts 全量注册,bundle 体积优化归后续。

文档索引

  • 设计文档(spec):docs/superpowers/specs/2026-10-12-unified-kefu-platform-design.md
  • Phase 1 实施计划:docs/superpowers/plans/2026-10-12-phase1-engine-merge-tenantization.md
  • Phase 2 实施计划:docs/superpowers/plans/2026-10-13-phase2-platform-layer.md
  • Phase 3 实施计划:docs/superpowers/plans/2026-10-13-phase3-admin-spa.md
  • 部署坑手册:Phase 4 补 docs/deploy-production.md(尚未存在;Phase 4 部署记档段已锚定要点)