From e343155e6c03832ab3a2e742762181d1dc4e3b5a Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 7 Mar 2026 04:06:17 +0000 Subject: [PATCH] docs: add comprehensive project documentation in Chinese Add full project documentation covering: - docs/README.md: project overview, tech stack, quick start guide - docs/architecture.md: system architecture, layer responsibilities, data flow - docs/concepts.md: core concepts (SSE, A2A protocol, HITL, event system, etc.) - docs/notes.md: usage notes, configuration, caveats - docs/backend/core/: orchestrator, planner, super agent, agent connections, conversation management, task management - docs/backend/adapters/: asset data adapters (YFinance, AKShare, AdapterManager) - docs/backend/server/: HTTP server API routes, schemas, DB layer - docs/backend/agents/: Research Agent, Auto Trading Agent, AI Hedge Fund agents - docs/frontend/pages/: frontend pages and routing - docs/frontend/agent-chat/: chat interface components and renderers - docs/frontend/state/: state management (Zustand), SSE client, API layer - docs/examples/: end-to-end usage examples https://claude.ai/code/session_01PXAkfeJgDnDzdeT3hUCzXX --- docs/README.md | 122 ++++++++++ docs/architecture.md | 181 ++++++++++++++ docs/backend/adapters/README.md | 220 +++++++++++++++++ docs/backend/agents/README.md | 148 ++++++++++++ docs/backend/agents/ai-hedge-fund.md | 120 ++++++++++ docs/backend/agents/auto-trading-agent.md | 134 +++++++++++ docs/backend/agents/research-agent.md | 129 ++++++++++ docs/backend/core/README.md | 184 ++++++++++++++ docs/backend/core/agent-connection.md | 195 +++++++++++++++ docs/backend/core/conversation.md | 161 +++++++++++++ docs/backend/core/task.md | 113 +++++++++ docs/backend/server/README.md | 187 +++++++++++++++ docs/concepts.md | 196 +++++++++++++++ docs/examples/README.md | 279 ++++++++++++++++++++++ docs/frontend/agent-chat/README.md | 173 ++++++++++++++ docs/frontend/pages/README.md | 165 +++++++++++++ docs/frontend/state/README.md | 255 ++++++++++++++++++++ docs/notes.md | 158 ++++++++++++ 18 files changed, 3120 insertions(+) create mode 100644 docs/README.md create mode 100644 docs/architecture.md create mode 100644 docs/backend/adapters/README.md create mode 100644 docs/backend/agents/README.md create mode 100644 docs/backend/agents/ai-hedge-fund.md create mode 100644 docs/backend/agents/auto-trading-agent.md create mode 100644 docs/backend/agents/research-agent.md create mode 100644 docs/backend/core/README.md create mode 100644 docs/backend/core/agent-connection.md create mode 100644 docs/backend/core/conversation.md create mode 100644 docs/backend/core/task.md create mode 100644 docs/backend/server/README.md create mode 100644 docs/concepts.md create mode 100644 docs/examples/README.md create mode 100644 docs/frontend/agent-chat/README.md create mode 100644 docs/frontend/pages/README.md create mode 100644 docs/frontend/state/README.md create mode 100644 docs/notes.md diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 000000000..f17825c70 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,122 @@ +# ValueCell 项目文档 + +ValueCell 是一个社区驱动的多智能体(Multi-Agent)金融应用平台,集成多个顶级投资风格的 AI Agent,帮助用户进行股票行情查看、投资研究、自动化交易等。 + +## 目录结构 + +``` +docs/ +├── README.md # 本文件:项目概述与导航 +├── architecture.md # 整体架构与分层说明 +├── concepts.md # 核心概念(SSE / A2A / 事件模型等) +├── notes.md # 使用注意事项与常见问题 +├── backend/ +│ ├── core/ +│ │ ├── README.md # 核心调度层(Orchestrator / Planner / SuperAgent) +│ │ ├── agent-connection.md # Agent 连接管理(Card / Client / Connect / Listener) +│ │ ├── conversation.md # 对话管理(Manager / Store / ItemStore / 模型) +│ │ └── task.md # 任务管理(Task / TaskManager) +│ ├── adapters/ +│ │ └── README.md # 资产数据适配器(YFinance / AKShare / Manager) +│ ├── server/ +│ │ └── README.md # HTTP 服务端(Routers / Schemas / Services / DB) +│ └── agents/ +│ ├── README.md # 内置 Agent 概览 +│ ├── research-agent.md # 研究 Agent(SEC 文件 / 知识库 / Web 搜索) +│ ├── auto-trading-agent.md # 自动交易 Agent(加密货币 / Binance) +│ └── ai-hedge-fund.md # AI 对冲基金(多投资大师风格分析) +├── frontend/ +│ ├── pages/ +│ │ └── README.md # 页面路由与各页功能 +│ ├── agent-chat/ +│ │ └── README.md # Agent 对话界面组件 +│ └── state/ +│ └── README.md # 状态管理与 SSE 通信客户端 +└── examples/ + └── README.md # 典型使用场景与示例 +``` + +--- + +## 项目简介 + +| 属性 | 说明 | +|------|------| +| 定位 | 多智能体金融分析与交易平台 | +| 协议 | Agent2Agent(A2A)协议驱动的分布式 Agent 网络 | +| 数据流 | 后端 SSE 流式推送,前端实时渲染 | +| 存储 | SQLite 持久化对话与消息历史 | +| LLM | 支持 OpenRouter、OpenAI、Anthropic、Google、Ollama | +| 行情数据 | YFinance(美股/港股)、AKShare(A 股/港股) | +| 前端框架 | React 19 + React Router v7 + TailwindCSS + Zustand | +| 后端框架 | Python FastAPI + Agno Agent 框架 | + +--- + +## 技术栈速览 + +### 后端(`python/`) + +| 技术 | 版本/说明 | +|------|-----------| +| Python | 3.12+ | +| FastAPI | HTTP 服务框架,支持 SSE 流 | +| Uvicorn | ASGI 服务器 | +| Agno | Agent 框架(Agno by A2A Protocol) | +| a2a-sdk | Agent2Agent 协议 SDK | +| SQLite | 本地对话历史持久化(`valuecell.db`) | +| yfinance | 雅虎财经行情数据 | +| akshare | A 股/港股行情数据 | +| Pydantic v2 | 数据校验与序列化 | +| uv | Python 包管理器 | + +### 前端(`frontend/`) + +| 技术 | 版本/说明 | +|------|-----------| +| React | 19.2 | +| React Router | v7(文件式路由) | +| Zustand | 5.x,全局状态管理 | +| TanStack Query | v5,服务端数据请求 | +| TailwindCSS | v4,原子化样式 | +| shadcn/ui + Radix | UI 组件库 | +| ECharts | 图表库(K 线、折线图等) | +| Vite / rolldown | 构建工具 | +| Bun | 包管理器 / 运行时 | +| Tauri v2 | 可选桌面应用支持 | + +--- + +## 快速启动 + +```bash +# 1. 克隆仓库 +git clone https://github.com/ValueCell-ai/valuecell.git +cd valuecell + +# 2. 配置环境变量 +cp .env.example .env +# 编辑 .env,填入 OPENROUTER_API_KEY 等必要参数 + +# 3. 启动全栈(前端 + 后端 + Agents) +bash start.sh # Linux / macOS +.\start.ps1 # Windows PowerShell +``` + +启动后访问:`http://localhost:1420` + +--- + +## 更多文档 + +- [整体架构](architecture.md) +- [核心概念](concepts.md) +- [使用注意](notes.md) +- [后端核心调度层](backend/core/README.md) +- [数据适配器](backend/adapters/README.md) +- [HTTP 服务端](backend/server/README.md) +- [内置 Agent](backend/agents/README.md) +- [前端页面](frontend/pages/README.md) +- [Agent 对话组件](frontend/agent-chat/README.md) +- [状态管理](frontend/state/README.md) +- [示例说明](examples/README.md) diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 000000000..af052fd7a --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,181 @@ +# ValueCell 整体架构 + +## 系统分层 + +``` +┌─────────────────────────────────────────────────────────┐ +│ Frontend(前端) │ +│ React 19 + React Router v7 + Zustand + TailwindCSS │ +│ 页面: Home / Market / Agent Chat / Setting │ +└────────────────────┬───────────────────────┬────────────┘ + │ REST API │ SSE 流 + ▼ ▼ +┌─────────────────────────────────────────────────────────┐ +│ Backend Server(后端服务) │ +│ FastAPI + Uvicorn(端口 8000) │ +│ Routers: /api/v1/{agents, watchlist, conversation, ...} │ +│ Services: AgentStreamService / ConversationService │ +└────────────────────┬────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────┐ +│ Core 调度层(python/valuecell/core/) │ +│ │ +│ ┌──────────────┐ ┌──────────────┐ ┌─────────────┐ │ +│ │ SuperAgent │──▶│ Planner │──▶│ Orchestrator│ │ +│ │ (意图分流) │ │ (任务规划) │ │ (生命周期) │ │ +│ └──────────────┘ └──────────────┘ └──────┬──────┘ │ +│ │ │ +│ ┌───────────────────────────────────────────▼─────────┐ │ +│ │ ResponseBuffer + ResponseRouter │ │ +│ │ (流式缓冲 + 持久化 + UI 推送) │ │ +│ └──────────────────────────────────────────────────── ┘ │ +└────────────────────────────────────────────────────────┬─┘ + │ A2A Protocol + ┌────────────────────────────────────▼───────────┐ + │ Remote Agent 层(分布式 Agent) │ + │ ┌─────────────┐ ┌──────────────────────────┐ │ + │ │ Research │ │ AI Hedge Fund │ │ + │ │ Agent │ │ (BenGraham/Buffett/...) │ │ + │ └─────────────┘ └──────────────────────────┘ │ + │ ┌─────────────┐ ┌──────────────────────────┐ │ + │ │ AutoTrading │ │ 更多 Agent... │ │ + │ │ Agent │ │ │ │ + │ └─────────────┘ └──────────────────────────┘ │ + └─────────────────────────────────────────────────┘ + │ + ┌────────────────────────────────────▼───────────┐ + │ 数据层 │ + │ SQLite(对话历史) YFinance / AKShare(行情) │ + └────────────────────────────────────────────────┘ +``` + +--- + +## 各层职责 + +### 1. 前端层(`frontend/src/`) + +| 模块 | 职责 | +|------|------| +| `app/home/` | 行情列表、观察列表(Watchlist)、股票详情 | +| `app/market/` | Agent 市场,展示可用的所有 Agent 卡片 | +| `app/agent/` | Agent 对话页面,聊天 + 流式渲染 | +| `app/setting/` | 用户设置(Memory 管理等) | +| `store/agent-store.ts` | Zustand 全局状态,管理对话数据树 | +| `lib/sse-client.ts` | Fetch-based SSE 客户端,接收后端流式事件 | +| `components/valuecell/renderer/` | 各类消息渲染器(Markdown/Report/ToolCall/...) | + +### 2. 后端服务层(`python/valuecell/server/`) + +| 模块 | 职责 | +|------|------| +| `api/app.py` | FastAPI 应用工厂,注册中间件、路由、异常处理器 | +| `api/routers/agent_stream.py` | SSE 流式接口 `POST /api/v1/agents/stream` | +| `api/routers/conversation.py` | 对话历史 CRUD | +| `api/routers/watchlist.py` | 观察列表管理 | +| `api/routers/user_profile.py` | 用户 Profile(语言/时区/Memory) | +| `services/agent_stream_service.py` | 调用 Core 层 Orchestrator 产生流式响应 | +| `db/` | SQLAlchemy ORM + SQLite,持久化 Agent/Asset/Watchlist | + +### 3. 核心调度层(`python/valuecell/core/`) + +| 模块 | 职责 | +|------|------| +| `coordinate/super_agent.py` | 前置意图分析,决定直接回答 or 转交 Planner | +| `coordinate/planner.py` | 基于 Agno Agent + LLM 将用户意图拆解为 Task 列表 | +| `coordinate/orchestrator.py` | 整体生命周期管理:HITL、Task 执行、流式响应、持久化 | +| `coordinate/response_buffer.py` | 流式 chunk 聚合,为稳定 item_id 做段落级缓冲 | +| `coordinate/response_router.py` | 状态事件路由,触发持久化副作用 | +| `agent/connect.py` | Remote Agent 连接管理(A2A 协议) | +| `conversation/` | 对话元数据 + 消息条目存储(内存/SQLite 双后端) | +| `task/` | Task 状态机(pending→running→completed/failed) | + +### 4. Remote Agent 层(各 Agent 进程) + +每个 Agent 作为独立的 HTTP 服务运行,通过 A2A 协议与 Core 层通信: + +| Agent | 端口 | 说明 | +|-------|------|------| +| ResearchAgent | 10014 | SEC 文件 + Web 搜索 + 知识库 | +| AutoTradingAgent | 10013 | 加密货币自动交易(Binance/模拟盘) | +| AI Hedge Fund Agents | 10011~10020 | 多种投资大师风格(BenGraham/Buffett/...) | +| InvestmentResearchAgent | 可配置 | 综合投研报告 | + +### 5. 数据层 + +| 数据源 | 说明 | +|--------|------| +| SQLite(`valuecell.db`) | 对话历史、消息条目、Watchlist、用户 Profile | +| YFinance | 美股、港股、加密货币实时/历史行情 | +| AKShare | A 股(沪/深/北)、港股行情 | +| OpenRouter / OpenAI / Anthropic | LLM 推理调用 | +| SEC EDGAR | 美股上市公司定期/事件型报告(10-K / 8-K 等) | + +--- + +## 完整请求数据流 + +### 用户发送一条消息(以 Agent 对话为例) + +``` +用户在前端输入消息 + │ + ▼ +前端 SSEClient.connect(body) + │ POST /api/v1/agents/stream + │ { query, agent_name, conversation_id } + ▼ +AgentStreamRouter(FastAPI SSE) + │ StreamingResponse + ▼ +AgentStreamService.stream_query_agent() + │ + ▼ +AgentOrchestrator.process_user_input() + │ + ├─── [无 target_agent_name] SuperAgent.run() + │ 判断: ANSWER / HANDOFF_TO_PLANNER + │ + ├─── [转交 Planner] ExecutionPlanner.create_plan() + │ Agno Agent (LLM) → PlannerResponse → Task 列表 + │ [可能] HITL: UserInputRequest → plan_require_user_input 事件 + │ + └─── 执行 Task 列表(串行) + │ + ▼ + RemoteConnections.start_agent() → A2A Client + │ + ▼ + 远端 Agent 处理(流式输出) + │ + ▼ + TaskStatusUpdateEvent → ResponseBuffer.annotate() + │ + ▼ + 持久化 SaveItem → SQLite + │ + ▼ + SSE chunk → 前端 + │ + ▼ +前端 SSEClient.onData(SSEData) + ▼ +useAgentStore.dispatchAgentStore(action) + ▼ +updateAgentConversationsStore() 更新状态树 + ▼ +React 重新渲染对话区域 +``` + +--- + +## 配置文件与启动脚本 + +| 文件 | 说明 | +|------|------| +| `.env.example` | 环境变量模板,含 API Key、模型 ID、端口等 | +| `start.sh / start.ps1` | 一键启动前端 + 后端 + 所有 Agent | +| `python/configs/agent_cards/*.json` | Agent 描述卡片,定义 Agent URL / 技能 / 元数据 | +| `frontend/vite.config.ts` | Vite 构建配置,前端开发服务端口 1420 | +| `python/valuecell/server/config/settings.py` | 后端配置(从环境变量读取) | diff --git a/docs/backend/adapters/README.md b/docs/backend/adapters/README.md new file mode 100644 index 000000000..117983cb5 --- /dev/null +++ b/docs/backend/adapters/README.md @@ -0,0 +1,220 @@ +# 资产数据适配器(Asset Adapters) + +路径:`python/valuecell/adapters/assets/` + +资产数据适配器层负责从不同数据源获取行情数据(实时价格、历史价格、资产信息、搜索),并通过统一接口暴露给上层服务。 + +--- + +## 模块文件 + +| 文件 | 类 | 职责 | +|------|-----|------| +| `base.py` | `BaseDataAdapter` | 所有适配器的抽象基类 | +| `yfinance_adapter.py` | `YFinanceAdapter` | 雅虎财经(美股、港股、加密货币) | +| `akshare_adapter.py` | `AKShareAdapter` | AKShare(A 股、港股) | +| `manager.py` | `AdapterManager`, `WatchlistManager` | 适配器路由管理 + 观察列表管理 | +| `types.py` | `Asset`, `AssetPrice`, `AssetSearchResult`, ... | 统一数据类型定义 | +| `i18n_integration.py` | — | 多语言资产名称支持 | + +--- + +## 统一 Ticker 格式 + +所有适配器使用 `EXCHANGE:SYMBOL` 格式作为内部唯一标识: + +| 交易所 | 格式 | 示例 | +|--------|------|------| +| NASDAQ | `NASDAQ:SYMBOL` | `NASDAQ:AAPL` | +| NYSE | `NYSE:SYMBOL` | `NYSE:JPM` | +| AMEX | `AMEX:SYMBOL` | `AMEX:GLD` | +| HKEX | `HKEX:5位数字(含前导零)` | `HKEX:00700` | +| SSE(上交所) | `SSE:6位代码` | `SSE:601398` | +| SZSE(深交所) | `SZSE:6位代码` | `SZSE:000001` | +| BSE(北交所) | `BSE:6位代码` | `BSE:835368` | +| CRYPTO | `CRYPTO:SYMBOL` | `CRYPTO:BTC` | + +--- + +## 数据类型 + +### Asset(资产信息) + +```python +class Asset: + ticker: str # 内部 Ticker + asset_type: AssetType # STOCK / ETF / CRYPTO / INDEX / BOND + names: AssetNames # 多语言名称 + market_info: MarketInfo # 交易所、国家、货币 + sector: Optional[str] + industry: Optional[str] +``` + +### AssetPrice(价格数据) + +```python +class AssetPrice: + ticker: str + current_price: float + open: float + high: float + low: float + close: float + volume: float + change: float # 涨跌额 + change_percent: float # 涨跌幅 + timestamp: datetime + data_source: DataSource +``` + +### AssetSearchResult(搜索结果) + +```python +class AssetSearchResult: + ticker: str + asset_type: AssetType + names: List[str] # 多语言名称列表 + exchange: Exchange + country: str + relevance_score: float # 相关性评分(用于排序) +``` + +--- + +## BaseDataAdapter(抽象基类) + +所有适配器必须实现的接口: + +```python +class BaseDataAdapter(ABC): + @abstractmethod + def get_capabilities(self) -> List[AdapterCapability]: + """返回此适配器支持的交易所/资产类型组合""" + + @abstractmethod + def validate_ticker(self, ticker: str) -> bool: + """验证 ticker 是否属于此适配器支持范围""" + + @abstractmethod + def get_asset_info(self, ticker: str) -> Optional[Asset]: + """获取资产详细信息""" + + @abstractmethod + def get_real_time_price(self, ticker: str) -> Optional[AssetPrice]: + """获取实时价格""" + + @abstractmethod + def get_multiple_prices(self, tickers: List[str]) -> Dict[str, Optional[AssetPrice]]: + """批量获取价格(并行优化)""" + + @abstractmethod + def get_historical_prices(self, ticker, start_date, end_date, interval) -> List[AssetPrice]: + """获取历史价格""" + + @abstractmethod + def search_assets(self, query: AssetSearchQuery) -> List[AssetSearchResult]: + """搜索资产""" +``` + +--- + +## AdapterManager(适配器路由管理器) + +`AdapterManager` 通过路由表将请求分发到正确的适配器,并提供自动故障转移(failover)。 + +### 路由机制 + +``` +ticker = "NASDAQ:AAPL" + │ exchange = "NASDAQ" + ▼ +exchange_routing["NASDAQ"] = [YFinanceAdapter, ...] + │ 首个通过 validate_ticker() 的适配器获胜 + ▼ +YFinanceAdapter.get_real_time_price("NASDAQ:AAPL") + │ 成功 → 返回 + │ 失败 → 尝试下一个 failover 适配器 + ▼ +结果写入 _ticker_cache(下次快速命中) +``` + +### 并行搜索 + +`search_assets()` 使用 `ThreadPoolExecutor` 并行查询所有适配器,并对结果去重: + +```python +with ThreadPoolExecutor(max_workers=len(target_adapters)) as executor: + future_to_adapter = { + executor.submit(adapter.search_assets, query): adapter + for adapter in target_adapters + } + # 等待所有完成,合并结果 +``` + +### LLM Fallback 搜索 + +当所有适配器均无结果时,触发基于 LLM 的兜底搜索(`_fallback_search_assets`): +1. 调用 `PRODUCT_MODEL_ID` 模型生成可能的 Ticker 列表 +2. 逐个调用 `get_asset_info()` 验证是否真实存在 +3. 返回验证通过的结果 + +--- + +## 各适配器支持范围 + +### YFinanceAdapter + +| 支持范围 | 说明 | +|----------|------| +| 美股 | NASDAQ / NYSE / AMEX | +| 港股 | HKEX(代码格式:`0700.HK`)| +| 加密货币 | CRYPTO(格式:`BTC-USD`)| +| 数据质量 | 实时(延迟约 15 分钟),历史数据完整 | +| 中国可用性 | 部分地区可能需要代理 | + +### AKShareAdapter + +| 支持范围 | 说明 | +|----------|------| +| A 股 | SSE(上交所)、SZSE(深交所)、BSE(北交所)| +| 港股 | HKEX | +| 数据质量 | 实时(延迟约 15 分钟),免费 | +| 限制 | 频率过高可能触发限流 | + +--- + +## WatchlistManager(观察列表管理) + +`WatchlistManager` 提供用户观察列表的增删查功能,并集成 `AdapterManager` 批量获取价格: + +```python +manager = WatchlistManager(adapter_manager) + +# 创建观察列表 +watchlist = manager.create_watchlist(user_id="u1", name="我的自选") + +# 添加股票 +manager.add_asset_to_watchlist(user_id="u1", ticker="NASDAQ:AAPL") + +# 批量获取价格 +prices = manager.get_watchlist_prices(user_id="u1") +# 返回 {"NASDAQ:AAPL": AssetPrice(...), ...} +``` + +--- + +## 全局单例 + +```python +from valuecell.adapters.assets import get_adapter_manager, get_watchlist_manager + +adapter_manager = get_adapter_manager() # 全局单例 AdapterManager +watchlist_manager = get_watchlist_manager() # 全局单例 WatchlistManager +``` + +在 FastAPI 应用启动时(`lifespan`),系统会自动初始化并配置 YFinance 和 AKShare 适配器: + +```python +manager.configure_yfinance() +manager.configure_akshare() +``` diff --git a/docs/backend/agents/README.md b/docs/backend/agents/README.md new file mode 100644 index 000000000..0e29d2e46 --- /dev/null +++ b/docs/backend/agents/README.md @@ -0,0 +1,148 @@ +# 内置 Agent 概览 + +路径:`python/valuecell/agents/`,`python/valuecell/third_party/` + +ValueCell 内置多种专业投资与交易 Agent,每个 Agent 作为独立的 HTTP 服务运行,通过 A2A 协议与 Core 调度层交互。 + +--- + +## Agent 架构 + +每个 Agent 遵循统一的实现模式: + +``` +Agent HTTP Server(A2A 兼容) + │ 基于 valuecell.core.agent.decorator + │ 监听指定端口 + ▼ +BaseAgent 子类 + │ 实现 stream() 和可选的 notify() 方法 + ▼ +Agno Agent(内部 LLM 推理) + │ 工具调用、知识库查询 + ▼ +数据源(SEC EDGAR / Web / 行情数据等) +``` + +### BaseAgent 抽象接口 + +```python +class BaseAgent(ABC): + async def stream(self, query, conversation_id, task_id, dependencies) + -> AsyncGenerator[StreamResponse, None]: + """用户触发的流式响应(SSE)""" + + async def notify(self, query, conversation_id, task_id, dependencies) + -> AsyncGenerator[NotifyResponse, None]: + """Agent 主动触发的推送(如定期报告/价格告警)""" +``` + +--- + +## Agent 列表 + +| Agent | 端口 | 文件路径 | 功能 | +|-------|------|----------|------| +| [ResearchAgent](research-agent.md) | 10014 | `agents/research_agent/` | SEC 文件分析 + Web 搜索 + 知识库问答 | +| [AutoTradingAgent](auto-trading-agent.md) | 10013 | `agents/auto_trading_agent/` | 加密货币自动交易(Binance / 模拟盘) | +| [AI Hedge Fund Agents](ai-hedge-fund.md) | 10011~10020 | `third_party/ai-hedge-fund/` | 多投资大师风格(BenGraham / Buffett / ...) | +| InvestmentResearchAgent | 10014 | `third_party/TradingAgents/` | 综合投研报告 | + +--- + +## Agent Card 配置 + +每个 Agent 在 `python/configs/agent_cards/` 目录下有对应的 JSON 配置文件。Planner 通过扫描这些文件来了解可用的 Agent 能力。 + +### 配置字段说明 + +```json +{ + "name": "ResearchAgent", // 唯一标识,与代码中的 name 一致 + "display_name": "Research Agent", // 前端展示名称 + "url": "http://localhost:10014/", // Agent HTTP 服务地址 + "description": "...", // Agent 能力描述(影响 Planner 路由决策) + "capabilities": { + "streaming": true, // 是否支持流式输出 + "push_notifications": false // 是否支持主动推送通知 + }, + "skills": [ // 技能列表(影响 Planner 路由决策) + { + "id": "research", + "name": "Research", + "description": "...", + "examples": ["分析 AAPL..."], // 用于帮助 Planner 理解何时使用 + "tags": ["research"] + } + ], + "enabled": true, // false 时不被 Planner 发现 + "metadata": { "version": "1.0.0" } +} +``` + +--- + +## 依赖(Dependencies)注入 + +每次 Task 执行时,Orchestrator 将用户的上下文信息作为 `dependencies` 传入 Agent: + +```python +metadata[DEPENDENCIES] = { + USER_PROFILE: { # 用户 Profile(语言偏好、Memory 等) + "language": "zh-Hans", + "timezone": "Asia/Shanghai", + "memory": [...] + }, + CURRENT_CONTEXT: {}, # 当前执行上下文(扩展预留) + LANGUAGE: "zh-Hans", # 当前语言 + TIMEZONE: "Asia/Shanghai", # 当前时区 +} +``` + +Agent 在 `build_ctx_from_dep(dependencies)` 中解析并注入到 Agno Agent 的上下文。 + +--- + +## 响应组件类型 + +Agent 除了输出普通文本外,还可以通过 `component_generator` 事件推送结构化 UI 组件: + +```python +# 生成研究报告组件 +yield streaming.component( + ComponentType.REPORT, + ReportComponentData( + title="AAPL 财报分析", + data="...", # Markdown 内容 + create_time="2025-01-01 10:00:00" + ) +) + +# 生成可过滤折线图 +yield streaming.component( + ComponentType.FILTERED_LINE_CHART, + FilteredLineChartComponentData( + title="价格走势", + data="[['日期', 'BTC价格'], ['2025-01-01', 50000], ...]", + create_time="..." + ) +) +``` + +--- + +## 添加新 Agent + +1. 创建 Agent 实现类(继承 `BaseAgent`) +2. 创建启动脚本(`__main__.py`),使用 `create_agent_app()` 包装并启动 HTTP 服务 +3. 在 `python/configs/agent_cards/` 添加对应的 JSON Card 文件(`enabled: true`) +4. 在 `start.sh` 中添加启动命令 +5. 重启服务后,Planner 会自动发现新 Agent + +--- + +## 详细文档 + +- [Research Agent](research-agent.md) +- [Auto Trading Agent](auto-trading-agent.md) +- [AI Hedge Fund Agents](ai-hedge-fund.md) diff --git a/docs/backend/agents/ai-hedge-fund.md b/docs/backend/agents/ai-hedge-fund.md new file mode 100644 index 000000000..dd9bd4ce0 --- /dev/null +++ b/docs/backend/agents/ai-hedge-fund.md @@ -0,0 +1,120 @@ +# AI Hedge Fund Agents(AI 对冲基金) + +路径:`python/valuecell/third_party/ai-hedge-fund/` + +AI Hedge Fund 是对 [virattt/ai-hedge-fund](https://github.com/virattt/ai-hedge-fund) 的集成,包含多个模拟顶级投资大师风格的 AI Agent,各自采用不同的分析方法论对股票进行评估。 + +--- + +## 投资大师 Agent 列表 + +| Agent | 投资风格 | 核心方法论 | +|-------|----------|-----------| +| `BenGrahamAgent` | 价值投资 | 安全边际、格雷厄姆数、清算价值 | +| `WarrenBuffettAgent` | 价值+成长投资 | 护城河、ROIC、长期持有 | +| `CharlieMungerAgent` | 价值投资(心智模型) | 心智模型、逆向思维、商业质量 | +| `PeterLynchAgent` | 成长投资 | PEG 比率、行业专精、GARP | +| `PhilFisherAgent` | 成长投资 | 定性分析、管理层质量、scuttlebutt | +| `BillAckmanAgent` | 激进投资者 | 集中持仓、催化剂识别、管理层变革 | +| `CathieWoodAgent` | 颠覆性创新 | 指数级成长、科技颠覆、五年视野 | +| `MichaelBurryAgent` | 深度价值/逆向 | 深度基本面研究、极端逆向操作 | +| `StanleyDruckenmillerAgent` | 宏观投资 | 宏观经济判断、流动性分析、押注尾部事件 | +| `MohnishPabraiAgent` | Dhandho 投资 | 低风险高收益、复制成功范式 | +| `AswathDamodaranAgent` | 估值 | DCF、相对估值、价值驱动因子 | +| `RakeshJhunjhunwalaAgent` | 印度市场价值 | 印度股市、长期持有 | +| `FundamentalsAnalystAgent` | 基本面 | 综合财务指标分析 | +| `PortfolioManagerAgent` | 投资组合 | 多 Agent 协作,最终投资决策 | +| `RiskManagerAgent` | 风险管理 | 风险评估、仓位管理 | +| `TechnicalAnalystAgent` | 技术分析 | 图表形态、指标分析 | +| `ValuationAgent` | 估值 | 多维估值方法 | + +--- + +## 重要限制 + +> **当前仅支持以下 5 只美股股票代码**: +> `AAPL`, `GOOGL`, `MSFT`, `NVDA`, `TSLA` + +这是上游数据源(`yfinance` + `finnhub`)的限制,未来版本将扩展支持范围。对其他股票代码的查询会被 Agent 拒绝。 + +--- + +## Agent 协作架构 + +这些 Agent 使用 LangGraph 构建多 Agent 协作工作流: + +``` +用户输入(股票代码 + 时间范围) + │ + ├── 并行分析阶段 + │ ├── FundamentalsAnalystAgent → 财务指标分析 + │ ├── TechnicalAnalystAgent → 技术指标分析 + │ ├── ValuationAgent → 估值分析 + │ └── [投资大师 Agents] → 各自风格分析 + │ + └── 决策阶段 + ├── RiskManagerAgent → 风险评估 → 仓位建议 + └── PortfolioManagerAgent → 综合所有分析 → 最终交易决策 +``` + +--- + +## Agent Card 配置 + +每个 Agent 在 ValueCell 中有对应的 Card 文件(默认 `enabled: false`,需手动启用): + +``` +python/configs/agent_cards/ +├── ben_graham_agent.json (端口 10011) +├── warren_buffett_agent.json (端口 10012, 示例) +├── investment_research_agent.json (综合投研) +└── ... +``` + +启用方法:将对应 JSON 文件中的 `"enabled": false` 改为 `"enabled": true`,然后重启服务。 + +--- + +## 数据来源 + +| 数据类型 | 来源 | 需要 Key | +|----------|------|----------| +| 财务数据 | Yahoo Finance | 否 | +| 新闻/情绪 | Finnhub | 是(`FINNHUB_API_KEY`) | +| 内幕交易 | Finnhub | 是 | +| Web 搜索 | OpenAI Browsing / Tavily | 是(`OPENAI_API_KEY`) | + +--- + +## 独立运行 + +AI Hedge Fund 可以脱离 ValueCell UI 独立运行(命令行模式),详见上游项目文档: + +```bash +cd python/third_party/ai-hedge-fund +uv run python src/main.py --ticker AAPL --show-reasoning +``` + +--- + +## 启动说明 + +作为 ValueCell 的 Remote Agent 启动: + +```bash +cd python/third_party/ai-hedge-fund +bash launch_adapter.sh +``` + +`launch_adapter.sh` 会启动 A2A 兼容的 HTTP 服务,使 ValueCell Core 层可以通过 A2A 协议与之通信。 + +--- + +## 配置要求 + +| 变量 | 说明 | +|------|------| +| `AI_HEDGE_FUND_PARSER_MODEL_ID` | 请求解析模型(默认 `google/gemini-2.5-flash`) | +| `OPENAI_API_KEY` | 财务数据搜索 | +| `FINNHUB_API_KEY` | 新闻与内幕交易数据 | +| `OPENROUTER_API_KEY` | Agent LLM 推理(主要提供商) | diff --git a/docs/backend/agents/auto-trading-agent.md b/docs/backend/agents/auto-trading-agent.md new file mode 100644 index 000000000..b931911df --- /dev/null +++ b/docs/backend/agents/auto-trading-agent.md @@ -0,0 +1,134 @@ +# Auto Trading Agent(自动交易 Agent) + +路径:`python/valuecell/agents/auto_trading_agent/` +端口:`10013`(默认) + +Auto Trading Agent 是一个加密货币自动交易系统,集成技术分析、AI 信号生成、仓位管理和交易执行功能,支持 Binance 真实交易和模拟盘(Paper Trading)。 + +--- + +## 核心功能 + +| 功能模块 | 文件 | 说明 | +|----------|------|------| +| 主 Agent 逻辑 | `agent.py` | 多实例管理、流式输出、通知推送 | +| 技术分析 | `technical_analysis.py` | RSI、MACD、布林带等指标 + AI 信号生成 | +| 投资组合决策 | `portfolio_decision_manager.py` | 综合多个资产分析决策 | +| 仓位管理 | `position_manager.py` | 持仓跟踪、盈亏计算 | +| 交易执行 | `trading_executor.py` | 下单、撤单、仓位平仓 | +| 市场数据 | `market_data.py` | 实时/历史行情获取 | +| 交易所适配 | `exchanges/` | Binance / Paper Trading 适配器 | +| 交易记录 | `trade_recorder.py` | 交易历史记录与统计 | +| 格式化输出 | `formatters.py` | 消息格式化(发送到前端) | + +--- + +## 交易所支持 + +| 交易所 | 文件 | 说明 | +|--------|------|------| +| Binance | `exchanges/binance_exchange.py` | 真实加密货币交易 | +| Paper Trading | `exchanges/paper_trading.py` | 模拟盘,不产生真实订单 | +| Base | `exchanges/base_exchange.py` | 抽象基类 | + +--- + +## 多实例架构 + +Auto Trading Agent 支持在同一 Agent 实例中运行多个独立的交易实例(每个用于不同的资产/策略): + +```python +# 多实例状态结构 +# {session_id: {instance_id: TradingInstanceData}} +self._trading_instances: Dict[str, Dict[str, TradingInstanceData]] = {} +``` + +每个实例拥有独立的: +- 配置(资产、交易对、初始资金) +- 仓位状态 +- 交易历史 + +--- + +## 技术分析指标 + +`TechnicalAnalyzer` 计算以下指标: + +| 指标 | 说明 | +|------|------| +| RSI | 相对强弱指数(超买/超卖) | +| MACD | 移动平均收敛散度(趋势跟踪) | +| 布林带 | 价格波动区间 | +| EMA | 指数移动平均 | +| 成交量分析 | 量价关系 | + +`AISignalGenerator` 使用 LLM 综合技术指标,输出交易信号(买入/卖出/持有)。 + +--- + +## 流程说明 + +### stream()(用户触发) + +用户通过聊天界面配置并启动交易实例: + +``` +用户输入:帮我对 BTC 进行自动交易,初始资金 1000 USDT,使用保守策略 + +AutoTradingAgent.stream() + │ LLM 解析 TradingRequest(资产、金额、策略) + ▼ +创建 TradingInstanceData + │ 初始化 TechnicalAnalyzer + TradingExecutor + ▼ +开始交易循环(每 X 分钟检查一次) + │ 获取市场数据 → 技术分析 → AI 信号 → 执行决策 + ▼ +流式输出交易状态更新(FilteredLineChartComponentData / FilteredCardPushNotificationComponentData) +``` + +### notify()(Agent 主动推送) + +当 `TaskPattern.RECURRING` 时,Agent 定期运行分析并主动推送通知到前端(无需用户触发)。 + +--- + +## 输出组件类型 + +| 组件类型 | 说明 | +|----------|------| +| `filtered_line_chart` | 资产价格走势图(可按时间范围过滤) | +| `filtered_card_push_notification` | 交易信号卡片(买入/卖出/持有 + 理由) | + +--- + +## 配置 + +### 环境变量 + +| 变量 | 说明 | +|------|------| +| `TRADING_PARSER_MODEL_ID` | 请求解析模型(继承 `DEFAULT_AGENT_MODEL`) | +| `OPENROUTER_API_KEY` | LLM 推理 | + +### AutoTradingConfig + +```python +class AutoTradingConfig(BaseModel): + exchange: str # "binance" / "paper" + symbol: str # 如 "BTC/USDT" + initial_capital: float # 初始资金 + strategy: str # "conservative" / "moderate" / "aggressive" + check_interval: int # 检查间隔(秒),默认 300(5 分钟) + api_key: Optional[str] # Binance API Key(真实交易时需要) + api_secret: Optional[str] +``` + +--- + +## 注意事项 + +1. **模拟盘优先**:生产环境建议先使用 `paper` 模式验证策略 +2. **资金风险**:真实交易存在亏损风险,请谨慎使用 +3. **API 限制**:Binance API 有请求频率限制,`check_interval` 不建议设置过小 +4. **无法保证盈利**:AI 信号仅供参考,不构成投资建议 diff --git a/docs/backend/agents/research-agent.md b/docs/backend/agents/research-agent.md new file mode 100644 index 000000000..ae38a764e --- /dev/null +++ b/docs/backend/agents/research-agent.md @@ -0,0 +1,129 @@ +# Research Agent(研究 Agent) + +路径:`python/valuecell/agents/research_agent/` +端口:`10014`(默认) + +Research Agent 是一个综合性的金融研究助手,整合了 SEC 文件获取、Web 搜索和向量知识库,能够回答关于上市公司基本面、监管文件、市场动态等问题。 + +--- + +## 核心功能 + +| 功能 | 工具 | 说明 | +|------|------|------| +| 定期报告分析 | `fetch_periodic_sec_filings` | 获取 10-K、10-Q 等定期报告 | +| 事件报告分析 | `fetch_event_sec_filings` | 获取 8-K、6-K 等事件型报告 | +| 网络搜索 | `web_search` | 实时搜索最新市场信息 | +| 知识库问答 | `knowledge`(向量库) | 基于预建知识库的深度问答 | + +--- + +## 模块文件 + +| 文件 | 说明 | +|------|------| +| `core.py` | `ResearchAgent` 主类实现 | +| `sources.py` | 数据源工具(SEC / Web 搜索函数) | +| `knowledge.py` | Agno 向量知识库配置 | +| `prompts.py` | LLM 系统提示和期望输出格式 | +| `schemas.py` | 数据模型 | +| `vdb.py` | 向量数据库配置 | +| `__main__.py` | A2A HTTP 服务启动入口 | + +--- + +## 实现原理 + +```python +class ResearchAgent(BaseAgent): + def __init__(self): + self.knowledge_research_agent = Agent( + model=get_model("RESEARCH_AGENT_MODEL_ID"), + tools=[ + fetch_periodic_sec_filings, # SEC 定期报告 + fetch_event_sec_filings, # SEC 事件报告 + web_search, # Web 搜索 + ], + knowledge=knowledge, # 向量知识库 + search_knowledge=True, # 自动搜索知识库 + add_history_to_context=True, # 多轮对话历史 + ) + + async def stream(self, query, conversation_id, task_id, dependencies): + response_stream = self.knowledge_research_agent.arun( + query, + stream=True, + stream_intermediate_steps=True, # 流式输出工具调用过程 + ) + async for event in response_stream: + if event.event == "RunContent": + yield streaming.message_chunk(event.content) + elif event.event == "ToolCallStarted": + yield streaming.tool_call_started(...) + elif event.event == "ToolCallCompleted": + yield streaming.tool_call_completed(...) +``` + +--- + +## SEC 工具说明 + +### fetch_periodic_sec_filings + +获取 SEC 定期报告(10-K 年报、10-Q 季报等): +- 使用 `edgar` 库访问 SEC EDGAR 数据库 +- 需要在 `.env` 中配置 `SEC_EMAIL`(SEC 要求标识请求者身份) +- 返回文件的文本内容,供 LLM 分析 + +### fetch_event_sec_filings + +获取 SEC 事件型报告(8-K 重大事件、6-K 外国私人发行人等): +- 同样使用 `edgar` 库 +- 适用于获取公司最新公告、财报发布等事件 + +--- + +## 向量知识库 + +Research Agent 配备了一个向量知识库(`knowledge.py`),可以预加载公司研究报告、行业分析等文档,支持语义检索: + +- **嵌入模型**:通过 `EMBEDDER_*` 环境变量配置 +- **存储后端**:向量数据库(pgvector 或内存) +- **检索方式**:Agent 在回答问题前自动搜索知识库 + +> **注意**:若使用 OpenRouter 作为 LLM 提供商,需要单独配置 Embedding 服务(`EMBEDDER_API_KEY`, `EMBEDDER_BASE_URL`, `EMBEDDER_MODEL_ID`),因为 OpenRouter 不支持 Embedding 模型。 + +--- + +## 使用示例 + +``` +用户:分析苹果公司 2024 年财报,关注营收增长和利润率变化 + +ResearchAgent 执行流程: +1. Tool Call: fetch_periodic_sec_filings("AAPL", "10-K", "2024") +2. 解析文件内容,提取关键财务数据 +3. Tool Call: web_search("Apple Q4 2024 earnings analysis") +4. 综合 SEC 数据和最新市场分析,生成研究报告 +5. 输出 Markdown 格式的综合分析报告 +``` + +--- + +## 配置 + +### 必需环境变量 + +| 变量 | 说明 | +|------|------| +| `RESEARCH_AGENT_MODEL_ID` | Agent 使用的 LLM 模型 ID(默认:`google/gemini-2.5-flash`) | +| `SEC_EMAIL` | SEC EDGAR API 要求的邮箱标识 | + +### 可选环境变量(向量知识库) + +| 变量 | 说明 | +|------|------| +| `EMBEDDER_API_KEY` | Embedding 服务 API Key | +| `EMBEDDER_BASE_URL` | Embedding 服务 URL | +| `EMBEDDER_MODEL_ID` | Embedding 模型 ID | +| `EMBEDDER_DIMENSION` | Embedding 向量维度(默认 1568) | diff --git a/docs/backend/core/README.md b/docs/backend/core/README.md new file mode 100644 index 000000000..4e50aa429 --- /dev/null +++ b/docs/backend/core/README.md @@ -0,0 +1,184 @@ +# 核心调度层(Core Coordinate) + +路径:`python/valuecell/core/coordinate/` + +核心调度层是 ValueCell 的"大脑",负责将用户输入转化为可执行的 Agent 任务序列,并实时流式地将结果返回给调用方。 + +--- + +## 模块概览 + +| 文件 | 类/函数 | 职责 | +|------|---------|------| +| `super_agent.py` | `SuperAgent` | 前置意图分析,决定直接回答 or 转交 Planner | +| `planner.py` | `ExecutionPlanner` | LLM 驱动的任务规划,支持 HITL | +| `orchestrator.py` | `AgentOrchestrator` | 整个生命周期的协调器 | +| `response_buffer.py` | `ResponseBuffer` | 流式 chunk 段落级聚合与稳定 ID 管理 | +| `response_router.py` | `ResponseRouter` | 事件路由,触发持久化副作用 | +| `response.py` | `ResponseFactory` | 各类 `BaseResponse` 工厂方法 | +| `models.py` | `ExecutionPlan`, `PlannerResponse` | 规划数据模型 | + +--- + +## SuperAgent + +**文件**:`super_agent.py` + +SuperAgent 是请求处理的第一道"分流器"。当用户在没有指定具体 Agent 的情况下(即对话入口是通用的 `ValueCellAgent`)提问时,SuperAgent 先行运行: + +```python +class SuperAgentDecision(str, Enum): + ANSWER = "answer" # SuperAgent 直接回答 + HANDOFF_TO_PLANNER = "handoff_to_planner" # 转交 Planner 规划 +``` + +- **工具**:配备 `Crawl4aiTools`(网页爬取),可实时获取网络信息 +- **直接回答场景**:简单问候、一般性金融知识问答 +- **转交场景**:需要调用专业 Agent(分析股票、执行交易等)时 + +```python +# SuperAgent 使用 Agno Agent + LLM 推理 +response = await self.agent.arun( + user_input.query, + session_id=user_input.meta.conversation_id, + user_id=user_input.meta.user_id, + add_history_to_context=True, +) +# 返回 SuperAgentOutcome(decision, answer_content, enriched_query, reason) +``` + +--- + +## ExecutionPlanner + +**文件**:`planner.py` + +Planner 将用户的自然语言需求转化为结构化的 `ExecutionPlan`(由多个 `Task` 组成)。 + +### 核心流程 + +```python +plan = await planner.create_plan(user_input, user_input_callback, thread_id) +``` + +1. 创建 Agno Agent(携带 `tool_get_enabled_agents` 工具) +2. Agent 调用 `tool_get_enabled_agents()` 获取所有可用 Agent 的 AgentCard +3. LLM 根据用户需求和 Agent 能力列表,输出 `PlannerResponse`(JSON Schema 强制格式) +4. 将 `PlannerResponse.tasks` 转换为 `Task` 对象列表 + +### HITL 支持 + +若 Agent 运行时 `run_response.is_paused == True`(Agno 的 HITL 机制),Planner 会: +1. 遍历 `tools_requiring_user_input`,每个字段创建一个 `UserInputRequest` +2. 调用 `user_input_callback(request)` 注册到 Orchestrator +3. `await request.wait_for_response()` 阻塞直到用户回复 +4. 填入用户回复后调用 `agent.continue_run()` 恢复执行 + +### PlannerResponse 结构 + +```python +class PlannerResponse(BaseModel): + adequate: bool # 信息是否充分 + reason: str # 判断依据 + tasks: List[TaskItem] # 任务列表 + +class TaskItem(BaseModel): + agent_name: str # 目标 Agent 名称 + query: str # 发给该 Agent 的具体问题 + pattern: TaskPattern # ONCE / RECURRING +``` + +--- + +## AgentOrchestrator + +**文件**:`orchestrator.py` + +Orchestrator 是整个系统的中枢,管理从用户输入到最终响应的完整生命周期。 + +### 主入口 + +```python +async def process_user_input(user_input: UserInput) -> AsyncGenerator[BaseResponse, None]: +``` + +#### 执行路径 + +``` +用户输入 + │ + ├── conversation 不存在 → create_conversation() → yield conversation_started + │ + ├── conversation.status == REQUIRE_USER_INPUT + │ └── _handle_conversation_continuation() (HITL 恢复路径) + │ + └── 普通新请求 + └── _handle_new_request() + │ + ├── [无 target_agent_name] SuperAgent.run() + │ ├── ANSWER → 直接 yield 回答 + │ └── HANDOFF_TO_PLANNER → 更新 query,继续规划 + │ + └── ExecutionPlanner.create_plan() + └── _execute_plan_with_input_support(plan) + └── 串行执行每个 Task + └── _execute_task_with_input_support(task) +``` + +### 关键状态管理 + +| 属性 | 类型 | 作用 | +|------|------|------| +| `user_input_manager` | `UserInputManager` | 管理等待用户回复的 HITL 请求 | +| `_execution_contexts` | `Dict[str, ExecutionContext]` | 被 HITL 暂停的执行上下文,TTL 1 小时 | +| `conversation_manager` | `ConversationManager` | 对话元数据 + 消息条目存储 | +| `task_manager` | `TaskManager` | Task 状态机管理 | +| `agent_connections` | `RemoteConnections` | Remote Agent 连接池 | +| `_response_buffer` | `ResponseBuffer` | 流式 chunk 段落聚合 | + +### 公开 API + +| 方法 | 说明 | +|------|------| +| `process_user_input()` | 处理新用户输入(SSE 流) | +| `provide_user_input()` | 提供 HITL 用户回复 | +| `has_pending_user_input()` | 检查是否有等待回复的请求 | +| `get_user_input_prompt()` | 获取等待回复的提示文字 | +| `close_conversation()` | 关闭对话并清理资源 | +| `get_conversation_history()` | 获取持久化的历史记录 | +| `cleanup()` | 清理过期上下文,停止所有连接 | + +--- + +## ResponseBuffer + +**文件**:`response_buffer.py` + +将流式 `message_chunk` 事件聚合为"段落"级别再持久化,避免 SQLite 产生大量碎片化记录,同时为前端提供稳定的 `item_id`(用于流式打字效果的原地更新)。 + +### 工作原理 + +``` +收到 message_chunk(content="Hello") + │ annotate(response) → 返回带稳定 item_id 的 response + ▼ +BufferEntry.parts.append("Hello") + +收到 message_chunk(content=" world") + │ annotate(response) → 返回同一 item_id + ▼ +BufferEntry.parts.append(" world") + +task_completed → flush_task() + │ + ▼ +SaveItem(item_id, content="Hello world") → 写入 SQLite +``` + +--- + +## 相关文档 + +- [Agent 连接管理](agent-connection.md) +- [对话管理](conversation.md) +- [任务管理](task.md) diff --git a/docs/backend/core/agent-connection.md b/docs/backend/core/agent-connection.md new file mode 100644 index 000000000..57e1b18a0 --- /dev/null +++ b/docs/backend/core/agent-connection.md @@ -0,0 +1,195 @@ +# Agent 连接管理 + +路径:`python/valuecell/core/agent/` + +该模块负责管理后端与各 Remote Agent 之间的连接,包括 Agent 描述解析、HTTP 客户端连接、通知监听器等。 + +--- + +## 模块文件 + +| 文件 | 类/函数 | 职责 | +|------|---------|------| +| `card.py` | `parse_local_agent_card_dict()`, `find_local_agent_card_by_agent_name()` | 解析 JSON AgentCard 文件 | +| `client.py` | `AgentClient` | A2A 协议 HTTP 客户端 | +| `connect.py` | `RemoteConnections`, `AgentContext` | Agent 连接池管理 | +| `listener.py` | `NotificationListener` | 接收远端 Agent 主动推送的监听器 | +| `decorator.py` | `@agent_handler` | 将普通函数包装为 Agent 处理器 | +| `responses.py` | `streaming.*` | Agent 响应工厂方法 | + +--- + +## AgentCard(Agent 描述卡片) + +**文件**:`card.py`,配置文件:`python/configs/agent_cards/*.json` + +AgentCard 是 A2A 协议定义的 Agent 元数据对象,描述 Agent 的能力、技能、URL 等信息。 + +### JSON 格式示例 + +```json +{ + "name": "InvestmentResearchAgent", + "display_name": "Investment Research Agent", + "url": "http://localhost:10014/", + "description": "综合投研报告生成...", + "capabilities": { + "streaming": true, + "push_notifications": false + }, + "skills": [ + { + "id": "research", + "name": "Research", + "description": "分析股票基本面...", + "examples": ["分析 AAPL 的财务状况"], + "tags": ["research", "fundamentals"] + } + ], + "enabled": true, + "metadata": { + "version": "1.0.0", + "author": "ValueCell Team", + "tags": ["research"] + } +} +``` + +### 解析逻辑 + +`parse_local_agent_card_dict()` 在解析时会: +1. 移除非标准字段(`enabled`, `metadata`, `display_name`) +2. 填充缺省字段(`description`, `capabilities`, `version` 等) +3. 使用 Pydantic `AgentCard.model_validate()` 校验 + +### 加载时机 + +`RemoteConnections._load_remote_contexts()` 在首次需要 Agent 时(懒加载)扫描 `python/configs/agent_cards/` 目录: +- 只加载 `enabled: true` 的 Agent +- 只加载有有效 `url` 的 Agent + +--- + +## AgentClient + +**文件**:`client.py` + +封装了 A2A SDK 的 HTTP 客户端,负责向远端 Agent 发送消息并接收流式响应。 + +```python +client = AgentClient(url="http://localhost:10014/", push_notification_url=None) +await client.ensure_initialized() # 连接并获取 AgentCard + +# 发送消息(流式) +async for remote_task, event in await client.send_message( + query, + conversation_id=conversation_id, + metadata=metadata, + streaming=True, +): + # event: TaskStatusUpdateEvent | TaskArtifactUpdateEvent | None + ... +``` + +--- + +## RemoteConnections(连接池) + +**文件**:`connect.py` + +统一管理所有 Remote Agent 的连接状态,支持懒加载和并发安全的连接建立。 + +### AgentContext 数据结构 + +```python +@dataclass +class AgentContext: + name: str + url: Optional[str] + local_agent_card: Optional[AgentCard] + client: Optional[AgentClient] # HTTP 客户端 + listener_task: Optional[asyncio.Task] # 通知监听器任务 + listener_url: Optional[str] # 监听器 URL +``` + +### 关键方法 + +| 方法 | 说明 | +|------|------| +| `start_agent(agent_name)` | 连接到 Agent(懒加载,带锁防并发) | +| `get_client(agent_name)` | 获取 AgentClient(按需启动) | +| `get_agent_card(agent_name)` | 获取 AgentCard | +| `get_all_agent_cards()` | 获取所有已加载的 AgentCard(供 Planner 使用) | +| `list_available_agents()` | 列出所有已配置的 Agent 名称 | +| `list_running_agents()` | 列出当前已连接的 Agent | +| `stop_agent(agent_name)` | 断开连接并清理资源 | +| `stop_all()` | 停止所有连接 | + +### 连接建立流程 + +``` +start_agent("ResearchAgent") + │ 获取 agent_lock(防并发重复连接) + ▼ +_get_or_create_context("ResearchAgent") + │ 懒加载:扫描 JSON 文件填充 _contexts + ▼ +_ensure_client(ctx) + │ AgentClient(url) → ensure_initialized() + │ 获取 Agent 远端的 AgentCard(含 capabilities) + ▼ +[可选] _ensure_listener(ctx) + │ 若 Agent 支持 push_notifications + ▼ +返回 AgentCard +``` + +--- + +## NotificationListener + +**文件**:`listener.py` + +当 Remote Agent 配置了 `push_notifications: true` 时,Core 层会启动一个轻量级 HTTP 服务器监听推送通知。 + +```python +listener = NotificationListener( + host="localhost", + port=5000, + notification_callback=my_callback, +) +listener_task = asyncio.create_task(listener.start_async()) +# 监听 http://localhost:5000/notify +``` + +--- + +## Agent 装饰器 + +**文件**:`decorator.py` + +将一个普通的 `BaseAgent` 子类包装为 A2A 兼容的 HTTP 服务,使其可以被 `AgentClient` 调用: + +```python +from valuecell.core.agent.decorator import create_agent_app + +app = create_agent_app(agent_instance, agent_card) +# 启动 uvicorn 运行 app 即可提供 A2A 兼容接口 +``` + +--- + +## 响应工厂(streaming 模块) + +**文件**:`responses.py` + +Agent 实现中用于生成标准化 `StreamResponse` 的工厂函数: + +```python +from valuecell.core.agent.responses import streaming + +yield streaming.message_chunk("Hello, ") # 文本 chunk +yield streaming.tool_call_started(id, name) # 工具调用开始 +yield streaming.tool_call_completed(result, id, name) # 工具调用完成 +yield streaming.done() # 流结束信号 +``` diff --git a/docs/backend/core/conversation.md b/docs/backend/core/conversation.md new file mode 100644 index 000000000..4021bacd0 --- /dev/null +++ b/docs/backend/core/conversation.md @@ -0,0 +1,161 @@ +# 对话管理(Conversation) + +路径:`python/valuecell/core/conversation/` + +对话管理模块负责维护对话元数据和消息条目的存储,提供内存和 SQLite 两种后端实现。 + +--- + +## 模块文件 + +| 文件 | 类 | 职责 | +|------|-----|------| +| `models.py` | `Conversation`, `ConversationStatus` | 对话元数据模型 | +| `conversation_store.py` | `ConversationStore`, `InMemoryConversationStore`, `SQLiteConversationStore` | 对话元数据持久化 | +| `item_store.py` | `ItemStore`, `InMemoryItemStore`, `SQLiteItemStore` | 消息条目持久化 | +| `manager.py` | `ConversationManager` | 高层管理接口,协调两个 Store | + +--- + +## 数据模型 + +### Conversation(对话元数据) + +```python +class Conversation(BaseModel): + conversation_id: str + user_id: str + title: Optional[str] + agent_name: Optional[str] + status: ConversationStatus # ACTIVE / INACTIVE / REQUIRE_USER_INPUT + created_at: datetime + updated_at: datetime +``` + +### ConversationStatus 状态机 + +``` +ACTIVE ──────────────────────────────────► INACTIVE + │ ▲ + │ require_user_input() │ + ▼ │ +REQUIRE_USER_INPUT ──── activate() ────────► ACTIVE +``` + +| 状态 | 说明 | +|------|------| +| `ACTIVE` | 正常活跃状态,可接收新消息 | +| `INACTIVE` | 已停用,不再接收新消息 | +| `REQUIRE_USER_INPUT` | 等待 HITL 用户输入,Orchestrator 会拒绝新的普通请求 | + +### ConversationItem(消息条目) + +```python +class ConversationItem(BaseModel): + item_id: str + role: Role # USER / AGENT / SYSTEM + agent_name: Optional[str] + event: ConversationItemEvent # 事件类型 + conversation_id: str + thread_id: Optional[str] + task_id: Optional[str] + payload: str # JSON 序列化的 ResponsePayload +``` + +--- + +## 存储后端 + +### 双后端设计 + +``` +ConversationManager + ├── conversation_store: ConversationStore + │ ├── InMemoryConversationStore(测试/开发) + │ └── SQLiteConversationStore(生产) + │ + └── item_store: ItemStore + ├── InMemoryItemStore(测试/开发) + └── SQLiteItemStore(生产) +``` + +### 生产配置(Orchestrator 初始化时) + +```python +db_path = resolve_db_path() # 从环境变量 VALUECELL_SQLITE_DB 获取 +conversation_manager = ConversationManager( + conversation_store=SQLiteConversationStore(db_path=db_path), + item_store=SQLiteItemStore(db_path=db_path), +) +``` + +--- + +## ConversationManager API + +### 对话元数据操作 + +| 方法 | 说明 | +|------|------| +| `create_conversation(user_id, title, conversation_id, agent_name)` | 创建新对话 | +| `get_conversation(conversation_id)` | 按 ID 获取对话 | +| `update_conversation(conversation)` | 更新对话元数据 | +| `delete_conversation(conversation_id)` | 删除对话及所有消息 | +| `list_user_conversations(user_id, limit, offset)` | 列出用户对话 | +| `conversation_exists(conversation_id)` | 检查对话是否存在 | + +### 状态管理 + +| 方法 | 说明 | +|------|------| +| `activate_conversation(conversation_id)` | 设置为 ACTIVE | +| `deactivate_conversation(conversation_id)` | 设置为 INACTIVE | +| `require_user_input(conversation_id)` | 设置为 REQUIRE_USER_INPUT | +| `set_conversation_status(conversation_id, status)` | 设置任意状态 | + +### 消息条目操作 + +| 方法 | 说明 | +|------|------| +| `add_item(role, event, conversation_id, ...)` | 添加消息条目 | +| `get_conversation_items(conversation_id, event, component_type)` | 查询消息历史 | +| `get_latest_item(conversation_id)` | 获取最新一条 | +| `get_item(item_id)` | 按 ID 获取单条 | +| `get_item_count(conversation_id)` | 获取消息数量 | +| `get_items_by_role(conversation_id, role)` | 按角色过滤 | + +--- + +## Payload 序列化 + +`add_item()` 接收 `ResponsePayload`(Pydantic BaseModel),自动调用 `model_dump_json(exclude_none=True)` 序列化为 JSON 字符串存储: + +```python +# 存储时 +payload_str = payload.model_dump_json(exclude_none=True) + +# 读取时(通过 ResponseFactory.from_conversation_item()) +payload_dict = json.loads(item.payload) +response = ResponseFactory.create_from_event_and_payload(item.event, payload_dict) +``` + +--- + +## 与 Orchestrator 的交互 + +Orchestrator 通过 `ResponseBuffer` 的 `flush` 机制将聚合后的内容持久化: + +```python +# Orchestrator._persist_items() +for it in items: + await self.conversation_manager.add_item( + role=it.role, + event=it.event, + conversation_id=it.conversation_id, + thread_id=it.thread_id, + task_id=it.task_id, + payload=it.payload, + item_id=it.item_id, + agent_name=it.agent_name, + ) +``` diff --git a/docs/backend/core/task.md b/docs/backend/core/task.md new file mode 100644 index 000000000..481afa201 --- /dev/null +++ b/docs/backend/core/task.md @@ -0,0 +1,113 @@ +# 任务管理(Task) + +路径:`python/valuecell/core/task/` + +Task 是 Orchestrator 派发给 Remote Agent 的原子执行单元。TaskManager 管理 Task 的状态转换和生命周期。 + +--- + +## 数据模型 + +### Task + +```python +@dataclass +class Task: + task_id: str # 自动生成的唯一 ID + conversation_id: str # 所属对话(可能是子对话) + thread_id: str # 所属 thread + user_id: str # 发起用户 + agent_name: str # 目标 Agent 名称 + query: str # 发给 Agent 的具体问题 + status: TaskStatus # 状态 + pattern: TaskPattern # 执行模式 + remote_task_ids: List[str] # 远端 A2A task ID 列表 + handoff_from_super_agent: bool # 是否由 SuperAgent 转交 +``` + +### TaskStatus 状态机 + +``` +PENDING ──► RUNNING ──► COMPLETED + │ + └──► FAILED + │ + └──► CANCELLED +``` + +| 状态 | 说明 | +|------|------| +| `PENDING` | 已创建,等待执行 | +| `RUNNING` | 正在向远端 Agent 发送请求 | +| `COMPLETED` | 远端 Agent 成功返回 | +| `FAILED` | 执行失败(网络、Agent 错误等) | +| `CANCELLED` | 手动取消(如对话关闭) | + +### TaskPattern(执行模式) + +| 模式 | 说明 | 用途 | +|------|------|------| +| `ONCE` | 执行一次 | 常规查询 | +| `RECURRING` | 周期执行 | 定期推送通知(如价格告警) | + +--- + +## TaskManager + +**文件**:`manager.py` + +内存中的 Task 状态机管理器(任务不持久化,仅在内存中跟踪生命周期)。 + +### 关键方法 + +| 方法 | 说明 | +|------|------| +| `update_task(task)` | 注册/更新 Task | +| `start_task(task_id)` | 设置为 RUNNING | +| `complete_task(task_id)` | 设置为 COMPLETED | +| `fail_task(task_id, reason)` | 设置为 FAILED | +| `cancel_task(task_id)` | 设置为 CANCELLED | +| `cancel_conversation_tasks(conversation_id)` | 取消某对话下所有活跃 Task | +| `get_task(task_id)` | 获取 Task 对象 | +| `list_tasks(conversation_id)` | 列出对话下所有 Task | + +### 并发安全 + +TaskManager 内部使用 `asyncio.Lock` 保证状态转换的原子性。 + +--- + +## Task 与 Conversation 的关系 + +- **普通模式**(`handoff_from_super_agent=False`):Task 的 `conversation_id` 与主对话相同,所有消息写入同一对话 +- **SuperAgent 转交模式**(`handoff_from_super_agent=True`):每个 Task 创建一个**新的子对话**(`generate_conversation_id()`),在前端展示为可展开/折叠的子对话区域(`subagent_conversation` 组件类型) + +``` +主对话 (conversation_id = "conv-001") + ├── thread_started + ├── subagent_conversation (start, agent="ResearchAgent", sub_conv_id="conv-002") + │ ├── [子对话 conv-002 的消息...] + │ └── subagent_conversation (end) + └── done +``` + +--- + +## 与 Orchestrator 的交互 + +```python +# 注册 Task +await self.task_manager.update_task(task) + +# 开始执行 +await self.task_manager.start_task(task_id) + +# 完成 +await self.task_manager.complete_task(task_id) + +# 失败 +await self.task_manager.fail_task(task_id, str(e)) + +# 对话关闭时取消所有 Task +await self.task_manager.cancel_conversation_tasks(conversation_id) +``` diff --git a/docs/backend/server/README.md b/docs/backend/server/README.md new file mode 100644 index 000000000..62d31a639 --- /dev/null +++ b/docs/backend/server/README.md @@ -0,0 +1,187 @@ +# HTTP 服务端(Server API) + +路径:`python/valuecell/server/` + +ValueCell 的后端 HTTP 服务基于 FastAPI 构建,提供 RESTful API 和 SSE 流式接口。 + +--- + +## 目录结构 + +``` +valuecell/server/ +├── main.py # uvicorn 启动入口 +├── api/ +│ ├── app.py # FastAPI 应用工厂 +│ ├── exceptions.py # 异常处理器 +│ ├── routers/ # 路由模块 +│ │ ├── agent.py # Agent 信息接口 +│ │ ├── agent_stream.py # SSE 流式接口 +│ │ ├── conversation.py # 对话管理接口 +│ │ ├── watchlist.py # 观察列表接口 +│ │ ├── user_profile.py # 用户 Profile 接口 +│ │ ├── i18n.py # 国际化接口 +│ │ └── system.py # 系统信息接口 +│ └── schemas/ # Pydantic 请求/响应模型 +├── config/ +│ ├── settings.py # 配置(从环境变量读取) +│ └── i18n.py # 国际化配置 +├── db/ +│ ├── connection.py # SQLAlchemy 连接 +│ ├── init_db.py # 自动建表 +│ └── models/ # ORM 模型 +│ ├── agent.py # Agent 表 +│ ├── asset.py # 资产表 +│ ├── watchlist.py # 观察列表表 +│ └── user_profile.py # 用户 Profile 表 +│ └── repositories/ # 数据库访问层 +└── services/ # 业务逻辑层 + ├── agent_stream_service.py # 调用 Orchestrator 产生 SSE + ├── agent_service.py # Agent 列表/详情服务 + ├── conversation_service.py # 对话历史服务 + ├── assets/ + │ └── asset_service.py # 资产行情服务 + └── user_profile_service.py # 用户 Profile 服务 +``` + +--- + +## API 路由总览 + +所有接口的 URL 前缀为 `/api/v1`。 + +### Agent 相关 + +| 方法 | 路径 | 说明 | +|------|------|------| +| `GET` | `/api/v1/agents` | 获取所有可用 Agent 列表 | +| `GET` | `/api/v1/agents/{agent_name}` | 获取单个 Agent 详情 | +| `POST` | `/api/v1/agents/stream` | **SSE 流式对话接口**(主要接口) | + +### 对话历史 + +| 方法 | 路径 | 说明 | +|------|------|------| +| `GET` | `/api/v1/conversations` | 获取用户对话列表 | +| `GET` | `/api/v1/conversations/{conversation_id}` | 获取单个对话详情 | +| `GET` | `/api/v1/conversations/{conversation_id}/history` | 获取对话历史消息 | +| `DELETE` | `/api/v1/conversations/{conversation_id}` | 删除对话 | + +### 观察列表 + +| 方法 | 路径 | 说明 | +|------|------|------| +| `GET` | `/api/v1/watchlist` | 获取用户观察列表 | +| `POST` | `/api/v1/watchlist` | 创建观察列表 | +| `POST` | `/api/v1/watchlist/items` | 添加股票到观察列表 | +| `DELETE` | `/api/v1/watchlist/items/{ticker}` | 从观察列表移除 | +| `GET` | `/api/v1/watchlist/prices` | 批量获取观察列表价格 | +| `GET` | `/api/v1/stocks/search` | 搜索股票/资产 | +| `GET` | `/api/v1/stocks/{ticker}` | 获取资产详情 | +| `GET` | `/api/v1/stocks/{ticker}/sparkline` | 获取 Sparkline 数据 | + +### 用户 Profile + +| 方法 | 路径 | 说明 | +|------|------|------| +| `GET` | `/api/v1/user/profile` | 获取用户 Profile | +| `PUT` | `/api/v1/user/profile` | 更新用户 Profile | +| `GET` | `/api/v1/user/memory` | 获取用户 Memory 条目 | +| `DELETE` | `/api/v1/user/memory/{memory_id}` | 删除 Memory 条目 | + +### 系统与 I18n + +| 方法 | 路径 | 说明 | +|------|------|------| +| `GET` | `/` | 应用信息(name, version, environment) | +| `GET` | `/api/v1/system/health` | 健康检查 | +| `GET` | `/api/v1/i18n/languages` | 获取支持的语言列表 | +| `PUT` | `/api/v1/i18n/language` | 切换当前语言 | + +--- + +## SSE 流式接口详解 + +**路径**:`POST /api/v1/agents/stream` +**文件**:`api/routers/agent_stream.py` + +### 请求体 + +```json +{ + "query": "分析苹果公司最新财报", + "agent_name": "InvestmentResearchAgent", + "conversation_id": "conv-uuid-xxx" +} +``` + +| 字段 | 类型 | 必填 | 说明 | +|------|------|------|------| +| `query` | string | 是 | 用户输入文本 | +| `agent_name` | string | 否 | 指定目标 Agent;为空时路由到 SuperAgent | +| `conversation_id` | string | 否 | 对话 ID;为空时自动生成 | + +### 响应格式 + +响应 `Content-Type: text/event-stream`,每行格式: + +``` +data: {"event": "message_chunk", "data": {"conversation_id": "...", "thread_id": "...", "task_id": "...", "item_id": "...", "role": "agent", "payload": {"content": "Hello"}}}\n\n +``` + +### 请求生命周期 + +1. FastAPI 创建 `StreamingResponse` +2. `AgentStreamService.stream_query_agent()` 调用 `AgentOrchestrator.process_user_input()` +3. Orchestrator 生成器 yield `BaseResponse` 对象 +4. Router 将每个 `BaseResponse` 序列化为 JSON,格式化为 SSE 格式 + +--- + +## 异常处理 + +| 异常类型 | HTTP 状态码 | 说明 | +|----------|-------------|------| +| `APIException` | 4xx/5xx(可配置) | 业务逻辑异常 | +| `RequestValidationError` | 422 | Pydantic 请求校验失败 | +| `Exception`(通用) | 500 | 未预期异常 | + +--- + +## 数据库层 + +### ORM 模型(`db/models/`) + +| 表 | 模型 | 字段概要 | +|----|------|----------| +| `agents` | `AgentModel` | name, display_name, enabled, metadata, created_at | +| `assets` | `AssetModel` | ticker, asset_type, names, market_info | +| `watchlists` | `WatchlistModel` | user_id, name, tickers, is_default | +| `user_profiles` | `UserProfileModel` | user_id, language, timezone, memory | + +### Repository 层(`db/repositories/`) + +提供 CRUD 操作的封装,隔离 ORM 与 Service 层: + +```python +# 示例 +repo = WatchlistRepository(session) +watchlists = await repo.get_user_watchlists(user_id) +``` + +--- + +## 配置(`config/settings.py`) + +所有配置从环境变量读取,使用 `@lru_cache()` 缓存单例: + +```python +settings = get_settings() + +settings.API_HOST # 默认 "0.0.0.0" +settings.API_PORT # 默认 8000 +settings.API_DEBUG # 默认 false(true 时开启 /docs) +settings.DATABASE_URL # SQLite 路径 +settings.CORS_ORIGINS # 允许的 CORS 来源 +settings.LOCALE_DIR # 多语言文件目录 +``` diff --git a/docs/concepts.md b/docs/concepts.md new file mode 100644 index 000000000..985329e9e --- /dev/null +++ b/docs/concepts.md @@ -0,0 +1,196 @@ +# ValueCell 核心概念 + +本文介绍 ValueCell 中贯穿前后端的核心设计概念,理解这些概念有助于快速定位代码、扩展功能或调试问题。 + +--- + +## 1. 对话层次模型(Conversation Hierarchy) + +ValueCell 采用四层 ID 体系来唯一标识任意一条消息: + +``` +conversation_id + └── thread_id (用户每次提问产生一个 thread) + └── task_id (每个 thread 可能执行多个 task) + └── item_id (最小渲染单元:一段文字、一次工具调用等) +``` + +| 层次 | 说明 | 持久化 | +|------|------|--------| +| `conversation_id` | 与某个 Agent 的完整会话,类似"聊天室" | SQLite `conversations` 表 | +| `thread_id` | 用户一次提问到回复完毕的完整交互 | SQLite `conversation_items` 的字段 | +| `task_id` | Orchestrator 派发给 Remote Agent 的原子执行单元 | 内存 TaskManager | +| `item_id` | 最小持久化/渲染单元,对应一条 `ConversationItem` | SQLite `conversation_items` 表 | + +--- + +## 2. SSE 事件系统(Server-Sent Events) + +前后端通过 HTTP SSE(单向流)实时通信。后端的 `POST /api/v1/agents/stream` 接口返回 `text/event-stream`,每个事件格式为: + +``` +data: {"event": "", "data": {...}}\n\n +``` + +### 事件类型分类 + +#### 系统生命周期事件(`SystemResponseEvent`) +| 事件 | 触发时机 | +|------|----------| +| `conversation_started` | 新对话首次创建 | +| `thread_started` | 用户每次发消息,新 thread 开始 | +| `plan_require_user_input` | Planner 需要用户补充信息(HITL) | +| `plan_failed` | 规划失败 | +| `system_failed` | 系统级异常 | +| `done` | 整个响应结束 | + +#### 任务状态事件(`TaskStatusEvent`) +| 事件 | 触发时机 | +|------|----------| +| `task_started` | Remote Agent 开始执行 | +| `task_completed` | Remote Agent 执行完毕 | +| `task_failed` | Remote Agent 执行失败 | + +#### 流式内容事件(`StreamResponseEvent`) +| 事件 | 触发时机 | +|------|----------| +| `message_chunk` | Agent 输出文本 chunk(流式) | +| `tool_call_started` | Agent 调用工具开始 | +| `tool_call_completed` | 工具调用返回结果 | +| `reasoning_started` | Agent 开始推理 | +| `reasoning` | 推理过程中间步骤 | +| `reasoning_completed` | 推理结束 | + +#### 组件生成事件(`CommonResponseEvent`) +| 事件 | 触发时机 | +|------|----------| +| `component_generator` | Agent 生成结构化 UI 组件(报告/图表/子对话等) | + +--- + +## 3. Agent2Agent(A2A)协议 + +ValueCell 使用 Google 的 [A2A 协议](https://github.com/google/a2a-spec) 实现后端与各 Agent 之间的通信。 + +### 核心概念 +- **AgentCard**:描述 Agent 的 JSON 文件,包含 URL、capabilities、skills 等元数据,位于 `python/configs/agent_cards/*.json` +- **AgentClient**:Core 层用于向远端 Agent 发送 A2A 消息的 HTTP 客户端 +- **TaskStatusUpdateEvent**:远端 Agent 通过流式 HTTP 返回的状态更新事件 +- **Push Notifications**(可选):远端 Agent 主动推送通知到 Core 层注册的 listener 端口 + +### 通信流程 +``` +Orchestrator + │ client.send_message(query, streaming=True) + ▼ +AgentClient(HTTP POST 到 Agent URL) + │ + ▼ +Remote Agent HTTP Server + │ yields TaskStatusUpdateEvent(流式) + ▼ +AgentClient 接收并 yield (remote_task, event) + │ + ▼ +Orchestrator 根据 event 类型路由响应 +``` + +--- + +## 4. Agno Agent 框架 + +ValueCell 内部使用 [Agno](https://docs.agno.com) 框架构建 Planner、SuperAgent 和具体的业务 Agent。 + +### 关键特性 +| 特性 | 说明 | +|------|------| +| `Agent.run()` / `Agent.arun()` | 同步/异步执行,返回 `RunResponse` | +| `stream=True` | 开启流式输出 | +| `use_json_mode=True` | 强制 Agent 输出 JSON,配合 `output_schema` 做结构化解析 | +| `add_history_to_context=True` | 自动注入历史消息到 LLM 上下文 | +| `enable_session_summaries=True` | 超长对话自动摘要 | +| `tools=[...]` | 为 Agent 注册可调用工具 | +| `knowledge=...` | 向量知识库(用于 ResearchAgent) | +| `db=InMemoryDb()` | Session 持久化后端(内存) | +| `is_paused` | Human-in-the-Loop:Agent 暂停等待用户输入 | + +--- + +## 5. Human-in-the-Loop(HITL) + +当 Planner Agent 检测到信息不足或需要用户确认时,会触发 HITL 流程: + +``` +ExecutionPlanner.create_plan() + │ run_response.is_paused == True + ▼ +UserInputRequest(prompt) ← asyncio.Event 驱动的等待对象 + │ + ▼ +Orchestrator._handle_user_input_request() → UserInputManager 注册 + │ + ▼ +前端收到 plan_require_user_input 事件 → 显示提示 → 用户回复 + │ + ▼ +Orchestrator.provide_user_input(conversation_id, response) + │ → UserInputRequest.provide_response(response) → asyncio.Event.set() + ▼ +Planner 恢复执行 → 继续 agent.continue_run() +``` + +--- + +## 6. ResponseBuffer 流式缓冲 + +由于 Agent 输出是逐 token 的 chunk 流,直接存储每个 chunk 会产生大量碎片化记录。`ResponseBuffer` 负责将连续 chunk 聚合为"段落"(paragraph)级别的 `ConversationItem` 再持久化: + +- 每个"段落"分配一个稳定的 `item_id`(在流式期间不变) +- 前端可以通过相同的 `item_id` 进行原地更新(流式打字效果) +- 当 task 完成时,调用 `flush_task()` 将所有缓冲内容写入 SQLite + +--- + +## 7. 组件类型(ComponentType) + +除普通文本消息外,Agent 还可以生成结构化 UI 组件,通过 `component_generator` 事件推送到前端: + +| 类型 | 说明 | 前端渲染器 | +|------|------|-----------| +| `report` | 研究报告(Markdown 格式) | `ReportRenderer` | +| `subagent_conversation` | 子 Agent 对话展开/收起 | `ChatSectionComponent` | +| `filtered_line_chart` | 可过滤折线图(交易数据) | ECharts 图表 | +| `filtered_card_push_notification` | 卡片式推送通知 | 推送通知卡片 | + +--- + +## 8. Asset Ticker 格式 + +ValueCell 使用统一的内部 Ticker 格式:`EXCHANGE:SYMBOL` + +| 交易所 | 示例 | 适配器 | +|--------|------|--------| +| `NASDAQ` | `NASDAQ:AAPL` | YFinance | +| `NYSE` | `NYSE:JPM` | YFinance | +| `HKEX` | `HKEX:00700` | AKShare / YFinance | +| `SSE` | `SSE:601398` | AKShare | +| `SZSE` | `SZSE:000001` | AKShare | +| `BSE` | `BSE:835368` | AKShare | +| `CRYPTO` | `CRYPTO:BTC` | YFinance | + +--- + +## 9. 用户 Profile 与国际化 + +用户 Profile 包含语言、时区、Memory 等设置,每次 Agent 执行时通过 `dependencies` 注入到 Agent 上下文: + +```python +metadata[DEPENDENCIES] = { + USER_PROFILE: user_profile_data, # 用户个性化设置 + CURRENT_CONTEXT: {}, # 当前上下文(扩展预留) + LANGUAGE: get_current_language(), # 如 "en-US" / "zh-Hans" + TIMEZONE: get_current_timezone(), # 如 "America/New_York" +} +``` + +支持语言:`en_US`, `en_GB`, `zh-Hans`, `zh-Hant` diff --git a/docs/examples/README.md b/docs/examples/README.md new file mode 100644 index 000000000..f337021bf --- /dev/null +++ b/docs/examples/README.md @@ -0,0 +1,279 @@ +# 使用示例 + +本文介绍 ValueCell 的典型使用场景,帮助快速理解系统的端到端工作流程。 + +--- + +## 示例 1:与 Research Agent 对话分析股票 + +### 场景 + +用户想了解苹果公司最新财报情况。 + +### 操作步骤 + +1. 在前端访问 `/market`,找到 `Research Agent` 卡片,点击进入 +2. 在对话框输入:**"分析苹果公司(AAPL)2024年年报,关注营收和利润变化"** +3. 系统自动处理并流式输出结果 + +### 后端处理流程 + +``` +前端 POST /api/v1/agents/stream + { query: "分析苹果...", agent_name: "InvestmentResearchAgent" } + │ + ▼ +AgentOrchestrator.process_user_input() + └── 创建新 thread → yield thread_started + │ + ▼ +ExecutionPlanner(Agno Agent + LLM) + └── 解析需求 → 生成 ExecutionPlan + TaskItem: + - agent_name: "InvestmentResearchAgent" + - query: "Analyze AAPL 2024 annual report..." + │ + ▼ +RemoteConnections.start_agent("InvestmentResearchAgent") + └── A2A HTTP 连接到 localhost:10014 + │ + ▼ +ResearchAgent.stream() + ├── yield tool_call_started("fetch_periodic_sec_filings") + ├── [SEC EDGAR 获取 10-K 文件] + ├── yield tool_call_completed(result) + ├── yield tool_call_started("web_search") + ├── [搜索最新分析师报告] + ├── yield tool_call_completed(result) + └── yield message_chunk * N(流式输出分析报告) +``` + +### 前端展示 + +- 工具调用显示为可折叠的 `ToolCallRenderer` 条目(显示正在查询 SEC 文件) +- 分析报告通过 `MarkdownRenderer` 流式打字机效果输出 +- 如果 Agent 生成了报告组件(`component_type: "report"`),会在右侧显示完整报告 + +--- + +## 示例 2:使用 SuperAgent 进行通用问答 + +### 场景 + +用户不知道应该使用哪个 Agent,直接在通用入口提问。 + +### 操作步骤 + +访问 `/agent/ValueCellAgent`(通用入口),输入:**"巴菲特的价值投资原则是什么?"** + +### 处理流程 + +``` +AgentOrchestrator + └── user_input.target_agent_name == "ValueCellAgent" + │ + ▼ +SuperAgent.run(user_input) + ├── Agno Agent 分析意图 + └── 决策: ANSWER(直接回答,不需要专业 Agent) + │ + ▼ +直接输出回答(message_chunk 流) + (不经过 Planner,不调用 Remote Agent) +``` + +### 转交 Planner 的情况 + +如果用户输入:**"帮我分析一下 NVDA 最近的投资价值"** + +``` +SuperAgent.run() + └── 决策: HANDOFF_TO_PLANNER + enriched_query: "Analyze NVDA investment value including fundamentals, technicals and valuation" + │ + ▼ +ExecutionPlanner.create_plan() + └── 路由到 WenBuffettAgent 或 AswathDamodaranAgent +``` + +--- + +## 示例 3:HITL(Human-in-the-Loop)交互 + +### 场景 + +Planner 发现用户请求信息不完整,需要澄清。 + +### 操作步骤 + +用户输入:**"帮我买一些股票"**(信息不完整) + +### 处理流程 + +``` +ExecutionPlanner.create_plan() + └── Agno Agent 检测到信息不足 + UserControlFlowTools 触发(或 LLM 主动请求) + │ + ▼ +UserInputRequest(prompt="请告诉我您想买哪只股票,以及投资金额?") + │ + ▼ +前端收到 plan_require_user_input 事件 + └── 显示提示:"请告诉我您想买哪只股票,以及投资金额?" + │ +用户回复: "买 BTC,投入 1000 USDT" + │ + ▼ +Orchestrator.provide_user_input(conversation_id, "买 BTC,投入 1000 USDT") + └── UserInputRequest.provide_response() → asyncio.Event.set() + │ + ▼ +Planner 恢复执行 + └── 生成完整 ExecutionPlan → 路由到 AutoTradingAgent +``` + +--- + +## 示例 4:观察列表(Watchlist)管理 + +### 场景 + +用户在首页添加股票到自选列表并查看实时行情。 + +### 操作步骤 + +1. 访问 `/home`,点击搜索图标 +2. 在 `StockSearchModal` 中搜索 "腾讯" +3. 选择 `HKEX:00700`,添加到 Watchlist + +### 前端请求流程 + +```typescript +// 1. 搜索股票 +POST /api/v1/stocks/search +{ query: "腾讯", limit: 10 } +// → AdapterManager 并行查询 AKShare + YFinance +// → 返回 [{ ticker: "HKEX:00700", names: ["腾讯控股"], ... }] + +// 2. 添加到 Watchlist +POST /api/v1/watchlist/items +{ ticker: "HKEX:00700" } +// → WatchlistRepository 写入 SQLite + +// 3. 首页展示 +GET /api/v1/watchlist/prices +// → AdapterManager.get_multiple_prices(["HKEX:00700", ...]) +// → 并行从 AKShare 获取所有价格 +// → 返回 { "HKEX:00700": { current_price: 385.0, change_percent: 1.2, ... } } +``` + +### 实时数据刷新 + +前端使用 TanStack Query 定时刷新: +```typescript +const { data } = useQuery({ + queryKey: ["watchlist-prices"], + queryFn: stockApi.getWatchlistPrices, + refetchInterval: 60 * 1000, // 每分钟刷新 +}); +``` + +--- + +## 示例 5:查看对话历史 + +### 场景 + +用户重新打开页面,想继续之前与 Research Agent 的对话。 + +### 处理流程 + +``` +前端加载 /agent/InvestmentResearchAgent + │ + ▼ +GET /api/v1/conversations?agent_name=InvestmentResearchAgent + → 返回对话列表(按 updated_at 排序) + │ +用户点击历史对话 "conv-001" + │ + ▼ +GET /api/v1/conversations/conv-001/history + → 后端调用 AgentOrchestrator.get_conversation_history(conv-001) + → 从 SQLite 读取 ConversationItem 列表 + → 通过 ResponseFactory 转换为 BaseResponse 列表 + → 返回 SSEData[] 格式 + │ + ▼ +前端 dispatchAgentStoreHistory(conversationId, history) + → batchUpdateAgentConversationsStore() 批量重建状态树 + │ + ▼ +ChatThreadArea 渲染完整历史对话 +``` + +--- + +## 示例 6:添加自定义 Agent + +### 场景 + +开发者想要添加一个新的 Agent(如"技术分析 Agent")。 + +### 实现步骤 + +**第 1 步**:实现 Agent 类 + +```python +# python/valuecell/agents/my_agent/core.py +from valuecell.core.types import BaseAgent, StreamResponse +from valuecell.core.agent.responses import streaming + +class MyTechnicalAgent(BaseAgent): + async def stream(self, query, conversation_id, task_id, dependencies): + # 你的业务逻辑 + yield streaming.message_chunk("分析中...") + # ... 调用技术指标工具 + yield streaming.message_chunk("RSI: 65,处于中性区间") + yield streaming.done() +``` + +**第 2 步**:创建启动脚本 + +```python +# python/valuecell/agents/my_agent/__main__.py +import uvicorn +from valuecell.core.agent.decorator import create_agent_app +from .core import MyTechnicalAgent + +agent = MyTechnicalAgent() +app = create_agent_app(agent, agent_card) # agent_card 从 JSON 文件加载 + +if __name__ == "__main__": + uvicorn.run(app, host="0.0.0.0", port=10020) +``` + +**第 3 步**:添加 Agent Card + +```json +// python/configs/agent_cards/my_technical_agent.json +{ + "name": "MyTechnicalAgent", + "display_name": "Technical Analysis Agent", + "url": "http://localhost:10020/", + "description": "专业的技术分析...", + "skills": [...], + "enabled": true +} +``` + +**第 4 步**:在 `start.sh` 添加启动命令 + +```bash +uv run python -m valuecell.agents.my_agent & +``` + +**第 5 步**:重启服务 + +Planner 会自动发现新 Agent,并在适合的场景下路由请求到该 Agent。 diff --git a/docs/frontend/agent-chat/README.md b/docs/frontend/agent-chat/README.md new file mode 100644 index 000000000..f5be8c43b --- /dev/null +++ b/docs/frontend/agent-chat/README.md @@ -0,0 +1,173 @@ +# Agent 对话界面组件 + +路径:`frontend/src/app/agent/components/chat-conversation/` + +Agent 对话界面是 ValueCell 最核心的前端模块,负责展示与 Agent 的实时流式对话。 + +--- + +## 组件树 + +``` +ChatConversationArea(入口,包裹 MultiSectionProvider) +├── ChatConversationHeader -- Agent 头像、名称、状态 +├── ChatWelcomeScreen -- 无消息时的欢迎页 +├── ChatThreadArea -- 对话主体(线程列表) +│ └── ChatItemArea(每个 thread) +│ └── [各类消息渲染器] +│ ├── MarkdownRenderer -- 普通文本 +│ ├── ReportRenderer -- 研究报告 +│ ├── ToolCallRenderer -- 工具调用日志 +│ ├── SecFeedRenderer -- SEC 报告 +│ ├── ModelTradeRenderer -- 模型交易信号 +│ └── ChatConversationRenderer -- 子对话展开 +├── ChatInputArea -- 输入框 + 发送按钮 +├── ChatSectionComponent(右侧栏) -- 特殊组件侧边展示(如交易图表) +│ └── [SectionComponentType 渲染器] +└── ChatMultiSectionComponent(详情视图)-- 点击展开的详情区 +``` + +--- + +## 各组件说明 + +### ChatConversationArea + +**文件**:`chat-conversation-area.tsx` + +顶层容器,包裹 `MultiSectionProvider`,根据是否有消息历史决定显示欢迎页还是对话区: + +```typescript +const hasMessages = currentConversation?.threads && + Object.keys(currentConversation.threads).length > 0; + +if (!hasMessages) { + return ; +} + +return ( +
+
{/* 主区域 */} + + + +
+ {/* 右侧特殊组件区域(如折线图) */} + {sections && Object.entries(sections).map(([type, items]) => + + )} + {/* 详情弹出区 */} + {currentSection && } +
+); +``` + +### ChatThreadArea + +**文件**:`chat-thread-area.tsx` + +渲染所有 thread(按时间顺序),每个 thread 包含一次完整的问答交互: +- 显示用户问题(`thread_started` 事件的 payload) +- 显示 Agent 回复(各 task 的消息流) +- 流式输出时显示 `ChatStreamingIndicator`(打字中动画) + +### ChatItemArea + +**文件**:`chat-item-area.tsx` + +渲染单个 thread 内的所有消息条目(`ChatItem[]`),按 `component_type` 分发到对应渲染器: + +```typescript +switch (item.component_type) { + case "message_chunk": return ; + case "report": return ; + case "tool_call_started": + case "tool_call_completed": return ; + case "sec_feed": return ; + case "subagent_conversation": return ; + case "filtered_line_chart": return ; + // ... +} +``` + +### ChatInputArea + +**文件**:`chat-input-area.tsx` + +消息输入框,支持: +- 多行文本输入(`ScrollTextarea`) +- `Enter` 发送,`Shift+Enter` 换行 +- `disabled` 状态(流式输出期间禁止发送) +- `variant="chat"` 和 `variant="welcome"` 两种外观 + +### ChatSectionComponent + +**文件**:`chat-section-component.tsx` + +右侧特殊组件区域,渲染 `SectionComponentType` 类型的消息(如 `filtered_line_chart`、`filtered_card_push_notification`)。点击某个条目可在 `ChatMultiSectionComponent` 中展开详情。 + +### ChatWelcomeScreen + +**文件**:`chat-welcome-screen.tsx` + +首次进入 Agent 对话时的欢迎页,包含 Agent 介绍和输入框。 + +--- + +## 渲染器(Renderers) + +路径:`frontend/src/components/valuecell/renderer/` + +| 渲染器 | 文件 | 适用场景 | +|--------|------|----------| +| `MarkdownRenderer` | `markdown-renderer.tsx` | 普通文本,支持 GFM Markdown | +| `ReportRenderer` | `report-renderer.tsx` | Agent 生成的结构化研究报告(带标题、时间戳) | +| `ToolCallRenderer` | `tool-call-renderer.tsx` | 工具调用开始/完成的日志(可折叠) | +| `SecFeedRenderer` | `sec-feed-renderer.tsx` | SEC 报告摘要 | +| `ModelTradeRenderer` | `model-trade-renderer.tsx` | 单条交易信号卡片 | +| `ModelTradeTableRenderer` | `model-trade-table-renderer.tsx` | 多条交易信号表格 | +| `ChatConversationRenderer` | `chat-conversation-renderer.tsx` | 子 Agent 对话(可展开/折叠)| +| `UnknownRenderer` | `unknown-renderer.tsx` | 未知类型的兜底渲染 | + +--- + +## 流式渲染机制 + +### 数据流 + +``` +后端 SSE chunk → useSSE() hook → dispatchAgentStore(action) + │ + ▼ +updateAgentConversationsStore(store, action) + │ 根据 event 类型将数据插入/更新正确位置 + ▼ +agentStore[conversationId].threads[threadId].tasks[taskId].items[] + │ + ▼ +React 重新渲染 ChatThreadArea → ChatItemArea → 渲染器 +``` + +### item_id 稳定性 + +流式输出期间,同一段落的所有 `message_chunk` 拥有相同的 `item_id`,前端通过 `item_id` 找到已存在的 `ChatItem` 并追加内容(而不是创建新条目),实现"打字机效果"。 + +--- + +## MultiSectionProvider + +**文件**:`frontend/src/provider/multi-section-provider.tsx` + +管理右侧详情区的展示状态,当用户点击某个 Section 条目时,`currentSection` 更新并触发 `ChatMultiSectionComponent` 展示详情: + +```typescript +const { currentSection, setCurrentSection } = useMultiSection(); +``` + +--- + +## Agent 头像(AgentAvatar) + +**文件**:`components/valuecell/agent-avatar.tsx` + +根据 `agent_name` 映射到对应的头像图片(`assets/png/agents/*.png`),包含每位投资大师的头像图片。 diff --git a/docs/frontend/pages/README.md b/docs/frontend/pages/README.md new file mode 100644 index 000000000..1d3f46001 --- /dev/null +++ b/docs/frontend/pages/README.md @@ -0,0 +1,165 @@ +# 前端页面(Pages) + +路径:`frontend/src/app/` + +ValueCell 前端基于 React Router v7 构建,使用文件式路由(file-based routing)。 + +--- + +## 路由配置 + +**文件**:`frontend/src/routes.ts` + +```typescript +export default [ + index("app/redirect-to-home.tsx"), // "/" → 重定向到 /home + + ...prefix("/home", [ + layout("app/home/_layout.tsx", [ + index("app/home/home.tsx"), // "/home" → 行情主页 + route("/stock/:stockId", "app/home/stock.tsx"), // "/home/stock/NASDAQ:AAPL" → 股票详情 + ]), + ]), + + route("/market", "app/market/agents.tsx"), // "/market" → Agent 市场 + + ...prefix("/agent", [ + route("/:agentName", "app/agent/chat.tsx"), // "/agent/ResearchAgent" → 对话页面 + route("/:agentName/config", "app/agent/config.tsx"), // 配置页 + ]), + + ...prefix("/setting", [ + layout("app/setting/_layout.tsx", [ + index("app/setting/memory.tsx"), // "/setting" → 设置/Memory + ]), + ]), +] +``` + +--- + +## 各页面说明 + +### 1. Home(行情主页)`/home` + +**文件**:`app/home/home.tsx`,`app/home/_layout.tsx` + +**功能**: + +- **Sparkline 股票列表**(`SparklineStockList`):展示用户 Watchlist 中的股票,每个卡片显示名称、价格、涨跌幅和迷你折线图 +- **股票详情列表**(`StockDetailsList`):展示更详细的行情信息 +- **股票搜索**(`StockSearchModal`):搜索并添加股票到 Watchlist +- **Agent 建议**(`AgentSuggestionsList`):推荐可用的 Agent + +**侧边栏**(`AppSidebar`):导航菜单 + 对话历史列表 + +#### 数据请求 + +```typescript +// 使用 TanStack Query 获取 Watchlist 数据 +const { data: watchlist } = useQuery({ + queryKey: ["watchlist"], + queryFn: () => stockApi.getWatchlist(), +}); + +// 使用自定义 Hook 获取 Sparkline 数据 +const { sparklineStocks } = useSparklineStocks(watchlistTickers); +``` + +--- + +### 2. Stock 详情页 `/home/stock/:stockId` + +**文件**:`app/home/stock.tsx` + +**功能**: + +- 展示单只股票的详细行情(价格、涨跌幅、成交量等) +- K 线图 / 折线图展示历史价格 +- 基本信息(公司简介、行业、市值等) + +--- + +### 3. Market(Agent 市场)`/market` + +**文件**:`app/market/agents.tsx` + +**功能**: + +- 展示所有可用的 Agent 卡片列表(`AgentCard`) +- 每个卡片显示 Agent 名称、描述、能力标签 +- 点击进入对应的 Agent 对话页面 + +#### 数据请求 + +```typescript +// 从后端获取 Agent 列表 +const { data: agents } = useQuery({ + queryKey: ["agents"], + queryFn: () => agentApi.getAgents(), +}); +``` + +--- + +### 4. Agent 对话页 `/agent/:agentName` + +**文件**:`app/agent/chat.tsx` + +**功能**: + +- 与指定 Agent 进行实时流式对话 +- 展示对话历史(从后端恢复) +- 流式渲染 Agent 的回复(文本/组件/工具调用等) +- 支持多种消息类型的渲染(Markdown/报告/折线图等) + +**子组件详见**:[Agent 对话界面文档](../agent-chat/README.md) + +--- + +### 5. Agent 配置页 `/agent/:agentName/config` + +**文件**:`app/agent/config.tsx` + +**功能**: + +- 配置特定 Agent 的参数(如 AutoTradingAgent 的交易对、资金等) + +--- + +### 6. Setting(设置页)`/setting` + +**文件**:`app/setting/memory.tsx`,`app/setting/_layout.tsx` + +**功能**: + +- **Memory 管理**:查看和删除 Agent 记忆的用户偏好信息 +- 每条 Memory 显示为 `MemoryItemCard` + +--- + +## 共享布局组件 + +### AppSidebar(`components/valuecell/app-sidebar.tsx`) + +全局侧边栏,包含: +- 应用导航(Home / Market / Setting) +- 当前 Agent 的对话历史列表(`AppConversationSheet`) +- 菜单项(`agent-menus.tsx`, `stock-menus.tsx`) + +### AppConversationSheet(`components/valuecell/app-conversation-sheet.tsx`) + +侧边栏中展开的对话历史面板,显示历史对话列表,支持: +- 切换历史对话 +- 删除历史对话 + +--- + +## Skeleton 加载态 + +所有页面均有对应的骨架屏(Skeleton)状态,防止数据加载期间页面空白: + +| 骨架屏 | 文件 | 对应页面 | +|--------|------|----------| +| `SparklineStockListSkeleton` | `skeleton/sparkline-stock-list-skeleton.tsx` | Home 行情列表 | +| `AgentMarketSkeleton` | `skeleton/agent-market-skeleton.tsx` | Market 页 | diff --git a/docs/frontend/state/README.md b/docs/frontend/state/README.md new file mode 100644 index 000000000..d91269ae2 --- /dev/null +++ b/docs/frontend/state/README.md @@ -0,0 +1,255 @@ +# 前端状态管理与 SSE 通信 + +路径:`frontend/src/store/`, `frontend/src/lib/`, `frontend/src/hooks/` + +--- + +## 整体状态架构 + +``` +SSEClient(lib/sse-client.ts) + │ 解析后端 SSE 事件 + ▼ +useSSE hook(hooks/use-sse.ts) + │ 处理连接状态、错误 + ▼ +useAgentStore(store/agent-store.ts) + │ Zustand 全局状态 + │ dispatchAgentStore(action: SSEData) + ▼ +updateAgentConversationsStore(lib/agent-store.ts) + │ 纯函数:将 SSE 事件更新到状态树 + ▼ +AgentConversationsStore 状态树 + │ { [conversationId]: ConversationView } + ▼ +React 组件订阅 → 重新渲染 +``` + +--- + +## SSEClient(`lib/sse-client.ts`) + +基于 `fetch` + `ReadableStream` 实现的 SSE 客户端,支持自定义 Headers(解决原生 `EventSource` 不支持 POST 和自定义 Headers 的局限)。 + +### 特性 + +- **POST 请求**:支持发送 JSON body(原生 EventSource 仅支持 GET) +- **自定义 Headers**:支持认证 Token 等 +- **连接状态管理**:`CONNECTING / OPEN / CLOSED` 三态 +- **超时处理**:握手超时(默认 30 秒) +- **容错解析**:使用 `best-effort-json-parser` 解析不完整 JSON(流式传输中可能出现截断) + +### 使用方式 + +```typescript +const client = new SSEClient( + { + url: "/api/v1/agents/stream", + timeout: 30000, + }, + { + onData: (data: SSEData) => { + store.dispatchAgentStore(data); + }, + onOpen: () => console.log("Connected"), + onClose: () => console.log("Closed"), + onError: (err) => console.error(err), + } +); + +await client.connect(JSON.stringify({ query, agent_name, conversation_id })); + +// 关闭连接 +client.close(); +// 清理资源 +client.destroy(); +``` + +--- + +## useSSE Hook(`hooks/use-sse.ts`) + +封装 `SSEClient`,提供 React 友好的 API: + +```typescript +const { isStreaming, sendMessage } = useSSE({ + url: "/api/v1/agents/stream", + agentName: "ResearchAgent", + conversationId: "conv-xxx", + onData: (data) => dispatch(data), +}); + +// 发送消息(建立 SSE 连接) +await sendMessage("分析苹果公司财报"); +``` + +### 状态说明 + +| 状态 | 说明 | +|------|------| +| `isStreaming` | 是否正在接收流(控制输入框 disabled 状态)| + +--- + +## useAgentStore(`store/agent-store.ts`) + +Zustand store,管理所有对话的状态树。 + +### 状态结构 + +```typescript +interface AgentStoreState { + agentStore: AgentConversationsStore; // { [conversationId]: ConversationView } + curConversationId: string; // 当前激活的对话 ID +} + +// AgentConversationsStore 的数据形状 +{ + "conv-001": { + threads: { + "thread-001": { + tasks: { + "task-001": { + items: [ChatItem, ChatItem, ...] + } + } + } + }, + sections: { + "filtered_line_chart": [ChatItem, ...] // 右侧组件区数据 + } + } +} +``` + +### Actions + +| Action | 说明 | +|--------|------| +| `dispatchAgentStore(action: SSEData)` | 处理单条 SSE 事件,更新状态树 | +| `dispatchAgentStoreHistory(conversationId, history)` | 批量加载历史记录(页面刷新后恢复) | +| `setCurConversationId(id)` | 切换当前对话 | +| `resetStore()` | 重置所有状态 | + +### 选择器 Hook + +```typescript +// 获取当前对话数据 +const { curConversation, curConversationId } = useCurrentConversation(); + +// 获取指定对话数据 +const conversation = useConversationById("conv-001"); + +// 获取操作方法(避免不必要渲染) +const { dispatchAgentStore, setCurConversationId } = useAgentStoreActions(); +``` + +--- + +## updateAgentConversationsStore(`lib/agent-store.ts`) + +纯函数(使用 [mutative](https://github.com/unadlib/mutative) 实现不可变更新),将 SSE 事件映射到状态树的正确位置: + +### 事件处理逻辑 + +```typescript +function updateAgentConversationsStore( + store: AgentConversationsStore, + action: SSEData +): AgentConversationsStore { + switch (action.event) { + case "thread_started": + // 在对应 conversation 下创建新 thread + store[conversationId].threads[threadId] = { tasks: {} }; + + case "task_started": + // 在 thread 下创建新 task + store[conversationId].threads[threadId].tasks[taskId] = { items: [] }; + + case "message_chunk": + // 追加文本到现有 item(相同 item_id),或创建新 item + const existingItem = findItemById(items, item_id); + if (existingItem) { + existingItem.payload.content += content; // 打字机效果 + } else { + items.push(newChatItem); + } + + case "component_generator": + // 特殊组件类型(report/filtered_line_chart 等) + // 部分类型放入 conversation.sections(右侧区域) + // 部分类型放入 thread 内联显示 + if (isSectionType(component_type)) { + store[conversationId].sections[component_type].push(item); + } else { + items.push(item); + } + + case "tool_call_started": + case "tool_call_completed": + // 工具调用条目(相同 item_id 合并为一条) + ... + } +} +``` + +--- + +## API 客户端(`lib/api-client.ts`) + +封装 `fetch` 的通用 HTTP 客户端,用于非 SSE 的 REST 请求: + +```typescript +const apiClient = createApiClient({ baseURL: "/api/v1" }); + +// GET 请求 +const agents = await apiClient.get("/agents"); + +// POST 请求 +const result = await apiClient.post("/watchlist/items", { ticker: "NASDAQ:AAPL" }); +``` + +--- + +## API 模块(`api/`) + +各业务域的 API 封装: + +| 文件 | 函数 | 说明 | +|------|------|------| +| `api/agent.ts` | `getAgents()`, `getAgent(name)` | Agent 列表/详情 | +| `api/conversation.ts` | `getConversations()`, `getHistory(id)`, `deleteConversation(id)` | 对话管理 | +| `api/stock.ts` | `searchStocks()`, `getWatchlist()`, `addToWatchlist()`, `getSparkline()` | 行情数据 | +| `api/setting.ts` | `getUserProfile()`, `updateProfile()`, `getMemory()`, `deleteMemory(id)` | 用户设置 | + +--- + +## TanStack Query 集成 + +数据请求使用 TanStack Query(`@tanstack/react-query`)管理缓存和状态: + +```typescript +// 查询示例 +const { data: agents, isLoading } = useQuery({ + queryKey: ["agents"], + queryFn: agentApi.getAgents, + staleTime: 5 * 60 * 1000, // 5分钟缓存 +}); + +// Mutation 示例 +const { mutate: addToWatchlist } = useMutation({ + mutationFn: (ticker) => stockApi.addToWatchlist(ticker), + onSuccess: () => queryClient.invalidateQueries({ queryKey: ["watchlist"] }), +}); +``` + +--- + +## 常量(`constants/`) + +| 文件 | 说明 | +|------|------| +| `constants/agent.ts` | `AGENT_COMPONENT_TYPE`(所有组件类型枚举)、`AGENT_SECTION_COMPONENT_TYPE`(右侧区域类型)、`AGENT_MULTI_SECTION_COMPONENT_TYPE`(详情区类型)| +| `constants/api.ts` | API 基础 URL 配置 | +| `constants/stock.ts` | 股票相关常量 | diff --git a/docs/notes.md b/docs/notes.md new file mode 100644 index 000000000..b32bfaf02 --- /dev/null +++ b/docs/notes.md @@ -0,0 +1,158 @@ +# 使用注意事项 + +## 环境配置 + +### 必填 API Key + +启动前必须在 `.env` 文件中配置以下关键参数,否则 Agent 无法正常工作: + +| 变量 | 说明 | 获取方式 | +|------|------|----------| +| `OPENROUTER_API_KEY` | 主要 LLM 提供商(覆盖大部分 Agent) | https://openrouter.ai | +| `SEC_EMAIL` | SEC EDGAR API 要求的邮箱标识 | 任意有效邮箱 | + +### 可选 API Key + +| 变量 | 说明 | 相关 Agent | +|------|------|-----------| +| `GOOGLE_API_KEY` | 使用 Google 原生 API(非 OpenRouter 路由) | Planner / ResearchAgent | +| `OPENAI_API_KEY` | OpenAI 原生接口 | TradingAgents(第三方) | +| `FINNHUB_API_KEY` | 金融新闻与内幕交易数据 | TradingAgents(第三方) | +| `EMBEDDER_API_KEY` / `EMBEDDER_BASE_URL` | 自定义 Embedding 接口(OpenRouter 不支持 Embedding) | ResearchAgent 知识库 | + +> **注意**:如使用 OpenRouter 且需要 ResearchAgent 的向量知识库功能,必须单独配置 Embedding 参数,因为 OpenRouter 不提供 Embedding 模型。 + +--- + +## 模型 ID 配置 + +`.env` 中各 Agent 对应的模型 ID 变量: + +| 变量 | 默认值 | 用途 | +|------|--------|------| +| `PLANNER_MODEL_ID` | `google/gemini-2.5-flash` | 任务规划 + SuperAgent | +| `RESEARCH_AGENT_MODEL_ID` | `google/gemini-2.5-flash` | 研究 Agent | +| `SEC_PARSER_MODEL_ID` | `openai/gpt-4o-mini` | SEC 文件解析 | +| `SEC_ANALYSIS_MODEL_ID` | `deepseek/deepseek-chat-v3-0324` | SEC 分析 | +| `AI_HEDGE_FUND_PARSER_MODEL_ID` | `google/gemini-2.5-flash` | AI 对冲基金解析 | +| `PRODUCT_MODEL_ID` | `anthropic/claude-haiku-4.5` | 资产搜索 Fallback | + +> 模型 ID 遵循 OpenRouter 格式:`provider/model-name`,可在 [openrouter.ai/models](https://openrouter.ai/models) 查找。 + +--- + +## Agent Card 配置 + +每个 Remote Agent 通过 `python/configs/agent_cards/*.json` 定义: + +```json +{ + "name": "BenGrahamAgent", + "url": "http://localhost:10011/", + "enabled": false, + "skills": [...] +} +``` + +- **`enabled: false`** 的 Agent 不会被 Planner 发现,也不会被连接。启用时将 `enabled` 改为 `true` +- **`url`** 必须与 Agent 实际监听的地址一致 +- Agent Card 文件修改后需要重启服务端才能生效 + +--- + +## AI Hedge Fund Agent 特殊限制 + +`python/third_party/ai-hedge-fund/` 目录下的投资大师 Agent(BenGraham/Buffett 等)目前**仅支持以下股票代码**: + +``` +AAPL, GOOGL, MSFT, NVDA, TSLA +``` + +输入其他股票代码会被 Agent 拒绝。这是上游项目的数据限制,未来版本将扩展支持范围。 + +--- + +## 自动交易 Agent 注意事项 + +- **模拟盘优先**:`AutoTradingAgent` 默认使用 Paper Trading(模拟交易)模式,不会产生真实下单 +- **真实交易需配置**:使用 Binance 真实账户需要在请求中明确指定 exchange 类型及 API Key +- **加密货币专属**:当前 AutoTradingAgent 仅支持加密货币市场(Binance),不支持股票市场 + +--- + +## 数据库 + +- 默认 SQLite 数据库路径:项目根目录 `valuecell.db` +- 可通过 `VALUECELL_SQLITE_DB` 环境变量覆盖路径(SQLAlchemy URL 格式) +- 数据库在首次启动时自动创建表结构(`init_db.py`) +- 对话历史持久化到 SQLite,重启服务后历史保留 + +--- + +## 行情数据 + +### YFinance(默认) +- 免费,无需 API Key +- 覆盖:美股、港股、加密货币 +- 中国大陆网络环境可能访问不稳定,可配置 `XUEQIU_TOKEN` 作为备用 + +### AKShare(中国市场) +- 免费,无需 API Key +- 覆盖:A 股(沪深北)、港股 +- 数据延迟约 15 分钟 + +### 搜索 Fallback +- 当两个适配器均无结果时,触发 LLM-based 搜索兜底(使用 `PRODUCT_MODEL_ID`) +- 该过程会消耗 LLM Token,高频搜索建议留意用量 + +--- + +## 端口使用 + +| 服务 | 默认端口 | 配置变量 | +|------|----------|----------| +| 前端开发服务器 | 1420 | Vite 配置 | +| 后端 API 服务 | 8000 | `API_PORT` | +| ResearchAgent | 10014 | Agent Card JSON | +| AutoTradingAgent | 10013 | Agent Card JSON | +| AI Hedge Fund Agents | 10011~10020 | Agent Card JSON | + +确保以上端口未被其他进程占用。 + +--- + +## CORS + +后端默认允许所有来源(`CORS_ORIGINS=*`)。生产部署时应修改 `CORS_ORIGINS` 为具体域名: + +```env +CORS_ORIGINS=https://your-domain.com,https://api.your-domain.com +``` + +--- + +## 日志 + +- 后端运行日志输出到 `logs/{timestamp}/*.log` +- Agent 调试模式:设置 `AGENT_DEBUG_MODE=true` 可在控制台看到 Agno Agent 的详细推理过程 +- API 接口调试文档:设置 `API_DEBUG=true` 后访问 `http://localhost:8000/docs`(Swagger UI) + +--- + +## 并发注意 + +- 同一 `conversation_id` 的请求在 Orchestrator 中是串行处理的(通过 asyncio.Lock 保护) +- 不同对话是并发处理的,无相互影响 +- 执行上下文(`ExecutionContext`)TTL 为 1 小时(`DEFAULT_CONTEXT_TIMEOUT_SECONDS=3600`),超时后 HITL 状态会被清除 + +--- + +## 前端本地开发 + +```bash +cd frontend +bun install +bun run dev # 启动 Vite 开发服务器(端口 1420) +``` + +前端通过 Vite proxy 将 `/api` 请求转发到后端 `http://localhost:8000`,因此需要先启动后端服务。