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.jsapi/agent/oauth.jsagents/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.jsagents/acquisition/tools/seo.tools.jsservices/acquisition/gbp-sync-service.jsapi/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.jsagents/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.jsservices/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.jsagents/acquisition/tools/review.tools.js (evaluateReviewRequest, sendReviewRequest)services/acquisition/review-evaluator.jsagents/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.jsagents/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.jsconfig/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 或预约 |
获客子图的出口边 A10 对应宏观 DAG 的 E1 获客 → 转化(状态 ⚠️)。
当前状态:GBP Sync 推送服务/价格/营业时间到 Google 商家页面,增加被发现概率。
但"被发现"到"发起预约"之间依赖 GBP Console 手动配置 booking URL(Reserve with Google 或自定义链接),
平台无法通过 API 自动设置该链路。
可能的改进路径:
{{system.booking_link}} 已可用)
Review Agent 的入口事件 checkout.completed 来自 Payment 子图(E4 收款 → 留存)。
Inngest review-solicitation 函数监听该事件,完成跨子图数据流。这条入口边已完全连通。