From 090b1c18b9990c4df6f3c38df7fc293e55558713 Mon Sep 17 00:00:00 2001 From: Zhaofeng Zhang <24791380+vcfgv@users.noreply.github.com> Date: Thu, 30 Oct 2025 17:18:38 +0800 Subject: [PATCH 1/5] docs: update CONTRIBUTING.md and add agent development guide --- .github/CONTRIBUTING.md | 28 +- docs/CONTRIBUTE_AN_AGENT.md | 504 ++++++++++++++++++ .../valuecell/agents/research_agent/core.py | 2 +- python/valuecell/core/agent/__init__.py | 4 + 4 files changed, 536 insertions(+), 2 deletions(-) create mode 100644 docs/CONTRIBUTE_AN_AGENT.md diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 3c8ec9cae..b144bbd18 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -42,7 +42,8 @@ We welcome code contributions! See the [Development Setup](#development-setup) s ***Reference Doc*** > Multi-agent system architecture: [CORE_ARCHITECTURE](../docs/CORE_ARCHITECTURE.md) -> Configuration documentation: [CONFIGURATION_GUIDE](../docs/CONFIGURATION_GUIDE.md) +> Configuration documentation: [CONFIGURATION_GUIDE](../docs/CONFIGURATION_GUIDE.md) +> Agent development guide: [CONTRIBUTING_AN_AGENT](../docs/CONTRIBUTING_AN_AGENT.md) ## Development Setup @@ -93,6 +94,31 @@ bun install ### Backend and Agents +For detailed information on building and contributing agents, see the [Agent Development Guide](../docs/CONTRIBUTING_AN_AGENT.md). + +Quick start: + +**Run the API server:** + +```bash +cd python +python -m valuecell.server.main +``` + +**Run the Research Agent:** + +```bash +cd python +python -m valuecell.agents.research_agent +``` + +> [!TIP] +> Set your environment first. At minimum, configure `OPENROUTER_API_KEY` (or `GOOGLE_API_KEY`) and `SEC_EMAIL`. See [Configuration Guide](../docs/CONFIGURATION_GUIDE.md). + +### Code Style + +### Backend and Agents + This section shows how to run the backend locally and build new agents. #### Architecture at a glance diff --git a/docs/CONTRIBUTE_AN_AGENT.md b/docs/CONTRIBUTE_AN_AGENT.md new file mode 100644 index 000000000..643b7baea --- /dev/null +++ b/docs/CONTRIBUTE_AN_AGENT.md @@ -0,0 +1,504 @@ +# Contributing an Agent to ValueCell + +This guide explains how to build, integrate, and contribute new agents to ValueCell's multi-agent financial platform. + +## Table of Contents + +- [Architecture Overview](#architecture-overview) +- [Create a New Agent](#create-a-new-agent) +- [Add an Agent Configuration](#add-an-agent-configuration-required) +- [Register Agent in Launch Script](#register-agent-in-launch-script) +- [Use Models and Tools](#use-models-and-tools-inside-an-agent) +- [Event System](#event-system-contracts) +- [Launch Backend](#launch-backend) +- [Debugging Agent Behavior](#debugging-agent-behavior) + +## Architecture Overview + +Understanding the system architecture is crucial for building agents: + +- **API backend**: `valuecell.server` (FastAPI/uvicorn). Entry: `valuecell.server.main` +- **Agents**: Located under `valuecell.agents.` with a `__main__.py` for `python -m` execution +- **Core contracts**: `valuecell.core.types` define response events and data shapes +- **Streaming helpers**: `valuecell.core.agent.stream` for emitting events + +For more details, see + +- [Core Architecture Documentation](./CORE_ARCHITECTURE.md) +- [Configuration Guide](./CONFIGURATION_GUIDE.md) +- [Main Contributing Guide](../.github/CONTRIBUTING.md). + +## Create a New Agent + +Creating a new agent involves three core steps: + +1. **Implement the Agent Module** - Create the Python module with your agent's logic +2. **Add Agent Card** - Define metadata of agents +3. **Add Agent Configuration** - Configure models parameters + +Let's walk through each step in detail. + +### Step 1: Create Agent Directory Structure + +Create a new directory for your agent under `python/valuecell/agents/`: + +```bash +mkdir -p python/valuecell/agents/hello_agent +touch python/valuecell/agents/hello_agent/__init__.py +touch python/valuecell/agents/hello_agent/__main__.py +touch python/valuecell/agents/hello_agent/core.py +``` + +### Step 2: Implement Your Agent Logic + +In `core.py`, subclass `BaseAgent` and implement the `stream()` method: + +```python +# file: valuecell/agents/hello_agent/core.py +from typing import AsyncGenerator, Optional, Dict +from valuecell.core.types import BaseAgent, StreamResponse +from valuecell.core.agent import streaming + +class HelloAgent(BaseAgent): + async def stream( + self, + query: str, # User query content + conversation_id: str, # Conversation ID + task_id: str, # Task ID + dependencies: Optional[Dict] = None, # Optional context (language, timezone, etc.) + ) -> AsyncGenerator[StreamResponse, None]: + """ + Process user queries and return streaming responses. + + Args: + query: User query content + conversation_id: Unique identifier for the conversation + task_id: Unique identifier for the task + dependencies: Optional dependencies containing language, timezone, and other context + + Yields: + StreamResponse: Stream response containing content and completion status + """ + # Send a few chunks, then finish + yield streaming.message_chunk("Thinking…") + yield streaming.message_chunk(f"You said: {query}") + yield streaming.done() +``` + +### Step 3: Add Agent Entry Point + +In `__main__.py`, wrap your agent for standalone execution. This file enables launching your agent with `uv run -m`: + +```python +# file: valuecell/agents/hello_agent/__main__.py +import asyncio +from valuecell.core.agent import create_wrapped_agent +from .core import HelloAgent + +if __name__ == "__main__": + agent = create_wrapped_agent(HelloAgent) + asyncio.run(agent.serve()) +``` + +> [!IMPORTANT] +> Always place the wrap and serve logic in `__main__.py`. This pattern enables: +> +> - Consistent agent launching via `uv run -m valuecell.agents.your_agent` +> - Integration with the ValueCell launch script +> - Standardized transport and event emission + +Run your agent: + +```bash +cd python +uv run -m valuecell.agents.hello_agent +``` + +> [!TIP] +> The wrapper standardizes transport and event emission so your agent integrates with the UI and logs consistently. + +## Add an Agent Configuration (Required) + +Agent configurations define how your agent uses models, embeddings, and runtime parameters. Create a YAML file in `python/configs/agents/`. + +### Create Configuration File + +Create `python/configs/agents/hello_agent.yaml`: + +```yaml +name: "Hello Agent" +enabled: true + +# Model configuration +models: + # Primary reasoning model + primary: + model_id: "anthropic/claude-haiku-4.5" + provider: "openrouter" + + # Fallback models for different providers + provider_models: + siliconflow: "Qwen/Qwen3-235B-A22B-Thinking-2507" + google: "gemini-2.5-flash" + + # Model parameters + parameters: + temperature: 0.7 + max_tokens: 4096 + +# Environment variable overrides +env_overrides: + HELLO_AGENT_MODEL_ID: "models.primary.model_id" + HELLO_AGENT_PROVIDER: "models.primary.provider" + HELLO_AGENT_TEMPERATURE: "models.primary.parameters.temperature" +``` + +> [!TIP] +> The YAML filename should match your agent's module name (e.g., `hello_agent.yaml` for `hello_agent` module). This naming convention helps maintain consistency across the codebase. + +### Using Configuration in Your Agent + +Load your agent's configuration using the config manager. The agent name passed to `get_model_for_agent()` must match the YAML filename (without the `.yaml` extension): + +```python +from valuecell.utils.model import get_model_for_agent + +class HelloAgent(BaseAgent): + def __init__(self, **kwargs): + super().__init__(**kwargs) + # Automatically loads configuration from hello_agent.yaml + # Agent name "hello_agent" must match the YAML filename + self.model = get_model_for_agent("hello_agent") + + async def stream(self, query, conversation_id, task_id, dependencies=None): + # Use your configured model + response = await self.model.generate(query) + yield streaming.message_chunk(response) + yield streaming.done() +``` + +### Runtime Configuration Override + +You can override configuration via environment variables: + +```bash +# Override model at runtime +export HELLO_AGENT_MODEL_ID="anthropic/claude-3.5-sonnet" +export HELLO_AGENT_TEMPERATURE="0.9" + +# Run your agent with overrides +uv run -m valuecell.agents.hello_agent +``` + +> [!TIP] +> For detailed configuration options including embedding models, fallback providers, and advanced patterns, see [CONFIGURATION_GUIDE](./CONFIGURATION_GUIDE.md). + +## Add an Agent Card + +Agent Cards declare how your agent is discovered and served. Place a JSON file under: + +`python/configs/agent_cards/` + +The `name` must match your agent class name (e.g., `HelloAgent`). The `url` decides the host/port your wrapped agent will bind to. + +### Minimal Example + +```json +{ + "name": "HelloAgent", + "url": "http://localhost:10010", + "description": "A minimal example agent that echoes input.", + "capabilities": { "streaming": true, "push_notifications": false }, + "default_input_modes": ["text"], + "default_output_modes": ["text"], + "version": "1.0.0", + "skills": [ + { + "id": "echo", + "name": "Echo", + "description": "Echo user input back as streaming chunks.", + "tags": ["example", "echo"] + } + ] +} +``` + +> [!TIP] +> +> - Filename can be anything (e.g., `hello_agent.json`), but `name` must equal your agent class (used by `create_wrapped_agent`) +> - Optional `enabled: false` will disable loading. Extra fields like `display_name` or `metadata` are ignored +> - Change the `url` port if it's occupied. The wrapper reads host/port from this URL when serving +> - If you see "No agent configuration found … in agent cards", check the `name` and the JSON location + +## Register Agent in Launch Script + +To enable your agent to be launched via the project's launch script, register it in `python/scripts/launch.py`. + +### Add Agent to Launch Script + +Open `python/scripts/launch.py` and add your agent: + +```python +# Define agent name constant +HELLO_AGENT_NAME = "HelloAgent" + +# Add to AGENTS list +AGENTS = [ + RESEARCH_AGENT_NAME, + AUTO_TRADING_AGENT_NAME, + HELLO_AGENT_NAME, # Add your agent here +] + +# Add launch command mapping +MAP_NAME_COMMAND[HELLO_AGENT_NAME] = ( + f"uv run --env-file {ENV_PATH_STR} -m valuecell.agents.hello_agent" +) +``` + +Now you can launch your agent using the project's start script. The `start.sh` (or `start.ps1` on Windows) script automatically invokes `launch.py`: + +```bash +# Launch all registered agents including yours +bash start.sh + +# Or manually run the launch script +cd python +python scripts/launch.py +``` + +The launch script will: + +- Start your agent as a background service +- Create log files in `logs/{timestamp}/HelloAgent.log` +- Display the agent's URL for monitoring + +> [!NOTE] +> The current launch script implementation will be optimized in future releases with automatic agent discovery and registration. For now, manual registration is required. + +## Use Models and Tools Inside an Agent + +Agents can use tools to extend their capabilities. Tools are Python functions that the agent can call during execution. + +### Defining Tools + +```python +from agno.agent import Agent +from agno.db.in_memory import InMemoryDb +from valuecell.utils.model import get_model_for_agent + +def search_stock_info(ticker: str) -> str: + """ + Search for stock information by ticker symbol. + + Args: + ticker: Stock ticker symbol (e.g., "AAPL", "GOOGL") + + Returns: + Stock information as a string + """ + # Your tool implementation here + return f"Stock info for {ticker}" + +def calculate_metrics(data: dict) -> dict: + """ + Calculate financial metrics from stock data. + + Args: + data: Dictionary containing financial data + + Returns: + Dictionary with calculated metrics + """ + # Your calculation logic here + return {"pe_ratio": 25.5, "market_cap": "2.5T"} + +class MyAgent(BaseAgent): + def __init__(self, **kwargs): + super().__init__(**kwargs) + self.inner = Agent( + ... + tools=[search_stock_info, calculate_metrics], # Register your tools + ... + ) +``` + +### Tool Best Practices + +- **Clear docstrings**: Tools should have descriptive docstrings that explain their purpose and parameters +- **Type hints**: Use type hints for all parameters and return values +- **Error handling**: Implement proper error handling within tools +- **Focused functionality**: Each tool should do one thing well + +> [!TIP] +> For more information, refer to [Tools - Agno](https://docs.agno.com/concepts/agents/tools). + +## Event System (Contracts) + +The event system enables real-time communication between agents and the UI. All events are defined in `valuecell.core.types`. + +### Stream Events + +Events for streaming agent responses: + +- `MESSAGE_CHUNK` - A chunk of the agent's response message +- `TOOL_CALL_STARTED` - Agent begins executing a tool +- `TOOL_CALL_COMPLETED` - Tool execution finished +- `COMPONENT_GENERATOR` - Rich format components (charts, tables, reports, etc.) +- `DONE` - Indicating the streaming is finished + +#### Component Generator Events + +The `COMPONENT_GENERATOR` event allows agents to send rich UI components beyond plain text. This enables interactive visualizations, structured data displays, and custom widgets. + +**Supported Component Types:** + +- `report` - Research reports with formatted content +- `profile` - Company or stock profiles +- `filtered_line_chart` - Interactive line charts with data filtering +- `filtered_card_push_notification` - Notification cards with filter options +- `scheduled_task_controller` - UI for managing scheduled tasks +- `scheduled_task_result` - Display results of scheduled tasks + +**Example: Emitting a Component** + +```python +from valuecell.core.agent import streaming + +# Create a line chart component +yield streaming.component_generator( + component_type="filtered_line_chart", + content={ + "title": "Stock Price Trends", + "data": [ + ["Date", "AAPL", "GOOGL", "MSFT"], + ["2025-01-01", 150.5, 2800.3, 380.2], + ["2025-01-02", 152.1, 2815.7, 382.5], + ], + "create_time": "2025-01-15 10:30:00" + } +) + +# Create a report component +yield streaming.component_generator( + component_type="report", + content={ + "title": "Q4 2024 Financial Analysis", + "data": "## Executive Summary\n\nRevenue increased by 15%...", + "url": "https://example.com/reports/q4-2024", + "create_time": "2025-01-15 10:30:00" + } +) +``` + +> [!TIP] +> Component data structures are defined in `valuecell.core.types`. See `ReportComponentData`, `FilteredLineChartComponentData`, and other component payload classes for required fields. + +### Emitting Events in Your Agent + +Use the `streaming.*` helpers to emit events. Here's a practical example based on the Research Agent implementation: + +```python +from agno.agent import Agent +from valuecell.core.agent import streaming +from valuecell.utils.model import get_model_for_agent + +class MyAgent(BaseAgent): + def __init__(self, **kwargs): + super().__init__(**kwargs) + self.inner = Agent( + model=get_model_for_agent("my_agent"), + tools=[...], # your tool functions + # ... other configuration + ) + + async def stream(self, query, conversation_id, task_id, dependencies=None): + # Stream responses from the inner agent + response_stream = self.inner.arun( + query, + stream=True, + stream_intermediate_steps=True, + session_id=conversation_id, + ) + + # Process and forward events from the inner agent + async for event in response_stream: + if event.event == "RunContent": + # Emit message chunks as they arrive + yield streaming.message_chunk(event.content) + + elif event.event == "ToolCallStarted": + # Notify UI that a tool is being called + yield streaming.tool_call_started( + event.tool.tool_call_id, + event.tool.tool_name + ) + + elif event.event == "ToolCallCompleted": + # Send tool results back to UI + yield streaming.tool_call_completed( + event.tool.result, + event.tool.tool_call_id, + event.tool.tool_name + ) + + # Signal completion + yield streaming.done() +``` + +> [!TIP] +> Refer to [Running Agents - Agno](https://docs.agno.com/concepts/agents/running-agents) for details + +> [!TIP] +> The UI automatically renders different event types appropriately - messages as text, tool calls with icons, etc. See the complete Research Agent implementation in `python/valuecell/agents/research_agent/core.py`. + +## Launch Backend + +### Run the API Server + +From the `python/` folder: + +```bash +cd python +python -m valuecell.server.main +``` + +### Run the Agent + +Run the Hello Agent as a standalone service: + +```bash +cd python +python -m valuecell.agents.hello_agent +``` + +> [!TIP] +> Set your environment first. At minimum, configure `SILICONFLOW_API_KEY` (and `OPENROUTER_API_KEY`) and `SEC_EMAIL`. See [CONFIGURATION_GUIDE](./CONFIGURATION_GUIDE.md). +> Optional: set `AGENT_DEBUG_MODE=true` to trace model behavior locally. + +## Debugging Agent Behavior + +Use `AGENT_DEBUG_MODE` to enable verbose traces from agents and planners: + +- Logs prompts, tool calls, intermediate steps, and provider response metadata +- Helpful to investigate planning decisions and tool routing during development + +Enable in your `.env`: + +```bash +AGENT_DEBUG_MODE=true +``` + +> [!CAUTION] +> Debug mode can log sensitive inputs/outputs and increases log volume/latency. Enable only in local/dev environments; keep it off in production. + +## Questions? + +If you have questions: + +- πŸ’¬ Join our [Discord](https://discord.com/invite/84Kex3GGAh) +- πŸ“§ Email us at [public@valuecell.ai](mailto:public@valuecell.ai) +- πŸ› Open an issue for bug reports + +--- + +Thank you for contributing to ValueCell! πŸš€πŸš€πŸš€ diff --git a/python/valuecell/agents/research_agent/core.py b/python/valuecell/agents/research_agent/core.py index f645c8f6c..ec1392459 100644 --- a/python/valuecell/agents/research_agent/core.py +++ b/python/valuecell/agents/research_agent/core.py @@ -18,7 +18,7 @@ web_search, ) from valuecell.agents.utils.context import build_ctx_from_dep -from valuecell.core.agent.responses import streaming +from valuecell.core.agent import streaming from valuecell.core.types import BaseAgent, StreamResponse from valuecell.utils.env import agent_debug_mode_enabled from valuecell.utils.model import get_model diff --git a/python/valuecell/core/agent/__init__.py b/python/valuecell/core/agent/__init__.py index d95002076..80183c56e 100644 --- a/python/valuecell/core/agent/__init__.py +++ b/python/valuecell/core/agent/__init__.py @@ -3,9 +3,13 @@ # Core agent functionality from .client import AgentClient from .connect import RemoteConnections +from .responses import streaming +from .decorator import create_wrapped_agent __all__ = [ # Core agent exports "AgentClient", "RemoteConnections", + "streaming", + "create_wrapped_agent", ] From 839ffaea7b9fa99c8f1a23fd8df872322f5cd437 Mon Sep 17 00:00:00 2001 From: Zhaofeng Zhang <24791380+vcfgv@users.noreply.github.com> Date: Thu, 30 Oct 2025 18:01:00 +0800 Subject: [PATCH 2/5] fix: reorder imports in agent module for clarity --- python/valuecell/core/agent/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/valuecell/core/agent/__init__.py b/python/valuecell/core/agent/__init__.py index 80183c56e..7e4a91565 100644 --- a/python/valuecell/core/agent/__init__.py +++ b/python/valuecell/core/agent/__init__.py @@ -3,8 +3,8 @@ # Core agent functionality from .client import AgentClient from .connect import RemoteConnections -from .responses import streaming from .decorator import create_wrapped_agent +from .responses import streaming __all__ = [ # Core agent exports From a866b689b731bffd30bfc02d513f274b3b7f1245 Mon Sep 17 00:00:00 2001 From: Zhaofeng Zhang <24791380+vcfgv@users.noreply.github.com> Date: Thu, 30 Oct 2025 18:15:10 +0800 Subject: [PATCH 3/5] docs: simplify model configuration section in agent contribution guide --- docs/CONTRIBUTE_AN_AGENT.md | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/docs/CONTRIBUTE_AN_AGENT.md b/docs/CONTRIBUTE_AN_AGENT.md index 643b7baea..019313b97 100644 --- a/docs/CONTRIBUTE_AN_AGENT.md +++ b/docs/CONTRIBUTE_AN_AGENT.md @@ -131,30 +131,20 @@ enabled: true # Model configuration models: - # Primary reasoning model + # Primary model primary: model_id: "anthropic/claude-haiku-4.5" provider: "openrouter" - - # Fallback models for different providers - provider_models: - siliconflow: "Qwen/Qwen3-235B-A22B-Thinking-2507" - google: "gemini-2.5-flash" - - # Model parameters - parameters: - temperature: 0.7 - max_tokens: 4096 # Environment variable overrides env_overrides: HELLO_AGENT_MODEL_ID: "models.primary.model_id" HELLO_AGENT_PROVIDER: "models.primary.provider" - HELLO_AGENT_TEMPERATURE: "models.primary.parameters.temperature" ``` > [!TIP] > The YAML filename should match your agent's module name (e.g., `hello_agent.yaml` for `hello_agent` module). This naming convention helps maintain consistency across the codebase. +> For detailed configuration options including embedding models, fallback providers, and advanced patterns, see [CONFIGURATION_GUIDE](./CONFIGURATION_GUIDE.md). ### Using Configuration in Your Agent From 8eb5054170839a22327c039949ba0b28e1457121 Mon Sep 17 00:00:00 2001 From: Zhaofeng Zhang <24791380+vcfgv@users.noreply.github.com> Date: Thu, 30 Oct 2025 18:19:25 +0800 Subject: [PATCH 4/5] docs: add agent processing flow essentials to contribution guide --- docs/CONTRIBUTE_AN_AGENT.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/CONTRIBUTE_AN_AGENT.md b/docs/CONTRIBUTE_AN_AGENT.md index 019313b97..7d4c743e4 100644 --- a/docs/CONTRIBUTE_AN_AGENT.md +++ b/docs/CONTRIBUTE_AN_AGENT.md @@ -85,6 +85,13 @@ class HelloAgent(BaseAgent): yield streaming.done() ``` +**Agent Processing Flow Essentials:** + +1. **Return Text Content**: Use `streaming.message_chunk()` to return text responses. You can send complete messages or split them into smaller chunks for better streaming UX. +2. **Signal Completion**: Always end with `streaming.done()` to indicate the agent has finished processing. + +This simple flow enables real-time communication with the UI, displaying responses as they're generated. + ### Step 3: Add Agent Entry Point In `__main__.py`, wrap your agent for standalone execution. This file enables launching your agent with `uv run -m`: From adcbd6d7c59bfaa091b9df810d9e7cad3cc5fa0e Mon Sep 17 00:00:00 2001 From: Zhaofeng Zhang <24791380+vcfgv@users.noreply.github.com> Date: Thu, 30 Oct 2025 18:24:32 +0800 Subject: [PATCH 5/5] docs: enhance agent contribution guide with AI assistant bootstrapping instructions --- docs/CONTRIBUTE_AN_AGENT.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/CONTRIBUTE_AN_AGENT.md b/docs/CONTRIBUTE_AN_AGENT.md index 7d4c743e4..6d5cd53c6 100644 --- a/docs/CONTRIBUTE_AN_AGENT.md +++ b/docs/CONTRIBUTE_AN_AGENT.md @@ -2,6 +2,22 @@ This guide explains how to build, integrate, and contribute new agents to ValueCell's multi-agent financial platform. +## Kickstart πŸš€ + +Want to quickly create a new agent? You can use an AI coding assistant like GitHub Copilot, Cursor, or other Agent Coders to bootstrap your agent automatically! + +Simply share this guide with your AI assistant and ask: + +> "Please create a HelloAgent following this guide." + +The AI will read through this documentation and generate all necessary files: + +- Agent module (`core.py`, `__main__.py`, `__init__.py`) +- Configuration files (YAML and JSON) +- Launch script registration + +This is the fastest way to get started and learn the agent structure hands-on! + ## Table of Contents - [Architecture Overview](#architecture-overview)