项目矩阵 业务运营 Enexora-loop
EEagle 主维护 · 公众号工单 + 公司技术运营中枢

nexora-loop

公众号工单 + AI 客服 + 项目/环境/机器人的编排层 — 原 wechat-bot-tickets · 现 cspy prod

中小公司没专职客服,公众号留言爆掉接不动、手动答复慢、又没工单系统沉淀。nexora-loop 把"公众号收消息 → AI 客服分诊 → ESCALATE 升级 → 自动派工程师 bot → admin 审核 → 推回用户"一条龙串完;同时长出第二条价值线——公司技术运营中枢:Project / Environment / Feature / ProjectNote 入库,绑 Uptime Kuma 监控,1 个客服 bot 调度 N 个工程师 bot,bot 通讯走 Mattermost DM(替代了原 Clawline relay)。2026-04 立项叫 wechat-bot-tickets,5/6 改名 nexora-loop,prod 域名 cspy.mvp.restry.cn 沿用别名不动。

访问 cspy.mvp.restry.cn Restry/nexora-loop · 双 push GitHub + GitLab 196 commits · 6 周
196
commits · 6 周
30+
Prisma 表 / 19 model
3
渠道 · 微信 + Feishu + Widget
N
工程师 bot · 1 客服调度

核心能力 · Five Capabilities

5 件事,把"接公众号 + 跑工单 + 调 bot"串成闭环

每个能力先说"业务方原本要付什么代价",再补一行技术细节。共同前提:用户消息从公众号 / Feishu / Web Widget 三个渠道进来,共用一套 Conversation / Ticket / Message 抽象,bot 在 Mattermost DM 端跑,admin 在 panel 端审核。

公众号工单全链路
用户 → AI → 工程师 → admin
用户在公众号留言 → 客服 bot 应答 → 触发 ESCALATE 自动建工单 → 派工程师 bot 给建议 → admin 审核后推回用户。一条消息全程留痕。
/api/wechat · executeTicket · 4 态状态机
engineer bot 体系
1 客服 bot · N 工程师 bot
每个 BotcategoryTags,工单 AI 分诊产出 category → 自动指派对应工程师 bot;无匹配走 default fallback,绝不卡死。
Bot.categoryTags · auto-assign · default
Mattermost 通信层
替代 Clawline relay
每个 bot 绑 mmBotUserId,后端 sendAndWaitViaWs + 双 idle 窗口(typing 5s + post 1.5s);前端 SSE 流式转推浏览器,三面板共用 ws-client 单例。
lib/mm/* · sendAndWait · /api/mm/events
Project + Environment
公司技术运营中枢
6 个项目入库,每个项目挂 N 个环境(dev/staging/prod);环境绑 kumaMonitorIds 实时拉 Uptime Kuma 状态,不缓存 DB;ProjectNote 4 种 kind + pin。
Project · Environment · ProjectNote · Kuma 直拉
Web Widget 嵌入式
2026-05-30 Phase 1 上线
JS snippet 嵌第三方网站,用户可在站内直接发消息 → 进 cspy Conversation。同期 Feishu channel 桥接飞书机器人到工单。多渠道收口同一套抽象。
WidgetApp · FeishuUser · ChannelEventLog

架构 · Three channels × One core × Three domains

一张图看清:三入口 → nexora-loop 核心 → 三主域 → 外部依赖

微信公众号 callback 不直连 cspy,先走上游 wx-gateway-pucs 网关分流;Feishu 通过 channel adapter 接入;Web Widget 直接打到 cspy 自有 API。核心仅 1 个 Next.js 进程,内部分工单 / 对话 / 项目运营三主域;依赖外部 Mattermost(bot 通讯)、Uptime Kuma(健康)、Azure OpenAI(AI 推理)。

User Channels(三入口)
微信公众号callback → wx-gateway-pucs → /api/wechat
Feishu / Web WidgetFeishuUser · WidgetApp · 2026-05-30 上线
incoming message → Conversation
Core Service
nexora-loop · pm2 cspy · port 3791
cspy.mvp.restry.cn
Next.js 16.2.6 · React 19 · Prisma 6 · Postgres · NextAuth v5 · pnpm
Three Domains(内部分工)
Domain 1 · 工单
Ticket / Feature 流转
TicketTicketEventFeatureFeatureEventNotification
Domain 2 · 对话
Conversation / Bot 通信
ConversationMessageBotSessionWechatUserFeishuUser
Domain 3 · 项目运营
Project / Env / Note
ProjectEnvironmentProjectNoteWidgetAppSystemConfigAdminUser
External Dependencies
wx-gateway-pucs
公众号 callback + access_token 集中
Mattermost
mm.cn.restry.cn · bot DM + WS 订阅
Uptime Kuma
kuma.mvp.restry.cn · 实时拉 status
Azure OpenAI
AI 分诊 / DIAGNOSIS / REPLY
Domain 1 工单 Domain 2 对话 Domain 3 项目运营

关键里程碑 · Timeline

6 周 · 196 commits · 从公众号工单原型到多渠道编排平台

从 4 月底 wechat-bot-tickets 立项,5 月一边扩 Project/Environment/Feature 一边把 bot 通讯从 Clawline relay 切到 Mattermost DM;5 月底完成 Next 14→16 升级 + Feishu/Widget Phase 1。⭐ 标记结构性里程碑。

2026-04-27 ★ 里程碑wechat-bot-tickets 立项 · 公众号 onboarding 流程 + 客服 bot 应答 + Ticket 4 态状态机最小闭环。
2026-04-30 集成测试取代 mocked 单测(cb1feeb):全部走真 DB + 真 relay,父父明确铁律。
2026-05-06 ★ 重命名wechat-bot-tickets → nexora-loop · pm2 / prod 域名 / mvp-deployer 项目名全保留 cspy 别名不动,只动源码 repo。
2026-05-09 ★ 里程碑P1-P5 engineer bot 体系 · 一客服 bot N 工程师 bot · categoryTags 自动指派 + default fallback · executeTicket 串起 ESCALATE → 派单 → 审核。
2026-05-10 Next 14 → 16 + React 18 → 19(595824b)· 一天打完 codemod + useFormState → useActionState + middleware export 修复。
2026-05-10 ★ 切换bot 通讯 Clawline relay → Mattermost DM(3caff91)· 看中 MM DM 自带持久化历史,bot 端天然能看完整上下文。
2026-05-15 W8 ProjectNote(8843d59):4 种 kind + pin + 时间线 + AI 注入,项目记忆开始沉淀。
2026-05-18 ★ 里程碑W9 Feature 模块闭环(698529b → 1c4b1f3)· 需求开发流 + 状态机 + 评论时间线 + 指派触发 bot 分析 + 持续对话。
2026-05-23 MM 切换两大坑同期修(247b82c · e24d1e7):edit-aware 静默窗口防截断 + 删 buildHistoryDigest 解双重历史。详见名场面。
2026-05-25 WS 单例 + SSE 流式(5645643 → 211e5b7 → fa0473d):sendAndWait 从轮询换 WS 订阅,三面板统一 typing indicator + streaming render。
2026-05-30 ★ 里程碑Feishu + Widget Phase 1 同期上线(06f89ac)· cspy 部署成功 · 多渠道收口同一套 Conversation/Message 抽象。
2026-06-01 通用 deployment guide(552d0cf)· 解耦 mvp-deployer 依赖,允许 self-host 部署。

名场面 · 1 个最值钱的坑(深度)

切到 Mattermost DM 后的双连击:流式截断 + 双重历史

从 Clawline relay 切到 MM DM 那天看上去就是改个 transport,实际踩到两个相互独立的坑——一个出现在传输层(bot 多次 edit 同一条 post),一个出现在应用层(MM channel 本来就持久,cspy 还在塞历史摘要)。两个坑加起来导致用户和 bot 都"看上去说话不正常"。

HALL OF FAME · MM 切换双坑

sendAndWait edit-aware 静默窗口 + 删 buildHistoryDigest

症状 1
admin 在工单聊天面板看到 bot 回复只剩半截("在呢 👋"),但拿 MM 客户端打开同一个 DM channel 是完整长文。两边数据不一致,看起来像 cspy 在裁字。
根因 1
OpenClaw bot 模仿流式样式:先发一条短占位 post,再多次 edit 同一条 post 把内容补全。lib/mm/client.ts 原版抓到第一条带内容 post 就立刻 return,后续 edit 全丢。
修法 1
edit-aware 静默窗口Map<id, MmPost> 记 collected,每轮 poll 覆盖,候选 message 连续 5s 不变才 return(247b82c)。后又改成 WS 订阅 + 双 idle(typing 5s + post 1.5s,aa95cc5),省掉轮询。
症状 2
admin 跟 bot 多轮对话,bot 反复回复"我之前已经分析过了" / "如前所述",像被洗脑。每次都得重新 kick off 一遍。
根因 2
lib/feature/bot-chat.ts 每次都拼一段 markdown 历史摘要 buildHistoryDigest 塞 prompt;但 MM DM channel 本来就持久,bot 在 MM 端能看完整 channel 历史 → 双重历史困惑
修法 2
buildHistoryDigest,reply 路径改裸消息 [feature:title]\n${msg} 单行 tag,kickoff 仍带 brief(e24d1e7)。
// lib/mm/client.ts — edit-aware 静默窗口(后续被 WS 单例替代)
const collected = new Map<string, MmPost>()
while (Date.now() - lastChange < QUIESCENCE_MS) {
  const posts = await fetchSince(channelId, sinceTs)
  for (const p of posts) collected.set(p.id, p)  // 覆盖式合并 新 post + edit
  if (changed) lastChange = Date.now()
}
return [...collected.values()].filter(matchesCandidate).pop()
沉淀:这两个坑加上"MM bot 端历史是有状态的、不要在应用层再塞一遍",统一进了项目 skill wechat-bot-tickets-cspy/SKILL.md。之后 SSE 流式接入 relay(P6)也被这个原则推翻,最终在 P8 简化成 4 态状态机 + ESCALATE/ANALYSIS 解析(602101c),不再拼历史。教训:每加一个 transport,先问"它本来记什么、不记什么",不要让应用层和传输层抢着维护历史。

📊 当前现状 · Status

五个维度,逐条带具体数字 / 表名 / commit / domain

不写"一切正常 / 反馈正面"这类空话。下面五个分组每条都对应一个可 grep 到的真实事实:生产部署 / 数据规模 / 接入渠道 / 最近 7 天变动 / 运维状态。

生产部署

  • 域名:cspy.mvp.restry.cn(mvp-deployer 项目名沿用 cspy)
  • pm2 进程:cspy · port 3791 · online
  • :Next.js 16.2.6 + React 19.2.6(2026-05-10 升级完)
  • 部署链路:mvp-deployer 异步 build + prisma migrate deploy,通用 deployment guide 已写
  • 双 push:origin 配 GitHub Restry/nexora-loop + GitLab 内网,一条 push 同步两端

数据规模

  • 30+ Prisma 表 / 19 model:Bot · WechatUser · Conversation · Session · Message · Ticket · TicketEvent · AdminUser
  • 运营域:Project · Environment · Notification · SystemConfig · ProjectNote
  • 需求域:Feature · FeatureEvent
  • 多渠道:FeishuUser · WidgetApp · ChannelEventLog(2026-05-30 Phase 1 新增)
  • 表前缀 wbt_:历史遗留(原 repo 名 wechat-bot-tickets),新表沿用一致,改名无价值

接入渠道 + bot 名册

  • 公众号 callback:配在 wx-gateway-pucs(wxmsg.mvp.restry.cn)· 按 scene_str 分流到 cspy /api/wechat
  • OpenClaw bot:nexora-mazu(客服 + 工程师双角色)· 妈祖客服 · 喜铺客服 · 通用客服 — 每个 BotcategoryTags + mmBotUserId
  • Mattermost:mm.cn.restry.cn · WS 单例 + DM 通信 + SSE 转推浏览器
  • Web Widget(2026-05-30):JS snippet 嵌第三方网站 · Feishu channel(同期):飞书机器人桥接进工单

最近 7 天主要变动(git log)

  • 552d0cf docs: 通用 deployment guide — 解耦 mvp-deployer,允许 self-host
  • 06f89ac merge: Feishu Phase 1 + Widget Phase 1(★ 部署到 cspy 2026-05-30)
  • 7ab02d1 feat(widget): Phase 1 — 嵌入式 web chat widget + Admin panel
  • 6c39263 feat(feishu): Phase 1 — Feishu channel + shared abstractions
  • 2be09fa refactor(ui): bots & features 面板镜像 consult-panel streaming 模型
  • fa0473d feat(ui): typing indicator + streaming render 统一三面板
  • 211e5b7 feat(mm): SSE endpoint 流式转推 WS 事件到前端
  • aa95cc5 refactor(mm): sendAndWait 改 WS 订阅(替换轮询)

运维状态

  • MM 通信稳定:edit-aware 静默窗口 + WS 单例 + 双 idle(typing 5s / post 1.5s)三件套生效,流式截断不复发
  • engineer bot 自动指派:categoryTags 命中 → 派对应 bot,无匹配走 default fallback,工单不卡死
  • 4 态状态机(P8 简化后)稳定运行:open / processing / waiting / closed · ESCALATE/ANALYSIS 解析正常
  • GitNexus 已索引:2098 symbols · 3637 relationships · 175 flows,bot 改代码前先查上下文
  • 集成测试(真 DB + 真 MM)持续跑,关键路径必过;少量纯函数 unit 走 vitest.unit.config.ts

🚧 未结清债务 · Open Debt

8 条具体待办 · 每条带优先级 / 工作量 / 现状

不写"P1 待办 N 条"这种模糊罗列。每条 4 列:优先级(P0 立刻 / P1 本周 / P2 排期) · 工作量(S < 0.5d · M 1-2d · L 3d+) · 待办 · 现状是什么。来源:.hermes/memory.md 踩坑历史 / 近 30 commits / 本地 skill NEEDS-DECISION 段。

优先级 工作量 待办 现状(还差什么)
P0 M prod 泄漏 secrets 轮换 2026-04-28 部署日志被 dump 进外部聊天,SEED_ADMIN_PASSWORD / MM_USER_TOKEN / WECHAT_APPSECRET 等明文外泄。需逐个 rotate + 同步到 mvp-deployer env + 重启 pm2,且核对 prod admin 实际密码(可能已被手改未同步)。
P1 M need_human 不进 ACTIVE_STATUSES → 重复开单 用户人工兜底后再发消息,因 need_human 不在活跃工单匹配集,会开第二张单。memory.md 已知坑 #1。修法:扩 ACTIVE_STATUSES 或新增 reuse 路径。
P1 S 复用 existing ticket 不刷 summary/category 同一用户连续发新消息复用旧工单时,Ticket.summarycategory 保持初版,工程师 bot 拿到的上下文过期。需在 reuse 分支重跑 AI 分诊。
P1 L 跨 conversation / 跨用户工单合并 同一业务问题被不同用户分别提,产生 N 张独立工单,admin 无合并入口;需先建 TicketGroup 抽象 + admin 合并 UI(可拖拽 / 多选合并)。
P1 M P9.2 AI 建议自动首次生成 + 拆 DIAGNOSIS/REPLY 两块 当前 AI 建议需 admin 手动触发,且 DIAGNOSIS(诊断思路)和 REPLY(给用户的话)合一团,admin 编辑成本高。需 ESCALATE 后自动跑一次,并把两块拆开分别可编辑。
P1 L P9.3 admin ↔ 工程师 bot 多轮 进行中 · 当前 admin 给 bot 提问只能单轮,bot 回完就结束;需在 Feature 面板复用 ChatBubble/ChatComposer,沿用 MM DM persistent channel 直接对话。
P2 M prod admin 密码漂移自动检测 SEED_ADMIN_PASSWORD env 跟 DB 实际密码长期不同步,登录失败每次都要先 reset。需启动时对比 DB hash,不一致告警。
P2 S Widget / Feishu 渠道接入业务方验证 Phase 1 已发,目前仅 cspy 自用样例;需找 2-3 个真实接入跑一轮,验证 ChannelEventLog 抽象是否够、widget JS snippet 文档够不够。

🛠 技术栈 · Stack

RuntimeNext.js 16 · React 19 · pnpm · PM2 DBPostgres · Prisma 6 · wbt_ 前缀 AuthNextAuth v5 beta · AUTH_TRUST_HOST UIshadcn/ui · Tailwind 3.4 · stone 暖灰系 Bot 通信Mattermost DM · WS 单例 · SSE 转推 AIAzure OpenAI · OpenClaw bot Iconslucide-react Opsmvp-deployer · Uptime Kuma · GitNexus QA集成测试(真 DB + 真 MM) · vitest unit LangTypeScript 5