Trim the research phase before trusting profiling output

The legacy path was already narrowed to market-only compact execution, but the research stage remained the slowest leg and the profiler lacked persistent raw event artifacts for comparison. This change further compresses the compact prompts for Bull Researcher, Bear Researcher, and Research Manager, adds durable raw event dumps to the graph profiler, and keeps profiling evidence out of the runtime contract itself.

Constraint: No new dependencies and no runtime-contract pollution for profiling-only data
Rejected: Add synthetic timing fields back into the subprocess protocol | those timings are not real graph-stage boundaries and would mislead diagnosis
Rejected: Skip raw event dump persistence and rely on console output | makes multi-run comparison and regression tracking fragile
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep profiling as an external diagnostic surface; if stage timing ever enters contracts again, it must come from real graph boundaries
Tested: python -m pytest web_dashboard/backend/tests/test_executors.py web_dashboard/backend/tests/test_services_migration.py web_dashboard/backend/tests/test_api_smoke.py -q
Tested: python -m compileall tradingagents/agents/researchers/bull_researcher.py tradingagents/agents/researchers/bear_researcher.py tradingagents/agents/managers/research_manager.py orchestrator/profile_stage_chain.py
Tested: real provider profiling via orchestrator/profile_stage_chain.py with market-only compact settings; dump persisted to orchestrator/profile_runs/600519.SS_2026-04-10_20260413T184742Z.json
Not-tested: browser/manual consumption of the persisted profiling dump
This commit is contained in:
陈少杰 2026-04-14 02:51:07 +08:00
parent 8a4f0ad540
commit baf67dbd58
5 changed files with 43 additions and 21 deletions

1
.gitignore vendored
View File

@ -1,5 +1,6 @@
# Git worktrees
.worktrees/
orchestrator/profile_runs/
# Byte-compiled / optimized / DLL files
__pycache__/

View File

@ -5,6 +5,8 @@ import json
import signal
import time
from collections import defaultdict
from datetime import datetime, timezone
from pathlib import Path
from tradingagents.graph.propagation import Propagator
from tradingagents.graph.trading_graph import TradingAgentsGraph
@ -34,6 +36,7 @@ def build_parser() -> argparse.ArgumentParser:
parser.add_argument("--analysis-prompt-style", default="compact")
parser.add_argument("--selected-analysts", default="market")
parser.add_argument("--overall-timeout", type=int, default=120)
parser.add_argument("--dump-dir", default="orchestrator/profile_runs")
return parser
@ -65,6 +68,10 @@ def main() -> None:
phase_totals = defaultdict(float)
started_at = time.monotonic()
last_at = started_at
dump_dir = Path(args.dump_dir)
dump_dir.mkdir(parents=True, exist_ok=True)
run_id = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ")
dump_path = dump_dir / f"{args.ticker.replace('/', '_')}_{args.date}_{run_id}.json"
def alarm_handler(signum, frame):
raise _ProfileTimeout(f"profiling timeout after {args.overall_timeout}s")
@ -97,6 +104,7 @@ def main() -> None:
"analysis_prompt_style": args.analysis_prompt_style,
"node_timings": node_timings,
"phase_totals_seconds": {key: round(value, 3) for key, value in phase_totals.items()},
"dump_path": str(dump_path),
}
except Exception as exc:
payload = {
@ -108,10 +116,12 @@ def main() -> None:
"error": str(exc),
"node_timings": node_timings,
"phase_totals_seconds": {key: round(value, 3) for key, value in phase_totals.items()},
"dump_path": str(dump_path),
}
finally:
signal.alarm(0)
dump_path.write_text(json.dumps(payload, ensure_ascii=False, indent=2))
print(json.dumps(payload, ensure_ascii=False, indent=2))

View File

@ -17,7 +17,10 @@ def create_research_manager(llm, memory):
investment_debate_state = state["investment_debate_state"]
curr_situation = f"{market_research_report}\n\n{sentiment_report}\n\n{news_report}\n\n{fundamentals_report}"
past_memories = memory.get_memories(curr_situation, n_matches=2)
past_memories = memory.get_memories(
curr_situation,
n_matches=1 if use_compact_analysis_prompt() else 2,
)
past_memory_str = ""
for i, rec in enumerate(past_memories, 1):
@ -32,12 +35,14 @@ Return a concise response with:
3. Simple execution plan
Past lessons:
{truncate_prompt_text(past_memory_str, 400)}
{truncate_prompt_text(past_memory_str, 180)}
{instrument_context}
Debate history:
{truncate_prompt_text(history, 1200)}"""
{truncate_prompt_text(history, 700)}
Keep the full answer under 180 words."""
else:
prompt = f"""As the portfolio manager and debate facilitator, your role is to critically evaluate this round of debate and make a definitive decision: align with the bear analyst, the bull analyst, or choose Hold only if it is strongly justified based on the arguments presented.

View File

@ -18,7 +18,10 @@ def create_bear_researcher(llm, memory):
fundamentals_report = state["fundamentals_report"]
curr_situation = f"{market_research_report}\n\n{sentiment_report}\n\n{news_report}\n\n{fundamentals_report}"
past_memories = memory.get_memories(curr_situation, n_matches=2)
past_memories = memory.get_memories(
curr_situation,
n_matches=1 if use_compact_analysis_prompt() else 2,
)
past_memory_str = ""
for i, rec in enumerate(past_memories, 1):
@ -27,15 +30,15 @@ def create_bear_researcher(llm, memory):
if use_compact_analysis_prompt():
prompt = f"""You are a Bear Analyst. Make the strongest concise short case against the stock.
Use only the highest-signal evidence from the reports below. Address the latest bull point directly. Keep the answer under 220 words and end with a clear stance.
Use only the highest-signal evidence from the reports below. Address the latest bull point directly. Keep the answer under 140 words and end with a clear stance.
Market report: {truncate_prompt_text(market_research_report, 800)}
Sentiment report: {truncate_prompt_text(sentiment_report, 500)}
News report: {truncate_prompt_text(news_report, 500)}
Fundamentals report: {truncate_prompt_text(fundamentals_report, 700)}
Debate history: {truncate_prompt_text(history, 600)}
Last bull argument: {truncate_prompt_text(current_response, 400)}
Past lessons: {truncate_prompt_text(past_memory_str, 400)}
Market: {truncate_prompt_text(market_research_report, 420)}
Sentiment: {truncate_prompt_text(sentiment_report, 220)}
News: {truncate_prompt_text(news_report, 220)}
Fundamentals: {truncate_prompt_text(fundamentals_report, 320)}
Debate history: {truncate_prompt_text(history, 260)}
Last bull argument: {truncate_prompt_text(current_response, 180)}
Past lessons: {truncate_prompt_text(past_memory_str, 180)}
"""
else:
prompt = f"""You are a Bear Analyst making the case against investing in the stock. Your goal is to present a well-reasoned argument emphasizing risks, challenges, and negative indicators. Leverage the provided research and data to highlight potential downsides and counter bullish arguments effectively.

View File

@ -18,7 +18,10 @@ def create_bull_researcher(llm, memory):
fundamentals_report = state["fundamentals_report"]
curr_situation = f"{market_research_report}\n\n{sentiment_report}\n\n{news_report}\n\n{fundamentals_report}"
past_memories = memory.get_memories(curr_situation, n_matches=2)
past_memories = memory.get_memories(
curr_situation,
n_matches=1 if use_compact_analysis_prompt() else 2,
)
past_memory_str = ""
for i, rec in enumerate(past_memories, 1):
@ -27,15 +30,15 @@ def create_bull_researcher(llm, memory):
if use_compact_analysis_prompt():
prompt = f"""You are a Bull Analyst. Make the strongest concise long case for the stock.
Use only the highest-signal evidence from the reports below. Address the latest bear point directly. Keep the answer under 220 words and end with a clear stance.
Use only the highest-signal evidence from the reports below. Address the latest bear point directly. Keep the answer under 140 words and end with a clear stance.
Market report: {truncate_prompt_text(market_research_report, 800)}
Sentiment report: {truncate_prompt_text(sentiment_report, 500)}
News report: {truncate_prompt_text(news_report, 500)}
Fundamentals report: {truncate_prompt_text(fundamentals_report, 700)}
Debate history: {truncate_prompt_text(history, 600)}
Last bear argument: {truncate_prompt_text(current_response, 400)}
Past lessons: {truncate_prompt_text(past_memory_str, 400)}
Market: {truncate_prompt_text(market_research_report, 420)}
Sentiment: {truncate_prompt_text(sentiment_report, 220)}
News: {truncate_prompt_text(news_report, 220)}
Fundamentals: {truncate_prompt_text(fundamentals_report, 320)}
Debate history: {truncate_prompt_text(history, 260)}
Last bear argument: {truncate_prompt_text(current_response, 180)}
Past lessons: {truncate_prompt_text(past_memory_str, 180)}
"""
else:
prompt = f"""You are a Bull Analyst advocating for investing in the stock. Your task is to build a strong, evidence-based case emphasizing growth potential, competitive advantages, and positive market indicators. Leverage the provided research and data to address concerns and counter bearish arguments effectively.