自托管漏斗启动
在操作者控制的基础设施上,从零开始将漏斗发布上线,使用 Autonnel(Apache-2.0)。此技能是构建步骤;首先使用 sales-funnel-blueprint 设计漏斗,如果尚未决定,则使用 funnel-platform-picker 确认自托管是正确的选择。
步骤 1:选择运行方式
| 路径 | 低流量成本 | 运维负担 | 适用场景 |
|---|---|---|---|
| Cloudflare Workers | 几乎为零,外加一个 Postgres | 无服务器,无需修补 | 生产环境默认选项。 漏斗页面主要是静态资源,Workers 可免费且无限制地服务 |
| Docker | 一个 VPS 或容器主机的成本 | 您负责:升级、备份、可用性 | 两分钟内本地评估,或用于您已运行并希望保留数据的服务器 |
| 源码检出(Node) | 一个 VPS 或容器主机的成本 | 您负责 | 修改 Autonnel 本身,或直接运行 Node 构建 |
除非操作者有特定原因,否则推荐使用 Workers:其定价模式几乎完美匹配漏斗场景,并且消除了人们避免自托管的一整类工作。
无论哪种方式,都先使用 Docker 进行初次查看。这是看到产品的最快方式,并且您本地构建的任何东西都不会浪费:两种路径都使用相同的数据库模式和相同的管理后台。
步骤 2a:Cloudflare Workers(近乎免费的生产路径)
为何成本接近零
漏斗流量绝大多数是页面、图片和脚本的请求。在 Workers 上,这些属于静态资源请求,免费且无限制,无存储成本——只有调用 Worker 的请求(服务端渲染的页面、结账、API)才需计费。漏斗的动态面很小:订单表单、接受追加销售、回传队列。
已验证的 Cloudflare 免费计划限制(于 2026-08 检查;使用前请确认最新数值):
| 资源 | Workers 免费计划 |
|---|---|
| 静态资源请求 | 免费且无限制,无存储费用 |
| Worker 调用 | 每天 100,000 次请求 |
| Hyperdrive(Postgres 连接池) | 免费套餐可用,每天 100,000 次数据库查询 |
| Workers KV(页面缓存) | 每天 100,000 次读取,每天 1,000 次写入,1 GB 存储 |
| Cron 触发器 | 支持(仓库提供了 scheduled 处理器) |
不免费的部分:Postgres。Hyperdrive 将连接池连接到您提供的数据库,因此您仍然需要一个 Postgres 提供商。托管提供商有自己的免费套餐和限制,这是需要规划的唯一成本项。
您首先会达到的上限是 KV 写入,而不是请求。 每天 1,000 次写入对于服务页面来说很充裕,但对于发布来说则较紧张,因为发布会使缓存的条目失效并刷新。大量编辑的一天可能会耗尽写入配额,而流量远未达到任何限制。如果发布在流量正常时开始失败,就是这个限制,而不是一个 bug。
部署
该仓库提供了完整的 Workers 工具链:包含 cron scheduled 处理器的 Worker 入口(src/cf-worker.ts)、wrangler.toml 生成、KV 缓存连接和用于 Postgres 的 Hyperdrive。
npx wrangler login
npx wrangler kv namespace create CACHE_KV
# $DATABASE_URL 是操作者自己的 Postgres 连接字符串,在 shell 中导出
# 请勿将凭据写入此命令或 wrangler.toml 中。
npx wrangler hyperdrive create autonnel-db --connection-string="$DATABASE_URL"
每个命令都会打印一个 ID。将它们放在项目旁边的 .env 中:
CF_WORKER_NAME=my-funnels
CF_KV_NAMESPACE_ID=<来自 kv namespace create 的 ID>
CF_HYPERDRIVE_CONFIG_ID=<来自 hyperdrive create 的 ID>
然后设置 Secrets 并部署:
npx wrangler secret put DATABASE_URL
npx wrangler secret put AUTH_SESSION_SECRET # openssl rand -hex 32
npx wrangler secret put CREDENTIALS_ENCRYPTION_KEY # openssl rand -base64 32
npm run deploy:cf
deploy:cf 先从模板构建并生成 wrangler.toml,因此没有单独的生成步骤。Cron 表达式从应用的 cron 注册表中读取,而不是手动写入配置——请勿手动编辑生成的 wrangler.toml,因为每次构建都会被覆盖。
如果缺少 CF_* 变量,生成过程会失败并指明变量名称。这是预期行为;这些变量没有静默默认值。
在首次访问之前,针对同一个 Postgres 应用一次数据库 schema,然后打开 Worker URL 并完成 /setup 向导以创建管理员帐户。
另外,还提供 npm run dev:cf(Workers 运行时的开发服务器)和 npm run preview:cf(通过 wrangler dev 进行本地预览)。当目标是 Workers 时,建议优先使用这些命令而不是普通的 astro dev,因为运行时不同。
后续操作
下面的 CLI 命令需要数据库访问权限,而不是容器。从 DATABASE_URL 指向同一 Postgres 的检出目录中运行:
npx autonnel admin:create [email protected] 'a-strong-password'
npx autonnel password:reset [email protected]
步骤 2b:Docker(本地评估或您自己的服务器)
从 https://github.com/autonnel/autonnel(Apache-2.0)获取仓库,检出一个发布标签,并阅读其 docker-compose.yml——它声明了将要运行的镜像和端口。从该检出目录中:
docker compose up
打开 http://localhost:4321 并完成 /setup。Compose 文件会启动 Postgres、应用 schema 并运行应用。无需其他操作即可启动:商店、支付、媒体存储、电子邮件和 AI 稍后在管理后台配置,且仅用于实际使用的功能。
在将其暴露到公共主机之前,在 docker-compose.yml 旁边放置一个 .env 文件,填入真实的 Secrets——发货的默认值是用于开发的非安全值,它们的存在只是为了第一次本地运行无需任何配置:
AUTH_SESSION_SECRET=$(openssl rand -hex 32)
CREDENTIALS_ENCRYPTION_KEY=$(openssl rand -base64 32)
ADMIN_DOMAIN=admin.example.com
每个值只生成一次并保持稳定。轮换 AUTH_SESSION_SECRET 会使所有会话失效;轮换 CREDENTIALS_ENCRYPTION_KEY 会使存储的提供商凭据不可读,这意味着需要重新输入每个支付和平台的凭据。
多架构镜像已发布到 GHCR,可直接使用 docker run 连接到现有数据库:
# DATABASE_URL、AUTH_SESSION_SECRET 和 CREDENTIALS_ENCRYPTION_KEY 来自
# 操作者自己的环境——切勿将其值写入命令或文件中。
docker run -p 4321:4321 \
-e DATABASE_URL \
-e AUTH_SESSION_SECRET \
-e CREDENTIALS_ENCRYPTION_KEY \
-e ADMIN_DOMAIN="admin.example.com" \
ghcr.io/autonnel/autonnel:v1.5.0
固定到精确标签而不是 :latest,并在拉取较新标签后重新应用 schema——compose 文件中的一次性 schema 服务会在每次 docker compose up 时重新运行。供编排器使用的健康端点:/api/health(涵盖数据库和缓存连接)。
容器内的管理 CLI:
docker compose exec app node dist/cli/index.js admin:create [email protected] 'a-strong-password'
步骤 2c:源码检出(Node)
用于修改 Autonnel 本身,或在您已拥有的主机上运行 Node 构建。需要 Node 22+ 和 Postgres:
npm create autonnel@latest my-funnel
cd my-funnel
cp .env.example .env # 设置 DATABASE_URL 和 ADMIN_DOMAIN
pnpm install # pnpm 10+;仓库锁定了 npm 会忽略的 override
npm run db:push
npm run dev # 或:npm run build && npm run start
这会克隆仓库并丢弃其 git 历史。Schema 位于检出的 prisma/schema.prisma;db:push 会同步它。管理 CLI:在项目目录下运行 npx autonnel admin:create [email protected] 'a-strong-password'。
步骤 3:仅配置漏斗所需的内容
在管理后台上方的设置中:
| 设置项 | 需要用于 | 选项 |
|---|---|---|
| 电商 | 产品和订单数据 | Shopify、WooCommerce、Picocart |
| 支付 | 收款 | Stripe、PayPal |
| 存储 | 图片/视频上传 | 任何兼容 S3 的存储桶(R2、S3、MinIO) |
| 邮件 | 收据、召回活动 | SMTP、Resend、AWS SES |
| LLM | AI 页面生成 | 任何兼容 OpenAI 的端点 |
| 广告平台 | 服务器端转化(全部四个);广告级别花费/点击报告(仅 Facebook、Google Ads、TikTok) | Facebook、TikTok、Google Ads、Bing |
避免返工的操作顺序:先配置目录(它约束了结账可售卖的物品),然后是支付,接着是存储,然后是邮件,最后是广告平台——跟踪需要基于真实订单进行验证,因此需要其他功能先正常运行。连接广告平台用于花费报告是一个独立步骤,与将其绑定到漏斗用于回传不同(参见 server-side-conversion-tracking);如果您两者都需要,则都要做。
在 Workers 上,R2 是明显的存储选择:它兼容 S3,并将媒体出站流量保留在 Cloudflare 内部。
步骤 4:构建页面
漏斗角色直接映射到漏斗规范:
| 漏斗中的角色 | 用途 | 每个漏斗允许多个? |
|---|---|---|
LANDING | 入口页面,每个流量角度一个 | 是 |
CHECKOUT | 订单表单 | 否——每个漏斗一个 |
UPSELL | 购买后优惠,按排序形成链条 | 是 |
THANKYOU | 确认页面 | 否 |
ERROR | 支付失败/回退页面 | 否 |
这些是页面在漏斗内部的角色。通过 API,页面的自身类型为 CHECKOUT | THANKYOU | ERROR | UPSELL | CUSTOM——着陆页创建为 CUSTOM,通过绑定到漏斗中成为 LANDING。
提供两个编辑器:基于组件的可视化编辑器(输出可 diff 的 JSON)和用于导入页面的原始 HTML。对于任何将进行 A/B 测试或稍后由智能体编辑的页面,首选组件编辑器——JSON diff 审查清晰,HTML 则不然。
先构建结账页面,再构建着陆页。结账页面决定了实际可售卖的物品和价格,先写的着陆页可能会承诺结账无法提供的内容。
步骤 5:连接漏斗
- 创建漏斗,然后附加页面及其角色和顺序。
- 同一个页面可以被多个漏斗引用。领域强制执行的唯一唯一性规则是
stepSlug在一个漏斗内唯一(且一个页面在每个漏斗中最多出现一次)——没有跨漏斗拒绝需要设计。仅在希望两个漏斗产生差异时克隆着陆页,因为对共享页面的编辑会影响所有引用它的漏斗。 - 如果已有
THANKYOU和ERROR页面,则在创建漏斗时自动绑定;如果租户没有,则在上线前创建它们。没有错误页面的漏斗在付款被拒绝时会静默失败。 - 用于广告活动的 URL 是着陆页自身在店铺主机上的 slug——
https://shop.example.com/{page.slug}——绝不是/n/步骤 URL。/n/{funnelId}/{stepSlug}会向前重定向到下一步,因此它属于页面的 CTA 链接,而不是广告。这样编写 CTA 可以在步骤被替换为另一个页面时保持买家路径稳定。 - 发布是显式且按页面和漏斗版本化的——发布页面不会发布漏斗绑定。
步骤 6:发送流量前进行检测
不可协商的启动前检查:
- 一次真实的端到端购买,使用真实支付提供商,包括:基础订单、一个接受的追加销售、一个拒绝的追加销售、感谢页、收据邮件,以及订单出现在连接的商店中。
- 对该订单进行一次退款,以确认每次收费的退款路径正常工作。
- 点击 ID 覆盖——确认
fbclid/ttclid/gclid/msclkid到达订单记录。参见server-side-conversion-tracking。 - 服务器端转化到达每个广告平台的事件调试器,附带点击 ID,并且与浏览器事件不重复。
- 错误页面可访问,通过强制使用被拒绝的卡进行确认。
在 Workers 上,再增加一项:确认 cron scheduled 处理器正在触发(排队的回传和召回活动依赖于此)。一个静默丢失 cron 触发器的部署看起来正常,但后台工作会悄然停止。
只有完成这些检查后才能增加花费。一个没有经过真实交易推送的漏斗,其故障率是未知的,而不是低的。
步骤 7:通过 MCP 从智能体操作
实例将其管理 API 作为 MCP 工具暴露在 /api/mcp,因此智能体可以在没有 UI 的情况下构建和更改漏斗。
{
"mcpServers": {
"autonnel": {
"transport": "http",
"url": "https://<your-autonnel-host>/api/mcp",
"headers": { "Authorization": "Bearer <your-api-key>" }
}
}
}
直接使用原始 HTTP 调用 /api/mcp 而不是 MCP 客户端有两个传输要求,SDK 通常隐藏这些要求。端点使用 Streamable HTTP:请求必须携带 Accept: application/json, text/event-stream——缺少此头会被拒绝并返回 406,然后才会触及工具层——成功响应以 SSE 帧的形式返回(event: message / data: {...}),因此需要从 data: 中解包 JSON-RPC 信封才能解析。
在应用前面还有一个陷阱:位于 Cloudflare 后面的实例,如果启用了默认的托管机器人规则,会拒绝 Python 标准库的用户代理。urllib 客户端发送 Python-urllib/3.x 并在每个路径(包括 /api/health)上收到 403,纯文本正文为 error code: 1010,这看起来完全像身份验证或权限失败,并且不是 JSON,因此解析正文的客户端在能看到状态码之前就会抛出异常。只有该代理字符串被阻止——curl、python-requests、httpx、axios、node 和空的 UA 都能通过。如果您用 Python 编写脚本,请设置显式的 User-Agent 或使用 requests/httpx;如果每个调用(包括未经身份验证的)都返回 403,这就是原因,并且任何 API 密钥都无法解决。
项目根目录下的 .mcp.json 用于 Claude Code;claude_desktop_config.json 用于 Claude Desktop。在 admin → 设置 → API 密钥中生成密钥。每个调用都限定在生成密钥的租户范围内,因此密钥无法访问其他租户的数据。工具和资源直接在数据库中创建和更新行——无需用户操作或确认——并且大多数操作在提交前都会进行数据校验。失败的写入通常会返回一条解释错误的消息,但某些校验失败会返回更详细的错误。
步骤 8:指标和观察
Autonnel 暴露了几个 Prometheus 风格的计数器:step_view_total、order_placed_total、order_failed_total、webhook_delivery_total。在 /api/metrics 上通过 HTTP 获取。如果您运行了 Prometheus,进行抓取;如果没有,对于调试 curl 就足够了。
MCP 工具表面
当通过 API 密钥进行身份验证时,管理 API 在 /api/mcp 上暴露以下 MCP 工具:
page_create、page_update、page_delete、page_publish——对应 CRUD 操作。页面类型包括 CHECKOUT、THANKYOU、ERROR、UPSELL、CUSTOM。作为对象创建登录页面。
funnel_create——创建带元数据的空漏斗。
step_create、step_update、step_delete——创建一个步骤将页面绑定到漏斗。步骤具有 stepSlug 和 weight,后者控制漏斗中的顺序。
funnel_publish——发布整个漏斗:创建一个包含所有页面及其步骤分配的可部署版本。
product_sync——从连接的目录提供商提取产品和变体。
catalog_list_products——列出目录中所有产品及其变体。
conversion_log_lookup——通过点击 ID 或外部参考查找转化事件。
ad_account_list——列出已配置账号的广告账户。
每个工具的更多细节可在管理 UI 或直接调用端点时通过 X-MCP-Help 标头获得。
重要的是,通过 API 创建的元素在 admin UI 中管理功能受限:UI 支持发布、版本管理、删除和整个对象的查看,但特定于编辑器的工具(例如,拖放排序或内联文本编辑器)仅适用于 UI 搜索路径。
导致大多数写入失败的错误
根据生产使用情况,以下是造成大多数静默管道失败的原因:
| 规则 | 原因 | 症状 |
|---|---|---|
| 发布一个页面也会发布其绑定到的漏斗的当前版本 | 呀!一个新发布对页面和其关联的漏斗都创建了新的版本记录。页面版本不绑定到漏斗版本,而是侧面记录 | 页面发布后,漏斗状态陈旧;另一个版本冲突让你回滚 |
| 不要假设步骤会保留权重秩序 | 插入新步骤会为所有步骤重新计算权重,但在高并发下不会保持顺序 | 页面顺序混乱 |
funnel_publish 在未设置的漏斗上失败 | 没有设置支付、没有产品、没有存储端点、没有设置邮件 | API 返回“基本配置缺失” |
| 在漏斗之外编辑页面影响所有引用它的漏斗 | 对页面的编辑会传播到所有地方。这里没有“草稿版”与“发布版”,只有草稿 vs 发布 | 怀疑地看 |
| 未能设置错误页面意味着静默失败(向用户显示 500) | 错误绑定是强制性的,但不会在漏斗创建时检查 | 订购失败,用户看到 500 |
| 多云校验在创建过程中运行,但只在某些字段上 | 验证模式在 MCP 中是严格的:匹配或不匹配 | “Field X”不经意地被跳过 |
| 预定义类别匹配敏感 | JSON 必须仅匹配字符串列表。任何额外的键——不匹配 | API 抛出“意外的字段” |
| 关于删除和对活动漏斗的更改的约束条件:如果在活动漏斗中使用了,不能删除页面 | 确保维护活跃模板 | 不能删除 |
下一步
现在您已完成“构建”阶段:部署、配置、构建和连接。在此之后:
- 销售漏斗蓝图——设计漏斗:页面映射、流量划分、追加销售阶梯、定价
- 服务器端转化追踪——使用系统保证将事件发送到广告平台(在连接广告平台之后)
- Gerónimo 信件——使用 AI 自动生成销售页面
- 漏斗平台选择器——如果 self-host 不是,帮助选择