fix(agents): stop priming tool calls in schema-only structured agents

- with_structured_output binds a single tool (the schema), so a primed model emitted
  an unknown web_search call and the attempt was discarded for a free-text retry,
  costing an extra round trip and the typed output
- drop the tool-range wording from the no-tool sentiment analyst and state the
  constraint once via a shared NO_EXTERNAL_TOOLS #1130
This commit is contained in:
Yijia-Xiao
2026-07-18 06:28:38 +00:00
parent 3f6c082695
commit 030b434585
6 changed files with 174 additions and 4 deletions

View File

@@ -36,6 +36,7 @@ from tradingagents.agents.utils.agent_utils import (
get_news,
)
from tradingagents.agents.utils.structured import (
NO_EXTERNAL_TOOLS,
bind_structured,
invoke_structured_or_freetext,
)
@@ -86,7 +87,11 @@ def create_sentiment_analyst(llm):
"You are a helpful AI assistant, collaborating with other assistants."
" If you or any other assistant has the FINAL TRANSACTION PROPOSAL: **BUY/HOLD/SELL** or deliverable,"
" prefix your response with FINAL TRANSACTION PROPOSAL: **BUY/HOLD/SELL** so the team knows to stop."
" Today's date is {current_date}; treat it as 'now' for all analysis and tool-call date ranges. {instrument_context}"
# No tool-calling here: the data is pre-fetched into the
# prompt, so tool-range wording would only invite a
# hallucinated tool call (#1130).
" Today's date is {current_date}; treat it as 'now' for all analysis. {instrument_context}"
" " + NO_EXTERNAL_TOOLS +
"\n{system_message}",
),
MessagesPlaceholder(variable_name="messages"),

View File

@@ -16,6 +16,7 @@ from tradingagents.agents.utils.agent_utils import (
get_language_instruction,
)
from tradingagents.agents.utils.structured import (
NO_EXTERNAL_TOOLS,
bind_structured,
invoke_structured_or_freetext,
)
@@ -61,7 +62,9 @@ def create_portfolio_manager(llm):
---
Be decisive and ground every conclusion in specific evidence from the analysts.{get_language_instruction()}"""
Be decisive and ground every conclusion in specific evidence from the analysts.
{NO_EXTERNAL_TOOLS}{get_language_instruction()}"""
final_trade_decision = invoke_structured_or_freetext(
structured_llm,

View File

@@ -8,6 +8,7 @@ from tradingagents.agents.utils.agent_utils import (
get_language_instruction,
)
from tradingagents.agents.utils.structured import (
NO_EXTERNAL_TOOLS,
bind_structured,
invoke_structured_or_freetext,
)
@@ -40,7 +41,9 @@ Commit to a clear stance whenever the debate's strongest arguments warrant one;
---
**Debate History:**
{history}""" + get_language_instruction()
{history}
{NO_EXTERNAL_TOOLS}""" + get_language_instruction()
investment_plan = invoke_structured_or_freetext(
structured_llm,

View File

@@ -12,6 +12,7 @@ from tradingagents.agents.utils.agent_utils import (
get_language_instruction,
)
from tradingagents.agents.utils.structured import (
NO_EXTERNAL_TOOLS,
bind_structured,
invoke_structured_or_freetext,
)
@@ -31,7 +32,8 @@ def create_trader(llm):
"content": (
"You are a trading agent analyzing market data to make investment decisions. "
"Based on your analysis, provide a specific recommendation to buy, sell, or hold. "
"Anchor your reasoning in the analysts' reports and the research plan."
"Anchor your reasoning in the analysts' reports and the research plan. "
+ NO_EXTERNAL_TOOLS
+ get_language_instruction()
),
},

View File

@@ -28,6 +28,16 @@ logger = logging.getLogger(__name__)
T = TypeVar("T", bound=BaseModel)
# Schema-only structured output binds exactly one tool (the schema itself), so a
# model that reaches for a search tool emits an unknown tool call and the whole
# structured attempt is discarded for a free-text retry. Agents on this path
# state the constraint explicitly rather than relying on the binding alone
# (#1130).
NO_EXTERNAL_TOOLS = (
"Use only the evidence provided in this prompt. Do not call external tools "
"or search the web; if something is missing, say so explicitly."
)
def bind_structured(llm: Any, schema: type[T], agent_name: str) -> Any | None:
"""Return ``llm.with_structured_output(schema)`` or ``None`` if unsupported.