This commit is contained in:
Peter Wong 2026-04-03 01:35:27 +01:00
parent 167cd037de
commit eb7453d603
3 changed files with 36 additions and 2 deletions

2
.gitignore vendored
View File

@ -218,3 +218,5 @@ __marimo__/
# Cache
**/data_cache/
CLAUDE.md
reports/*
results/*

View File

@ -667,6 +667,10 @@ def save_report_to_disk(final_state, ticker: str, save_path: Path):
analysts_dir.mkdir(exist_ok=True)
(analysts_dir / "fundamentals.md").write_text(final_state["fundamentals_report"])
analyst_parts.append(("Fundamentals Analyst", final_state["fundamentals_report"]))
if final_state.get("quant_report"):
analysts_dir.mkdir(exist_ok=True)
(analysts_dir / "quant.md").write_text(final_state["quant_report"])
analyst_parts.append(("Quant Analyst", final_state["quant_report"]))
if analyst_parts:
content = "\n\n".join(f"### {name}\n{text}" for name, text in analyst_parts)
sections.append(f"## I. Analyst Team Reports\n\n{content}")

View File

@ -1,13 +1,23 @@
import re
from langchain_core.messages import ToolMessage
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from tradingagents.agents.utils.quant_tools import get_quant_analysis
from tradingagents.agents.utils.agent_utils import build_instrument_context, get_language_instruction
def _looks_like_raw_tool_call(text: str) -> bool:
"""Return True if the LLM output a tool call as plain text instead of via the API."""
stripped = re.sub(r"^```+\w*\s*", "", text.strip())
return bool(re.search(r'"name"\s*:', stripped) and re.search(r'"arguments"\s*:', stripped))
def create_quant_analyst(llm):
def quant_analyst_node(state):
current_date = state["trade_date"]
instrument_context = build_instrument_context(state["company_of_interest"])
ticker = state["company_of_interest"]
instrument_context = build_instrument_context(ticker)
tools = [get_quant_analysis]
@ -49,7 +59,25 @@ def create_quant_analyst(llm):
report = ""
if len(result.tool_calls) == 0:
report = result.content
content = result.content if isinstance(result.content, str) else ""
if _looks_like_raw_tool_call(content):
# The model output the tool call as plain text instead of using the
# function-calling API (common with weaker/local models). Call the
# tool directly so the quant data is always populated.
try:
raw_data = get_quant_analysis.invoke(
{"ticker": ticker, "analysis_date": current_date}
)
except Exception as e:
raw_data = f"Quant analysis unavailable: {e}"
# Feed the raw data back to the LLM so it can write the narrative report.
tool_msg = ToolMessage(content=raw_data, tool_call_id="fallback")
followup = chain.invoke(state["messages"] + [result, tool_msg])
fc = followup.content if isinstance(followup.content, str) else ""
report = fc if fc and not _looks_like_raw_tool_call(fc) else raw_data
result = followup
else:
report = content
return {
"messages": [result],