Skip to content

Commit 7c4cbe1

Browse files
bloveclaude
andcommitted
fix(c-generative-ui): tighten plan_tools to call ONE tool for filter/scope
"Filter to cancelled flights only" was calling all 4 tools (kpis + trend + airlines + disruptions) and dumping the filtered result as plaintext instead of letting the data_grid component re-render. The per-turn plan_tools context just said "decide which tools to call" — too permissive; gpt-5-mini defaults to refreshing everything. Tighter rules now in the per-turn system context (not just the static prompt file, which the model demonstrably ignores for tool-selection): 1) FILTER / SCOPE → exactly ONE tool, the one backing the affected component, with new parameters. No spec regen. 2) STRUCTURAL → regen spec, then call only tools for NEW components. 3) QUESTION → no tools, no JSON, just prose. Calling all four is now explicitly reserved for "refresh" / "reload" / "update everything". Applied to both umbrella backend and standalone. Co-Authored-By: Claude Opus 4.7 <[email protected]>
1 parent 22b2fd1 commit 7c4cbe1

2 files changed

Lines changed: 40 additions & 6 deletions

File tree

cockpit/chat/generative-ui/python/src/graph.py

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,28 @@ async def generate_shell(state: DashboardState) -> DashboardState:
4747

4848

4949
async def plan_tools(state: DashboardState) -> DashboardState:
50-
"""On follow-up turns, let the LLM decide which tools to call."""
50+
"""On follow-up turns, pick the MINIMAL set of tools — usually one."""
5151
context = (
5252
f"The current dashboard spec is:\n{state['dashboard_spec']}\n\n"
53-
"Based on the user's message, decide which tools to call to update the dashboard data. "
54-
"If the user asks a question about the data that doesn't need fresh data, just respond conversationally."
53+
"Classify the user's message and act ONCE — do not refetch everything.\n"
54+
"\n"
55+
"1) FILTER / SCOPE existing data (e.g. 'filter to cancelled flights only',\n"
56+
" 'show last 6 months', 'limit to enterprise', 'sort by date',\n"
57+
" 'only show delayed', 'top 3'): call EXACTLY ONE tool — the one that\n"
58+
" backs the affected component — with the new parameters. Do NOT call\n"
59+
" the other tools. Do NOT regenerate the spec.\n"
60+
"\n"
61+
"2) STRUCTURAL change (e.g. 'add a card for X', 'remove the table',\n"
62+
" 'split this into two columns'): regenerate the spec, then call only\n"
63+
" the tools needed to populate NEW components.\n"
64+
"\n"
65+
"3) QUESTION about existing data (e.g. 'why', 'how', 'explain',\n"
66+
" 'what does this mean'): respond conversationally in plain prose.\n"
67+
" Call NO tools. Output NO JSON.\n"
68+
"\n"
69+
"If none of these fit, call only the smallest set of tools you need.\n"
70+
"Calling all four tools is reserved for an explicit 'refresh' /\n"
71+
"'reload' / 'update everything' request."
5572
)
5673
messages = [SystemMessage(content=_PROMPT + "\n\n" + context)] + state["messages"]
5774
response = await _llm_with_tools.ainvoke(messages)

cockpit/langgraph/streaming/python/src/dashboard_graph.py

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,28 @@ async def generate_shell(state: DashboardState) -> DashboardState:
4747

4848

4949
async def plan_tools(state: DashboardState) -> DashboardState:
50-
"""On follow-up turns, let the LLM decide which tools to call."""
50+
"""On follow-up turns, pick the MINIMAL set of tools — usually one."""
5151
context = (
5252
f"The current dashboard spec is:\n{state['dashboard_spec']}\n\n"
53-
"Based on the user's message, decide which tools to call to update the dashboard data. "
54-
"If the user asks a question about the data that doesn't need fresh data, just respond conversationally."
53+
"Classify the user's message and act ONCE — do not refetch everything.\n"
54+
"\n"
55+
"1) FILTER / SCOPE existing data (e.g. 'filter to cancelled flights only',\n"
56+
" 'show last 6 months', 'limit to enterprise', 'sort by date',\n"
57+
" 'only show delayed', 'top 3'): call EXACTLY ONE tool — the one that\n"
58+
" backs the affected component — with the new parameters. Do NOT call\n"
59+
" the other tools. Do NOT regenerate the spec.\n"
60+
"\n"
61+
"2) STRUCTURAL change (e.g. 'add a card for X', 'remove the table',\n"
62+
" 'split this into two columns'): regenerate the spec, then call only\n"
63+
" the tools needed to populate NEW components.\n"
64+
"\n"
65+
"3) QUESTION about existing data (e.g. 'why', 'how', 'explain',\n"
66+
" 'what does this mean'): respond conversationally in plain prose.\n"
67+
" Call NO tools. Output NO JSON.\n"
68+
"\n"
69+
"If none of these fit, call only the smallest set of tools you need.\n"
70+
"Calling all four tools is reserved for an explicit 'refresh' /\n"
71+
"'reload' / 'update everything' request."
5572
)
5673
messages = [SystemMessage(content=_PROMPT + "\n\n" + context)] + state["messages"]
5774
response = await _llm_with_tools.ainvoke(messages)

0 commit comments

Comments
 (0)