From dd68e6694e3cabce8912eacae29ed1c48798e0c5 Mon Sep 17 00:00:00 2001 From: Zhaofeng Zhang <24791380+vcfgv@users.noreply.github.com> Date: Thu, 16 Oct 2025 15:08:06 +0800 Subject: [PATCH 1/9] refactor: remove SEC agent and update agent list in launch script --- python/configs/agent_cards/sec_agent.json | 58 ----------------------- python/scripts/launch.py | 10 ++-- 2 files changed, 5 insertions(+), 63 deletions(-) delete mode 100644 python/configs/agent_cards/sec_agent.json diff --git a/python/configs/agent_cards/sec_agent.json b/python/configs/agent_cards/sec_agent.json deleted file mode 100644 index 760a5dcd5..000000000 --- a/python/configs/agent_cards/sec_agent.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "name": "SECAgent", - "display_name": "SEC Agent", - "url": "http://localhost:10003/", - "description": "SecAgent can analyze SEC filings like 10-Q, 10-K, 13-F and analyze stock holdings of institutional investment managers. It can chat about stock performance, financial metrics, and market trends or track specific stocks and provide updates.", - "skills": [ - { - "id": "analyze_13f_filings", - "name": "Analyze 13F Filings", - "description": "Analyze 13F filings to extract stock holdings and provide insights on institutional investment managers' portfolios.", - "examples": [ - "What are the top holdings of Berkshire Hathaway in the latest 13F filing?", - "How has the portfolio of Vanguard Group changed over the last four quarters?", - "Can you provide insights on the stock performance of Apple and Microsoft based on recent 13F filings?" - ], - "tags": [ - "13F", - "stock holdings", - "institutional investors" - ] - }, - - { - "id": "financial_data_queries", - "name": "Financial data queries", - "description": "Analyze financial extract stock holdings and provide insights on institutional investment managers' portfolios.", - "examples": [ - "What are the 10K fillings of Berkshire Hathaway indicate?" - ], - "tags": [ - "stock holdings", - "institutional investors" - ] - }, - - { - "id": "track_company_info", - "name": "Track company sec info update", - "description": "Track and analyze company's information changes .", - "examples": [ - "Please check Berkshire Hathaway’s filings and notify me with an analysis whenever there’s an update." - ], - "tags": [ - "track_company" - ] - } - ], - "enabled": true, - "metadata": { - "version": "1.0.0", - "author": "ValueCell Team", - "tags": [ - "sec", - "13f", - "fund-analysis" - ] - } -} \ No newline at end of file diff --git a/python/scripts/launch.py b/python/scripts/launch.py index 572baca6b..a3301fb1e 100644 --- a/python/scripts/launch.py +++ b/python/scripts/launch.py @@ -30,9 +30,9 @@ "ValuationAnalystAgent": "valuation_analyst", "WarrenBuffettAgent": "warren_buffett", } -SEC_AGENT_NAME = "SECAgent" TRADING_AGENTS_NAME = "TradingAgents" -AGENTS = list(MAP_NAME_ANALYST.keys()) + [SEC_AGENT_NAME, TRADING_AGENTS_NAME] +RESEARCH_AGENT_NAME = "ResearchAgent" +AGENTS = list(MAP_NAME_ANALYST.keys()) + [TRADING_AGENTS_NAME, RESEARCH_AGENT_NAME] PROJECT_DIR = Path(__file__).resolve().parent.parent.parent PYTHON_DIR = PROJECT_DIR / "python" @@ -50,12 +50,12 @@ MAP_NAME_COMMAND[name] = ( f"cd {PYTHON_DIR_STR}/third_party/ai-hedge-fund && uv run --env-file {ENV_PATH_STR} -m adapter --analyst {analyst}" ) -MAP_NAME_COMMAND[SEC_AGENT_NAME] = ( - f"uv run --env-file {ENV_PATH_STR} -m valuecell.agents.sec_agent" -) MAP_NAME_COMMAND[TRADING_AGENTS_NAME] = ( f"cd {PYTHON_DIR_STR}/third_party/TradingAgents && uv run --env-file {ENV_PATH_STR} -m adapter" ) +MAP_NAME_COMMAND[RESEARCH_AGENT_NAME] = ( + f"uv run --env-file {ENV_PATH_STR} -m valuecell.agents.research_agent" +) BACKEND_COMMAND = ( f"cd {PYTHON_DIR_STR} && uv run --env-file {ENV_PATH_STR} -m valuecell.server.main" ) From b2839406bb8a083480a36de8fdb03cc643b5cc81 Mon Sep 17 00:00:00 2001 From: Zhaofeng Zhang <24791380+vcfgv@users.noreply.github.com> Date: Thu, 16 Oct 2025 16:34:39 +0800 Subject: [PATCH 2/9] feat: add context utility functions for language and timezone handling --- .../valuecell/agents/research_agent/core.py | 3 ++ python/valuecell/agents/utils/__init__.py | 0 python/valuecell/agents/utils/context.py | 49 +++++++++++++++++++ 3 files changed, 52 insertions(+) create mode 100644 python/valuecell/agents/utils/__init__.py create mode 100644 python/valuecell/agents/utils/context.py diff --git a/python/valuecell/agents/research_agent/core.py b/python/valuecell/agents/research_agent/core.py index c1d1a6d0d..c01a5ec8e 100644 --- a/python/valuecell/agents/research_agent/core.py +++ b/python/valuecell/agents/research_agent/core.py @@ -20,6 +20,7 @@ from valuecell.core.agent.responses import streaming from valuecell.core.types import BaseAgent, StreamResponse from valuecell.utils.env import agent_debug_mode_enabled +from valuecell.agents.utils.context import build_ctx_from_dep def _get_model_based_on_env() -> str: @@ -63,6 +64,8 @@ async def stream( stream=True, stream_intermediate_steps=True, session_id=conversation_id, + add_dependencies_to_context=True, + dependencies=build_ctx_from_dep(dependencies) ) async for event in response_stream: if event.event == "RunContent": diff --git a/python/valuecell/agents/utils/__init__.py b/python/valuecell/agents/utils/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/python/valuecell/agents/utils/context.py b/python/valuecell/agents/utils/context.py new file mode 100644 index 000000000..e0411f0f2 --- /dev/null +++ b/python/valuecell/agents/utils/context.py @@ -0,0 +1,49 @@ +from typing import Any, Dict, Optional + +from valuecell.core.constants import LANGUAGE, TIMEZONE + + +def build_ctx_from_dep( + dep: Optional[Dict[str, Any]], +) -> Dict[str, str] | None: + if not dep: + return None + + context = {} + lang_ctx = _build_lang_ctx_from_dep(dep) + if lang_ctx: + context["compose_answer_hint"] = lang_ctx + + return context + + +def _build_lang_ctx_from_dep( + dependencies: Optional[Dict[str, Any]], +) -> str | None: + if not dependencies: + return None + + user_lang = dependencies.get(LANGUAGE) + user_tz = dependencies.get(TIMEZONE) + + parts = [] + parts.append( + "When composing your answer, consider the user's language and timezone:" + ) + if user_lang: + parts.append(f"- Preferred language: {user_lang}") + else: + parts.append( + "- Preferred language: not set. Infer the user's language from their query and respond in that language." + ) + + if user_tz: + parts.append( + f"- Timezone: {user_tz} (use this to interpret or present times/dates)" + ) + else: + parts.append( + "- Timezone: not set. Do NOT ask the user for their timezone unless absolutely necessary. Instead, infer timezone from context (locale, timestamps, phrasing) when possible; if you cannot reasonably infer it, default to UTC when presenting absolute times." + ) + + return "\n".join(parts) From 08a89bb52dccbe945e61fdfbb630095f261c3943 Mon Sep 17 00:00:00 2001 From: Zhaofeng Zhang <24791380+vcfgv@users.noreply.github.com> Date: Thu, 16 Oct 2025 16:35:00 +0800 Subject: [PATCH 3/9] fix: correct formatting in tools section of financial research assistant prompt --- python/valuecell/agents/research_agent/prompts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/valuecell/agents/research_agent/prompts.py b/python/valuecell/agents/research_agent/prompts.py index 1c9e3e21b..56b16698a 100644 --- a/python/valuecell/agents/research_agent/prompts.py +++ b/python/valuecell/agents/research_agent/prompts.py @@ -3,7 +3,7 @@ You are a financial research assistant. Your primary objective is to satisfy the user's information request about a company's financials, filings, or performance with accurate, sourceable, and actionable answers. -- + - fetch_periodic_sec_filings(ticker_or_cik, forms, year?, quarter?, limit?): Use this for scheduled reports like 10-K/10-Q when you need primary-source facts (revenue, net income, MD&A text). Prefer batching by year to reduce calls. Note: year/quarter filters apply to filing_date (edgar behavior), not period_of_report. If year is omitted, the tool returns the latest filings using `limit` (default 10). If quarter is provided, year must also be provided. - fetch_event_sec_filings(ticker_or_cik, forms, start_date?, end_date?, limit?): Use this for event-driven filings like 8-K and ownership forms (3/4/5). Use date ranges and limits to control scope. - Knowledge base search: Use the agent's internal knowledge index to find summaries, historical context, analyst commentary, and previously ingested documents. From 6e3e7ea4493045f33655a75bfcdc59abe8b94bf2 Mon Sep 17 00:00:00 2001 From: Zhaofeng Zhang <24791380+vcfgv@users.noreply.github.com> Date: Thu, 16 Oct 2025 16:37:02 +0800 Subject: [PATCH 4/9] refactor: simplify agent selection by using all available agents instead of a checkbox --- python/scripts/launch.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/python/scripts/launch.py b/python/scripts/launch.py index a3301fb1e..9eaa4055c 100644 --- a/python/scripts/launch.py +++ b/python/scripts/launch.py @@ -9,8 +9,6 @@ from pathlib import Path from typing import Dict -import questionary - # Mapping from agent name to analyst key (for ai-hedge-fund agents) MAP_NAME_ANALYST: Dict[str, str] = { "AswathDamodaranAgent": "aswath_damodaran", @@ -77,10 +75,11 @@ def main(): log_dir = f"{PROJECT_DIR_STR}/logs/{timestamp}" # Use questionary multi-select to allow choosing multiple agents - selected_agents = questionary.checkbox( - "Choose agents to launch (use space to select, enter to confirm):", - choices=AGENTS, - ).ask() + # selected_agents = questionary.checkbox( + # "Choose agents to launch (use space to select, enter to confirm):", + # choices=AGENTS, + # ).ask() + selected_agents = AGENTS if not selected_agents: print("No agents selected.") From fd0ad79d92c2af51005f9c4cbf50060f2949e66b Mon Sep 17 00:00:00 2001 From: Zhaofeng Zhang <24791380+vcfgv@users.noreply.github.com> Date: Thu, 16 Oct 2025 16:42:40 +0800 Subject: [PATCH 5/9] fix: improve error message for agent card resolution failure --- python/valuecell/core/agent/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/valuecell/core/agent/client.py b/python/valuecell/core/agent/client.py index d333834eb..6bb386c34 100644 --- a/python/valuecell/core/agent/client.py +++ b/python/valuecell/core/agent/client.py @@ -65,7 +65,7 @@ async def _setup_client(self): except Exception as e: raise RuntimeError( "Failed to resolve agent card. Maybe the agent URL is incorrect or the agent is unreachable." - " Agents could be launched via `scripts/launch_agent.py`." + " Check the agent logs for more details." ) from e self._client = client_factory.create(self.agent_card) From 2e61f1081e803067ba2c99c45b6064fb529cbadf Mon Sep 17 00:00:00 2001 From: Zhaofeng Zhang <24791380+vcfgv@users.noreply.github.com> Date: Thu, 16 Oct 2025 16:43:08 +0800 Subject: [PATCH 6/9] fix style --- python/valuecell/agents/research_agent/core.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/valuecell/agents/research_agent/core.py b/python/valuecell/agents/research_agent/core.py index c01a5ec8e..898227119 100644 --- a/python/valuecell/agents/research_agent/core.py +++ b/python/valuecell/agents/research_agent/core.py @@ -17,10 +17,10 @@ fetch_event_sec_filings, fetch_periodic_sec_filings, ) +from valuecell.agents.utils.context import build_ctx_from_dep from valuecell.core.agent.responses import streaming from valuecell.core.types import BaseAgent, StreamResponse from valuecell.utils.env import agent_debug_mode_enabled -from valuecell.agents.utils.context import build_ctx_from_dep def _get_model_based_on_env() -> str: @@ -65,7 +65,7 @@ async def stream( stream_intermediate_steps=True, session_id=conversation_id, add_dependencies_to_context=True, - dependencies=build_ctx_from_dep(dependencies) + dependencies=build_ctx_from_dep(dependencies), ) async for event in response_stream: if event.event == "RunContent": From 0f1d109736375b4cf7aa38190744265f36f694e6 Mon Sep 17 00:00:00 2001 From: Zhaofeng Zhang <24791380+vcfgv@users.noreply.github.com> Date: Thu, 16 Oct 2025 16:47:39 +0800 Subject: [PATCH 7/9] fix incorrect type hints --- python/valuecell/agents/research_agent/core.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/valuecell/agents/research_agent/core.py b/python/valuecell/agents/research_agent/core.py index 898227119..96c6fe718 100644 --- a/python/valuecell/agents/research_agent/core.py +++ b/python/valuecell/agents/research_agent/core.py @@ -23,7 +23,7 @@ from valuecell.utils.env import agent_debug_mode_enabled -def _get_model_based_on_env() -> str: +def _get_model_based_on_env(): model_id = os.getenv("RESEARCH_AGENT_MODEL_ID") if os.getenv("GOOGLE_API_KEY"): return Gemini(id=model_id or "gemini-2.5-flash") @@ -59,7 +59,7 @@ async def stream( task_id: str, dependencies: Optional[Dict] = None, ) -> AsyncGenerator[StreamResponse, None]: - response_stream: Iterator[RunOutputEvent] = self.knowledge_research_agent.arun( + response_stream = self.knowledge_research_agent.arun( query, stream=True, stream_intermediate_steps=True, From b9e0b419638bc6bf18b36f733896a2d2c4ac7c12 Mon Sep 17 00:00:00 2001 From: Zhaofeng Zhang <24791380+vcfgv@users.noreply.github.com> Date: Thu, 16 Oct 2025 16:47:59 +0800 Subject: [PATCH 8/9] fix: remove unused imports in core.py --- python/valuecell/agents/research_agent/core.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/valuecell/agents/research_agent/core.py b/python/valuecell/agents/research_agent/core.py index 96c6fe718..7dfca2bf6 100644 --- a/python/valuecell/agents/research_agent/core.py +++ b/python/valuecell/agents/research_agent/core.py @@ -1,7 +1,7 @@ import os -from typing import AsyncGenerator, Dict, Iterator, Optional +from typing import AsyncGenerator, Dict, Optional -from agno.agent import Agent, RunOutputEvent +from agno.agent import Agent from agno.db.in_memory import InMemoryDb from agno.models.google import Gemini from agno.models.openrouter import OpenRouter From 1785ba7a9db6e3697b31531802118e3dd42d80f0 Mon Sep 17 00:00:00 2001 From: Zhaofeng Zhang <24791380+vcfgv@users.noreply.github.com> Date: Thu, 16 Oct 2025 16:50:18 +0800 Subject: [PATCH 9/9] fix tests --- python/valuecell/core/agent/tests/test_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/valuecell/core/agent/tests/test_client.py b/python/valuecell/core/agent/tests/test_client.py index 8ed907789..045029ca7 100644 --- a/python/valuecell/core/agent/tests/test_client.py +++ b/python/valuecell/core/agent/tests/test_client.py @@ -282,7 +282,7 @@ async def test_ensure_initialized_card_resolution_failure(self): error_message = str(exc_info.value) assert "Failed to resolve agent card" in error_message - assert "scripts/launch_agent.py" in error_message + assert "Check the agent logs" in error_message assert "Connection timeout" in str( exc_info.value.__cause__ ) # Original exception should be chained