From a1fd963c6933a38ae143a5b95b6b111b20777427 Mon Sep 17 00:00:00 2001 From: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com> Date: Sun, 14 Jun 2026 02:14:08 +0500 Subject: [PATCH] feat(service-ai): steer the agent to visualize_data for chart requests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Small models (e.g. claude-haiku) sometimes answered a "draw a bar chart" request with a markdown TABLE — running query_data/aggregate_data and formatting the numbers — instead of calling visualize_data. This was a tool-selection problem, not a capability gap: the chart preference was buried as guideline #7 and competed with guideline #4 ("format with markdown tables"). - data-explorer-skill.ts: add a prominent "Choosing the right tool" section ABOVE the guidelines — chart intent (incl. CN terms 图表/柱状图/折线图/饼图/画图) → MUST call visualize_data; never substitute a table; reconcile the table-formatting guideline; fix duplicate guideline numbering. - visualize-data.tool.ts: strengthen the tool description to be imperative ("the ONLY tool that draws a chart… you MUST call this, not a table; if you already fetched the numbers, still call visualize_data to render them"). Prompt-only tuning — no behavior/contract change. Raises the likelihood the model reaches for visualize_data on a plain chart request without an explicit "use visualize_data" nudge. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../src/skills/data-explorer-skill.ts | 9 ++++++--- .../src/tools/visualize-data.tool.ts | 18 +++++++++++------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/packages/services/service-ai/src/skills/data-explorer-skill.ts b/packages/services/service-ai/src/skills/data-explorer-skill.ts index c49bba7a74..cf3de127a1 100644 --- a/packages/services/service-ai/src/skills/data-explorer-skill.ts +++ b/packages/services/service-ai/src/skills/data-explorer-skill.ts @@ -27,15 +27,18 @@ Capabilities: - Perform aggregations and statistical analysis (count, sum, avg, min, max) - Render results as a CHART when a visualization communicates the answer better than text +Choosing the right tool (decide this BEFORE querying): +- The user wants a CHART — they say "chart/plot/graph/visualize/draw", "图表/柱状图/折线图/饼图/画图/可视化", OR they ask to show/compare/break down a count or sum grouped by a category, OR a trend over time → you MUST call visualize_data. It is the ONLY tool that draws a chart; query_data/aggregate_data return numbers, never a chart. Do NOT answer a chart request with a markdown table. If you already fetched the numbers, still call visualize_data to render them. The chart shows inline automatically — afterwards reply with one or two sentences describing it; do NOT re-print the numbers as a table. +- The user wants the underlying records or a single number → query_data / query_records / aggregate_data. + Guidelines: 1. Always use the describe_object tool first to understand a table's structure before querying it. 2. Do NOT assume generic fields like \`status\`, \`is_active\`, \`deleted_at\`, \`type\`, or \`enabled\` exist on every object — they almost never do. Field names in \`where\`, \`fields\`, \`orderBy\`, \`groupBy\`, and aggregations MUST come from describe_object output. If the tool returns an "Unknown field" error, call describe_object on that object and retry with real field names. 3. Respect the user's current context — if they are viewing a specific object or record, use that as the default scope. -4. When presenting data, format it in a clear and readable way using markdown tables or bullet lists. +4. For record lists or a plain numeric answer, format clearly with markdown tables or bullet lists. When a chart was requested, use visualize_data instead of a table (see "Choosing the right tool" above). 5. For large result sets, summarize the data and mention the total count. 6. When performing aggregations, explain the results in plain language. -7. Prefer the visualize_data tool when the user asks to "chart", "plot", "graph", "visualize", or "show a breakdown/trend/distribution", or whenever a count/sum grouped by a category (or a trend over time) is the answer. The chart renders inline automatically — after calling it, just describe briefly what the chart shows; do NOT also dump the raw numbers as a table. -8. If a query returns no results, suggest possible reasons and alternative queries. +7. If a query returns no results, suggest possible reasons and alternative queries. 8. Never expose internal IDs unless the user explicitly asks for them. 9. Always answer in the same language the user is using.`, tools: [ diff --git a/packages/services/service-ai/src/tools/visualize-data.tool.ts b/packages/services/service-ai/src/tools/visualize-data.tool.ts index cc0dd82638..ae68b3e7a5 100644 --- a/packages/services/service-ai/src/tools/visualize-data.tool.ts +++ b/packages/services/service-ai/src/tools/visualize-data.tool.ts @@ -117,13 +117,17 @@ export const VISUALIZE_DATA_TOOL: AIToolDefinition = { name: 'visualize_data', label: 'Visualize Data (Chart)', description: - 'Aggregate a data object and render the result as a CHART in the chat. ' + - 'Use this — instead of query_records / aggregate_data — whenever the ' + - 'best answer is a visualization (counts/sums grouped by a category, a ' + - 'trend over time, a distribution, etc.). The chart is shown to the user ' + - 'automatically; you only need to briefly describe what it shows. ' + - 'Field names in `dimension`, `measures[].field` and `where` MUST be real ' + - 'fields obtained from describe_object — do NOT guess generic names.', + 'Aggregate a data object and render the result as a CHART shown inline in ' + + 'the chat. This is the ONLY tool that draws a chart. You MUST call this — ' + + 'NOT query_records / aggregate_data, and NOT a markdown table — whenever ' + + 'the user asks to chart/plot/graph/visualize/draw data, or to show, ' + + 'compare, or break down a count or sum grouped by a category, or a trend ' + + 'over time. If you already fetched the numbers with another tool, still ' + + 'call visualize_data to render them. After it runs the chart is shown to ' + + 'the user automatically; reply with one or two sentences describing it and ' + + 'do NOT re-print the data as a table. Field names in `dimension`, ' + + '`measures[].field` and `where` MUST be real fields obtained from ' + + 'describe_object — do NOT guess generic names.', parameters: { type: 'object', properties: {