From 9952af198a92ed73ef6be3ba60692af68056c28b Mon Sep 17 00:00:00 2001 From: qixinbo Date: Wed, 1 Apr 2026 10:00:40 +0800 Subject: [PATCH] fix: prompt for viz enhanced --- backend/app/skills_builtin/nl2sql/SKILL.md | 4 ++- .../app/skills_builtin/visualization/SKILL.md | 5 +++- backend/app/tools/nl2sql.py | 15 ++++++++--- backend/app/tools/visualization.py | 25 ++++++++++++++++++- backend/main.py | 6 +++++ 5 files changed, 49 insertions(+), 6 deletions(-) diff --git a/backend/app/skills_builtin/nl2sql/SKILL.md b/backend/app/skills_builtin/nl2sql/SKILL.md index f102cce..c0a0cc7 100644 --- a/backend/app/skills_builtin/nl2sql/SKILL.md +++ b/backend/app/skills_builtin/nl2sql/SKILL.md @@ -17,8 +17,10 @@ You are an expert data analyst. You have access to a powerful `nl2sql` tool that - Call the `nl2sql` tool with the user's natural language query. - If the user explicitly asks to "visualize" or "plot" the data in the SAME message as the query (e.g., "Show me sales by region and plot it as a pie chart"), you can set `generate_chart=True` in the `nl2sql` tool. - If the user ONLY asks to query data, set `generate_chart=False` (default). +- If `generate_chart=True` was used and a chart is returned, do not call the `visualization` tool again in the same turn. +- Do not use `exec`, Python, matplotlib, or any manual plotting flow for this task. ## After using the tool - The tool will return a summary of the executed query and a sample of the results. - Use this information to provide a clear, concise, and helpful response to the user. -- If a chart was successfully generated by the tool, inform the user that the chart is available in the visualization panel. +- If a chart was successfully generated by the tool, reuse that Vega chart in the visualization panel. diff --git a/backend/app/skills_builtin/visualization/SKILL.md b/backend/app/skills_builtin/visualization/SKILL.md index f317a4c..89c4a38 100644 --- a/backend/app/skills_builtin/visualization/SKILL.md +++ b/backend/app/skills_builtin/visualization/SKILL.md @@ -12,11 +12,14 @@ You are an expert data visualization specialist. You have access to a `visualiza ## When to use this skill - When the user asks to visualize, plot, or draw a chart based on data that has ALREADY been queried or is currently in context. - Examples: "Visualize it as a bar chart", "Plot the trend over time", "Draw a pie chart of the regions". -- DO NOT use this tool if the data hasn't been queried yet. If the user asks a new question and wants it visualized (e.g., "Show me sales and plot it"), use the `nl2sql` tool with `generate_chart=True` instead, or call `nl2sql` first and then this tool. +- DO NOT use this tool if the data hasn't been queried yet. If the user asks a new question and wants it visualized (e.g., "Show me sales and plot it"), use the `nl2sql` tool with `generate_chart=True`. +- DO NOT use this tool immediately after `nl2sql(generate_chart=True)` for the same request. ## How to use this skill - Call the `visualization` tool with the user's specific visualization request (e.g., "plot as a pie chart"). - The tool relies on the data from the most recent SQL query. It will automatically read this data from the context. +- Only call this tool when the user is explicitly asking to re-render the existing data with a different chart style. +- Do not use `exec`, Python, matplotlib, or manual plotting scripts. ## After using the tool - The tool will return a success message and the reasoning for the chosen chart type. diff --git a/backend/app/tools/nl2sql.py b/backend/app/tools/nl2sql.py index aab0108..6c8cd86 100644 --- a/backend/app/tools/nl2sql.py +++ b/backend/app/tools/nl2sql.py @@ -1,5 +1,6 @@ import json import logging +import re from typing import Any from nanobot.agent.tools.base import Tool @@ -9,12 +10,19 @@ from fastapi.encoders import jsonable_encoder logger = logging.getLogger(__name__) -def _build_sql_chart_viz(nl2sql_result: NL2SQLResponse) -> dict: +def _normalize_query(value: str) -> str: + return re.sub(r"\s+", "", (value or "")).lower() + + +def _build_sql_chart_viz(nl2sql_result: NL2SQLResponse, query: str) -> dict: chart = nl2sql_result.chart payload = { "sql": nl2sql_result.sql, "result": nl2sql_result.result, "chart": chart.model_dump(by_alias=True, exclude_none=True) if chart else None, + "chart_query": query, + "chart_query_normalized": _normalize_query(query), + "chart_generated_by": "nl2sql", "error": nl2sql_result.error, } return jsonable_encoder(payload) @@ -34,7 +42,8 @@ class NL2SQLTool(Tool): return ( "Query the connected database or data source using natural language. " "Use this tool when the user asks to query, analyze, aggregate, or fetch data from the database. " - "Set generate_chart=True if the user also wants to visualize or plot the data." + "Set generate_chart=True if the user also wants to visualize or plot the data. " + "If generate_chart=True, do not call visualization again for the same request." ) @property @@ -76,7 +85,7 @@ class NL2SQLTool(Tool): # Always save visualization payload to context so the chat stream can pick it up # Even if there's an error, we want the frontend to see the generated SQL - viz_payload = _build_sql_chart_viz(result) + viz_payload = _build_sql_chart_viz(result, query) existing_viz = current_viz_data.get() if isinstance(existing_viz, dict): existing_viz.clear() diff --git a/backend/app/tools/visualization.py b/backend/app/tools/visualization.py index 7752254..0520928 100644 --- a/backend/app/tools/visualization.py +++ b/backend/app/tools/visualization.py @@ -1,4 +1,5 @@ import logging +import re from typing import Any from nanobot.agent.tools.base import Tool @@ -8,6 +9,10 @@ from fastapi.encoders import jsonable_encoder logger = logging.getLogger(__name__) + +def _normalize_query(value: str) -> str: + return re.sub(r"\s+", "", (value or "")).lower() + class VisualizationTool(Tool): """ Tool for generating a visualization (chart) from existing data. @@ -22,7 +27,8 @@ class VisualizationTool(Tool): return ( "Generate a chart or visualization based on the most recently queried data. " "Use this tool when the user asks to plot, visualize, or create a chart from data that has already been retrieved. " - "Note: This tool relies on the data from the last executed SQL query. If no query has been executed yet, you must use the nl2sql tool first." + "Note: This tool relies on the data from the last executed SQL query. If no query has been executed yet, you must use the nl2sql tool first. " + "Do not call this tool right after nl2sql(generate_chart=True) for the same request." ) @property @@ -47,6 +53,20 @@ class VisualizationTool(Tool): return "Error: No data available to visualize. Please query the data first using the nl2sql tool." try: + existing_viz = current_viz_data.get() or {} + existing_chart = existing_viz.get("chart") if isinstance(existing_viz, dict) else None + existing_result = existing_viz.get("result") if isinstance(existing_viz, dict) else None + existing_query_normalized = ( + existing_viz.get("chart_query_normalized") if isinstance(existing_viz, dict) else None + ) + if ( + existing_chart + and existing_result == data + and existing_query_normalized + and existing_query_normalized == _normalize_query(query) + ): + return "Chart already exists for this query and dataset. Reusing existing Vega visualization." + if on_progress: await on_progress("正在分析数据特征并生成可视化方案...") @@ -61,6 +81,9 @@ class VisualizationTool(Tool): "sql": existing_viz.get("sql", ""), "result": data, "chart": chart_response.model_dump(by_alias=True, exclude_none=True), + "chart_query": query, + "chart_query_normalized": _normalize_query(query), + "chart_generated_by": "visualization", "error": None, } encoded_viz = jsonable_encoder(viz_payload) diff --git a/backend/main.py b/backend/main.py index 1b0d995..15b30b0 100644 --- a/backend/main.py +++ b/backend/main.py @@ -470,6 +470,9 @@ async def nanobot_chat(request: ChatRequest): instructions = [] if request.route_mode == "sql" or request.prefer_sql_chart: instructions.append("Use the nl2sql tool to answer the query") + instructions.append("If the user also asks for visualization, set generate_chart=true in the same nl2sql call") + instructions.append("Do not call visualization after nl2sql if a chart is already generated for this request") + instructions.append("Do not use exec, Python scripts, or matplotlib for chart plotting") elif request.route_mode == "chat": instructions.append("Normal chat mode. Do NOT use the nl2sql tool") @@ -581,6 +584,9 @@ async def nanobot_chat_stream(request: ChatRequest): instructions = [] if request.route_mode == "sql" or request.prefer_sql_chart: instructions.append("Use the nl2sql tool to answer the query") + instructions.append("If the user also asks for visualization, set generate_chart=true in the same nl2sql call") + instructions.append("Do not call visualization after nl2sql if a chart is already generated for this request") + instructions.append("Do not use exec, Python scripts, or matplotlib for chart plotting") elif request.route_mode == "chat": instructions.append("Normal chat mode. Do NOT use the nl2sql tool")