营销增长
自托管销售漏斗从部署到发布:用 Autonnel 在…
当需要在自己控制的基础设施上低成本自托管销售漏斗(落地页、结账、一键追加销售、感谢页),并让 AI 代理通过 MCP 以编程方式创建和编辑漏斗时使用。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 38/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 59/100 ×0.15
适用场景
自托管销售漏斗从部署到发布:用 Auto特定
解决思路
- 选择运行方式:默认推荐 Cloudflare Workers(生产环境,静态资源免费无限,只需自备 Postgres);Docker 用于两分钟本地评估或已有服务器;源码检出(Node)用于修改 Autonnel 本身或直接运行 Node 构建。无论哪条路径,都先用 Docker 快速查看产品,本地构建不会浪费,因为两条路径共用同一 schema 和管理 UI。
- Cloudflare Workers 部署:依次执行 npx wrangler login、npx wrangler kv namespace create CACHE_KV、npx wrangler hyperdrive create autonnel-db --connection-string="$DATABASE_URL"($DATABASE_URL 从操作者自己的 shell 环境读取,不要把凭据写进命令或 wrangler.toml)。把每条命令打印的 id 写入项目旁的 .env:CF_WORKER_NAME、CF_KV_NAMESPACE_ID、CF_HYPERDRIVE_CONFIG_ID。
- 设置密钥并部署: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,不要手工编辑生成的 wrangler.toml,每次构建都会覆盖;cron 表达式从应用的 cron 注册表读取。缺少任何 CF_* 变量时生成会失败并指出变量名,这是预期行为。
- 首次访问前先对同一个 Postgres 应用一次数据库 schema,然后打开 Worker URL 完成 /setup 向导创建管理员账号。目标为 Workers 时优先用 npm run dev:cf(Workers 运行时开发服务器)和 npm run preview:cf(wrangler dev 本地预览),而不是 astro dev,因为运行时不同。
- 后续运维 CLI(需要数据库访问而非容器):在检出目录中让 DATABASE_URL 指向同一个 Postgres,执行 npx autonnel admin:create you@example.com 'a-strong-password' 创建管理员,npx autonnel password:reset you@example.com 重置密码。
- Docker 路径:从 https://github.com/autonnel/autonnel 获取仓库(Apache-2.0),检出 release tag,阅读其 docker-compose.yml 了解镜像和端口,然后 docker compose up,打开 http://localhost:4321 完成 /setup。compose 文件会启动 Postgres、应用 schema 并运行应用;商店、支付、媒体存储、邮件和 AI 稍后在管理 UI 中按需配置。
- 公开主机前在 docker-compose.yml 旁放置真实密钥的 .env: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: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(环境变量值来自操作者自己的环境,不要写进命令或文件)。固定精确 tag 而非 :latest,拉取新 tag 后重新应用 schema(compose 文件的一次性 schema 服务每次 docker compose up 都会重跑)。健康检查端点 /api/health(覆盖数据库和缓存连通性)。容器内管理 CLI:docker compose exec app node dist/cli/index.js admin:create you@example.com 'a-strong-password'。
- 源码检出路径(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 会忽略的 overrides);npm run db:push;npm run dev(或 npm run build && npm run start)。schema 位于检出目录的 prisma/schema.prisma。管理 CLI:在项目目录执行 npx autonnel admin:create you@example.com 'a-strong-password'。
- 在管理 UI 的 Settings 中只配置漏斗需要的项:Ecommerce(Shopify、WooCommerce、Picocart)、Payments(Stripe、PayPal)、Storage(任意 S3 兼容桶:R2、S3、MinIO)、Email(SMTP、Resend、AWS SES)、LLM(任意 OpenAI 兼容端点)、Ad platforms(Facebook、TikTok、Google Ads、Bing;前三个还支持广告级花费/点击报告)。配置顺序:先 catalog(约束结账能卖什么),再 payments,再 storage,再 email,最后 ad platforms(跟踪要对照真实订单验证)。在 Workers 上 R2 是显然的存储选择。
- 构建页面:漏斗内角色为 LANDING(入口页,可多个)、CHECKOUT(订单表单,每个漏斗一个)、UPSELL(购后报价,可多个并排成链)、THANKYOU(确认页,一个)、ERROR(支付失败/兜底,一个)。API 上页面自身 type 为 CHECKOUT | THANKYOU | ERROR | UPSELL | CUSTOM,落地页以 CUSTOM 创建并通过在漏斗中绑定为 LANDING 成为落地页。优先用基于组件的可视化编辑器(输出可 diff 的 JSON),而非原始 HTML,便于 A/B 测试和代理后续编辑。先建结账页再建落地页,因为结账决定能卖什么和价格。
- 接线漏斗:创建漏斗后按角色和顺序附加页面。同一页面可被多个漏斗引用;唯一的唯一性规则是 stepSlug 在单个漏斗内唯一(且一个页面在每个漏斗中最多出现一次)。只有当两个漏斗需要分叉时才克隆落地页,因为编辑共享页面会影响所有引用它的漏斗。THANKYOU 和 ERROR 在创建时会从已有页面自动绑定;若租户没有则先创建,没有错误页的漏斗在支付被拒时会静默失败。
- 广告投放用的 URL 是 storefront 主机上落地页自己的 slug:https://shop.example.com/{page.slug},绝不用 /n/ 步骤 URL。/n/{funnelId}/{stepSlug} 是向前重定向到下一步,应放在页面 CTA 链接中,这样步骤换页时买家路径保持稳定。发布是显式的且按页面和按漏斗分别版本化——发布页面不会发布漏斗绑定。
- 上线前不可省略的检查:1) 用真实支付提供商完成一次真实端到端购买,包括基础订单、一次接受的追加销售、一次被拒的追加销售、感谢页、收据邮件、订单出现在已连接的商店中;2) 对该订单做一次退款,确认按笔退款路径可用;3) 确认 fbclid / ttclid / gclid / msclkid 到达订单记录;4) 在每个广告平台的事件调试器中确认服务端转化到达,附带 click id 且与浏览器事件无重复;5) 用强制拒付卡确认错误页可达。在 Workers 上再加一项:确认 cron scheduled 处理器在触发(排队的 postback 和召回活动依赖它)。只有完成这些后才增加投放。
- 通过 MCP 从代理操作:实例在 /api/mcp 暴露管理 API 为 MCP 工具。配置示例:{"mcpServers":{"autonnel":{"transport":"http","url":"https://<your-autonnel-host>/api/mcp","headers":{"Authorization":"Bearer <your-api-key>"}}}}。Claude Code 用项目根的 .mcp.json,Claude Desktop 用 claude_desktop_config.json。在管理 UI 的 Settings → API Keys 生成密钥,每次调用限定在该密钥的租户内;读工具任意密钥可用,写工具需要开启 writeAccess。写密钥当作生产凭据:每个代理一个,报告用只读密钥,撤销而非共享。
- 用原始 HTTP 调用 /api/mcp 时有两个传输要求:端点使用 Streamable HTTP,请求必须带 Accept: application/json, text/event-stream(否则在到达工具层前就返回 406);成功响应是 SSE 帧(event: message / data: {...}),解析前必须从 data: 中取出 JSON-RPC 信封。
- Cloudflare 默认托管 bot 规则会拒绝 Python 标准库的 user agent:urllib 客户端发送 Python-urllib/3.x 会在所有路径(包括 /api/health)收到 403 和纯文本 error code: 1010,看起来像认证或权限失败且不是 JSON。只有该 agent 字符串被拦截,curl、python-requests、httpx、axios、node 和空 UA 都通过。用 Python 脚本时显式设置 User-Agent 或使用 requests/httpx;如果所有调用(包括未认证的)都返回 403,就是这个原因,任何 API key 都修不好。
- 理解失败调用的返回形态:被 CDN 边缘拦截返回 HTTP 403 纯文本 error code: 1010 无 JSON;Accept 头缺失或不完整返回 HTTP 406 带 JSON-RPC error 对象(-32000);认证失败返回 HTTP 401 无 JSON-RPC 帧;未知工具名返回 HTTP 200 带真实 JSON-RPC error 对象和数字码(唯一这种形态的带内失败,因为发生在分发前);其他所有情况(缺少写权限、校验失败、未找到、冲突、服务器错误)返回 HTTP 200 且 result.isError === true,消息在 result.content[0].text,没有 JSON-RPC error 对象。所以:若存在 error,先看 HTTP 状态再下结论;否则在信任 result.content 前检查 result.isError,并阅读消息而非盲目重试。
- 写操作规则:漏斗步骤就是 { stepSlug, pageId },顺序即数组顺序;步骤在流程中的角色是被引用页面自身的 type,没有 pageType、order 或 subOrder 字段;stepSlug 在 add_funnel_page 上必填、在漏斗内唯一,构成 /n/{funnelId}/{stepSlug}(是向前重定向而非步骤自身 URL);schema 是 .strict(),多余字段会被拒绝而非忽略。步骤以引用的页面为键,所以一个页面在每个漏斗中最多出现一次,但可被多个漏斗引用。没有 LANDING 页面类型,页面 type 为 CHECKOUT | THANKYOU | UPSELL | ERROR | CUSTOM,大写存储。感谢页和错误页步骤是活引用而非快照,编辑该页面会影响所有引用它的漏斗,需要分叉时克隆。草稿和已发布分离:写入落在 draftData,publish: true 提升并失效渲染缓存;先写、用 get_page 验证、再第二次调用发布。draftData 不做结构校验,畸形文档会被接受,只在渲染时出错。组件类型必须来自 get_template({ key }),绝不用记忆中的列表,因为组件集会随版本改名和扩展,而 draftData 未校验,过时组件名会干净保存并渲染空白。内容必须匹配页面的 editorType:editorType 在 create_page 上可选且默认 PUCK,所以未指定创建的 HTML 页面会静默变成 Puck 页面;create_page 只接受 draftData,update_page 在 PUCK 页面接受 draftData、在 HTML / GRAPESJS 页面接受 htmlContent,不匹配会被拒绝;创建时显式设置 editorType,因为之后无法通过这些工具更改。媒体先于页面:upload_media 服务端抓取 URL 并返回 CDN URL 供组件 props 使用;本地文件没有 URL,改走 REST:POST /api/v1.1/media/upload,multipart/form-data 带 file 部分,返回 201 和 { assetId, url };只接受 image/jpeg|png|webp|gif 和 video/mp4|webm,无 writeAccess 的密钥得到与其他写端点相同的 403 写拒绝信封;若从未配置存储则返回 412 并指明 Settings -> Storage。API 无法删除页面:没有 delete_page 工具,/api/v1.1/pages/{pageId} 也没有 DELETE,只有 GET 和 PUT;漏斗可删(delete_funnel),页面不能,所以每个废弃或命名错误的页面都是永久的,直到有人在管理 UI 中删除;首次就慎重选择 slug,试运行用一次性实例而非正在构建的租户。get_stats 统计唯一用户而非浏览量。list_orders 不能按漏斗过滤,其金额把最小单位除以 100,对 USD/EUR 正确,对 JPY 或 BHD 错误。广告花费在 MCP 或 REST 上不可用:管理 UI 有 Facebook、Google Ads、TikTok 的广告级花费、点击和转化报告(在 Settings → Ads 连接,在 Analytics → Ads 或漏斗自己的 Analytics 标签查看),但都不是 MCP 工具或 /api/v1.1/ 端点;Bing 在任何地方都没有花费/点击报告,只有 token 模式的转化 postback。
- 遵循构建顺序:list_products → list_templates → get_template → 先媒体(远程 URL 用 upload_media(MCP),本地文件用 POST /api/v1.1/media/upload(REST,multipart),然后把返回的 CDN url 换进即将写入的内容)→ create_page × N(name、slug、type、editorType)→ update_page(draftData 或 htmlContent)→ get_page(验证)→ update_page({ publish: true }) → create_funnel → add_funnel_page × N(pageId + 唯一 stepSlug)→ get_funnel → GET /storefront/{page.slug} 期望 200。
- 理解 URL 语义:/n/{funnelId}/{stepSlug} 从不渲染它命名的步骤,而是向前重定向——找到该步骤,302 到下一步的裸页面 slug 并附加 ?funnelId=,在最后一步返回 404 No next step in funnel。entryStepSlug 也不是要宣传的 URL,它字面上是 steps[0],而 create_funnel 在你添加任何东西前就追加了感谢页和错误页步骤,所以这样构建的漏斗 steps[0] 是感谢页。页面在 storefront 主机上以自己的裸 slug 提供:https://{storefront-host}/{page.slug};从 get_funnel 读取 steps,选 page.type 为 CUSTOM 的步骤,宣传该步骤的 page.slug;仅当页面绑定多个漏斗时才附加 ?funnelId={id}。
- 裸 slug 只在非管理主机上工作:当主机不匹配 ADMIN_DOMAIN 时中间件把 /{slug} 重写到 storefront 渲染器;在管理主机上该路径落到管理应用并 404,看起来像未发布页面。因此只能通过 *.workers.dev 管理主机名访问的 Workers 部署在附加 storefront 域名前没有干净的落地页 URL,购买流量前先附加域名。
- 仅用 API key 验证时请求 /storefront/{slug}:该前缀在所有主机(包括管理主机)上都是公开的,渲染购物者看到的内容,但只提供已发布页面——草稿在那里 404 而 get_page 仍返回其 draftData。/preview/{slug} 确实渲染草稿但需要登录会话,API key 无法使用。花钱前确认 200 且正文包含预期文案。
- 修改线上页面:get_page → 只编辑你打算改的 props → update_page 不发布 → get_page 对比 → 发布。绝不要为一个标题重新生成整个 draftData blob;update_page 整体替换文档,而组件 JSON 携带你没有写、也无法重建的 prop 值。
- 注意 REST 与 MCP 不是同一表面:REST 与 MCP 使用相同的 Authorization: Bearer <api-key> 头,没有则返回裸 401。十三个工具通过桥接也可经 REST 访问,运行相同 schema 和处理器,因此不会漂移。七个是 MCP 独有:list_funnels、get_funnel、list_pages、get_page、list_products、deliver_order、get_stats。其中一些在同一路径有更旧的、独立编写的 REST 端点,响应形状或参数名不同(GET /products 用 q 而非 search)。不要假设同名 REST 端点与工具匹配。路径陷阱:/api/v1.1/templates 列出的是 Settings 的邮件模板,不是 Puck 页面模板;页面模板在 /api/v1.1/page-templates。
- 如实说明运维成本:Workers 路径无需打补丁服务器,但你要负责 Postgres 及其备份,免费计划限制是每日上限,会令操作失败而非计费,流量高峰前要知道自己最接近哪个限制。Docker 路径的升级、备份和正常运行时间都归你,schema 变更随镜像发布,升级时必须应用。支付配置及其 PCI 范围在所有路径上都是操作者的责任。自托管构建没有支持 SLA,GitHub 的 Issues 和 Discussions 是渠道。首次生产部署要按小时而非分钟预算,两分钟是本地 Docker 评估而非上线。文档:https://autonnel.com/docs。
适用边界 · 注意事项
- 需要开箱即用的托管服务、支持 SLA 或不愿自行负责 Postgres 备份、升级和正常运行时间时,不适合自托管。
- 只通过 *.workers.dev 管理主机名访问且尚未附加 storefront 域名时,没有干净的落地页 URL,不应购买流量。
- 需要代理通过 MCP 或 REST 查询广告花费/点击报告时不可行(Bing 连 UI 都没有),必须由人打开管理 UI 查看。