ai_scheduler/doc/AI_DEV_GUIDE.md

22 KiB
Raw Blame History

AI 数字销售助手 — AI 开发者快速指南

本文档面向 AI 编程助手,帮助你在最短时间内理解项目全貌并继续开发。


项目目标(务必理解)

构建一个能完全模仿真人销售在微信上与客户对话的 AI 系统。

核心差异:

  1. 数字销售分身:上传销售真实聊天记录,AI 提取其个人风格(方言/句式/语气/性格),构建"数字分身"
  2. 四维画像驱动:基于 销售风格 / 项目资料 / 聊天技巧 / 客户画像 综合理解场景后生成回复
  3. 自主运营:自动回复(按客户等级分策略)、主动触达(生日/活动)、画像自迭代、等级动态评估
  4. 最终愿景:销售只处理 AI 搞不定的关键对话,日常维护/跟进/促单全部由 AI 代劳

你写的每一行代码都应服务于:让 AI 更像那个销售、更懂那个客户、更专业地推进销售进程。


技术栈

Go 1.26 + Fiber v2 + GORM(MySQL) + MongoDB + Redis + Wire DI + 原生前端(零构建)

调用链:

HTTP → Fiber中间件(JWT) → Router → Service → Biz → Impl/Mongo → LLM
                                          ↓
                    registerCommon 统一包装 {code:0, message, data}

目录结构(★ 标注当前活跃开发区域)

ai_scheduler/
├── cmd/server/
│   ├── main.go              # 启动入口
│   ├── wire.go              # Wire DI 声明(//go:build wireinject)
│   └── wire_gen.go          # Wire 自动生成(禁止手动编辑)
├── config/config.yaml       # MySQL/Mongo/Redis/JWT/微信协议地址
├── internal/
│   ├── biz/                 # ★ 业务逻辑层
│   │   ├── advice_wx.go         # 微信回调处理 + 消息流水 + 会话聚合 + ★ 托管自动注册
│   │   ├── advice_wx_send.go    # 微信消息发送(含模拟真人打字延迟)
│   │   ├── advice_strategy.go   # 智能策略(四级回复/主动对话)
│   │   ├── advice_client.go     # 客户管理 + 等级评估
│   │   ├── advice_customer_new.go # ★ 微信好友 CRUD + 差集同步(ai_advice_customer)
│   │   ├── advice_label.go      # ★ 标签同步(与 DB 比对增删改)
│   │   ├── advice_chat.go       # AI 对话(LLM 调用 + ChatWithSession 复用优化)
│   │   ├── advice_evaluate.go   # 客户等级评估
│   │   ├── advice_proactive.go  # 主动触达
│   │   ├── advice_iterate.go    # 画像自迭代
│   │   ├── handle/、llm_service/ # ⚠️ 历史死代码,忽略
│   │   └── provider_set.go
│   ├── services/advice/     # ★ HTTP Handler 层
│   │   ├── wxhook.go            # 回调 + 会话 Handler
│   │   ├── wx_proxy.go          # 微信 API 代理(分发到 wxDispatchMap)
│   │   ├── wx_ws_hub.go         # WebSocket Hub(实时推送)
│   │   ├── customer_new.go      # ★ 好友 CRUD Handler(Diff 同时返回标签)
│   │   ├── label.go             # ★ 标签 Handler(List/Sync)
│   │   └── provider_set.go
│   ├── server/
│   │   ├── router/router.go     # 路由总装 + registerCommon 中间件
│   │   ├── router/advicer.go    # 全部业务路由(~80 个接口)
│   │   └── http.go              # NewHTTPServer 参数列表需同步
│   ├── entitys/advicer_data.go  # ★ 核心 DTO(最频繁修改)
│   ├── data/
│   │   ├── model/               # MySQL GORM 模型(*.gen.go 禁止手动编辑)
│   │   │   ├── ai_advice_customer.gen.go  # ★ 微信好友表
│   │   │   └── ai_advice_label.gen.go     # ★ 标签表
│   │   ├── impl/                # MySQL 数据访问(继承 DataTempBase[T])
│   │   │   ├── advice_customer_impl.go
│   │   │   └── advice_label_impl.go
│   │   ├── mongo_model/         # MongoDB 集合模型
│   │   └── constants/           # Prompt 模板、模型映射
│   ├── middleware/               # JWT 认证
│   ├── jobs/advice_task.go      # 定时任务(cron)
│   └── pkg/wx/                  # 微信协议封装(100+ API 端点)
├── web/                     # ★ 前端(零构建,静态托管)
│   ├── project.html         # ★ 版本号管理(修改 JS 后必须更新 ?v=N)
│   └── assets/
│       ├── css/theme.css    # 深色科技风设计系统
│       └── js/
│           ├── core.js      # 核心库(Core.api / Core.wxApi / Toast / Modal)
│           ├── wx.js        # ★ 微信工作台核心(state/通讯录/标签/WS/好友同步)
│           ├── wx_login.js  # ★ 登录模块(二维码/在线检测/登出)
│           └── wx_chat.js   # ★ 聊天模块(会话/消息/联系人详情气泡)
└── sql/                     # 数据库 DDL
    ├── 04_ai_advice_customer.sql
    └── 05_ai_advice_label.sql

数据模型

MySQL 核心表

表名 说明 关键字段
ai_advice_customer ★ 微信好友(self_wxid + user_name 唯一键) label_list, session_id(当前活跃会话)
ai_advice_label ★ 微信标签(self_wxid + label_id 唯一键) label_id, label_name
ai_advice_advicer 销售基本信息 wx_device_id(即 appId)
ai_advice_project 项目信息 wx_token
ai_advice_admin 管理员账号
ai_advice_session AI 对话会话 advicer_id(托管场景动态取最新版本)

MongoDB 核心集合

集合名 说明 关键字段
advicer_wx_msg ★ 微信消息流水 appId, selfWxid, wxid, direction, msgType, content, read
advicer_version 销售画像 dialectFeatures, sentencePatterns, toneTags, personalityTags
advicer_client 客户画像 clientLevel, personalInfo, purchasePurpose, coreDemands
advicer_project 项目资料维度(结构化) regionValue, competitionComparison, coreSellingPoints, supportingFacilities, developerBacking
advicer_project_data ★ 项目资料(扁平栏目) projectId + 动态栏目(key=栏目名, value=内容),与 advicer_project 是两个独立的集合
advicer_talk_skill 聊天技巧维度 needsMining, painPointResponse, closingTechniques, communicationRhythm, valueBuilding
advicer_activity 产品活动 name, content, startAt, endAt

命名差异

  • MySQL:蛇形(user_name)
  • MongoDB:驼峰 bson(userName)
  • 前端 JS:驼峰(userName)
  • API 响应:snake_case(user_name),前端手动映射

前端架构

核心机制

  • 零构建:<script> 标签按序引入,无打包工具
  • IIFE 模式:每个 JS 文件 (function(global){...})(window) 包裹
  • 版本号:<script src="xxx.js?v=42"> — 修改 JS/CSS 后必须更新 project.html 中的版本号
  • 全局命名空间:Core(核心库)、WxN(微信模块共享)、Wx(挂载点)

微信工作台模块(WxN 命名空间)

文件 职责
wx.js state 管理 / 生命周期 / 通讯录 / 标签 / WebSocket / 好友差集同步
wx_login.js 二维码登录 / 在线检测 / 登出
wx_chat.js 会话列表 / 消息渲染 / 发送 / 联系人详情悬浮气泡

架构要点:

  • wx.js 创建 WxN 命名空间,通过 Object.defineProperty 暴露 state getter
  • 子模块通过 WxN.state 访问最新状态(不在闭包中缓存 state 引用)
  • 子模块通过 WxN.xxx = fn 导出函数
  • 加载顺序:wx.js → wx_login.js → wx_chat.js

Core.js 速查

Core.api(path, params)    // 管理后台 API(POST JSON,JWT,自动解包 data)
                          // 完整路径:/api/v1/admin/advice/admin/{path}
Core.wxApi(path, params)  // 微信代理 API(自动注入 projectId)
                          // 完整路径:/api/v1/admin/advice/admin/wx/{path}
Core.toast(msg, type)     // type: "ok"|"err"|"warn"|"info"
Core.modal({title, body}) // 模态框
Core.confirm(msg)         // 确认对话框,返回 Promise<boolean>
Core.esc(str)             // HTML 转义

关键流程

初始化流程

页面加载 → mount() → freshState()
  → 解析 URL hash (#wx-advicer=advicerId)
  → fetchDeviceAndCheck(advicerId)
    → Core.api("advicer/info", {advicerId}) 获取 wx_device_id
    → state.app = { appId: wx_device_id }  ← 始终从 API 获取,不缓存
    → checkOnline()
      → 在线 → checkOnlineLoop()(内含 connectWs())
               loadProfile().then(loadConversations())  ← 需要 wxid
               loadContacts() → syncCustomerDiff()  ← diff 同时返回标签
               loadLabels()  ← 与微信同步标签到 DB
      → 离线 → getQrCode() 显示二维码

好友 + 标签同步流程

loadContacts() → syncCustomerDiff()
  → POST advicer/customer/diff { selfWxid, userNames: friends }
  → 后端返回 { list: [已有好友], diff: [缺失userName], labels: [标签列表] }
  → 用 list 填充 state.brief(联系人简要)
  → 用 labels 填充 state.labels + state.labelMap
  → diff 不为空时:分批 getBriefInfo → batch_add 入库

loadLabels()(并行)
  → Core.wxApi("label/list") 获取微信标签
  → POST advicer/label/sync 同步到 DB(比对增删改)
  → loadLabelsFromDb() 重新加载

消息回调流程

上游协议服务 → POST /api/v1/advicer/wx/callback
  → advice_wx.go:解析 AddMsg → 过滤 gh_ 公众号/msgType=other
  → 去重(AppId + NewMsgId)→ 入库 MongoDB advicer_wx_msg
  → WebSocket 广播 new_message(按 appId 匹配推送)
  → 触发智能策略(客户文本消息异步 AI 决策)
  → 触发托管自动注册(文本私聊消息异步检查)

托管自动注册流程

消息回调 → 文本私聊 → goroutine tryHostingAutoRegis
  → (1) 查 advicer(FindByWxDeviceId)→ hosting_enabled=1?
  → (2) 查 projectInfo → 取项目级 wxToken
  → (3) 若 peerWxid == "filehelper" 且 advicer.reply_filehelper=1 → 跳过标签检查,直接进入回复流程
        否则调微信 contacts/getDetailInfo 取好友 labelList("1,2,3" 形式)
        labelList 为空 → 直接 return,不做回复
  → (4) 取 ai_advice_label 中该销售 hosting=1 的标签 ID 列表
  → (5) 判断好友 labelList 与托管标签是否有交集,无交集 → return
  → (6) 查 ai_advice_customer 取 session_id
  → (7) 无会话 → autoRegisSession 全量加载数据并注册:
        - 加载版本数据(advicer_version)
        - 加载销售技巧(advicer_talk_skill,有 HostingSkillId 按 ID 取,无则按 projectId 取最新)
        - 加载客户画像(advicer_client,按 projectId+advicerId+wxid 查)
        - 加载项目资料(advicer_project 结构化 + advicer_project_data 扁平栏目)
        - 构建 ChatData → Regis 存入 Redis(key=chatdata:{sessionId})
  → (8) hostingChatAndSend:
        - 从 Redis 取 ChatData → buildChatPromptResponse 全量注入提示词
        - Chat 生成 AI 回复 → 拆段 → 模拟打字延迟 → 分段发送

关键设计:

  • 标签判断改用微信实时返回的 labelList,不再依赖 customer 表缓存的标签
  • projectInfo 提前到步骤 2 查询(后续 getDetailInfo 和 Chat 都需要 wxToken)
  • 客户昵称优先使用微信备注名(Remark),回退到 NickName,最后才用 customer 表中的昵称
  • customer 表仅用于存取 session_id,不再承担标签判断职责
  • 文件传输助手(filehelper)特殊路径:销售开启 reply_filehelper 后,跳过标签检查,直接作为客户进行 AI 回复

ChatData 与全量提示词注入

ChatData 结构(entitys/advicer.go):

type ChatData struct {
    ClientInfo     *AdvicerClientMongoEntity    // 客户画像(advicer_client)
    TalkSkill      *AdvicerTalkSkillMongoEntity // 销售技巧(advicer_talk_skill)
    ProjectInfo    *AdvicerProjectMongoEntity   // 项目资料-结构化(advicer_project)
    ProjectData    map[string]interface{}       // 项目资料-扁平栏目(advicer_project_data)
    AdvicerInfo    *AiAdviceAdvicerEntity       // 销售信息(MySQL)
    AdvicerVersion map[string]interface{}       // 销售人设维度数据(advicer_version)
    RuleDimension  string                       // 项目风控红线
}

提示词构建流程(buildChatPromptResponse):

System Prompt = BasePrompt + Mission + RuleDimension + BasePrompt2
              + [项目信息] (advicer_project JSON)
              + [项目资料] (advicer_project_data JSON)
              + [销售信息] (advicer MySQL JSON)
              + [销售人设风格] (advicer_version JSON)
              + [客户信息] (advicer_client JSON)
              + [销售技巧] (advicer_talk_skill JSON)
              + [输出格式] (永远在最后)
---
System Prompt = 聊天记录(客户/我 对话格式)
---
User Message = 当前消息

数据流:

  • Regis 时:autoRegisSession 全量加载所有数据 → 构建 ChatData → json.Marshal 存入 Redis(key=chatdata:{sessionId},TTL 1h)
  • Chat 时:从 Redis 取 ChatData → compactJSON(递归去空值省 token)→ 注入 system prompt
  • compactJSON:序列化前递归去除空字符串、null、空数组、空对象,减少无效 token

注意:advicer_project 和 advicer_project_data 是两个独立的 MongoDB 集合,前者存结构化维度(区域价值/竞品对比等),后者存扁平栏目数据(key=栏目名)。两者都需要加载。

模拟真人发送算法:

  • AI 回复拆分为多条短消息(每条 ≤80 字,按换行/句末标点拆分)
  • 初始延迟 3~8 秒(模拟阅读 + 思考)
  • 每条消息间延迟 = 思考时间(2~5s) + 打字时间(每字 300~500ms)
  • 提示词约束:每条消息 10~30 字,用 \n 分隔

WebSocket 实时通信

前端 connectWs() → /api/v1/advicer/ws?token=JWT
  → onopen 发送 {"action":"subscribe","appId": state.app.appId}
  → 后端 Broadcast(appId, event, data) → 匹配 client.appId → 推送
  → 前端 onmessage → handleWsEvent()
    → "new_message"   → WxN.handleNewMessage(data)
    → "online_status" → WxN.handleOnlineStatus(data)

当前开发状态(截至 2026-09-27)

已完成

  • 微信工作台:登录(二维码/在线检测)、通讯录、会话、聊天、AI 建议
  • 好友差集同步:ai_advice_customer 表 + customer/diff + batch_add/batch_update
  • 标签同步:ai_advice_label 表 + label/sync + label/list
  • 标签在 diff 接口一并返回(进入工作台时一次请求拿到好友 + 标签)
  • 标签 tab:展开/折叠、好友计数、loading 状态
  • 好友/会话列表展示联系人标签小标签
  • 联系人详情悬浮气泡(hover popup,含视口边界检测)
  • WebSocket 实时推送新消息 + 在线状态
  • 去掉 localStorage 缓存(brief/labels 全部内存 state)
  • 托管标签自动会话注册:检查标签交集 → 自动注册 AI 会话 → 回写 session_id
  • 托管自动回复:注册会话 → Chat → 拆段 → 模拟真人打字延迟 → 分段发送
  • 项目级 wx_token 支持:SendTextWithToken 方法,托管场景使用项目级 token
  • 查询优化:消除重复查询(projectInfo/advicer/session),新注册路径从 ~16 次降至 ~10 次 DB
  • 文件传输助手自动回复:销售级开关 reply_filehelper,开启后跳过标签检查直接 AI 回复
  • filehelper 普通客户化改造:走标准 customer 流程复用 session_id
  • ChatData 全量数据注入:Regis 时加载全部数据(项目/销售/客户/技巧/人设),Chat 时从 Redis 取出全量注入提示词
  • 提示词 token 优化:compactJSON 递归去空值,减少无效 token

待开发(下一步)

1. 聊天算法深度优化

目标:进一步提升 AI 回复的真实感,让对话更自然、更像真人销售。

优化方向:

  • 回复节奏优化:根据消息内容复杂度动态调整延迟(简单问候快一些,复杂问题慢一些)
  • 上下文感知回复:结合客户画像、历史对话、当前时间段等因素调整回复风格
  • 多轮对话连贯性:确保多轮对话中话题过渡自然,不重复、不突兀
  • 情绪感知:识别客户情绪(犹豫/急迫/不满),动态调整回复语气和节奏
  • 回复长度自适应:根据客户消息长度调整回复长度(客户发短句,AI 也回短句)
  • 提示词迭代:根据实际对话效果持续优化 BasePrompt,减少 AI 味、增强人味

涉及文件:

  • internal/data/constants/prompt.go(提示词模板)
  • internal/biz/advice_wx.go(splitReplyToChunks / hostingChatAndSend)
  • internal/biz/advice_wx_send.go(typingDelay 算法)
  • internal/biz/advice_chat.go(Chat / ChatWithSession)
  • internal/biz/advice_strategy.go(智能策略回复)

2. 标签标记为客户标签 → 客户管理联动

目标:给标签打上"客户标记",被标记标签下的好友自动识别为客户,在客户管理模块体现,并在销售托管(自动回复)开启时仅对这些客户生效。

设计思路:

  • ai_advice_label 表新增 is_customer_tag TINYINT(1) DEFAULT 0 字段(1=客户标签)
  • 新增 API:advicer/label/mark_customer { selfWxid, labelId, isCustomerTag: bool }
  • 前端标签 tab:每个标签右侧增加"客户标签"开关/标记按钮
  • 客户管理模块(advice_client.go / client.go):
    • 查询客户时,筛选 ai_advice_customer.label_list 包含已标记标签 ID 的记录
    • 客户列表展示来源标签名称
  • 智能策略(advice_strategy.go):
    • 自动回复/主动触达判断时,仅对"客户标签下的好友"生效(非客户标签的好友不触发 AI 自动回复)
    • 读取 ai_advice_label 中 is_customer_tag=1 的标签 ID 集合,与消息发送者的 label_list 取交集

涉及文件:

  • 后端:ai_advice_label.gen.go(加字段)、advice_label_impl.go(加 MarkCustomer 方法)、advice_label.go(加 MarkCustomer biz)、label.go(加 handler)、advicer_data.go(加 DTO)、advice_strategy.go(过滤逻辑)
  • 前端:wx.js(标签 tab 渲染标记按钮)、theme.css(标记样式)
  • SQL:06_ai_advice_label_customer_tag.sql(ALTER TABLE 或迁移脚本)

3. 好友/会话列表 → 给好友打标签

目标:在好友列表和会话列表中,可以对好友(非 chatroom)添加到指定标签,类似微信原生标签管理。

设计思路:

  • 利用微信协议 API contacts/modifyFriendLabel(或类似端点)修改好友的标签
  • 前端:联系人详情悬浮气泡(popup)中增加"管理标签"入口,点击弹出标签选择弹窗
    • 展示当前好友已有的标签(从 state.brief[wxid].labelList 解析)
    • 展示所有可用标签(state.labels)
    • 支持勾选/取消,确认后调用微信 API 修改,再更新 DB(ai_advice_customer.label_list)
  • 修改后需同步更新 state.brief[wxid].labelList 并重新渲染

涉及文件:

  • 后端:可能需要新增微信代理 API(pkg/wx/qs_*.go + api.go + wx_proxy.go wxDispatchMap)
  • 前端:wx_chat.js(popup 中加"管理标签"按钮 + 标签选择弹窗)、wx.js(标签更新逻辑)
  • DB:更新 ai_advice_customer.label_list 字段

Wire 依赖注入须知

新增模块后必须:

  1. 各层 provider_set.go 添加 Provider
  2. http.go 的 NewHTTPServer 参数列表添加 Service
  3. router.go 的 SetupRoutes 参数列表添加 Service
  4. router/advicer.go 的 AdvicerRouterRegist 参数列表添加 Service
  5. 执行 cd cmd/server && wire 重新生成 wire_gen.go
  6. go build ./cmd/server 编译验证

避坑清单

  1. *.gen.go 和 wire_gen.go 禁止手动编辑
  2. Mongo 全量更新是 $set 语义 — 必须"先读后合并再写",否则清空未提交字段
  3. 微信代理响应必须设 skip_response_wrap — 否则被 registerCommon 二次包装成 {data:{code,data}}
  4. 前端修改必须更新版本号 — project.html 中 ?v=N,不更新则浏览器用旧缓存
  5. state.app.appId 不缓存 — 每次 mount 从 advicer/info API 实时获取
  6. checkOnlineLoop() 已内含 connectWs() — 不要额外调用,否则连接级联断开
  7. loadProfile() 返回 Promise — 依赖 state.app.wxid 的操作必须在其 .then() 中执行
  8. 子模块通过 WxN.state getter 访问状态 — 不要在闭包中缓存 state 引用
  9. builder.Cond 查询用指针 — DataTemp 方法接受 *builder.Cond 指针
  10. dataTemp.ReqPageBo 分页字段是 Limit — 不是 PageSize
  11. 新增 Service 后同步三个文件 — http.go、router.go、advicer.go 函数签名都要加参数
  12. 会话/消息查询用 selfWxid 筛选 — 不用 appId(appId 会变)
  13. WebSocket 订阅 appId 必须与广播 appId 一致 — 前端用 wx_device_id,后端用回调的 Appid 字段
  14. 历史遗留代码(internal/biz/handle、llm_service)是死代码,可忽略
  15. advicer_project 和 advicer_project_data 是两个独立的 MongoDB 集合 — 前者存结构化维度(RegionValue/CompetitionComparison 等),后者存扁平栏目数据(key=栏目名, value=内容)。加载项目数据时两者都要查
  16. HostingSkillId 为空时取最新 — 用 VersionList(projectId) 取第一条,不是跳过
  17. PreviousResponseID 续写不继承 system prompt — Chat 时必须从 Redis 取 ChatData 重新注入全部数据,不能依赖 context cache 续写
  18. 修改 JS/CSS 后必须更新版本号 — 否则浏览器用旧缓存,看起来像没改

编译与部署

# 编译(仅验证业务代码)
go build ./internal/entitys/ ./internal/services/advice/ ./cmd/server/

# 本地运行
go run cmd/server/main.go

# Wire 重新生成
cd cmd/server && wire

# Docker 部署
docker-compose up -d

最后更新:2026-09-27