← Back to Business DAG Index

Acquisition Sub-DAG — 获客子图审计

095-acquisition-agent-group | SEO, Content, Review, Competitor, Landing
2026-03-22 | Audit by code trace

获客子图健康度

10
总边数
5
已连通
3
部分连通
2
断裂

获客子图 DAG

已连通 (代码 + 测试完整)
部分连通 (代码存在但链路松或测试缺)
断裂 (未实现或无代码连接)
graph TD
  OAUTH["OAuth Connection"]
  SEO["Local SEO Agent"]
  GBP_SYNC["GBP Sync Service"]
  CONTENT["Content Agent"]
  SAFETY["Content Safety Filter"]
  PUBLISH["Content Publisher"]
  REVIEW["Review Agent"]
  POLL["GBP Review Poller"]
  EVAL["Review Evaluator"]
  REPLY["Review Reply Service"]
  COMP["Competitor Tracker"]
  SNAP["Competitor Snapshots DB"]
  LANDING["Landing Page"]
  DEMO["Demo Request API"]
  CONV_EXIT["Conversion Entry (Booking)"]

  OAUTH -->|"A1 Token Management"| SEO
  OAUTH -->|"A1b Token Management"| POLL
  OAUTH -->|"A1c Token Management"| PUBLISH
  SEO -->|"A2 Sync Services/Hours"| GBP_SYNC
  CONTENT -->|"A3 Caption Generation"| SAFETY
  SAFETY -->|"A4 Approved Content"| PUBLISH
  REVIEW -->|"A5 Solicitation Flow"| EVAL
  POLL -->|"A6 New Reviews"| EVAL
  EVAL -->|"A7 AI Reply"| REPLY
  REPLY -->|"A7b Safety Check"| SAFETY
  COMP -->|"A8 Places API Snapshots"| SNAP
  LANDING -->|"A9 Demo Form"| DEMO
  GBP_SYNC -->|"A10 Booking URL"| CONV_EXIT

  style OAUTH fill:#1a2332,stroke:#8b5cf6,color:#e0e0e0
  style SEO fill:#1a2332,stroke:#3b82f6,color:#e0e0e0
  style GBP_SYNC fill:#1a2332,stroke:#3b82f6,color:#e0e0e0
  style CONTENT fill:#1a2332,stroke:#06b6d4,color:#e0e0e0
  style SAFETY fill:#1a2332,stroke:#f59e0b,color:#e0e0e0
  style PUBLISH fill:#1a2332,stroke:#f59e0b,color:#e0e0e0
  style REVIEW fill:#1a2332,stroke:#22c55e,color:#e0e0e0
  style POLL fill:#1a2332,stroke:#22c55e,color:#e0e0e0
  style EVAL fill:#1a2332,stroke:#22c55e,color:#e0e0e0
  style REPLY fill:#1a2332,stroke:#22c55e,color:#e0e0e0
  style COMP fill:#1a2332,stroke:#22c55e,color:#e0e0e0
  style SNAP fill:#1a2332,stroke:#22c55e,color:#e0e0e0
  style LANDING fill:#1a2332,stroke:#8b949e,color:#e0e0e0
  style DEMO fill:#1a2332,stroke:#8b949e,color:#e0e0e0
  style CONV_EXIT fill:#161b22,stroke:#ef4444,stroke-dasharray:5,color:#8b949e

  linkStyle 0 stroke:#22c55e,stroke-width:2px
  linkStyle 1 stroke:#22c55e,stroke-width:2px
  linkStyle 2 stroke:#22c55e,stroke-width:2px
  linkStyle 3 stroke:#22c55e,stroke-width:2px
  linkStyle 4 stroke:#f59e0b,stroke-width:2px,stroke-dasharray:5
  linkStyle 5 stroke:#ef4444,stroke-width:2px,stroke-dasharray:5
  linkStyle 6 stroke:#22c55e,stroke-width:2px
  linkStyle 7 stroke:#f59e0b,stroke-width:2px,stroke-dasharray:5
  linkStyle 8 stroke:#f59e0b,stroke-width:2px,stroke-dasharray:5
  linkStyle 9 stroke:#22c55e,stroke-width:2px
  linkStyle 10 stroke:#22c55e,stroke-width:2px
  linkStyle 11 stroke:#f59e0b,stroke-width:2px,stroke-dasharray:5
  linkStyle 12 stroke:#ef4444,stroke-width:2px,stroke-dasharray:5
    

边审计表

状态 涉及端口 代码路径 备注
A1 ✅ OAuth Connection → Agents API :3000 services/oauth/oauth-service.js
api/agent/oauth.js
agents/inngest/acquisition-functions.js (oauth-token-refresh cron)
OAuth 服务完整实现:initiate / callback / refresh / disconnect;
AES-256-GCM 加密 token;CSRF state HMAC 验证;
getActiveConnection() 自动刷新临近过期的 token(5 min buffer);
Inngest cron 每 45 min 批量刷新过期 token;
API 测试:tests/api/agent-oauth.test.js
单元测试:tests/unit/services/oauth-service.test.js
E2E 测试:tests/e2e/agent-oauth-connect.spec.ts
A2 ✅ SEO Agent → GBP Sync API :3000 agents/acquisition/local-seo.agent.js
agents/acquisition/tools/seo.tools.js
services/acquisition/gbp-sync-service.js
api/agent/seo.js (POST /sync)
Agent 通过 seo.tools.js 调用 gbp-sync-service 的 syncServices / syncHours / syncToGBP;
syncToGBP 含 3 次重试 + 指数退避,最终失败创建 required agent_task 告警;
GBP API 调用通过 gbp_api circuit breaker;
Inngest 函数 gbp-service-sync (service.updated) 和 gbp-hours-sync (store.hours_changed) 实现事件驱动自动同步;
API 端点 POST /api/agent/seo/sync 支持手动触发;
单元测试:tests/unit/services/gbp-sync.test.js
集成测试:tests/integration/gbp-sync.test.js
A3 ⚠️ Content Agent → Safety Filter API :3000 agents/acquisition/content.agent.js
agents/acquisition/tools/content.tools.js (generateCaption, generatePromoPost)
services/acquisition/content-safety-filter.js (filterContent)
Content tools 未使用共享 filterContent()
generateCaption 和 generatePromoPost 使用内联 unsafePatterns 正则数组做安全检查,
规则集与 content-safety-filter.js 不一致(内联版缺少 incentive terms、profanity blocklist、PII 检测、长度限制);
content-safety-filter.js 的 filterContent 仅被 review-reply-service.js 调用;
建议:content.tools.js 应调用共享 filterContent('social_post', ...) 而非内联正则;
单元测试:tests/unit/services/content-safety-filter.test.js(仅测共享版本)
A4 ❌ Safety Filter → Content Publish API :3000 services/acquisition/content-safety-filter.js
services/acquisition/content-publisher.js (publishPost)
api/agent/content.js (POST /posts/:id/approve)
publishPost() 未被任何 Inngest 函数或 API 端点调用
content-publisher.js 实现了完整的 IG / FB / Google Post 发布逻辑 + 24h engagement poll,但无入口触发;
/posts/:id/approve API 仅将状态更新为 scheduled,但无后续 cron/Inngest 函数消费 scheduled 状态并调用 publishPost();
getWeeklyPromoCount() 被 promo-post Inngest 函数使用,说明文件被部分引用;
断裂原因:缺少 "scheduled post publisher" Inngest cron 函数来消费 scheduled 状态帖子;
集成测试:tests/integration/content-publish.test.js(测服务本身,但无端到端调用链测试)
A5 ✅ Review Agent → Evaluator (Solicitation) API :3000 agents/acquisition/review.agent.js
agents/acquisition/tools/review.tools.js (evaluateReviewRequest, sendReviewRequest)
services/acquisition/review-evaluator.js
agents/inngest/acquisition-functions.js (review-solicitation)
Inngest review-solicitation 函数监听 checkout.completed 事件;
评估逻辑:feature flag → phone → tip threshold → cooldown → weekly cap → TCPA 静默窗口;
通过检测后 sleep(delayHours) 再发 SMS (SMSSenderService);
Agent tool evaluateReviewRequest 和 sendReviewRequest 也可手动触发;
所有决策记录到 agent_decisions 审计表;
单元测试:tests/unit/services/review-evaluator.test.js
集成测试:tests/integration/review-solicitation.test.js
A6 ⚠️ GBP Review Polling → Evaluation API :3000 services/acquisition/gbp-review-poller.js (pollReviews)
agents/inngest/acquisition-functions.js (gbp-review-poll cron */15)
services/acquisition/review-reply-service.js
pollReviews() 每 15 min 通过 Inngest cron 轮询 GBP Reviews API;
新 review 发出 google.review.new agent event via emitAgentEvent();
但无 Inngest 函数监听 google.review.new 来触发 generateReviewReply()
review-reply-service 的 generateReviewReply() 必须手动从 UI/API 触发;
pollReviews() 还匹配 review_requests 进行 solicitation 归因(reviewReceived = true);
checkAutoReplySuggestion() 分析历史接受率并建议启用自动回复(但仅生成 informational task);
松点:Polling → Auto-Reply 自动化链路未闭环(需人工审批后手动 publishReviewReply)
A7 ⚠️ Evaluator → Auto-Reply (Review Reply) API :3000 services/acquisition/review-evaluator.js (评估 solicitation)
services/acquisition/review-reply-service.js (generateReviewReply, publishReviewReply)
agents/acquisition/tools/review.tools.js (generateReviewReply, publishReviewReply tools)
generateReviewReply() 调用 Claude Sonnet 生成回复 → filterContent('review_reply') 安全检查 → logDecision → 创建 agent_task(negative = required, positive = advisory);
publishReviewReply() 将回复推送到 GBP API(PUT /reviews/:id/reply);
注意:publishReviewReply() 的 GBP API 调用 未包装 circuit breaker(违反项目 constitution 要求);
Agent tools 提供 generateReviewReply 和 publishReviewReply 两个工具,支持 human-in-the-loop 审核流程;
Anthropic API 调用通过 anthropic-sonnet circuit breaker 保护;
松点:完整链路需人工确认后才触发 publishReviewReply,无自动发布路径
A8 ✅ Competitor Tracker → Snapshots API :3000 services/acquisition/competitor-tracker.js
agents/acquisition/tools/seo.tools.js (takeCompetitorSnapshot, generateCompetitorReport)
agents/inngest/acquisition-functions.js (competitor-snapshot cron 0 3 * * 1)
api/agent/seo.js (GET/POST/DELETE /competitors, GET /competitors/report)
takeSnapshot() 调用 Google Places API (circuit breaker: google_places) 获取 rating + reviewCount;
UPSERT 到 competitor_snapshots 表(唯一约束防重复);
Inngest cron 每周一 3AM 批量快照 + generateWeeklyReport(),生成 informational agent_task;
API 完整:CRUD competitors + report endpoint;最多 5 个竞对/门店;
单元测试:tests/unit/services/competitor-report.test.js
E2E 测试:tests/e2e/agent-competitor-manage.spec.ts
A9 ✅ Landing Page → Demo Request Landing :3003, API :3000 api/demo-request.js
config/api-permission-map.js (public endpoint)
tests/api/demo-request.test.js
Landing Page 表单 POST /api/demo-request;
Rate limit: 3 req/hour/IP;
发送确认邮件给客户 + 通知邮件给管理员;
公开端点(无 auth);
注意:demo-request 仅发邮件通知,无自动化跟进到租户创建或预约系统
A10 ❌ Acquisition Exit → Conversion Entry - services/acquisition/gbp-sync-service.js (syncServices)
services/templating/variable-engine.js (booking_link 变量)
GBP listing 未配置 booking URL
syncServices() 推送服务/价格数据到 GBP,但 GBP serviceItems 格式中无 booking link 字段(GBP API 限制);
booking_link 变量在模板引擎中可用,但获客 Agent 不直接输出预约链接;
GBP 商家页面的预约按钮需在 Google Business Profile Dashboard 手动配置 URL,非 API 可控;
断裂原因:获客到转化的自动化闭环依赖 GBP Console 手动配置,平台无法程序化控制;
Demo-request 也仅邮件通知,不自动创建 trial tenant 或预约

松散点总结

跨子图引用

Acquisition Exit → Conversion Entry (E1)

获客子图的出口边 A10 对应宏观 DAG 的 E1 获客 → 转化(状态 ⚠️)。

当前状态:GBP Sync 推送服务/价格/营业时间到 Google 商家页面,增加被发现概率。 但"被发现"到"发起预约"之间依赖 GBP Console 手动配置 booking URL(Reserve with Google 或自定义链接), 平台无法通过 API 自动设置该链路。

可能的改进路径

Checkout Event → Review Solicitation (from Payment/Retention)

Review Agent 的入口事件 checkout.completed 来自 Payment 子图(E4 收款 → 留存)。
Inngest review-solicitation 函数监听该事件,完成跨子图数据流。这条入口边已完全连通。

健康度统计

5 条已连通 (A1, A2, A5, A8, A9)
3 条部分连通 (A3, A6, A7)
2 条断裂 (A4, A10)