| name | audience-report-jp |
| description | Generate DXP audience analysis report via show_widget for Japanese-market clients (English UI). Triggers on Segment ID + month, uploaded audience data files, or multi-segment comparison requests. English output only. |
Audience Report Skill (Japan — English UI)
Generate a comprehensive, visually rich audience analysis report inline in the Chat
interface using the Visualizer (show_widget tool).
Language rule: All report content — section titles, card labels, chart axes, tooltip
text, persona summaries, insights, and recommendations — must be written in English.
This includes any text rendered inside widgets. The audience selector UI for this
edition (tagtoo-audiences-jp.html) emits an English trigger phrase, which activates
this skill. This skill then calls the MCP with language="en", so all server-side
data (user profiles, keyword labels, etc.) is returned in English. Chinese text
should not appear in the MCP response or in the report output.
Trigger conditions
This skill activates when ANY of the following conditions are met:
- The user provides a Segment ID (e.g.,
tm:d1028, tm:d_3542_k_140 — starting with tm:) together with a YYYYMM month (e.g., 202602) and mentions "audience", "report", or "segment"
- The user uploads audience data files (keyword frequency JSON, consumption JSON, user profile TXT) and asks for analysis
- The user asks to compare multiple segments — "compare", "differences", "which audience"
Multiple Segment IDs automatically switch to comparison mode. Output is ALWAYS inline
widgets via show_widget — never file-based reports (no docx/pptx/pdf).
Deciding the report mode
Before producing anything, determine which mode to use:
- Single-segment mode — the user provides data for one segment. Produce a 7-section
deep-dive report (Sections 1–7 below).
- Multi-segment comparison mode — the user provides data for 2+ segments (e.g.,
multiple Segment IDs, multiple file sets, or explicitly asks to "compare"). Produce
a comparison report (Sections C1–C5 below).
Signals that indicate multi-segment mode:
- Multiple segment names or IDs mentioned in the prompt
- Multiple consumption_distribution files with different
segment fields
- Multiple user profile files for different segments
- The user says "比較", "compare", "異同", "differences", "哪個受眾", "which audience"
When in doubt, ask the user to confirm.
Expected input files
Each segment can have up to four types of data. Not all are required.
| File type | Format | Description |
|---|
| View item keyword frequency | JSON {"keyword": weight, ...} | Browse/view behavior keyword weights (0–100 scale) |
| Purchase keyword frequency | JSON {"keyword": weight, ...} | Actual purchase behavior keyword weights (0–100 scale) |
| Consumption distribution | JSON with consumption_distribution array of {range, user_count} | Spending power distribution |
| User profile description | TXT or JSON | AI-predicted demographics, interests, shopping preferences |
For multi-segment mode, the user provides one set of files per segment. File names
usually contain the segment name (e.g., Segment_轉址網站_-_超市零售_消費力原始資料.json).
Group files by segment before processing.
Section 0: Data fetching via Tagtoo MCP Gateway
When the user provides Segment ID(s) and a month (YYYYMM format) instead of
uploading files directly, fetch the report data via the Tagtoo MCP Gateway before
proceeding to report generation.
Required user inputs
| Input | Format | Example |
|---|
| Segment ID(s) | String, may contain colons and underscores | tm:d1028, tm:d_3542_k_140 |
| Month | YYYYMM | 202602 |
If the user says something like "Show me the audience report for tm:d1028 in February",
extract:
- Segment ID =
tm:d1028
- Month =
202602
If multiple Segment IDs are provided, fetch data for each and enter multi-segment
comparison mode automatically.
MCP tool
Use the dxp-mcp_get_audience_report tool from the Tagtoo MCP Gateway server.
One call per segment returns all four data types (user profile, browse keywords,
purchase keywords, consumption distribution) in a single response.
Parameters:
| Parameter | Type | Required | Description |
|---|
segment_id | string | Yes | Segment ID (e.g., tm:d1028) |
year_month | string | Yes | Report month in YYYYMM format (e.g., 202604) |
party_type | string | No | first, second, or insite_second. If omitted, auto-detects across all three types |
insite_or_outsite | string | No | Only applies when party_type is first. Values: insite or outsite. If omitted, prefers insite; falls back to outsite |
language | string | No | zh (default, Traditional Chinese) or en (English) |
For this skill, always set language to "en".
Response format
Success (single match):
{
"party_type": "second",
"insite_or_outsite": null,
"report": {
"user_profile_pic_url": "https://storage.googleapis.com/.../使用者輪廓.png",
"user_profile_txt": "1. 年齡:25-40歲\n2. 性別:...\n3. 興趣:...\n4. 購物偏好:...",
"purchase_power_pic_url": "https://storage.googleapis.com/.../消費力分佈.png",
"view_wordcloud_pic_url": "https://storage.googleapis.com/.../熱門瀏覽商品關鍵字.png",
"purchase_wordcloud_pic_url": "https://storage.googleapis.com/.../熱門購買商品關鍵字.png",
"purchase_power_data": {
"segment": "轉址網站 - 購物狂熱者",
"location": "",
"consumption_distribution": [
{"range": "1000以下", "user_count": 534},
{"range": "1001~3000", "user_count": 708},
{"range": "3001~5000", "user_count": 192}
]
},
"view_freq_data": {
"露營帳篷": 100.0,
"DHA魚油": 92.3,
"日拋隱形眼鏡": 87.1
},
"purchase_freq_data": {
"DHA魚油": 100.0,
"益生菌": 88.7,
"膠原蛋白": 82.4
}
}
}
report fields reference:
| Field | Type | Used by | Description |
|---|
user_profile_txt | string | Section 2 | AI-predicted persona text (demographics, interests, shopping preferences) |
user_profile_pic_url | string | — | Pre-rendered user profile image URL (reference only) |
purchase_power_data | object | Sections 1, 3, C1, C3 | Raw consumption distribution JSON with segment, location, and consumption_distribution array of {range, user_count} |
purchase_power_pic_url | string | — | Pre-rendered spending distribution chart URL (reference only) |
view_freq_data | object | Sections 1, 4, 6, C1, C4a | Browse keyword frequency: {"keyword": weight}, weight 0–100 |
purchase_freq_data | object | Sections 1, 5, 6, C1, C4b | Purchase keyword frequency: {"keyword": weight}, weight 0–100 |
view_wordcloud_pic_url | string | — | Pre-rendered browse word cloud image URL (fallback only) |
purchase_wordcloud_pic_url | string | — | Pre-rendered purchase word cloud image URL (fallback only) |
Success (multiple matches):
{
"matches": [
{"party_type": "first", "insite_or_outsite": "insite", "report": { ... }},
{"party_type": "second", "insite_or_outsite": null, "report": { ... }}
]
}
When multiple matches are returned, use the one whose party_type matches the user's
request. If the user did not specify, prefer second.
Failure:
{
"error": "查不到 segment tm:d9999 的報告"
}
Report the error to the user and skip sections that depend on missing data.
Important notes
- No manual GCS path construction needed. The MCP tool handles bucket routing,
party_type detection, and file discovery internally.
party_type auto-detection: If you omit party_type, the tool probes all
three types automatically. Only pass it explicitly if the user specifies one.
- Large response handling: The report JSON payload can be large. If the MCP tool
result is too large for context, it may be saved to a local file path. Use
bash_tool with Python to read and process it.
- Error handling: If a segment is not found for the given month, the response
contains an
error field. Report which data is missing and proceed with whatever
was successfully fetched.
Using the response
After calling dxp-mcp_get_audience_report, extract the report object from the
response. If the response contains a matches array (multiple matches), select the
one whose party_type matches the user's request (prefer second if unspecified),
then extract its report object.
All subsequent sections reference data fields via the report. prefix — for example,
report.view_freq_data means the view_freq_data field inside this report object,
report.purchase_power_data.consumption_distribution means the nested
consumption_distribution array inside purchase_power_data.
Multi-segment fetching
When multiple Segment IDs are provided, call dxp-mcp_get_audience_report once per
segment. Extract the report object from each response independently. Keep each
segment's report organized by segment ID. After all data is fetched, determine the
report mode (single vs multi-segment) and proceed to the appropriate sections below.
Single-segment mode (Sections 1–7)
Produce the report in this order. Skip sections if the corresponding data is not
available. Each section uses one show_widget call, with 1–3 paragraphs of prose
analysis between sections (never stack widgets back-to-back). Write all content
in English.
Section 1: Report header & KPI dashboard
Purpose: A one-glance summary — which segment, how much data, what scale.
Output: A banner with segment name, plus 3–4 metric cards showing:
total browse keywords, total purchase keywords, total consumers, dominant spending range.
Widget layout:
- Segment name + icon at the top
- CSS grid
repeat(4, minmax(0, 1fr)) of metric cards
- Each card: muted 13px label + 24px/500 number
- Card style:
background: var(--color-background-secondary), no border,
border-radius: var(--border-radius-md), padding 1rem
Labels to use (English): "Browse Keywords", "Purchase Keywords", "Total Consumers",
"Top Spending Range"
Data needed: Count keys in report.view_freq_data and report.purchase_freq_data;
sum user_count from report.purchase_power_data.consumption_distribution;
find the range with highest user_count.
Section 2: User persona
Purpose: Build empathy — who is this person before we look at any charts?
Output: Prose only (no widget). Summarize demographics, interests, and shopping
behavior from the profile file in English. Highlight implications for the segment.
Section 3: Consumption distribution
Purpose: Answer "how much does this audience spend?"
Output: A vertical bar chart (Chart.js) showing user count per spending range.
Widget layout:
- Canvas in
<div> with height: 320px; position: relative
type: 'bar', tooltip: ctx.parsed.y + ' users'
- Custom HTML legend below
Prose after widget: State the dominant range, its share of total, and what this
implies for the segment's value. Write in English.
Section 4: Browse interest word cloud
Purpose: Answer "what is this audience interested in?" — a word cloud reveals the
overall interest landscape at a glance, with font size encoding keyword weight.
Output: A word cloud of the top 30 browse keywords rendered via show_widget.
Mandatory: This section must be executed immediately after reading the data.
The following are strictly forbidden:
- ❌ Bar charts (horizontal or vertical) ❌ Tables ❌ Bullet lists ❌ Any chart type other than a word cloud
How to produce:
- From
report.view_freq_data, sort all keywords by weight descending, take top 30
- Run the Python template from
dxp-audience-report-style.md "Section 4/5: Single
Audience Word Cloud Python Template" in bash_tool, using browse parameters:
CONTAINER_ID=wc4, TITLE=Browse Keywords Top 30, TITLE_COLOR=#185FA5,
BG_COLOR=#f0f6ff, TOOLTIP_LABEL=Browse Score, PR=[55,138,221], LT=[168,205,239]
- Render the output HTML string with
show_widget
Prose after widget: Interpret what the top keywords collectively reveal about
the persona — life stage, aspirations, purchase intent signals. Write in English.
Section 5: Purchase behavior word cloud
Purpose: Answer "what do they actually buy here?" — ground truth of spending.
Output: A word cloud of the top 30 purchase keywords rendered via show_widget.
Mandatory: Same rules as Section 4 — word cloud only, no other chart types.
How to produce: Same flow as Section 4, but using report.purchase_freq_data and
purchase parameters: CONTAINER_ID=wc5, TITLE=Purchase Keywords Top 30,
TITLE_COLOR=#993C1D, BG_COLOR=#fff5f2, TOOLTIP_LABEL=Purchase Score,
PR=[216,90,48], LT=[240,180,154]
Prose after widget: Dominant purchase keywords, notable brands or product types,
and what this reveals about actual spending behavior. Write in English.
Section 6: Browse vs Purchase gap analysis
Purpose: Reveal the mismatch between interest and action — the most actionable
section, exposing unmet demand and the channel's real role.
Output: Two-column comparison layout + insight callout box.
Widget layout:
- Left column header: "Browse Interests" — top 10 browse keywords as colored pills
- Center: arrow icon
- Right column header: "Actual Purchases" — top 10 purchase keywords as colored pills
- Bottom: insight box (
background: var(--color-background-warning)) with English insight text
How to produce: From report.view_freq_data take the top 20 browse keywords;
from report.purchase_freq_data take the top 20 purchase keywords. Compare the two
sets. Identify keywords that appear only in browse (unmet demand / window shopping)
and only in purchase (habitual buying). Note any surprising gaps or overlaps.
Section 7: Marketing recommendations
Purpose: Turn insights into executable actions.
Output: 3–4 strategy cards stacked vertically.
Widget layout:
- Card style: white bg, 0.5px border, border-radius-lg, padding 1.25rem
- Priority badge with semantic bg colors (e.g., "High Priority", "Medium Priority")
- Highest-priority card:
border: 2px solid var(--color-border-info)
How to produce: Derive from Sections 3–6. Typical types: cross-sell, browse-to-
purchase conversion, promotion timing, persona-specific targeting. Write all card
content in English.
Multi-segment comparison mode (Sections C1–C5)
When 2+ segments are provided, the report shifts from deep-diving one audience to
comparing across audiences. The goal is to answer: how are these segments similar,
how are they different, and what does that mean for marketing strategy?
Before starting, process each segment's data independently using the same techniques
from single-segment mode (keyword grouping, consumption stats). Store the results
in a structured format so they can be compared side by side.
Section C1: Comparison dashboard
Purpose: Show all segments at a glance so the reader immediately sees scale
differences and can orient themselves.
Output: A table-like grid where each column is one segment.
Widget layout:
- Header row: segment names (one per column)
- Metric rows: Browse Keywords, Purchase Keywords, Total Consumers,
Top Spending Range — one value per segment per row. All labels in English.
- Highlight the highest/lowest value in each row with a subtle accent
(
background: var(--color-background-info) for highest,
background: var(--color-background-danger) for lowest)
- If 2 segments: use
repeat(2, minmax(0, 1fr))
- If 3–4 segments: use
repeat(N, minmax(0, 1fr))
- If 5+ segments: stack vertically (one card per segment) to avoid cramping
Prose after widget: Summarize the scale differences in English in one paragraph.
Section C2: Persona comparison
Purpose: Put the human profiles side by side so the reader can feel the contrast
before seeing data.
Output: A card per segment, each containing the persona summary in English
(condensed to 3–4 lines). Use a distinct color accent per segment.
Widget layout:
- Cards in a row (same grid as C1)
- Each card: color-coded top border (2px solid, using segment's assigned color),
white bg, 0.5px border on other sides
- Persona text: 13px, secondary color, 1.6 line-height
Prose after widget: Call out the key persona differences — age, gender, interests,
life stage in English. Are these fundamentally different people or variations of the same profile?
Section C3: Consumption comparison
Purpose: Answer "which segment spends more, and how does the spending shape differ?"
Output: A grouped or stacked bar chart (Chart.js) with one color per segment,
showing user count per spending range.
Widget layout:
type: 'bar' with multiple datasets, one per segment
- Each dataset gets its own color from the segment color assignments
- Group mode:
options.scales.x.stacked = false (side-by-side bars)
- Canvas wrapper height: 360px
- Custom legend showing segment name + color square. All labels in English.
Prose after widget: Compare dominant ranges, overall spending power, and
whether one segment skews higher or lower. Write in English.
Section C4: Interest & purchase overlap analysis
Purpose: The core comparison — where do these audiences overlap in what they
browse and buy, and where do they diverge? This reveals whether the segments can
be targeted with the same campaigns or need distinct strategies.
Output: Two widgets in sequence (with prose between them):
Widget C4a — Browse interest comparison:
Side-by-side word clouds, top 30 browse keywords per segment. Mandatory —
must use word clouds, no other chart type allowed.
How to build:
- For each segment, from
report.view_freq_data, sort by weight descending, take top 30
- Use the HTML template from
dxp-audience-report-style.md "Section C4: Side-by-Side
Word Clouds" — one word cloud column per segment in a CSS grid
- Each segment uses its assigned color ramp (Segment 1: Blue pr/lt, Segment 2: Coral pr/lt)
- Tooltip label:
Browse Score
- For 3+ segments, expand the grid to N columns
Prose between C4a and C4b: Note which keywords appear in multiple segments
(shared interest) vs which are unique to one segment (differentiators). Write in English.
Widget C4b — Purchase comparison:
Same structure as C4a, applied to purchase data. Top 30 purchase keywords per segment,
rendered as side-by-side word clouds. Mandatory.
How to build:
- For each segment, from
report.purchase_freq_data, sort by weight descending, take top 30
- Same template as C4a, change tooltip to
Purchase Score and title to
Purchase Keywords Comparison Top 30
Prose after C4b: Identify: shared purchase keywords (target together),
exclusive keywords (differentiate campaigns), and any surprising mismatches
between browse and purchase across segments. Write in English.
Section C5: Comparative recommendations
Purpose: Actionable strategies that leverage the differences between segments.
Output: Strategy cards, but now each card specifies which segment(s) it applies to.
All card content in English.
Widget layout: Same as Section 7, but each card includes an "Applies to:" line
with segment name badges.
How to produce recommendations:
- Shared strengths: categories strong across all segments → unified campaigns
- Unique opportunities: categories strong in one segment only → targeted campaigns
- Gap arbitrage: a category that Segment A browses and Segment B buys → cross-
promote between segments, or tailor the conversion strategy per segment
- Budget allocation: if one segment has higher consumption, recommend proportional
ad spend; if one segment has better browse-to-purchase alignment, recommend
efficiency plays
Data processing
The MCP response report.view_freq_data and report.purchase_freq_data are both
{"keyword": weight} objects. Sections 4, 5, C4a, and C4b all use the same pattern
— sort by weight, take top 30:
top30 = sorted(data.items(), key=lambda x: -x[1])[:30]
for keyword, weight in top30:
print(f'{keyword}: {weight:.1f}')
For multi-segment mode, run this once per segment and collect results:
segment_tops = {
'seg_a': sorted(data_a.items(), key=lambda x: -x[1])[:30],
'seg_b': sorted(data_b.items(), key=lambda x: -x[1])[:30],
}
lookup = {seg: dict(tops) for seg, tops in segment_tops.items()}
all_keywords = sorted(set(k for tops in segment_tops.values() for k, _ in tops))
for kw in all_keywords:
print(kw, {seg: lookup[seg].get(kw, 0) for seg in segment_tops})
No keyword translation: Display keywords as-is from the data. Do not translate
individual keywords or assign them to named categories.
Segment color assignments (multi-segment mode)
In comparison mode, each segment gets a dedicated color used across all charts and
cards. Assign in order of appearance:
| Segment index | Ramp | Primary (400) | Light fill (50) |
|---|
| Segment 1 | Blue | #378ADD | #E6F1FB |
| Segment 2 | Coral | #D85A30 | #FAECE7 |
| Segment 3 | Teal | #1D9E75 | #E1F5EE |
| Segment 4 | Purple | #7F77DD | #EEEDFE |
| Segment 5 | Amber | #854F0B | #FAEEDA |
These are for identifying segments. Within a single segment's charts (e.g., the
browse interest cards), use the full 9-ramp category palette below.
Category color palette (single-segment mode)
Used by the word cloud renderer in Sections 4, 5, C4a, and C4b. Each word cloud
interpolates between its light (lt) and primary (pr) color based on keyword weight.
| Index | Ramp | Dot/bar (400) | Card fill (50) | Card title (800) | Card subtitle (600) |
|---|
| 0 | Blue | #378ADD | #E6F1FB | #0C447C | #185FA5 |
| 1 | Coral | #D85A30 | #FAECE7 | #712B13 | #993C1D |
| 2 | Pink | #D4537E | #FBEAF0 | #72243E | #993556 |
| 3 | Teal | #1D9E75 | #E1F5EE | #085041 | #0F6E56 |
| 4 | Amber | #854F0B | #FAEEDA | #633806 | #854F0B |
| 5 | Green | #639922 | #EAF3DE | #27500A | #3B6D11 |
| 6 | Purple | #7F77DD | #EEEDFE | #3C3489 | #534AB7 |
| 7 | Red | #E24B4A | #FCEBEB | #791F1F | #A32D2D |
| 8 | Gray | #888780 | #F1EFE8 | #444441 | #5F5E5A |
Chart.js common setup
- Dark mode:
const isDark = matchMedia('(prefers-color-scheme: dark)').matches
- Ticks:
isDark ? '#c2c0b6' : '#5f5e5a'; Grid: isDark ? 'rgba(255,255,255,0.06)' : 'rgba(0,0,0,0.06)'
- Disable default legend; build custom HTML with 10px color squares
- Canvas in
<div> with explicit height and position: relative
- Load:
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.1/chart.umd.js">
- Round all numbers; canvas cannot resolve CSS variables — use hardcoded hex
After the report
End with a structural summary in English listing all sections produced, then offer next steps:
- Export to PPTX or PDF
- Extract categorization rules into a reusable mapping table
- (Multi-segment mode) Drill down into any single segment for a full deep-dive report