From 2073053682f1055b6c052db4142742dffd203d59 Mon Sep 17 00:00:00 2001 From: zhonghao lu Date: Thu, 25 Sep 2025 14:52:15 +0800 Subject: [PATCH 1/3] refactor:refactor sec agent json --- python/configs/agent_cards/sec_agent.json | 93 ++++----------------- python/valuecell/agents/sec_agent.py | 6 +- python/valuecell/core/coordinate/planner.py | 2 +- 3 files changed, 18 insertions(+), 83 deletions(-) diff --git a/python/configs/agent_cards/sec_agent.json b/python/configs/agent_cards/sec_agent.json index 91aa6148e..c0dbd0d85 100644 --- a/python/configs/agent_cards/sec_agent.json +++ b/python/configs/agent_cards/sec_agent.json @@ -20,94 +20,29 @@ "institutional investors" ] }, - { - "id": "compare_quarterly_reports", - "name": "Compare Quarterly Financial Reports", - "description": "Compare and analyze quarterly financial reports (10-Q) across multiple periods to identify trends, performance changes, and key financial metrics evolution.", - "examples": [ - "Compare Tesla's last three quarterly 10-Q reports", - "Analyze Apple's Q1 vs Q4 financial performance from their 10-Q filings", - "What are the key changes in Amazon's quarterly revenue and expenses over the past year?" - ], - "tags": [ - "10-Q", - "quarterly reports", - "financial analysis" - ] - }, - { - "id": "summarize_annual_reports", - "name": "Summarize Annual Financial Reports", - "description": "Extract and summarize key information from annual 10-K reports, including business overview, risk factors, financial highlights, and strategic initiatives.", + + { + "id": "financial_data_queries", + "name": "Financial data queries", + "description": "Analyze financial extract stock holdings and provide insights on institutional investment managers' portfolios.", "examples": [ - "Help me summarize Nvidia's latest 10-K report", - "What are the main highlights from Microsoft's annual 10-K filing?", - "Provide a comprehensive summary of Meta's business segments from their 10-K report" + "What are the 10K fillings of Berkshire Hathaway indicate?" ], "tags": [ - "10-K", - "annual reports", - "financial summary" - ] - }, - { - "id": "compare_industry_performance", - "name": "Compare Industry Financial Performance", - "description": "Analyze and compare financial performance metrics across companies within the same industry sector, identifying competitive positioning and market leaders.", - "examples": [ - "How does AMD's financial performance compare to Intel and Nvidia?", - "Compare the profitability metrics of JPMorgan, Bank of America, and Wells Fargo", - "Analyze the revenue growth rates of Netflix, Disney, and Warner Bros Discovery" - ], - "tags": [ - "industry comparison", - "financial metrics", - "competitive analysis" - ] - }, - { - "id": "analyze_revenue_mix_evolution", - "name": "Analyze Revenue Mix Evolution", - "description": "Track and analyze changes in company revenue composition, business segment performance, and revenue diversification strategies over time.", - "examples": [ - "How has Google's revenue mix changed over the past 3 years?", - "Analyze the evolution of Amazon's revenue streams from 2020 to 2023", - "What changes occurred in Apple's product revenue mix in the last 5 years?" - ], - "tags": [ - "revenue mix", - "business segments", - "financial evolution" - ] - }, - { - "id": "assess_financial_health_metrics", - "name": "Assess Financial Health Metrics", - "description": "Evaluate company financial health through key metrics analysis including liquidity ratios, debt levels, profitability margins, and cash flow patterns.", - "examples": [ - "Assess Tesla's current financial health based on their latest filings", - "What do the debt-to-equity ratios tell us about Boeing's financial stability?", - "Analyze the cash flow trends and liquidity position of General Electric" - ], - "tags": [ - "financial health", - "liquidity", - "debt analysis" + "stock holdings", + "institutional investors" ] }, + { - "id": "identify_risk_factors", - "name": "Identify Financial Risk Factors", - "description": "Extract and analyze risk factors disclosed in SEC filings, categorize risks by type, and assess potential impact on business operations and financial performance.", + "id": "track_company_info", + "name": "Track company sec info update", + "description": "Track and analyze company's information changes .", "examples": [ - "What are the main risk factors disclosed in Uber's latest 10-K filing?", - "Analyze the regulatory risks mentioned in JPMorgan's annual report", - "Compare the risk disclosures between traditional automakers and EV companies" + "Please check Berkshire Hathaway’s filings and notify me with an analysis whenever there’s an update." ], "tags": [ - "risk factors", - "financial risk", - "SEC filings" + "track_company" ] } ], diff --git a/python/valuecell/agents/sec_agent.py b/python/valuecell/agents/sec_agent.py index ce81fcffb..c3fe3c24e 100644 --- a/python/valuecell/agents/sec_agent.py +++ b/python/valuecell/agents/sec_agent.py @@ -101,7 +101,7 @@ def __init__(self): ) # Analysis agent self.analysis_agent = Agent( - model=OpenRouter(id=self.config.analysis_model_id), + model=OpenRouter(id=self.config.analysis_model_id, max_tokens=None), markdown=True, ) logger.info("SEC intelligent analysis agent initialized successfully") @@ -348,7 +348,7 @@ async def _process_financial_data_query( ] = await self.analysis_agent.arun( analysis_prompt, stream=True, stream_intermediate_steps=True ) - for event in response_stream: + async for event in response_stream: if event.event == "RunResponseContent": yield streaming.message_chunk(event.content) elif event.event == "ToolCallStarted": @@ -444,7 +444,7 @@ async def _process_fund_holdings_query( ] = await self.analysis_agent.arun( analysis_prompt, stream=True, stream_intermediate_steps=True ) - for event in response_stream: + async for event in response_stream: if event.event == "RunResponseContent": yield streaming.message_chunk(event.content) elif event.event == "ToolCallStarted": diff --git a/python/valuecell/core/coordinate/planner.py b/python/valuecell/core/coordinate/planner.py index 17c2b2569..4e1cb72b8 100644 --- a/python/valuecell/core/coordinate/planner.py +++ b/python/valuecell/core/coordinate/planner.py @@ -101,7 +101,7 @@ async def _analyze_input_and_create_tasks( # Create planning agent with appropriate tools and instructions agent = Agent( model=OpenRouter( - id=os.getenv("PLANNER_MODEL_ID", "openai/gpt-4o-mini"), + id=os.getenv("PLANNER_MODEL_ID", "google/gemini-2.5-pro"), max_tokens=None, ), tools=[ From 03b41f7c5033bbac216dd615b85fa0e90148ce9f Mon Sep 17 00:00:00 2001 From: zhonghao lu Date: Thu, 25 Sep 2025 17:25:33 +0800 Subject: [PATCH 2/3] add --- python/valuecell/agents/sec_agent.py | 2 +- python/valuecell/core/agent/decorator.py | 2 +- python/valuecell/server/services/agent_stream_service.py | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/python/valuecell/agents/sec_agent.py b/python/valuecell/agents/sec_agent.py index c3fe3c24e..8df904b56 100644 --- a/python/valuecell/agents/sec_agent.py +++ b/python/valuecell/agents/sec_agent.py @@ -67,7 +67,7 @@ def __init__(self): self.sec_email = os.getenv("SEC_EMAIL", "your.name@example.com") self.parser_model_id = os.getenv("SEC_PARSER_MODEL_ID", "openai/gpt-4o-mini") self.analysis_model_id = os.getenv( - "SEC_ANALYSIS_MODEL_ID", "deepseek/deepseek-chat-v3-0324" + "SEC_ANALYSIS_MODEL_ID", "google/gemini-2.5-pro" ) self.max_filings = int(os.getenv("SEC_MAX_FILINGS", "5")) self.request_timeout = int(os.getenv("SEC_REQUEST_TIMEOUT", "30")) diff --git a/python/valuecell/core/agent/decorator.py b/python/valuecell/core/agent/decorator.py index 99e064bad..20a46564d 100644 --- a/python/valuecell/core/agent/decorator.py +++ b/python/valuecell/core/agent/decorator.py @@ -106,7 +106,7 @@ async def execute(self, context: RequestContext, event_queue: EventQueue) -> Non # Prepare query and ensure a task exists in the system query = context.get_user_input() task = context.current_task - task_meta = context.metadata + task_meta = context.message.metadata or {} agent_name = self.agent.__class__.__name__ if not task: message = context.message diff --git a/python/valuecell/server/services/agent_stream_service.py b/python/valuecell/server/services/agent_stream_service.py index 350b94dd9..5d15c6845 100644 --- a/python/valuecell/server/services/agent_stream_service.py +++ b/python/valuecell/server/services/agent_stream_service.py @@ -37,6 +37,7 @@ async def stream_query_agent( user_id = "default_user" desired_agent_name = agent_name + session_id = agent_name + "_session_" + user_id user_input_meta = UserInputMetadata(user_id=user_id, session_id=session_id) From 92431dc9cf363c80630eded14bb1681259a8c3bc Mon Sep 17 00:00:00 2001 From: zhonghao lu Date: Thu, 25 Sep 2025 20:34:03 +0800 Subject: [PATCH 3/3] fix : fix sec agent ticker error --- python/valuecell/agents/sec_agent.py | 32 ++++++++++------------------ 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/python/valuecell/agents/sec_agent.py b/python/valuecell/agents/sec_agent.py index 8df904b56..1d90230d6 100644 --- a/python/valuecell/agents/sec_agent.py +++ b/python/valuecell/agents/sec_agent.py @@ -124,11 +124,11 @@ async def _extract_ticker_from_query(self, query: str) -> str: Query: {query} """ - response = self.analysis_agent.run(extraction_prompt) + response = await self.analysis_agent.arun(extraction_prompt) ticker = response.content.strip().upper() # Basic validation - if ticker == "UNKNOWN" or len(ticker) > 10 or not ticker.isalpha(): + if ticker == "UNKNOWN" or len(ticker) > 10: raise ValueError(f"Invalid ticker extracted: {ticker}") logger.info(f"Extracted ticker: {ticker} from query: {query}") @@ -150,9 +150,8 @@ async def _get_sec_filings(self, ticker: str) -> Dict[str, str]: for filing_type in filing_types: try: # Get the most recent filing of this type - filings = company.get_filings(form=filing_type).latest(1) - if filings: - filing = filings[0] + filing = company.get_filings(form=filing_type).latest() + if filing: # Create a hash of the filing content/metadata filing_content = f"{filing.accession_number}_{filing.filing_date}_{filing.form}" filing_hash = hashlib.md5(filing_content.encode()).hexdigest() @@ -228,7 +227,7 @@ async def _generate_filing_summary( Keep the summary concise but informative (2-3 paragraphs maximum). """ - response = self.analysis_agent.run(summary_prompt) + response = await self.analysis_agent.arun(summary_prompt) return response.content except Exception as e: @@ -256,7 +255,7 @@ async def _classify_query(self, query: str) -> QueryType: """ try: - response = self.classifier_agent.run(classification_prompt) + response = await self.classifier_agent.arun(classification_prompt) return response.content.query_type except Exception as e: logger.warning( @@ -265,9 +264,7 @@ async def _classify_query(self, query: str) -> QueryType: # If classification fails, default to 13F analysis (maintains backward compatibility) return QueryType.FUND_HOLDINGS - async def _process_financial_data_query( - self, ticker: str, session_id: str, task_id: str - ): + async def _process_financial_data_query(self, ticker: str): """ Process financial data queries (10-K, 8-K, 10-Q) """ @@ -365,9 +362,7 @@ async def _process_financial_data_query( except Exception as e: yield streaming.failed(f"Financial data query failed: {e}") - async def _process_fund_holdings_query( - self, ticker: str, session_id: str, task_id: str - ): + async def _process_fund_holdings_query(self, ticker: str): """ Process 13F fund holdings queries (original logic) """ @@ -423,7 +418,6 @@ async def _process_fund_holdings_query( - Specific changes in top 10 holdings - Calculate increase/decrease percentages for major positions - Analyze possible reasons for significant position adjustments - ### 4. Investment Strategy Insights - Investment style adjustments reflected in holdings changes - Market trend judgments and responses @@ -511,14 +505,10 @@ async def stream( # 3. Route to appropriate processing method based on query type if query_type == QueryType.FINANCIAL_DATA: - async for result in self._process_financial_data_query( - ticker, session_id, task_id - ): + async for result in self._process_financial_data_query(ticker): yield result else: # QueryType.FUND_HOLDINGS - async for result in self._process_fund_holdings_query( - ticker, session_id, task_id - ): + async for result in self._process_fund_holdings_query(ticker): yield result except Exception as e: @@ -579,7 +569,7 @@ async def notify(self, query: str, session_id: str, task_id: str): ticker, changes ) - yield notification.message(summary) + yield notification.component_generator(summary, "sec_feed") # Wait before next check await asyncio.sleep(check_interval)