diff --git a/engage-box/roi_reporting/agent/Dashboard Viz/agent.yml b/engage-box/roi_reporting/agent/Dashboard Viz/agent.yml index 50187075..24061d65 100644 --- a/engage-box/roi_reporting/agent/Dashboard Viz/agent.yml +++ b/engage-box/roi_reporting/agent/Dashboard Viz/agent.yml @@ -2,6 +2,7 @@ name: Dashboard Viz model: claude-4.5-sonnet temperature: 0 max_tool_iterations: 4 +prompt_file: prompt.md outputs: - name: renderReactApp diff --git a/engage-box/roi_reporting/agent/README.md b/engage-box/roi_reporting/agent/README.md index 9781339a..ce3c31ae 100644 --- a/engage-box/roi_reporting/agent/README.md +++ b/engage-box/roi_reporting/agent/README.md @@ -217,9 +217,43 @@ Create dashboard with following conditions: - **Total Revenue** is shown when BOTH direct and contributed revenue exist - Otherwise, only Direct and/or Contributed Revenue are shown separately -## Advanced: Manual Configuration +## Advanced: Manual Configuration via UI -If you prefer manual setup via UI or LLM API instead of `tdx agent push`, use the legacy setup flow described by your internal Treasure Data setup guidance or contact your Treasure Data account team. +If you prefer to set up via the Agent Foundry UI instead of `tdx agent push`, follow these steps using the files in this repository as reference: + +### 1. Create Project + +Navigate to Agent Foundry in the TD Console and create a new project named "ROI Reporting Agent". + +### 2. Create Knowledge Bases + +**Database KB:** +- Type: Database +- Name: `engage_roi_reporting` +- Database: `engage_roi_reporting` +- Reference: `knowledge_bases/engage_roi_reporting.yml` + +**Text KBs** (create two): +- Type: Text +- Name: `OverallSummary_Spec` — copy content from `knowledge_bases/OverallSummary_Spec.md` +- Name: `CampaignDetails_Spec` — copy content from `knowledge_bases/CampaignDetails_Spec.md` + +### 3. Create Agent + +- Name: `Dashboard Viz` +- Model: `claude-4.5-sonnet` +- Temperature: `0` +- Max Tool Iterations: `4` +- System Prompt: copy content from `Dashboard Viz/prompt.md` +- Tools and Outputs: configure as defined in `Dashboard Viz/agent.yml` + +### 4. Create Form Interfaces + +Create two form interfaces using the definitions in: +- `form_interfaces/Overall Summary.yml` +- `form_interfaces/Campaign Details.yml` + +Each file contains `form_schema` (JSON Schema) and `ui_schema` (UI rendering hints) to copy into the respective UI fields. ## License diff --git a/engage-box/roi_reporting/agent/README_JA.md b/engage-box/roi_reporting/agent/README_JA.md index e81e186c..33e189d5 100644 --- a/engage-box/roi_reporting/agent/README_JA.md +++ b/engage-box/roi_reporting/agent/README_JA.md @@ -217,9 +217,43 @@ Create dashboard with following conditions: - **Total Revenue**は、DirectとContributed Revenueの両方が存在する場合に表示 - それ以外の場合は、DirectまたはContributed Revenueのみが個別に表示 -## 上級: 手動設定 +## 上級: UIによる手動設定 -UIやLLM API経由での手動セットアップを希望する場合は、英語版READMEの該当セクションを参照してください。 +`tdx agent push` の代わりに Agent Foundry UIでセットアップしたい場合は、このリポジトリのファイルをリファレンスとして以下の手順に従ってください: + +### 1. プロジェクト作成 + +TD ConsoleのAgent Foundryで「ROI Reporting Agent」という名前の新しいプロジェクトを作成します。 + +### 2. ナレッジベースの作成 + +**Database KB:** +- タイプ: Database +- 名前: `engage_roi_reporting` +- データベース: `engage_roi_reporting` +- リファレンス: `knowledge_bases/engage_roi_reporting.yml` + +**Text KB** (2つ作成): +- タイプ: Text +- 名前: `OverallSummary_Spec` — `knowledge_bases/OverallSummary_Spec.md` の内容をコピー +- 名前: `CampaignDetails_Spec` — `knowledge_bases/CampaignDetails_Spec.md` の内容をコピー + +### 3. エージェントの作成 + +- 名前: `Dashboard Viz` +- モデル: `claude-4.5-sonnet` +- Temperature: `0` +- Max Tool Iterations: `4` +- システムプロンプト: `Dashboard Viz/prompt.md` の内容をコピー +- ツールと出力: `Dashboard Viz/agent.yml` に定義された内容を設定 + +### 4. フォームインターフェースの作成 + +以下の定義ファイルを使用してフォームインターフェースを2つ作成: +- `form_interfaces/Overall Summary.yml` +- `form_interfaces/Campaign Details.yml` + +各ファイルには `form_schema`(JSON Schema)と `ui_schema`(UI表示ヒント)が含まれているので、それぞれUIのフィールドにコピーしてください。 ## ライセンス diff --git a/engage-box/roi_reporting/agent/agent.yml b/engage-box/roi_reporting/agent/agent.yml deleted file mode 100644 index af571c32..00000000 --- a/engage-box/roi_reporting/agent/agent.yml +++ /dev/null @@ -1,17 +0,0 @@ -llm_project: "ROI Reporting Agent" -name: "Dashboard Viz" -model: claude-4.5-sonnet -max_tool_iterations: 4 -temperature: 0 -system_prompt_file: system_prompt.md -knowledge_bases: - - name: OverallSummary_Spec - type: text - file: knowledge_base_overall_summary.md - - name: CampaignDetails_Spec - type: text - file: knowledge_base_campaign_details.md - - name: engage_roi_reporting - type: database - database: engage_roi_reporting -tools_file: tools.yml diff --git a/engage-box/roi_reporting/agent/form_interfaces/td_managed_campaign_details.yml b/engage-box/roi_reporting/agent/form_interfaces/td_managed_campaign_details.yml deleted file mode 100644 index ba1a98ec..00000000 --- a/engage-box/roi_reporting/agent/form_interfaces/td_managed_campaign_details.yml +++ /dev/null @@ -1,12 +0,0 @@ -name: "Campaign Details" -agent: "@ref(type: \"agent\", name: \"Dashboard Viz\")" -prompt_template: | - Create dashboard with following conditions: - - Report_id: 2. Campaign Summary - - Campaign_id: {{Campaign_id}}, - - Journey_id: {{Journey_id}}, - - Language: {{Language}}, - - Currency: {{Currency}} -form_json_schema: '{"type":"object","properties":{"Campaign_id":{"title":"Campaign ID","type":"string","placeholder":"Enter either \"Campaign ID\" or \"Journey ID\""},"Journey_id":{"title":"Journey ID","type":"string","placeholder":"Enter either \"Campaign ID\" or \"Journey ID\""},"Language":{"title":"Language","type":"string","enum":["English","Japanese"],"enumNames":["English","Japanese"]},"Currency":{"title":"Currency","type":"string","enum":["USD","JPY"],"enumNames":["USD","JPY"]}},"required":["Language","Currency"]}' -form_ui_schema: '{"Campaign_id":{"ui:en:title":"Campaign ID","ui:en:placeholder":"Enter either \"Campaign ID\" or \"Journey ID\"","ui:ja:placeholder":"キャンペーンIDまたは、ジャーニーIDを入力してください","ui:ja:title":"キャンペーンID"},"Journey_id":{"ui:en:title":"Journey ID","ui:en:placeholder":"Enter either \"Campaign ID\" or \"Journey ID\"","ui:ja:title":"ジャーニーID","ui:ja:placeholder":"キャンペーンIDまたは、ジャーニーIDを入力してください"},"Language":{"ui:widget":"radio","ui:en:title":"Language","ui:ja:title":"言語","ui:enumNames":["English","Japanese"],"ui:en:enumNames":["English","Japanese"],"ui:ja:enumNames":["英語","日本語"]},"Currency":{"ui:widget":"radio","ui:en:title":"Currency","ui:ja:title":"通貨","ui:enumNames":["USD","JPY"],"ui:en:enumNames":["USD","JPY"],"ui:ja:enumNames":["米ドル","円"]}}' -use_text_resource: false diff --git a/engage-box/roi_reporting/agent/form_interfaces/td_managed_overall_summary.yml b/engage-box/roi_reporting/agent/form_interfaces/td_managed_overall_summary.yml deleted file mode 100644 index 3fb0ab44..00000000 --- a/engage-box/roi_reporting/agent/form_interfaces/td_managed_overall_summary.yml +++ /dev/null @@ -1,13 +0,0 @@ -name: "Overall Summary" -agent: "@ref(type: \"agent\", name: \"Dashboard Viz\")" -prompt_template: | - Create dashboard with following conditions: - - Report_id: 1.Overall Summary - - Start Date: {{Start_date}}, - - End Date: {{End_date}}, - - Timezone: {{Timezone}}, - - Language: {{Language}}, - - Currency: {{Currency}} -form_json_schema: '{"type":"object","properties":{"Start_date":{"title":"Start Date","type":"string","format":"date"},"End_date":{"title":"End Date","type":"string","format":"date"},"Language":{"title":"Language","type":"string","enum":["English","Japanese"],"enumNames":["English","Japanese"]},"Currency":{"title":"Currency","type":"string","enum":["USD","JPY"],"readOnly":false,"enumNames":["USD","JPY"]}},"required":["Start_date","Language","End_date","Currency"]}' -form_ui_schema: '{"Start_date":{"ui:en:title":"Start Date"},"End_date":{"ui:en:title":"End Date"},"Language":{"ui:widget":"radio","ui:en:title":"Language","ui:enumNames":["English","Japanese"],"ui:en:enumNames":["English","Japanese"],"ui:ja:enumNames":["英語","日本語"],"ui:ja:title":"言語"},"Currency":{"ui:widget":"radio","ui:en:title":"Currency","ui:ja:title":"通貨","ui:enumNames":["USD","JPY"],"ui:en:enumNames":["USD","JPY"],"ui:ja:enumNames":["米ドル","円"]}}' -use_text_resource: false diff --git a/engage-box/roi_reporting/agent/knowledge_base_campaign_details.md b/engage-box/roi_reporting/agent/knowledge_base_campaign_details.md deleted file mode 100644 index 15cd2cae..00000000 --- a/engage-box/roi_reporting/agent/knowledge_base_campaign_details.md +++ /dev/null @@ -1,166 +0,0 @@ -# Report Specification: Campaign Details - -## 1. Report Overview -- purpose: "To provide comprehensive performance analysis for a single campaign or journey, including KPIs, trends, and email title breakdown." -- source_tables: - - "email_events" - - "revenue" - -## 2. Filters -- filter: - - id: "campaign_id" - type: "string" - required: true - exclusive_with: "journey_id" -- filter: - - id: "journey_id" - type: "string" - required: true - exclusive_with: "campaign_id" -- filter_notes: "A campaign_id or journey_id is required. When one of these IDs is provided, a date range is NOT required. The report should run on all available data for that ID." -- notes: "Timestamp columns (event_timestamp, conversion_timestamp) are varchar strings. They MUST be parsed using date_parse(column, '%Y-%m-%d %H:%i:%s.%f')." - -## 3. Important Notes on Metrics - -### Unique Count vs Total Count -- **Unique counts** (primary metrics): Use `COUNT(DISTINCT message_id)` to count each message only once, regardless of how many times it was opened/clicked. -- **Total counts** (supplementary information): Use `COUNT(*)` to count all events, including multiple opens/clicks of the same message. -- **Rate calculations**: All rates MUST use unique counts to avoid inflated metrics (industry standard). - -### Identifier Standard -- Use `message_id` (Amazon SES unique message ID) for DISTINCT aggregations. -- This ensures consistency with Email Delivery Reports and industry best practices. - -### Time Granularity (for trend components) - -**CRITICAL: Timestamp Parsing** -- event_timestamp and conversion_timestamp are VARCHAR strings, NOT TIMESTAMP types. -- You MUST use `date_parse(column, '%Y-%m-%d %H:%i:%s.%f')` to parse these columns. -- DO NOT use `CAST(column AS DATE)` or `CAST(column AS TIMESTAMP)` - these will fail. - -The SQL agent must dynamically set the time grain based on the total date range of available data for the specified campaign_id or journey_id: -- **<=20 days**: daily granularity -- **21-89 days**: weekly granularity (starting Monday) -- **>=90 days**: monthly granularity - -This ensures optimal visualization density and performance. - -## 4. Component Definitions - -### Component 1: Engagement KPIs -- component: - - component_id: "kpi_summary_engagement" - - component_type: "kpi_card_group" - - title: "Engagement KPIs for {name} ({id})" - - source_tables: ["email_events"] - - metrics: - - { metric_id: "sends", display_name: "Sends", calculation: "COUNT(*) FROM email_events WHERE event_type = 'Send'", format: "integer" } - - { metric_id: "deliveries", display_name: "Deliveries", calculation: "COUNT(*) FROM email_events WHERE event_type = 'Delivery'", format: "integer" } - - { metric_id: "unique_opens", display_name: "Unique Opens", calculation: "COUNT(DISTINCT message_id) FROM email_events WHERE event_type = 'Open'", format: "integer", visual_priority: "primary" } - - { metric_id: "total_opens", display_name: "Total Opens", calculation: "COUNT(*) FROM email_events WHERE event_type = 'Open'", format: "integer", visual_priority: "tertiary", display_note: "Supplementary information" } - - { metric_id: "unique_open_rate", display_name: "Unique Open Rate", calculation: "(COUNT(DISTINCT message_id) WHERE event_type = 'Open') / (COUNT(*) WHERE event_type = 'Delivery')", format: "percentage", visual_priority: "primary" } - - { metric_id: "unique_clicks", display_name: "Unique Clicks", calculation: "COUNT(DISTINCT message_id) FROM email_events WHERE event_type = 'Click'", format: "integer", visual_priority: "primary" } - - { metric_id: "total_clicks", display_name: "Total Clicks", calculation: "COUNT(*) FROM email_events WHERE event_type = 'Click'", format: "integer", visual_priority: "tertiary", display_note: "Supplementary information" } - - { metric_id: "unique_click_rate", display_name: "Unique Click Rate (CTR)", calculation: "(COUNT(DISTINCT message_id) WHERE event_type = 'Click') / (COUNT(*) WHERE event_type = 'Delivery')", format: "percentage", visual_priority: "primary" } - - { metric_id: "bounces", display_name: "Bounces", calculation: "COUNT(*) FROM email_events WHERE event_type = 'Bounce'", format: "integer" } - - { metric_id: "bounce_rate", display_name: "Bounce Rate", calculation: "(COUNT(*) WHERE event_type = 'Bounce') / (COUNT(*) WHERE event_type = 'Send')", format: "percentage" } - - { metric_id: "unsubscribes", display_name: "Unsubscribes", calculation: "COUNT(*) FROM email_events WHERE event_type = 'Complaint'", format: "integer" } - - notes: | - - Visual priority guidance: - * "primary": Display prominently (e.g., 32px bold for count, 24px bold for rate) - * "tertiary": Display as supplementary info (e.g., 13px regular, "Total Opens: {value}") - - All rate calculations MUST use unique counts (COUNT DISTINCT message_id) to avoid inflated rates. - -### Component 2: Revenue KPIs -- component: - - component_id: "kpi_summary_revenue" - - component_type: "kpi_card_group" - - title: "Revenue KPIs for {name} ({id})" - - source_tables: ["revenue", "email_events"] - - display_condition: "Only show this component if filtering by campaign_id." - - query_hints: - - "To get revenue for a journey, you MUST JOIN the 'revenue' and 'email_events' tables on 'campaign_id'." - - metrics: - - { metric_id: "total_revenue", display_name: "Total Revenue", calculation: "SUM(total_revenue) from revenue table where attribution_type is 'direct' or 'contributed'", format: "currency", display_condition: "Only show if both direct and contributed revenue exist." } - - { metric_id: "direct_revenue", display_name: "Direct Revenue", calculation: "SUM(total_revenue) from revenue table where attribution_type = 'direct'", format: "currency" } - - { metric_id: "contributed_revenue", display_name: "Contributed Revenue", calculation: "SUM(total_revenue) from revenue table where attribution_type = 'contributed'", format: "currency" } - -### Component 3: Performance by Email Title -- component: - - component_id: "email_title_performance" - - component_type: "table" - - title: "Performance by Email Title" - - source_tables: ["email_events"] - - dimensions: - - { id: "email_title", display_name: "Email Title" } - - metrics: - - { metric_id: "sends", display_name: "Sends", calculation: "COUNT(*) WHERE event_type = 'Send'", format: "integer" } - - { metric_id: "deliveries", display_name: "Deliveries", calculation: "COUNT(*) WHERE event_type = 'Delivery'", format: "integer" } - - { metric_id: "open_rate", display_name: "Open Rate", calculation: "(COUNT(*) WHERE event_type = 'Open') / (COUNT(*) WHERE event_type = 'Delivery')", format: "percentage" } - - { metric_id: "ctr", display_name: "CTR (Click-Through Rate)", calculation: "(COUNT(*) WHERE event_type = 'Click') / (COUNT(*) WHERE event_type = 'Delivery')", format: "percentage" } - - { metric_id: "bounces", display_name: "Bounces", calculation: "COUNT(*) WHERE event_type = 'Bounce'", format: "integer" } - - { metric_id: "bounce_rate", display_name: "Bounce Rate", calculation: "(COUNT(*) WHERE event_type = 'Bounce') / (COUNT(*) WHERE event_type = 'Send')", format: "percentage" } - - { metric_id: "unsubscribes", display_name: "Unsubscribes", calculation: "COUNT(*) WHERE event_type = 'Complaint'", format: "integer" } - - sort_order: "sends DESC, email_title ASC" - - notes: | - - Query Construction: - 1. GROUP BY email_title directly from email_events table - 2. Filter by campaign_id or journey_id - 3. No need for event_master table join - - SQL agent should query with a LIMIT of 51. - - If 51 rows are returned, the VIZ agent should display a note: 'Showing top 50 titles only. There may be additional titles not displayed.' - - Display the first 50 rows only. - -### Component 4: Engagement Count Trend -- component: - - component_id: "engagement_count_trend" - - component_type: "line_chart" - - title: "Engagement Trend (Counts)" - - source_tables: ["email_events"] - - y_axis_shared: true - - visualization_hint: "mode: 'lines+markers'" - - metrics: - - { metric_id: "sends", display_name: "Sends", calculation: "COUNT(CASE WHEN event_type='Send' THEN 1 END)", format: "integer" } - - { metric_id: "deliveries", display_name: "Deliveries", calculation: "COUNT(CASE WHEN event_type='Delivery' THEN 1 END)", format: "integer" } - - { metric_id: "unique_opens", display_name: "Unique Opens", calculation: "COUNT(DISTINCT CASE WHEN event_type='Open' THEN message_id END)", format: "integer", visual_priority: "primary" } - - { metric_id: "unique_clicks", display_name: "Unique Clicks", calculation: "COUNT(DISTINCT CASE WHEN event_type='Click' THEN message_id END)", format: "integer", visual_priority: "primary" } - - { metric_id: "bounce_count", display_name: "Bounces", calculation: "COUNT(CASE WHEN event_type='Bounce' THEN 1 END)", format: "integer" } - - { metric_id: "complaint_count", display_name: "Complaints", calculation: "COUNT(CASE WHEN event_type='Complaint' THEN 1 END)", format: "integer" } - - notes: | - - This component is based on the 'event_timestamp' column. - - Zero-padding is NOT required. - - Time granularity is determined by the total date range of the campaign/journey data (see Section 3). - - All metrics use COUNT DISTINCT message_id for opens and clicks to avoid inflated counts. - -### Component 5: Conversions and Revenue Trend -- component: - - component_id: "conversions_trend" - - component_type: "line_chart" - - title: "Conversions & Revenue Trend" - - source_tables: ["revenue", "email_events"] - - y_axis_shared: false - - visualization_hint: "mode: 'lines+markers', dual y-axis (left: count, right: currency)" - - display_condition: "Only show if filtering by campaign_id." - - metrics: - - { metric_id: "conversions", display_name: "Conversions", calculation: "COUNT(DISTINCT conversion_id) FROM revenue", format: "integer", y_axis: "left" } - - { metric_id: "direct_revenue", display_name: "Direct Revenue", calculation: "SUM(total_revenue) FROM revenue WHERE attribution_type = 'direct'", format: "currency", y_axis: "right" } - - { metric_id: "contributed_revenue", display_name: "Contributed Revenue", calculation: "SUM(total_revenue) FROM revenue WHERE attribution_type = 'contributed'", format: "currency", y_axis: "right" } - - { metric_id: "total_revenue", display_name: "Total Revenue", calculation: "SUM(total_revenue) FROM revenue WHERE attribution_type IN ('direct', 'contributed')", format: "currency", y_axis: "right" } - - notes: | - - This component is based on the 'conversion_timestamp' column. - - Zero-padding is NOT required. - - Time granularity is determined by the total date range of the campaign/journey data (see Section 3). - - Use dual y-axis: left for count metrics (conversions), right for currency metrics (revenue). - - This component helps identify day-of-week patterns and campaign timing effects. - -## 5. Component Rendering Order - -The final output should render components in the following order: - -1. Title -2. Summary (data-driven insights) -3. kpi_summary_engagement -4. kpi_summary_revenue (if campaign_id and data exists) -5. engagement_count_trend -6. conversions_trend (if campaign_id and data exists) -7. email_title_performance diff --git a/engage-box/roi_reporting/agent/knowledge_base_campaign_details_JA.md b/engage-box/roi_reporting/agent/knowledge_base_campaign_details_JA.md deleted file mode 100644 index 90f2f66e..00000000 --- a/engage-box/roi_reporting/agent/knowledge_base_campaign_details_JA.md +++ /dev/null @@ -1,168 +0,0 @@ -# Report Specification: Campaign Details(日本語版) - -> **注意**: このドキュメントは参考資料です。正式なレポート仕様は英語版([knowledge_base_campaign_details.md](./knowledge_base_campaign_details.md))を参照してください。 - -## 1. レポート概要 -- purpose: "単一のキャンペーンまたはジャーニーの包括的なパフォーマンス分析を提供し、KPI、トレンド、メール件名別の内訳を含む。" -- source_tables: - - "email_events" - - "revenue" - -## 2. フィルタ -- filter: - - id: "campaign_id" - type: "string" - required: true - exclusive_with: "journey_id" -- filter: - - id: "journey_id" - type: "string" - required: true - exclusive_with: "campaign_id" -- filter_notes: "campaign_idまたはjourney_idが必要です。これらのIDの一つが提供された場合、日付範囲は不要です。レポートはそのIDの利用可能なすべてのデータで実行されるべきです。" -- notes: "タイムスタンプカラム(event_timestamp, conversion_timestamp)はvarchar文字列です。date_parse(column, '%Y-%m-%d %H:%i:%s.%f')を使用して解析する必要があります。" - -## 3. メトリクスに関する重要な注意事項 - -### ユニークカウント vs 総カウント -- **ユニークカウント**(主要メトリクス): `COUNT(DISTINCT message_id)`を使用して、開封/クリックされた回数に関わらず各メッセージを1回のみカウントします。 -- **総カウント**(補足情報): `COUNT(*)`を使用して、同じメッセージの複数回の開封/クリックを含むすべてのイベントをカウントします。 -- **率計算**: すべての率はユニークカウントを使用する必要があります(業界標準)。 - -### 識別子標準 -- DISTINCT集計には`message_id`(Amazon SESユニークメッセージID)を使用します。 -- これによりEmail Delivery Reportsおよび業界ベストプラクティスとの一貫性が保証されます。 - -### 時間粒度(トレンドコンポーネント用) - -**重要: タイムスタンプ解析** -- event_timestampとconversion_timestampはVARCHAR文字列であり、TIMESTAMPタイプではありません。 -- これらのカラムを解析するには`date_parse(column, '%Y-%m-%d %H:%i:%s.%f')`を使用する必要があります。 -- `CAST(column AS DATE)`または`CAST(column AS TIMESTAMP)`は使用しないでください - これらは失敗します。 - -SQLエージェントは、指定されたcampaign_idまたはjourney_idの利用可能なデータの総日付範囲に基づいて時間粒度を動的に設定する必要があります: -- **<=20日**: 日次粒度 -- **21-89日**: 週次粒度(月曜日開始) -- **>=90日**: 月次粒度 - -これにより最適な可視化密度とパフォーマンスが保証されます。 - -## 4. コンポーネント定義 - -### コンポーネント1: エンゲージメントKPI -- component: - - component_id: "kpi_summary_engagement" - - component_type: "kpi_card_group" - - title: "Engagement KPIs for {name} ({id})" - - source_tables: ["email_events"] - - metrics: - - { metric_id: "sends", display_name: "Sends", calculation: "COUNT(*) FROM email_events WHERE event_type = 'Send'", format: "integer" } - - { metric_id: "deliveries", display_name: "Deliveries", calculation: "COUNT(*) FROM email_events WHERE event_type = 'Delivery'", format: "integer" } - - { metric_id: "unique_opens", display_name: "Unique Opens", calculation: "COUNT(DISTINCT message_id) FROM email_events WHERE event_type = 'Open'", format: "integer", visual_priority: "primary" } - - { metric_id: "total_opens", display_name: "Total Opens", calculation: "COUNT(*) FROM email_events WHERE event_type = 'Open'", format: "integer", visual_priority: "tertiary", display_note: "補足情報" } - - { metric_id: "unique_open_rate", display_name: "Unique Open Rate", calculation: "(COUNT(DISTINCT message_id) WHERE event_type = 'Open') / (COUNT(*) WHERE event_type = 'Delivery')", format: "percentage", visual_priority: "primary" } - - { metric_id: "unique_clicks", display_name: "Unique Clicks", calculation: "COUNT(DISTINCT message_id) FROM email_events WHERE event_type = 'Click'", format: "integer", visual_priority: "primary" } - - { metric_id: "total_clicks", display_name: "Total Clicks", calculation: "COUNT(*) FROM email_events WHERE event_type = 'Click'", format: "integer", visual_priority: "tertiary", display_note: "補足情報" } - - { metric_id: "unique_click_rate", display_name: "Unique Click Rate (CTR)", calculation: "(COUNT(DISTINCT message_id) WHERE event_type = 'Click') / (COUNT(*) WHERE event_type = 'Delivery')", format: "percentage", visual_priority: "primary" } - - { metric_id: "bounces", display_name: "Bounces", calculation: "COUNT(*) FROM email_events WHERE event_type = 'Bounce'", format: "integer" } - - { metric_id: "bounce_rate", display_name: "Bounce Rate", calculation: "(COUNT(*) WHERE event_type = 'Bounce') / (COUNT(*) WHERE event_type = 'Send')", format: "percentage" } - - { metric_id: "unsubscribes", display_name: "Unsubscribes", calculation: "COUNT(*) FROM email_events WHERE event_type = 'Complaint'", format: "integer" } - - notes: | - - ビジュアル優先度ガイダンス: - * "primary": 目立つように表示(例: カウントは32pxボールド、率は24pxボールド) - * "tertiary": 補足情報として表示(例: 13px通常、"Total Opens: {value}") - - すべての率計算はユニークカウント(COUNT DISTINCT message_id)を使用して、膨張したメトリクスを避ける必要があります。 - -### コンポーネント2: 収益KPI -- component: - - component_id: "kpi_summary_revenue" - - component_type: "kpi_card_group" - - title: "Revenue KPIs for {name} ({id})" - - source_tables: ["revenue", "email_events"] - - display_condition: "campaign_idでフィルタリングしている場合のみこのコンポーネントを表示。" - - query_hints: - - "ジャーニーの収益を取得するには、'revenue'と'email_events'テーブルを'campaign_id'でJOINする必要があります。" - - metrics: - - { metric_id: "total_revenue", display_name: "Total Revenue", calculation: "SUM(total_revenue) from revenue table where attribution_type is 'direct' or 'contributed'", format: "currency", display_condition: "直接収益と貢献収益の両方が存在する場合のみ表示。" } - - { metric_id: "direct_revenue", display_name: "Direct Revenue", calculation: "SUM(total_revenue) from revenue table where attribution_type = 'direct'", format: "currency" } - - { metric_id: "contributed_revenue", display_name: "Contributed Revenue", calculation: "SUM(total_revenue) from revenue table where attribution_type = 'contributed'", format: "currency" } - -### コンポーネント3: メール件名別パフォーマンス -- component: - - component_id: "email_title_performance" - - component_type: "table" - - title: "Performance by Email Title" - - source_tables: ["email_events"] - - dimensions: - - { id: "email_title", display_name: "Email Title" } - - metrics: - - { metric_id: "sends", display_name: "Sends", calculation: "COUNT(*) WHERE event_type = 'Send'", format: "integer" } - - { metric_id: "deliveries", display_name: "Deliveries", calculation: "COUNT(*) WHERE event_type = 'Delivery'", format: "integer" } - - { metric_id: "open_rate", display_name: "Open Rate", calculation: "(COUNT(*) WHERE event_type = 'Open') / (COUNT(*) WHERE event_type = 'Delivery')", format: "percentage" } - - { metric_id: "ctr", display_name: "CTR (Click-Through Rate)", calculation: "(COUNT(*) WHERE event_type = 'Click') / (COUNT(*) WHERE event_type = 'Delivery')", format: "percentage" } - - { metric_id: "bounces", display_name: "Bounces", calculation: "COUNT(*) WHERE event_type = 'Bounce'", format: "integer" } - - { metric_id: "bounce_rate", display_name: "Bounce Rate", calculation: "(COUNT(*) WHERE event_type = 'Bounce') / (COUNT(*) WHERE event_type = 'Send')", format: "percentage" } - - { metric_id: "unsubscribes", display_name: "Unsubscribes", calculation: "COUNT(*) WHERE event_type = 'Complaint'", format: "integer" } - - sort_order: "sends DESC, email_title ASC" - - notes: | - - クエリ構築: - 1. email_eventsテーブルから直接email_titleでGROUP BY - 2. campaign_idまたはjourney_idでフィルタ - 3. event_masterテーブルとのJOINは不要 - - SQLエージェントはLIMIT 51でクエリする必要があります。 - - 51行が返された場合、VIZエージェントは注記を表示する必要があります: '上位50件のみ表示しています。追加の件名が表示されていない可能性があります。' - - 最初の50行のみを表示します。 - -### コンポーネント4: エンゲージメント数トレンド -- component: - - component_id: "engagement_count_trend" - - component_type: "line_chart" - - title: "Engagement Trend (Counts)" - - source_tables: ["email_events"] - - y_axis_shared: true - - visualization_hint: "mode: 'lines+markers'" - - metrics: - - { metric_id: "sends", display_name: "Sends", calculation: "COUNT(CASE WHEN event_type='Send' THEN 1 END)", format: "integer" } - - { metric_id: "deliveries", display_name: "Deliveries", calculation: "COUNT(CASE WHEN event_type='Delivery' THEN 1 END)", format: "integer" } - - { metric_id: "unique_opens", display_name: "Unique Opens", calculation: "COUNT(DISTINCT CASE WHEN event_type='Open' THEN message_id END)", format: "integer", visual_priority: "primary" } - - { metric_id: "unique_clicks", display_name: "Unique Clicks", calculation: "COUNT(DISTINCT CASE WHEN event_type='Click' THEN message_id END)", format: "integer", visual_priority: "primary" } - - { metric_id: "bounce_count", display_name: "Bounces", calculation: "COUNT(CASE WHEN event_type='Bounce' THEN 1 END)", format: "integer" } - - { metric_id: "complaint_count", display_name: "Complaints", calculation: "COUNT(CASE WHEN event_type='Complaint' THEN 1 END)", format: "integer" } - - notes: | - - このコンポーネントは'event_timestamp'カラムに基づいています。 - - ゼロパディングは不要です。 - - 時間粒度はキャンペーン/ジャーニーデータの総日付範囲によって決定されます(セクション3参照)。 - - すべてのメトリクスは開封とクリックにCOUNT DISTINCT message_idを使用して、膨張したカウントを避けます。 - -### コンポーネント5: コンバージョン・収益トレンド -- component: - - component_id: "conversions_trend" - - component_type: "line_chart" - - title: "Conversions & Revenue Trend" - - source_tables: ["revenue", "email_events"] - - y_axis_shared: false - - visualization_hint: "mode: 'lines+markers', dual y-axis (left: count, right: currency)" - - display_condition: "campaign_idでフィルタリングしている場合のみ表示。" - - metrics: - - { metric_id: "conversions", display_name: "Conversions", calculation: "COUNT(DISTINCT conversion_id) FROM revenue", format: "integer", y_axis: "left" } - - { metric_id: "direct_revenue", display_name: "Direct Revenue", calculation: "SUM(total_revenue) FROM revenue WHERE attribution_type = 'direct'", format: "currency", y_axis: "right" } - - { metric_id: "contributed_revenue", display_name: "Contributed Revenue", calculation: "SUM(total_revenue) FROM revenue WHERE attribution_type = 'contributed'", format: "currency", y_axis: "right" } - - { metric_id: "total_revenue", display_name: "Total Revenue", calculation: "SUM(total_revenue) FROM revenue WHERE attribution_type IN ('direct', 'contributed')", format: "currency", y_axis: "right" } - - notes: | - - このコンポーネントは'conversion_timestamp'カラムに基づいています。 - - ゼロパディングは不要です。 - - 時間粒度はキャンペーン/ジャーニーデータの総日付範囲によって決定されます(セクション3参照)。 - - デュアルy軸を使用: カウントメトリクス(コンバージョン)は左、通貨メトリクス(収益)は右。 - - このコンポーネントは曜日パターンやキャンペーンタイミング効果の特定に役立ちます。 - -## 5. コンポーネントレンダリング順序 - -最終出力は以下の順序でコンポーネントをレンダリングする必要があります: - -1. Title -2. Summary(データドリブンなインサイト) -3. kpi_summary_engagement -4. kpi_summary_revenue(campaign_idでデータが存在する場合) -5. engagement_count_trend -6. conversions_trend(campaign_idでデータが存在する場合) -7. email_title_performance diff --git a/engage-box/roi_reporting/agent/knowledge_base_overall_summary.md b/engage-box/roi_reporting/agent/knowledge_base_overall_summary.md deleted file mode 100644 index 44fdacc9..00000000 --- a/engage-box/roi_reporting/agent/knowledge_base_overall_summary.md +++ /dev/null @@ -1,140 +0,0 @@ -# Report Specification: OverallSummary - -## 1. Report Overview -- purpose: "To visualize key performance indicators (KPIs), trends, and top-performing campaigns/journeys over a specified period." -- source_tables: - - "daily_summary" - - "event_master" # Used for name lookups in rankings - -## 2. Filters -- filter: - - id: "date_range" - - type: "date" - - required: true - - notes: | - - A date range (start_date, end_date) is mandatory. - - If no range is provided, the SQL agent should return the min/max available dates and an error. - - The SQL agent will validate the range. If it exceeds 365 days, the agent will reject the request and return a message suggesting a valid 365-day range. - -## 3. Important Notes on Metrics - -**Note:** This section describes the calculation methodology. A user-facing disclaimer must be displayed at the end of the report (see Component: data_methodology_disclaimer). - -## 4. Component Definitions -- component: - - component_id: "kpi_summary" - - component_type: "kpi_card_group" - - title: "Overall Performance Summary" - - metrics: - - { metric_id: "sends", display_name: "Sends", calculation: "SUM(total_sends)", format: "integer" } - - { metric_id: "total_revenue", display_name: "Total Revenue", calculation: "SUM(total_revenue_direct + total_revenue_contributed)", format: "currency", display_condition: "Show only if SUM(total_revenue_direct) > 0 AND SUM(total_revenue_contributed) > 0" } - - { metric_id: "direct_revenue", display_name: "Direct Revenue", calculation: "SUM(total_revenue_direct)", format: "currency" } - - { metric_id: "contributed_revenue", display_name: "Contributed Revenue", calculation: "SUM(total_revenue_contributed)", format: "currency" } - - { metric_id: "conversions", display_name: "Conversions", calculation: "SUM(total_conversions)", format: "integer" } - - { metric_id: "deliveries", display_name: "Deliveries", calculation: "SUM(total_deliveries)", format: "integer" } - - { metric_id: "opens", display_name: "Opens", calculation: "SUM(total_opens)", format: "integer" } - - { metric_id: "open_rate", display_name: "Open Rate", calculation: "SUM(total_opens) / SUM(total_deliveries)", format: "percentage" } - - { metric_id: "clicks", display_name: "Clicks", calculation: "SUM(total_clicks)", format: "integer" } - - { metric_id: "click_rate", display_name: "Click Rate", calculation: "SUM(total_clicks) / SUM(total_deliveries)", format: "percentage" } - - { metric_id: "bounces", display_name: "Bounces", calculation: "SUM(total_hard_bounces + total_soft_bounces)", format: "integer" } - - { metric_id: "bounce_rate", display_name: "Bounce Rate", calculation: "SUM(total_hard_bounces + total_soft_bounces) / SUM(total_sends)", format: "percentage" } - - { metric_id: "unsubscribes", display_name: "Unsubscribes", calculation: "SUM(total_unsubscribes)", format: "integer" } - -- component: - - component_id: "campaign_performance_ranking" - - component_type: "table" - - title: "Top 5 Campaigns" - - source_tables: ["daily_summary", "event_master"] - - dimensions: - - { id: "campaign_name", display_name: "Campaign Name" } - - { id: "campaign_id", display_name: "Campaign ID" } - - metrics: - - { metric_id: "revenue", display_name: "Revenue", format: "currency" } - - { metric_id: "conversions", display_name: "Conversions", calculation: "SUM(total_conversions)", format: "integer" } - - { metric_id: "sends", display_name: "Sends", calculation: "SUM(total_sends)", format: "integer" } - - { metric_id: "deliveries", display_name: "Deliveries", calculation: "SUM(total_deliveries)", format: "integer" } - - { metric_id: "open_rate", display_name: "Open Rate", calculation: "SUM(total_opens) / SUM(total_deliveries)", format: "percentage" } - - { metric_id: "click_rate", display_name: "Click Rate", calculation: "SUM(total_clicks) / SUM(total_deliveries)", format: "percentage" } - - { metric_id: "bounce_rate", display_name: "Bounce Rate", calculation: "SUM(total_hard_bounces + total_soft_bounces) / SUM(total_sends)", format: "percentage" } - - orderby_clause_template: "ORDER BY SUM(total_revenue_direct + total_revenue_contributed) DESC, SUM(total_conversions) DESC, SUM(total_clicks) DESC, campaign_id DESC" - - notes: | - - This component ranks campaigns. The final sort order MUST use the provided 'orderby_clause_template'. - - Date filtering MUST use the 'summary_date' column (varchar) with string-based comparison (e.g., WHERE summary_date BETWEEN 'YYYY-MM-DD' AND 'YYYY-MM-DD'). - - The 'Revenue' column to be displayed is dynamic. The agent must construct a CASE statement to show Total, Direct, or Contributed Revenue based on the rules previously discussed. - -- component: - - component_id: "journey_performance_ranking" - - component_type: "table" - - title: "Top 5 Journeys" - - source_tables: ["daily_summary", "event_master"] - - dimensions: - - { id: "journey_name", display_name: "Journey Name" } - - { id: "journey_id", display_name: "Journey ID" } - - metrics: - - { metric_id: "revenue", display_name: "Revenue", format: "currency" } - - { metric_id: "conversions", display_name: "Conversions", calculation: "SUM(total_conversions)", format: "integer" } - - { metric_id: "sends", display_name: "Sends", calculation: "SUM(total_sends)", format: "integer" } - - { metric_id: "deliveries", display_name: "Deliveries", calculation: "SUM(total_deliveries)", format: "integer" } - - { metric_id: "open_rate", display_name: "Open Rate", calculation: "SUM(total_opens) / SUM(total_deliveries)", format: "percentage" } - - { metric_id: "click_rate", display_name: "Click Rate", calculation: "SUM(total_clicks) / SUM(total_deliveries)", format: "percentage" } - - { metric_id: "bounce_rate", display_name: "Bounce Rate", calculation: "SUM(total_hard_bounces + total_soft_bounces) / SUM(total_sends)", format: "percentage" } - - orderby_clause_template: "ORDER BY SUM(total_revenue_direct + total_revenue_contributed) DESC, SUM(total_conversions) DESC, SUM(total_clicks) DESC, journey_id DESC" - - notes: | - - This component ranks journeys. The final sort order MUST use the provided 'orderby_clause_template'. - - Date filtering MUST use the 'summary_date' column (varchar) with string-based comparison (e.g., WHERE summary_date BETWEEN 'YYYY-MM-DD' AND 'YYYY-MM-DD'). - - The 'Revenue' column to be displayed is dynamic. The agent must construct a CASE statement to show Total, Direct, or Contributed Revenue based on the rules previously discussed. - -- component: - - component_id: "performance_trend" - - component_type: "trend_chart" - - title: "Performance Trend" - - tabs: - - tab_name: "Engagement" - metrics: - - { metric_id: "sends", display_name: "Sends", calculation: "SUM(total_sends)" } - - { metric_id: "deliveries", display_name: "Deliveries", calculation: "SUM(total_deliveries)" } - - { metric_id: "clicks", display_name: "Clicks", calculation: "SUM(total_clicks)" } - - tab_name: "Revenue" - metrics: - - { metric_id: "revenue", display_name: "Revenue", calculation: "SUM(total_revenue_direct + total_revenue_contributed)" } - display_condition: "Show tab only if total revenue for the period > 0" - - notes: | - - This component uses the 'summary_date' (varchar) column. For date functions like date_trunc, it MUST be converted using CAST(summary_date AS DATE). - - The SQL agent must dynamically set the time grain based on the length of the date range: - * <=20 days: daily - * 21-89 days: weekly (starting Monday) - * >=90 days: monthly - - The agent must perform zero-padding to ensure a continuous time series. - -- component: - - component_id: "data_methodology_disclaimer" - - component_type: "text_note" - - title: "Data Aggregation Methodology" - - content: | - Note: The open rate and click rate in this report are calculated based on total event counts from the daily_summary table. - When the same email is opened or clicked multiple times, each event is counted separately. - This may result in rates appearing higher than unique open/click rates. - - For more detailed analysis with unique count-based metrics, please refer to the Campaign Details report. - - display_condition: "ALWAYS - This component MUST be displayed at the end of every report." - - notes: | - - CRITICAL: This disclaimer MUST always be rendered at the very end of the report, after all other components. - - This is not optional - it must appear in every OverallSummary report regardless of data or filters. - - Rendering style: Plain text only, no borders, no background color, no box shadow, no bold text. - - Use regular font weight (not bold), normal font size (14-16px). - - Background color: transparent or same as page background (white/light gray). - - No visual highlighting or emphasis - this should blend into the page as simple footer text. - -## 5. Component Rendering Order - -The final output MUST render components in the following order: - -1. Report Title -2. Summary (data-driven insights) -3. kpi_summary -4. campaign_performance_ranking -5. journey_performance_ranking -6. performance_trend -7. **data_methodology_disclaimer** ← MUST BE LAST - -**IMPORTANT:** The data_methodology_disclaimer component MUST always appear at the very end of the report, regardless of other component rendering or data availability. diff --git a/engage-box/roi_reporting/agent/knowledge_base_overall_summary_JA.md b/engage-box/roi_reporting/agent/knowledge_base_overall_summary_JA.md deleted file mode 100644 index d9f3226b..00000000 --- a/engage-box/roi_reporting/agent/knowledge_base_overall_summary_JA.md +++ /dev/null @@ -1,142 +0,0 @@ -# Report Specification: OverallSummary(日本語版) - -> **注意**: このドキュメントは参考資料です。正式なレポート仕様は英語版([knowledge_base_overall_summary.md](./knowledge_base_overall_summary.md))を参照してください。 - -## 1. レポート概要 -- purpose: "指定された期間における主要パフォーマンス指標(KPI)、トレンド、上位キャンペーン/ジャーニーを可視化する。" -- source_tables: - - "daily_summary" - - "event_master" # ランキングでの名前検索に使用 - -## 2. フィルタ -- filter: - - id: "date_range" - - type: "date" - - required: true - - notes: | - - 日付範囲(start_date, end_date)は必須です。 - - 範囲が指定されない場合、SQLエージェントは利用可能な最小/最大日付とエラーを返してください。 - - SQLエージェントは範囲を検証します。365日を超える場合、リクエストを拒否し、有効な365日範囲を提案するメッセージを返します。 - -## 3. メトリクスに関する重要な注意事項 - -**注意:** このセクションは計算方法論を説明します。レポートの最後にユーザー向け免責事項を表示する必要があります(Component: data_methodology_disclaimerを参照)。 - -## 4. コンポーネント定義 -- component: - - component_id: "kpi_summary" - - component_type: "kpi_card_group" - - title: "Overall Performance Summary" - - metrics: - - { metric_id: "sends", display_name: "Sends", calculation: "SUM(total_sends)", format: "integer" } - - { metric_id: "total_revenue", display_name: "Total Revenue", calculation: "SUM(total_revenue_direct + total_revenue_contributed)", format: "currency", display_condition: "SUM(total_revenue_direct) > 0 AND SUM(total_revenue_contributed) > 0の場合のみ表示" } - - { metric_id: "direct_revenue", display_name: "Direct Revenue", calculation: "SUM(total_revenue_direct)", format: "currency" } - - { metric_id: "contributed_revenue", display_name: "Contributed Revenue", calculation: "SUM(total_revenue_contributed)", format: "currency" } - - { metric_id: "conversions", display_name: "Conversions", calculation: "SUM(total_conversions)", format: "integer" } - - { metric_id: "deliveries", display_name: "Deliveries", calculation: "SUM(total_deliveries)", format: "integer" } - - { metric_id: "opens", display_name: "Opens", calculation: "SUM(total_opens)", format: "integer" } - - { metric_id: "open_rate", display_name: "Open Rate", calculation: "SUM(total_opens) / SUM(total_deliveries)", format: "percentage" } - - { metric_id: "clicks", display_name: "Clicks", calculation: "SUM(total_clicks)", format: "integer" } - - { metric_id: "click_rate", display_name: "Click Rate", calculation: "SUM(total_clicks) / SUM(total_deliveries)", format: "percentage" } - - { metric_id: "bounces", display_name: "Bounces", calculation: "SUM(total_hard_bounces + total_soft_bounces)", format: "integer" } - - { metric_id: "bounce_rate", display_name: "Bounce Rate", calculation: "SUM(total_hard_bounces + total_soft_bounces) / SUM(total_sends)", format: "percentage" } - - { metric_id: "unsubscribes", display_name: "Unsubscribes", calculation: "SUM(total_unsubscribes)", format: "integer" } - -- component: - - component_id: "campaign_performance_ranking" - - component_type: "table" - - title: "Top 5 Campaigns" - - source_tables: ["daily_summary", "event_master"] - - dimensions: - - { id: "campaign_name", display_name: "Campaign Name" } - - { id: "campaign_id", display_name: "Campaign ID" } - - metrics: - - { metric_id: "revenue", display_name: "Revenue", format: "currency" } - - { metric_id: "conversions", display_name: "Conversions", calculation: "SUM(total_conversions)", format: "integer" } - - { metric_id: "sends", display_name: "Sends", calculation: "SUM(total_sends)", format: "integer" } - - { metric_id: "deliveries", display_name: "Deliveries", calculation: "SUM(total_deliveries)", format: "integer" } - - { metric_id: "open_rate", display_name: "Open Rate", calculation: "SUM(total_opens) / SUM(total_deliveries)", format: "percentage" } - - { metric_id: "click_rate", display_name: "Click Rate", calculation: "SUM(total_clicks) / SUM(total_deliveries)", format: "percentage" } - - { metric_id: "bounce_rate", display_name: "Bounce Rate", calculation: "SUM(total_hard_bounces + total_soft_bounces) / SUM(total_sends)", format: "percentage" } - - orderby_clause_template: "ORDER BY SUM(total_revenue_direct + total_revenue_contributed) DESC, SUM(total_conversions) DESC, SUM(total_clicks) DESC, campaign_id DESC" - - notes: | - - このコンポーネントはキャンペーンをランク付けします。最終ソート順は提供された'orderby_clause_template'を使用する必要があります。 - - 日付フィルタリングは'summary_date'カラム(varchar)を使用し、文字列ベースの比較を行う必要があります(例: WHERE summary_date BETWEEN 'YYYY-MM-DD' AND 'YYYY-MM-DD')。 - - 表示する'Revenue'カラムは動的です。エージェントは以前に議論したルールに基づいてTotal、Direct、またはContributed Revenueを表示するCASE文を構築する必要があります。 - -- component: - - component_id: "journey_performance_ranking" - - component_type: "table" - - title: "Top 5 Journeys" - - source_tables: ["daily_summary", "event_master"] - - dimensions: - - { id: "journey_name", display_name: "Journey Name" } - - { id: "journey_id", display_name: "Journey ID" } - - metrics: - - { metric_id: "revenue", display_name: "Revenue", format: "currency" } - - { metric_id: "conversions", display_name: "Conversions", calculation: "SUM(total_conversions)", format: "integer" } - - { metric_id: "sends", display_name: "Sends", calculation: "SUM(total_sends)", format: "integer" } - - { metric_id: "deliveries", display_name: "Deliveries", calculation: "SUM(total_deliveries)", format: "integer" } - - { metric_id: "open_rate", display_name: "Open Rate", calculation: "SUM(total_opens) / SUM(total_deliveries)", format: "percentage" } - - { metric_id: "click_rate", display_name: "Click Rate", calculation: "SUM(total_clicks) / SUM(total_deliveries)", format: "percentage" } - - { metric_id: "bounce_rate", display_name: "Bounce Rate", calculation: "SUM(total_hard_bounces + total_soft_bounces) / SUM(total_sends)", format: "percentage" } - - orderby_clause_template: "ORDER BY SUM(total_revenue_direct + total_revenue_contributed) DESC, SUM(total_conversions) DESC, SUM(total_clicks) DESC, journey_id DESC" - - notes: | - - このコンポーネントはジャーニーをランク付けします。最終ソート順は提供された'orderby_clause_template'を使用する必要があります。 - - 日付フィルタリングは'summary_date'カラム(varchar)を使用し、文字列ベースの比較を行う必要があります。 - - 表示する'Revenue'カラムは動的です。 - -- component: - - component_id: "performance_trend" - - component_type: "trend_chart" - - title: "Performance Trend" - - tabs: - - tab_name: "Engagement" - metrics: - - { metric_id: "sends", display_name: "Sends", calculation: "SUM(total_sends)" } - - { metric_id: "deliveries", display_name: "Deliveries", calculation: "SUM(total_deliveries)" } - - { metric_id: "clicks", display_name: "Clicks", calculation: "SUM(total_clicks)" } - - tab_name: "Revenue" - metrics: - - { metric_id: "revenue", display_name: "Revenue", calculation: "SUM(total_revenue_direct + total_revenue_contributed)" } - display_condition: "期間の総収益 > 0の場合のみタブを表示" - - notes: | - - このコンポーネントは'summary_date'(varchar)カラムを使用します。date_truncなどの日付関数にはCAST(summary_date AS DATE)を使用する必要があります。 - - SQLエージェントは日付範囲の長さに基づいて時間粒度を動的に設定する必要があります: - * <=20日: 日次 - * 21-89日: 週次(月曜日開始) - * >=90日: 月次 - - エージェントは連続した時系列を確保するためにゼロパディングを実行する必要があります。 - -- component: - - component_id: "data_methodology_disclaimer" - - component_type: "text_note" - - title: "Data Aggregation Methodology" - - content: | - 注意: このレポートの開封率とクリック率は、daily_summaryテーブルの総イベントカウントに基づいて計算されています。 - 同じメールが複数回開封またはクリックされた場合、各イベントは個別にカウントされます。 - これにより、ユニーク開封/クリック率よりも高い率が表示される場合があります。 - - ユニークカウントベースのメトリクスによる詳細な分析については、Campaign Detailsレポートを参照してください。 - - display_condition: "ALWAYS - このコンポーネントはすべてのレポートの最後に必ず表示する必要があります。" - - notes: | - - 重要: この免責事項は常にレポートの最後、他のすべてのコンポーネントの後にレンダリングされる必要があります。 - - これはオプションではありません - データやフィルタに関わらず、すべてのOverallSummaryレポートに表示される必要があります。 - - レンダリングスタイル: プレーンテキストのみ、ボーダーなし、背景色なし、ボックスシャドウなし、ボールドテキストなし。 - - 通常のフォントウェイト(ボールドでない)、通常のフォントサイズ(14-16px)を使用。 - - 背景色: transparentまたはページ背景と同じ(白/ライトグレー)。 - - 視覚的なハイライトや強調なし - シンプルなフッターテキストとしてページに溶け込むべきです。 - -## 5. コンポーネントレンダリング順序 - -最終出力は以下の順序でコンポーネントをレンダリングする必要があります: - -1. Report Title -2. Summary(データドリブンなインサイト) -3. kpi_summary -4. campaign_performance_ranking -5. journey_performance_ranking -6. performance_trend -7. **data_methodology_disclaimer** ← 必ず最後 - -**重要:** data_methodology_disclaimerコンポーネントは、他のコンポーネントのレンダリングやデータの可用性に関わらず、常にレポートの最後に表示される必要があります。 diff --git a/engage-box/roi_reporting/agent/system_prompt.md b/engage-box/roi_reporting/agent/system_prompt.md deleted file mode 100644 index b483ef66..00000000 --- a/engage-box/roi_reporting/agent/system_prompt.md +++ /dev/null @@ -1,146 +0,0 @@ -## Your Role - -You are a unified ROI reporting agent that autonomously generates dashboard reports. Based on specifications and user instructions, you execute SQL queries against Trino, visualize intermediate results, and generate a single self-contained .jsx file. - -## Core Principles - -- Graceful Degradation: Deliver the best possible report using available data. If components fail, continue with successful ones. -- Autonomous Execution: Execute end-to-end without waiting for user prompts until final renderReactApp call. Never ask for optional parameters - proceed with available data. -- Progressive Disclosure: Show intermediate Plotly visualizations for each component. -- Silent Final Assembly: Last action must be a single renderReactApp call with complete code. - -## Execution Flow - -1. Planning - - Read YAML spec, list all components, create build plan - - Note: Summary executed LAST but rendered FIRST in output - - Evaluate component-level display_condition, exclude if false - - Normalize inputs: report_spec_name, component_id, filters - -2. Data Retrieval (Per Component) - - Schema Validation: Verify all required columns exist - - SQL Generation: Follow YAML spec and Trino SQL Cookbook below - - Apply filters strictly (no fuzzy matching) - - Execute query, retry on failure with corrections - - If zero rows: record error, skip component - - Show intermediate visualization via render_plotly_chart - -3. Summary Generation - - Execute AFTER all components (step 3), render FIRST in output (step 4) - - Provide data-driven insights using ONLY retrieved SQL results - - Describe what data shows, NOT what you generated - - Mention missing components/metrics if applicable - - Constraints: No calculations, no new queries, no assumptions - -4. Final Build - - Render order: Title → Summary → Components (spec order) - - Evaluate metric-level display_condition at render time - - Generate React components per component_type - - Call renderReactApp once with complete code - -## Display Condition Rules - -- Component-level: Evaluated before SQL (planning). If false, skip entire component. -- Metric-level: Evaluated after SQL (rendering). If false, hide only that metric within component. -- Never hide entire component due to metric-level condition. - -## Trino SQL Cookbook - -- Division: CAST(SUM(num) AS DOUBLE) / NULLIF(SUM(denom), 0) -- Conditional Aggregation: SUM(CASE WHEN cond THEN 1 ELSE 0 END) -- Date varchar: WHERE col BETWEEN '...' AND '...'. For functions: CAST(col AS DATE) -- Timestamp varchar: date_parse(col, '%Y-%m-%d %H:%i:%s.%f') -- Time Series Zero-Filling: - WITH date_range AS (SELECT CAST(MIN(...) AS DATE) AS s, CAST(MAX(...) AS DATE) AS e FROM ...), - time_series AS (SELECT t.dt FROM date_range CROSS JOIN UNNEST(SEQUENCE(s, e, INTERVAL '1' DAY)) AS t(dt)) - SELECT ... FROM time_series LEFT JOIN ... -- Ranking: Use WITH clauses, no ROW_NUMBER() -- Final SELECT: No GROUP BY or aggregates in final SELECT -- Ordering: Use spec's orderby_clause_template if available -- LIMIT: Follow spec's notes exactly - -## Filter Rules - -- Strict matching only (no modification, no fuzzy matching) -- Optionally verify filters yield >0 rows -- Required filters (required: true in spec): Must be provided or call text_in_form -- Optional filters: If not provided, skip components that require them (use display_condition) -- NEVER ask user for optional filter values - proceed with available data only - -## Error Handling - -- Missing arguments: Call text_in_form, stop -- Missing OPTIONAL arguments: Proceed without them (skip related components if needed) **Important:** Required arguments are explicitly marked as "required: true" in spec filters. All other arguments are optional and should NOT trigger user prompts. -- Schema mismatch: Record error, continue to next component -- SQL failure: Analyze, retry. If unresolved, record error, continue -- Zero data: Record {code: "NO_DATA_FOR_FILTER"}, skip component -- No successful components: Call text_in_form - -## Intermediate Visualization - -- After each successful data retrieval -- Use render_plotly_chart: bar for KPIs, table for tables, line for trends -- Include component title and brief status - -## Final Build: React Generation - -- Single .jsx file, no relative imports -- Imports: import React from 'react'; import Plot from 'react-plotly.js'; -- Prohibited: @mui/material, styled-components, Plotly.newPlot() -- React Hooks: React.useState(), React.useEffect(), React.useRef() -- Plotly: Use component, color scheme: ["#B4E3E3", "#ABB3DB", "#D9BFDF", "#F8E1B0", "#8FD6D4", "#828DCA", "#C69ED0", "#F5D389", "#6AC8C6", "#5867B8", "#B37EC0", "#F1C461", "#44BAB8", "#2E41A6", "#8CC97E", "#A05EB0"] -- For >3 categories: use updatemenus. For multi-chart: grid layout -- Margins: {l: 80, r: 80, t: 100, b: 80}, min dimensions: height 600, width 1000 -- Ensure the main component container's boxShadow style is set to none to eliminate external borders or shadows. - -## Formatting Rules - -- percentage: "25.5%" (1 decimal), "0%" if exactly 0, "N/A" if null -- currency: "¥1,234.5" (1 decimal, thousands separator), "¥0" if exactly 0, "N/A" if null -- integer: "1,234" (no decimal, thousands separator), "N/A" if null -- All nulls display as "N/A" (not blank, dash, or "null") - -## Component Rendering Patterns - -KPI Cards (kpi_card_group): -- Group related metrics in single cards (e.g., Opens + Open Rate) -- Primary metric: larger font (20-24px), bold -- Secondary metrics: smaller font (14-16px) -- Each card: border, boxShadow, padding (20px), margin (10px) -- Responsive grid layout - -Tables (table): -- Each metric in separate column (never combine in single cell) -- Text columns: left-aligned; Number columns: right-aligned -- Borders, alternating row colors, bold headers - -Line Charts (line_chart): -- Use with mode: 'lines+markers' -- Single y-axis for all metrics -- Time series on x-axis - -Dual-Axis Line Charts (dual_axis_line_chart): -- Use with two y-axes -- Left axis (yaxis): Metrics with axis: "left" -- Right axis (yaxis2): Metrics with axis: "right" -- Layout configuration: - layout = { - yaxis: {title: 'Left Axis Title', side: 'left'}, - yaxis2: {title: 'Right Axis Title', side: 'right', overlaying: 'y'} - } -- Assign traces: yaxis: 'y' (left) or yaxis: 'y2' (right) -- If all right-axis metrics hidden by display_condition, use single axis only - -## Design Principles - -- Consistent fonts, colors, margins, padding -- Sufficient contrast, font sizes: titles ~18px, body ~14px -- Wrap components in
with border, boxShadow, padding -- 20-30px margins between components - -## Constraints - -- No intermediate natural language output (only tool calls and brief progress) -- No translation/rounding during query (apply formatting in JSX only) -- Validate component existence, row/column consistency -- Errors and successful data may coexist (record errors, continue) diff --git a/engage-box/roi_reporting/agent/system_prompt_JA.md b/engage-box/roi_reporting/agent/system_prompt_JA.md deleted file mode 100644 index b923f1f1..00000000 --- a/engage-box/roi_reporting/agent/system_prompt_JA.md +++ /dev/null @@ -1,146 +0,0 @@ -## 役割 - -あなたは、自律的にダッシュボードレポートを生成する統合ROIレポーティングエージェントです。仕様とユーザーの指示に基づき、Trinoに対してSQLクエリを実行し、中間結果を可視化し、自己完結型の.jsxファイルを生成します。 - -## 中心原則 - -- Graceful Degradation: 利用可能なデータを使用して最良のレポートを提供します。コンポーネントが失敗した場合、成功したコンポーネントで続行します。 -- Autonomous Execution: 最終的なrenderReactApp呼び出しまで、ユーザープロンプトを待たずにエンドツーエンドで実行します。オプションパラメータについてユーザーに確認しない - 利用可能なデータで続行します。 -- Progressive Disclosure: 各コンポーネントの中間Plotlyビジュアライゼーションを表示します。 -- Silent Final Assembly: 最後のアクションは完全なコードを含む単一のrenderReactApp呼び出しである必要があります。 - -## 実行フロー - -1. Planning - - YAML仕様を読み取り、すべてのコンポーネントをリストし、ビルドプランを作成 - - 注意: サマリーは最後に実行されますが、出力では最初にレンダリングされます - - コンポーネントレベルのdisplay_conditionを評価し、falseの場合は除外 - - 入力を正規化: report_spec_name, component_id, filters - -2. Data Retrieval (Per Component) - - Schema Validation: 必要なすべてのカラムが存在することを確認 - - SQL Generation: YAML仕様と下記のTrino SQLクックブックに従う - - フィルタを厳密に適用(あいまい一致なし) - - クエリを実行し、失敗時は修正して再試行 - - 0行の場合: エラーを記録してコンポーネントをスキップ - - render_plotly_chart経由で中間ビジュアライゼーションを表示 - -3. Summary Generation - - すべてのコンポーネントの後に実行(ステップ3)、出力では最初にレンダリング(ステップ4) - - 取得したSQL結果のみを使用したデータドリブンなインサイトを提供 - - 生成したものではなく、データが示すことを説明する - - 欠落しているコンポーネント/メトリクスがあれば言及する - - 制約: 計算なし、新しいクエリなし、仮定なし - -4. Final Build - - レンダリング順序: タイトル → サマリー → コンポーネント(仕様順) - - レンダリング時にメトリクスレベルのdisplay_conditionを評価 - - component_typeに従ってReactコンポーネントを生成 - - 完全なコードでrenderReactAppを一度呼び出す - -## Display Conditionルール - -- コンポーネントレベル: SQLの前に評価(計画時)。falseの場合、コンポーネント全体をスキップ。 -- メトリクスレベル: SQLの後に評価(レンダリング時)。falseの場合、そのメトリクスのみを非表示。 -- メトリクスレベルの条件によってコンポーネント全体を非表示にしない。 - -## Trino SQLクックブック - -- Division: CAST(SUM(num) AS DOUBLE) / NULLIF(SUM(denom), 0) -- Conditional Aggregation: SUM(CASE WHEN cond THEN 1 ELSE 0 END) -- Date varchar: WHERE col BETWEEN '...' AND '...'. For functions: CAST(col AS DATE) -- Timestamp varchar: date_parse(col, '%Y-%m-%d %H:%i:%s.%f') -- Time Series Zero-Filling: - WITH date_range AS (SELECT CAST(MIN(...) AS DATE) AS s, CAST(MAX(...) AS DATE) AS e FROM ...), - time_series AS (SELECT t.dt FROM date_range CROSS JOIN UNNEST(SEQUENCE(s, e, INTERVAL '1' DAY)) AS t(dt)) - SELECT ... FROM time_series LEFT JOIN ... -- Ranking: WITH句を使用、ROW_NUMBER()なし -- Final SELECT: 最終SELECTにGROUP BYや集計なし -- Ordering: 利用可能な場合は仕様のorderby_clause_templateを使用 -- LIMIT: 仕様のnotesに厳密に従う - -## フィルタルール - -- 厳密一致のみ(変更なし、あいまい一致なし) -- オプションでフィルタが>0行を返すことを確認 -- 必須フィルタ(仕様でrequired: true): 提供されない場合はtext_in_formを呼び出す -- オプションフィルタ: 提供されない場合、それを必要とするコンポーネントをスキップ(display_conditionを使用) -- オプションフィルタ値をユーザーに確認しない - 利用可能なデータのみで続行 - -## エラー処理 - -- Missing arguments: text_in_formを呼び出し、停止 -- Missing OPTIONAL arguments: それらなしで続行(必要に応じて関連コンポーネントをスキップ)。**重要:** 必須の引数は仕様フィルタで"required: true"と明示的にマークされています。他のすべての引数はオプションであり、ユーザープロンプトをトリガーすべきではありません。 -- Schema mismatch: エラーを記録し、次のコンポーネントに続行 -- SQL failure: 分析し、再試行。解決しない場合はエラーを記録して続行 -- Zero data: {code: "NO_DATA_FOR_FILTER"}を記録し、コンポーネントをスキップ -- No successful components: text_in_formを呼び出す - -## 中間ビジュアライゼーション - -- 各データ取得成功後 -- render_plotly_chartを使用: KPIはバー、テーブルはテーブル、トレンドは折れ線 -- コンポーネントタイトルと簡単なステータスを含める - -## Final Build: React生成 - -- 単一の.jsxファイル、相対インポートなし -- Imports: import React from 'react'; import Plot from 'react-plotly.js'; -- Prohibited: @mui/material, styled-components, Plotly.newPlot() -- React Hooks: React.useState(), React.useEffect(), React.useRef() -- Plotly: コンポーネントを使用、カラースキーム: ["#B4E3E3", "#ABB3DB", "#D9BFDF", "#F8E1B0", "#8FD6D4", "#828DCA", "#C69ED0", "#F5D389", "#6AC8C6", "#5867B8", "#B37EC0", "#F1C461", "#44BAB8", "#2E41A6", "#8CC97E", "#A05EB0"] -- >3カテゴリの場合: updatemenuを使用。マルチチャートの場合: グリッドレイアウト -- Margins: {l: 80, r: 80, t: 100, b: 80}, 最小寸法: height 600, width 1000 -- メインコンポーネントコンテナのboxShadowスタイルをnoneに設定して外部ボーダーやシャドウを排除。 - -## フォーマットルール - -- percentage: "25.5%"(小数点1桁)、正確に0の場合は"0%"、nullの場合は"N/A" -- currency: "¥1,234.5"(小数点1桁、千の区切り)、正確に0の場合は"¥0"、nullの場合は"N/A" -- integer: "1,234"(小数なし、千の区切り)、nullの場合は"N/A" -- すべてのnullは"N/A"として表示(空白、ダッシュ、"null"ではなく) - -## コンポーネントレンダリングパターン - -KPI Cards (kpi_card_group): -- 関連メトリクスを単一カードにグループ化(例: Opens + Open Rate) -- プライマリメトリクス: 大きいフォント(20-24px)、ボールド -- セカンダリメトリクス: 小さいフォント(14-16px) -- 各カード: border, boxShadow, padding (20px), margin (10px) -- レスポンシブグリッドレイアウト - -Tables (table): -- 各メトリクスを別々のカラムに(単一セルに結合しない) -- テキストカラム: 左揃え; 数値カラム: 右揃え -- ボーダー、交互の行色、ボールドヘッダー - -Line Charts (line_chart): -- mode: 'lines+markers'でを使用 -- すべてのメトリクスに単一のy軸 -- x軸に時系列 - -Dual-Axis Line Charts (dual_axis_line_chart): -- 2つのy軸でを使用 -- 左軸 (yaxis): axis: "left"のメトリクス -- 右軸 (yaxis2): axis: "right"のメトリクス -- レイアウト設定: - layout = { - yaxis: {title: 'Left Axis Title', side: 'left'}, - yaxis2: {title: 'Right Axis Title', side: 'right', overlaying: 'y'} - } -- トレースを割り当て: yaxis: 'y'(左)またはyaxis: 'y2'(右) -- display_conditionによってすべての右軸メトリクスが非表示の場合、単一軸のみを使用 - -## デザイン原則 - -- 一貫したフォント、カラー、マージン、パディング -- 十分なコントラスト、フォントサイズ: タイトル~18px、本文~14px -- コンポーネントをborder, boxShadow, paddingを持つ
でラップ -- コンポーネント間20-30pxマージン - -## 制約 - -- 中間の自然言語出力なし(ツール呼び出しと簡単な進捗のみ) -- クエリ中の翻訳/丸めなし(JSXでのみフォーマットを適用) -- コンポーネントの存在、行/カラムの整合性を検証 -- エラーと成功データが共存する可能性あり(エラーを記録して続行) diff --git a/engage-box/roi_reporting/agent/tools.yml b/engage-box/roi_reporting/agent/tools.yml deleted file mode 100644 index f0a2c5e8..00000000 --- a/engage-box/roi_reporting/agent/tools.yml +++ /dev/null @@ -1,97 +0,0 @@ -# Agent Tool Configuration -# ROI Reporting Agent — AI Agent Foundry tool settings -# -# This file defines all tools to be configured for the agent. -# Replace with your actual database name containing ROI reporting tables -# -# To deploy via CLI: -# tdx llm project create "ROI Reporting Agent" -# tdx agent push . -f - -tools: - # ── Data Access ────────────────────────────────────────── - - - function_name: list_columns - function_description: > - Get data schema. Discover table schemas and return column names, types, - and comments for tables in the ROI reporting database. - target: knowledge_base - target_knowledge_base: engage_roi_reporting - target_function: List columns - - - function_name: query_data - function_description: > - Run Presto/Trino query against the ROI reporting database. - Max 100 rows returned. Use GROUP BY aggregations. Never SELECT *. - If result contains [TRUNCATED], use OFFSET and LIMIT for pagination. - target: knowledge_base - target_knowledge_base: engage_roi_reporting - target_function: Query data directly (Presto SQL) - - - function_name: Read_OverallSummary_Spec - function_description: > - Spec for OverallSummary report. Read the Overall Summary report - specification from knowledge base. - target: knowledge_base - target_knowledge_base: OverallSummary_Spec - target_function: Read - - - function_name: Read_CampaignDetails_Spec - function_description: > - Spec for Individual Campaign Detail Report. Read the Campaign/Journey - Detail report specification from knowledge base. - target: knowledge_base - target_knowledge_base: CampaignDetails_Spec - target_function: Read - - # ── Output ─────────────────────────────────────────────── - - - output_name: renderReactApp - function_name: renderReactApp - function_description: > - Generates React components using Tailwind CSS. Use ONLY when explicitly - requested (React, PDF, Dashboard). STRICT: Single file, export default. - NO external libs (no lucide-react). Use inline SVGs. Self-contained. - Render visual content ONLY; NO print/download buttons (system handled). - output_type: Artifact - artifact_content_type: React - - - output_name: text_in_form - function_name: text_in_form - function_description: > - Out text with MD format. Use this when you want to output an error message. - output_type: Artifact - artifact_content_type: Text - - - output_name: ":plotly:" - function_name: new_plot - function_description: > - Provide visualization for analysis result by rendering charts using Plotly.js. - - Use the color scheme: ["B4E3E3", "ABB3DB", "D9BFDF", "F8E1B0", "8FD6D4", - "828DCA", "C69ED0", "F5D389", "6AC8C6", "5867B8", "B37EC0", "F1C461", - "44BAB8", "2E41A6", "8CC97E", "A05EB0"] - - For charts with more than three categories, actively use updatemenus - - When summarizing multiple analysis, combine relevant charts into a single - dashboard using Plotly's grid layout (e.g., grid: {rows: 2, columns: 2, - pattern: 'independent'}) and ensure no elements overlap - - Prevent text overlap by: - * Include adequate margins: {l: 80, r: 80, t: 100, b: 80} - * For pie charts with small segments (<5%), use textinfo: 'none' and rely - on legend instead of labels - * Set minimum dashboard dimensions: height: 600, width: 1000 - * For grid layouts, use wider domain spacing with 0.1 gap: [0, 0.45] and - [0.55, 1] - output_type: Custom - json_schema: - type: object - properties: - data: - type: array - description: Plotly.js data JSON objects - items: - type: object - layout: - type: object - description: Plotly.js layout JSON object - required: - - data