Biz Next.js Chatbot OpenRouter — 一步为网站安装AI浮动聊天机器人
此技能将一个AI浮动聊天机器人小部件嵌入到现有的 Next.js 项目中。小部件位于右下角,完全响应式适配三种屏幕尺寸。它通过 OpenRouter 调用 LLM,并使用来自 FAQ 和课程/产品文档的知识库。在助手气泡中使用 react-markdown + remark-gfm 渲染 Markdown(数字/价格加粗、项目符号列表、可点击链接、内联代码),并能自动提取并保存潜在客户信息(姓名 + 电话 + 邮箱)到 KV 存储(Upstash Redis / Vercel KV),当用户在聊天中提供完整信息时触发——管理员可以通过 /admin 仪表盘查看。目标用户:需要在销售页面上实现 24/7 自动支持机器人 + 被动潜在客户捕获的独立创业者/课程创作者,不错过任何通过聊天咨询的客户。
核心理念:自动检测所有可能的项目信息(Next.js 版本、App/Pages Router、TS/JS、Tailwind/CSS),只向用户询问真正需要的 4 件事:(1) 如果有多个项目,选择哪一个;(2) FAQ;(3) 产品文档;(4) 语气/品牌名称。其余部分由代码自动完成 → 几分钟内上线。
何时使用
用户希望:
- 为销售课程/服务的着陆页添加 AI 客服聊天机器人
- 减少重复性问题(价格、路线图、退款、适合对象...)的回复负担
- 需要一个位于右下角、小巧且不遮挡内容的聊天小部件
- 需要聊天机器人精确理解他们的课程/产品内容(由 FAQ 驱动)
- 已有一个 Next.js 项目(刚运行完
/biz-sales-page-layout,或已有旧项目)
何时不使用
- 项目不是 Next.js(纯 React Vite、Vue、Svelte、纯 HTML)→ 此技能专为 Next.js 设计。可以适配但不优化。
- 需要具有数千页知识库的 RAG(检索增强生成)聊天机器人→ 需要向量数据库(Pinecone/Supabase pgvector),此技能使用简单的 system prompt,适用于 ≤ 20K token 的知识。
- 需要语音/多模态聊天机器人(发送图片)→ 此技能仅支持文本。
- 需要为每个用户将聊天历史记录保存到数据库→ 此技能为无状态、临时会话,后续可扩展。
- 后端不适用于 Vercel(需要长轮询、持久 WebSocket)→ OpenRouter 流式传输在 Vercel Edge 上运行良好,但如果需要单独的 WS 则不同。
用户将获得的输出
技能运行完成后:
-
用户 Next.js 项目中的新文件:
components/Chatbot.tsx(或.jsx)— 小部件 UI(通过react-markdown+remark-gfm渲染 Markdown)app/api/chat/route.ts(App Router)或pages/api/chat.ts(Pages Router)— 调用 OpenRouter 并自动提取潜在客户信息的 API 代理 + 流式传输lib/chatbot-knowledge.ts(或.js)— 知识库 + system prompt(包含回复格式化块 + 主动潜在客户收集块)lib/chat-lead.ts— 仅当项目有 KV/Upstash 时才创建— 提取器和保存器,按电话号码去重,TTL 90 天,命名空间chat-lead:{phone}- Markdown 样式
.chatbot-md追加到app/globals.css(来自templates/chatbot-markdown-styles.css) - 创建/追加
.env.local文件,包含OPENROUTER_API_KEY=(占位符) .env.example(如果尚不存在)以记录环境变量- 将
<Chatbot />挂载到app/layout.tsx或pages/_app.tsx
-
额外安装的包(通过从锁文件检测到的包管理器):
react-markdown— 安全地渲染 Markdown(无需dangerouslySetInnerHTML)remark-gfm— GitHub 风格的 Markdown(表格、自动链接、删除线)
-
会话结束时的明确指南:
- 用户必须自行完成的 3 个步骤:(a) 获取 OpenRouter API 密钥,(b) 粘贴到
.env.local,(c)npm run dev测试 - 如何在 Gemini 和 Sonnet 之间切换模型
- 如何稍后更新知识库
- 用户必须自行完成的 3 个步骤:(a) 获取 OpenRouter API 密钥,(b) 粘贴到
工作流程
步骤 1 — 检测 Next.js 项目
查找 Next.js 项目候选。运行:
# 查找 package.json 中包含 "next" 依赖项的文件,最大深度 4
find /Users/tonyhoang/Documents/GitHub -maxdepth 4 -name "package.json" -not -path "*/node_modules/*" 2>/dev/null | xargs grep -l '"next"' 2>/dev/null | head -20
(如果用户当前在特定的工作目录中,则优先从 cwd 向下查找,如果未找到,则回退到 ~/Documents/GitHub。)
分支处理:
- 0 个项目: 通知用户并停止。建议:“您有 Next.js 项目吗?如果没有,是否需要我使用
/biz-sales-page-layout创建一个新的,然后再回来?” - 1 个项目: 在继续之前与用户确认路径:“我将把聊天机器人安装到 [路径]。您同意吗?”
- ≥ 2 个项目: 列出路径和名称(来自
package.json的name),编号列表,让用户选择:“我发现了 N 个 Next.js 项目。您想安装到哪一个?”
步骤 2 — 探测项目上下文
在用户确认项目后,读取:
-
package.json→ 检查:- TypeScript(deps 中是否有
typescript?) - Tailwind(devDeps 中是否有
tailwindcss?) - 包管理器(锁文件:
pnpm-lock.yaml→ pnpm,yarn.lock→ yarn, 默认npm) - Next.js 版本
- TypeScript(deps 中是否有
-
目录结构 → 检查:
app/目录存在 → App Routerpages/目录存在(且没有app/)→ Pages Router- 两者都存在→ 优先为新文件使用 App Router(Next.js 正在迁移)
src/包装器 → 调整路径(例如src/app/、src/components/)
-
tsconfig.json→ 检查路径别名(@/*→./src/*还是./*) -
.env.local、.env.example→ 检查是否已存在 -
KV/Upstash 可用性(用于自动潜在客户捕获):
package.json中有@upstash/redis或@vercel/kv→ 有 KV → 脚手架 chat-lead.ts 并连接到路由- 存在文件
src/lib/kv.ts(或lib/kv.ts)导出kv→ 直接使用现有 helper(营地模式) - 没有 KV → 跳过脚手架 chat-lead.ts,通知用户:“项目没有 KV — 自动潜在客户捕获未启用。我可以运行
/biz-setup-sepay-payment先安装 KV 然后回来启用潜在客户捕获,或者跳过。”
将这些观察结果保存在一个小的表格中,以便在后续步骤中使用。
步骤 3 — 从用户收集知识库
向用户询问 4 个问题(合并到一条消息中,以便用户一次性回答,减少来回):
为了让聊天机器人准确回答,我需要 4 样东西:
1. **FAQ** — 常见问题及解答列表(价格、路线图、退款、适合对象、学习时间、课后支持...)。
请直接粘贴到这里,**或**提供文件路径(.md / .txt / .docx)。
2. **产品/课程文档** — 课程的详细描述:名称、每个模块的内容、学习成果、讲师、奖励、如果有多档价格则列出价格层级。
同样粘贴或提供文件路径。
3. **品牌名称 + 语气** — 聊天机器人使用的品牌/课程名称(例如“AI Mastery 的助手”)。机器人使用的语气:亲切的“您”(默认)/ 正式 / 随和的“你”。
4. **模型** — 默认 `google/gemini-3-flash-preview`(快速、便宜、适合客服)。想要使用 `anthropic/claude-sonnet-4.6`(质量更高,贵约 10 倍)用于早期阶段,或者如果初期对话量仍然较低,请告诉我。
您可以一次性回答吗?
当用户回复时:
- 如果只提供文件路径 → 使用 Read 工具读取该文件。
- 如果直接粘贴内容 → 使用粘贴的内容。
- 如果用户说“帮我自动生成占位符” → 创建通用的 FAQ 占位符,并标记
// TODO: user fill in以提醒用户需要更新。 - 如果没有品牌名称 → 默认“AI 助手”并使用“您”称呼。
步骤 4 — 生成文件到项目中
读取此技能 templates/ 目录下的模板并适配到项目中。模板包括:
templates/chatbot-knowledge.ts— 导出知识库和 systemPrompt 的模块。在此处注入 FAQ、产品信息、品牌名称和语气。包含现成的回复格式化块(Markdown 规则)+ 主动潜在客户收集块(教 LLM 在检测到购买意向时索要姓名/电话/邮箱,当用户提供时表示感谢并确认,不要说“已保存到数据库”)。templates/Chatbot.tsx— 小部件组件,优先使用 Tailwind。已导入react-markdown+remark-gfm,使用<ReactMarkdown remarkPlugins={[remarkGfm]}>渲染助手消息(仅助手 — 用户消息仍为纯whitespace-pre-wrap)。如果项目没有 Tailwind → 回退到templates/Chatbot-noTailwind.tsx+templates/chatbot.module.css。templates/chat-route-app.ts— App Router 的 API 路由(app/api/chat/route.ts)。包含现成的persistLeadIfReady钩子,在流关闭后以即发即弃方式运行 — 不阻塞响应。templates/chat-route-pages.ts— Pages Router 的 API 路由(pages/api/chat.ts)。templates/chat-lead.ts— 仅当项目有 KV/Upstash 时才搭建 — 正则表达式提取越南电话 + 邮箱 + 名称启发式(介绍模式“我叫 X”/“我是 X”/“姓名: X”),saveChatLead按电话号码去重,TTL 90 天,键命名空间chat-lead:{phone}。templates/chatbot-markdown-styles.css— 类.chatbot-md的 CSS 代码片段。必须追加到app/globals.css的末尾(或src/app/globals.css/styles/globals.css)。在追加之前,根据项目的品牌调色板更改强调色(#fde68a/#f59e0b)。templates/env-instructions.md— 获取 OpenRouter 密钥的指南片段,在最后一步使用。
适配模板时的规则:
-
TS vs JS: 如果项目只有 JS,将扩展名
.ts/.tsx更改为.js/.jsx并删除类型注解(不仅仅是重命名文件)。读取示例文件,解析,删除类型,写入。 -
路径别名: 如果
tsconfig将@/*映射到./src/*,则模板中的导入必须使用@/lib/chatbot-knowledge,并将文件放置在src/lib/中。如果没有别名,则使用相对路径。 -
模型: 将占位符
__MODEL_ID__替换为用户选择的模型(google/gemini-3-flash-preview或anthropic/claude-sonnet-4.6)。 -
品牌: 替换
__BRAND_NAME__、__BRAND_TONE__。 -
知识: 从 FAQ 和产品信息构建结构清晰的
systemPrompt(参见模板)。在 systemPrompt 中保留回复格式化块 — 这是教 LLM 输出适合小气泡聊天的 Markdown 的关键(使用粗体代替标题,一行一个短项目符号)。 -
安装用于 Markdown 渲染的包(必须 — 如果跳过,聊天机器人将在气泡中显示原始
**bold**而非加粗文本):# 检测锁文件 → 选择合适的包管理器 npm install react-markdown remark-gfm # 或:pnpm add react-markdown remark-gfm # 或:yarn add react-markdown remark-gfm -
追加 Markdown 样式: 读取
templates/chatbot-markdown-styles.css,根据项目的品牌调色板(在globals.css中查找--color-primary或等效项)交换强调色,然后追加到app/globals.css(或src/app/globals.css/styles/globals.css)的末尾。 -
挂载小部件:
- App Router: 编辑
app/layout.tsx(或src/app/layout.tsx),添加import Chatbot from '@/components/Chatbot'并在</body>前添加<Chatbot />。在编辑前读取当前的 layout.tsx 以保留 metadata、fonts、providers。 - Pages Router: 编辑
pages/_app.tsx,包装 return:<><Component {...pageProps} /><Chatbot /></>。在编辑前读取_app,保留现有的 providers。
- App Router: 编辑
-
自动潜在客户捕获连接(仅在步骤 2 中检测到 KV 时才执行):
- 将
templates/chat-lead.ts复制到lib/chat-lead.ts(或src/lib/chat-lead.ts)。 - 如果项目还没有
lib/kv.ts→ 创建:import { Redis } from "@upstash/redis"; export const kv = Redis.fromEnv(); - 路由处理程序导入:
import { extractLeadFromMessages, saveChatLead } from "@/lib/chat-lead"。 - 在流循环中:跟踪
assistantText(累积增量),当流关闭时 → 调用persistLeadIfReady(history, assistantText)(即发即弃,不要在响应路径中等待)。 - System prompt:确保主动潜在客户收集块已存在于
chatbot-knowledge.ts中(模板已包含)。 - 向用户确认: “我已启用自动潜在客户捕获 — 当客户在聊天中粘贴姓名/电话/邮箱时,系统会自动保存到 KV 命名空间
chat-lead:{SĐT}。您可以通过/admin查看潜在客户(如果您已安装/biz-admin-leads-dashboard)。”
- 将
步骤 5 — 设置 .env.local 和 .gitignore
-
检查
.env.local是否已存在?- 不存在: 创建新文件,内容如下:
# OpenRouter API 密钥 — 在 https://openrouter.ai/keys 获取 OPENROUTER_API_KEY= # 可选:用于 OpenRouter 跟踪引荐来源的站点 URL(有助于提高速率限制) NEXT_PUBLIC_SITE_URL=http://localhost:3000 - 已存在: 如果尚未包含,则追加上述两个变量,不要覆盖现有变量。
- 不存在: 创建新文件,内容如下:
-
更新
.env.example(如果尚未存在则创建):OPENROUTER_API_KEY=sk-or-v1-xxxxx NEXT_PUBLIC_SITE_URL=https://your-domain.com -
检查
.gitignore是否已包含.env.local?如果没有 → 追加.env.local和.env*.local。
步骤 6 — 向用户报告
输出一条清晰摘要消息,包含 4 个部分:
✅ 聊天机器人已成功安装到 [项目名称]
📁 已创建的文件:
- components/Chatbot.tsx(通过 react-markdown + remark-gfm 渲染 Markdown)
- app/api/chat/route.ts
- lib/chatbot-knowledge.ts
- app/globals.css(追加了 Markdown 样式 .chatbot-md)
- .env.local(占位符)
📦 已安装的包: react-markdown, remark-gfm
🔑 让聊天机器人运行所需的 3 个步骤:
1. 获取 OpenRouter API 密钥:
- 访问 https://openrouter.ai → 注册(可以使用 Google 登录)
- 访问 https://openrouter.ai/keys → 创建密钥
- 充值信用额度(https://openrouter.ai/credits)— Gemini 3 Flash 很便宜,$5 足以处理数千次聊天
- 复制密钥,格式为 `sk-or-v1-xxxxx`
2. 粘贴到 `.env.local` 文件中:
OPENROUTER_API_KEY=sk-or-v1-xxxxx-paste-vào-đây
3. 测试运行:
[npm/pnpm/yarn] run dev
打开 http://localhost:3000 → 点击右下角的气泡
🔄 在 Gemini ↔ Sonnet 之间切换模型:
打开 `app/api/chat/route.ts`,修改文件开头的 `model:` 变量。
- `google/gemini-3-flash-preview`(默认,$0.075/1M 输入,快速)
- `anthropic/claude-sonnet-4.6`(高级,~$3/1M 输入,质量更好)
📚 日后更新知识库:
编辑 `lib/chatbot-knowledge.ts` — 添加/修改 FAQ 和 productInfo,保存,热重载即可。
移动优先的响应式设计
小部件必须满足三种断点:
| 断点 | 行为 |
|---|---|
| ≤ 640px(手机) | 点击气泡 → 打开全屏覆盖层(z-index 9999),标题栏有 X 关闭按钮。输入框停靠在底部,带有 safe-area-inset 以避免被键盘遮挡。 |
| 641–1023px(平板) | 面板 380px × 70vh,固定在右下角,边距 16px。 |
| ≥ 1024px(桌面) | 面板 400px × 600px,固定在右下角,边距 24px。气泡为 60px 圆形。 |
...
[内容已截断以供翻译]