架构
技术栈和布局
互客鱼是一个包含两个前端的单一 Laravel 代码库。后端是 PHP 8.3+ 上的 Laravel 13 + Octane;管理 UI 是 Inertia v3 + React 19;访客小部件是 Preact ≤50KB。AI 技术栈默认使用 Cloudflare(Workers AI + Vectorize + Browser Rendering),以 OpenAI + Qdrant 作为备选。
技术栈
| 层级 | 技术 |
|---|---|
| 应用框架 | Laravel 13(PHP 8.3+) |
| 服务器 | FrankePHP 上的 Laravel Octane |
| 实时通信 | Laravel Reverb(WebSocket) |
| 队列 | Redis 上的 Laravel Horizon |
| 身份验证 | Laravel Fortify(会话、2FA、密码重置)+ Sanctum(API token) |
| 账单 | Laravel Cashier(Stripe) |
| 数据库 | Postgres 16 |
| 缓存/会话 | Redis 7 |
| 管理前端 | Inertia v3 + React 19、Tailwind v4、shadcn/ui(Radix)、Vite、严格 TS |
| 类型化路由 | Wayfinder(Laravel 路由的 TS 绑定) |
| 访客小部件 | Preact 10(别名为 React)+ Vite + 选择性 Tailwind v4 |
| LLM(首选) | Cloudflare Workers AI —— Llama 3.3 70B + bge-base-en-v1.5 |
| LLM(备选) | OpenAI gpt-4o-mini + text-embedding-3-small(以及 OpenRouter 作为路由器) |
| 向量存储(首选) | Cloudflare Vectorize |
| 向量存储(备选) | Qdrant(HTTP 客户端) |
| 爬虫(首选) | Cloudflare Browser Rendering |
| 爬虫(备选) | Browserless → 纯 HTTP |
| 对象存储 | Cloudflare R2(S3 兼容) |
| 托管 | Laravel Cloud |
| 可观测性 | Sentry + OpenTelemetry → Honeycomb / Grafana Cloud |
仓库布局
一个位于仓库根目录的 Laravel 应用。管理前端作为 Inertia 页面在同一应用中提供; 访客小部件是第二个独立的 Vite 构建。
hukeyu/ — Laravel 应用
├── app/
│ ├── Actions/Fortify/ — Fortify 钩子(CreateNewUser 等)
│ ├── Concerns/ — BelongsToWorkspace、BelongsToAgent traits
│ ├── Http/
│ │ ├── Controllers/Admin/ — 客户 + 管理 Inertia 控制器
│ │ ├── Controllers/Widget/ — /api/v1/widget/*(访客端,JWT)
│ │ └── Middleware/
│ ├── Models/ — Workspace、Agent、Conversation、Plan、…
│ ├── Services/
│ │ ├── Rag/ — Retriever、Chunker、PromptBuilder、CuratedAnswerMatcher
│ │ ├── Llm/ — OpenAiHttpClient、WorkersAiClient、Fakes
│ │ ├── Vector/ — VectorizeClient、QdrantHttpClient
│ │ ├── Crawl/ — CloudflareBrowserClient、AutoIndexPageVisit、PlainHttpCrawler
│ │ ├── Triggers/ — CtaSelector、LeadIntentDetector
│ │ ├── Analytics/ — EventStore(分析汇总 + 缺口检测在 app/Jobs/Analytics 中)
│ │ ├── Billing/ — StripeProductSync、MeteredBilling、PayPalClient、RazorpayClient
│ │ ├── Tools/ — ToolRegistry + EscalateToHumanTool(第 2 阶段)
│ │ ├── Vertical/ — VerticalPresetRegistry + 7 个预设类
│ │ ├── I18n/ — LocaleResolver、LocaleCatalog(132 种语言)
│ │ └── Widget/ — WidgetJwt、WidgetCopy、InlineBlockParser
│ ├── Jobs/Crawl/ — CrawlSourceJob、CrawlPageJob、IndexDocumentJob
│ ├── Jobs/Analytics/ — DetectGapJob(流后缺口检测)
│ └── Events/ — TokenStreamed、TurnCompleted、TurnFailed
├── resources/
│ ├── js/ — 管理 Inertia(默认 Vite 构建)
│ │ ├── pages/
│ │ ├── components/
│ │ └── …
│ ├── widget/ — 访客小部件(独立 Vite 构建)
│ ├── views/ — Blade(Inertia 根 + 营销 + 文档)
│ └── css/app.css — Tailwind v4 入口
├── routes/
│ ├── web.php
│ ├── api.php
│ └── channels.php
├── database/{migrations,factories,seeders}
├── tests/{Feature,Unit,Browser}
├── docs/PLAN.md — 完整工程计划
└── public/widget/ — 构建的小部件包
两个前端,一个后端
管理和客户界面是同一个 Inertia 应用——相同的 Vite 构建,相同的组件库。 角色通过路由组和中间件分离,而不是通过代码库。这为设计令牌、路由帮助器和 身份验证状态保持单一事实来源。
访客小部件则相反——它有意与管理代码共享任何内容。它不能从
resources/js/ 导入;它有自己的 Vite 配置;它有自己的路由器
(只是一个 Preact 组件树)和自己的状态。大小预算是硬性的 50KB 压缩——
管理功能不能渗入。
Reverb 和 WebSocket
Reverb 作为独立进程运行并支持:
- 收件箱实时更新 ——操作员在新消息到达时看到它们。
- 人工接管事件 ——当操作员认领对话时,访客的小部件获得"人工在此"事件。
- 操作员存在 ——可用/离开状态在团队成员之间同步。
频道默认是私有的——小部件使用其 JWT 加入 conversation.{id},
操作员应用使用其会话加入 workspace.{id}。
Cloudflare 一账单模式
设置 CLOUDFLARE_ACCOUNT_ID + CLOUDFLARE_API_TOKEN,
LLM、向量存储和爬虫都自动绑定到 Cloudflare。外部基础设施总成本:
$5/月 Workers Paid + 按请求使用。替换任何一个部分(例如,通过设置
QDRANT_URL 将 Vectorize 换成 Qdrant),绑定就会转移。
多租户隔离
每个租户范围的查询都由 BelongsToWorkspace trait 的全局作用域过滤。
跨越边界需要显式的 withoutWorkspaceScope() 并提供合理的注释。
有一个回归测试,如果带有 workspace_id 列的模型不使用该 trait,
构建将失败。详见 多租户。