一个人卖在线课程、电子书和模板,每天被同一批问题淹没:"这门课有免费试看吗?""账号怎么拿到?""我该买哪个套餐?"手工回复撑不起规模,于是他把客服做成了一个AI机器人——全天候应答,还能在无人介入的情况下直接发放试用账号。
![]()
这套系统跑在一台群晖NAS上,单个容器,TypeScript加Node 20。前端接grammY(轻量Telegram机器人框架),走webhook模式;Hono负责接收webhook并响应健康检查;Cloudflare隧道把域名直接指进容器,没有公网IP,不开端口。模型调用统一走自建的LiteLLM网关,机器人只认一个别名,换供应商或加兜底都不用改代码。对话和用户记忆存在PostgreSQL里。
为什么值得做?账算得很直白:机器人答掉的每个常规问题,都是团队不用再答的问题;半夜和周末的提问几秒内就有回应;一句"有试用吗"能在两次点击内变成已领取的试用账号;长期记忆让回头客被当成熟客打招呼;上新商品机器人自动就知道,不用重训、不用新写FAQ页。
先检索,再回答
把商品目录整段塞进提示词,通用大模型会编。所以这套机器人的顺序是检索优先。它有三层检索器:SKU词元匹配,用正则抓住CZH01这类精确编号;整句子串匹配,应付短而准的查询;分段匹配,拆开中文串、剥掉"有/吗/哪些"这类疑问助词,再过滤英文停用词。前五条命中结果作为"事实"注入系统提示词,模型被要求只依据检索到的内容作答,查不到就明说。
有个细节比看上去重要:顾客问"CZH01有免费版吗",机器人能答对,是因为检索器命中付费SKU时会自动挂上它的免费孪生条目并排到第二位。一个动作,把"抱歉没有"变成了正确的追加销售。
试用发放,全程无人
顾客有三种方式触发试用:/trial命令、一句自然语言(比如"我要这个试用"加商品链接)、或者带SKU的深链。机器人不绕n8n,直接写NocoDB,插入客户行和客户-商品行,既有webhook接手自动开通账号,机器人本身不碰文件服务器。整个流程幂等:客户已有账号就复用,不建重复;商品插入失败就回滚客户行,重试不会走进死胡同。同一个Telegram用户有多个账号时,机器人会把它们列成内联按钮,问清挂到哪一个。
需要可靠性的环节一律不用提示词工程,改用确定性状态机。购买意图在进模型之前就被拦截,驱动内联键盘(在线商店→管理员→返回),不给自由发挥的空间。深链用?start=携带SKU,但Telegram只允许字母、数字、下划线和连字符,冒号会静默失效——他踩过这个坑,分隔符从buy:SKU改成了buy-SKU。WhatsApp里的跑题消息会让模型输出一个哨兵值,代理层把它翻译成"什么都不发",机器人宁可沉默也不胡扯。
三个各吃掉一下午的bug
第一个是无限回复循环。服务器没在超时窗口内确认,Telegram会重投同一条更新;而处理函数因为模型延迟跑得太久,于是同一条消息被反复回答。修法是设50秒窗口并让超时直接返回,重投螺旋当场停住。
第二个是模型别名400错误。直接调原始模型名会报错,只有LiteLLM别名能用。这现在是仓库里的硬规则:永远用别名,不用上游原名。
第三个是WhatsApp的m_text bug。他通过浏览器扩展把WhatsApp Web事件POST到webhook,某个"自定义webhook载荷"设置里模板值为空字符串,扩展就把字段名m_text当成了消息正文发出去,机器人于是回复"我不懂m_text"。关掉那个设置、改用默认载荷,适配器就能正确读取。
三个故障没有一个是出在难的部分——模型和检索都没事,全栽在webhook生命周期和载荷契约这些无聊的边缘上。他的结论是:模型是最容易的部分,真正会断的是确认窗口、载荷格式和开通流程的幂等性,这些应该第一天就设计。
现在这一个容器同时服务Telegram和WhatsApp两个渠道,按SKU或自然语言检索到正确的商品条目,端到端发放试用账号,配套约175个单元测试覆盖确定性流程、检索和载荷契约。
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.