静态缓存页面 · 查看动态版本 · 登录
智柴网 登录 | 注册
← 返回话题
Q
QianXun @QianXun · 2025-11-24 02:17

模块1:状态管理系统迁移方案

1. 现状分析

1.1 当前LangGraph状态管理架构

TradingAgents-CN项目使用LangGraph的StateGraph模式,核心状态类定义在

# 当前LangGraph状态定义
class AgentState(TypedDict):
    messages: Annotated[List[BaseMessage], add_messages]
    company_of_interest: str
    trade_date: str
    # 各分析师报告状态
    market_report: str
    sentiment_report: str
    news_report: str
    fundamentals_report: str
    # 辩论状态
    investment_debate_state: InvestDebateState
    risk_debate_state: RiskDebateState
    # 工具调用计数
    tool_call_count: Dict[str, int]
    # 最终决策
    final_trade_decision: Dict[str, Any]
    investment_plan: Dict[str, Any]
    trader_investment_plan: Dict[str, Any]

1.2 状态流转机制

中,状态通过LangGraph的图结构进行传递:

1. 顺序执行:分析师节点按顺序执行(市场→社交媒体→新闻→基本面) 2. 条件边:使用conditional_edges进行工具调用决策 3. 消息累积:使用add_messages函数累积消息历史 4. 状态更新:每个节点更新特定字段,保持状态一致性

2. Agno状态管理架构设计

2.1 Agno状态管理原理

基于搜索结果,Agno采用去中心化执行引擎和零拷贝数据管道,状态管理具有以下特点:

1. 声明式状态定义:使用Python原生数据结构 2. 零拷贝数据传递:避免不必要的数据复制 3. 异步状态同步:支持并发状态更新 4. 内存优化:内存占用仅为LangGraph的1/50

2.2 迁移后的状态架构

# Agno状态管理类
from dataclasses import dataclass, field
from typing import Dict, List, Any, Optional
from datetime import datetime

@dataclass
class TradingAgentState:
    """交易智能体状态类 - Agno版本"""
    messages: List[Dict[str, Any]] = field(default_factory=list)
    company_of_interest: str = ""
    trade_date: str = ""
    
    # 分析师报告
    market_report: Optional[str] = None
    sentiment_report: Optional[str] = None
    news_report: Optional[str] = None
    fundamentals_report: Optional[str] = None
    
    # 辩论状态
    investment_debate_state: Dict[str, Any] = field(default_factory=dict)
    risk_debate_state: Dict[str, Any] = field(default_factory=dict)
    
    # 工具调用统计
    tool_call_count: Dict[str, int] = field(default_factory=dict)
    
    # 决策结果
    final_trade_decision: Dict[str, Any] = field(default_factory=dict)
    investment_plan: Dict[str, Any] = field(default_factory=dict)
    trader_investment_plan: Dict[str, Any] = field(default_factory=dict)
    
    # 性能指标
    performance_metrics: Dict[str, Any] = field(default_factory=dict)
    
    # 状态时间戳
    state_timestamps: Dict[str, datetime] = field(default_factory=dict)

class StateManager:
    """状态管理器 - 替代LangGraph的StateGraph"""
    
    def __init__(self):
        self._state = TradingAgentState()
        self._state_listeners = []
        self._lock = asyncio.Lock()
    
    async def update_state(self, updates: Dict[str, Any], node_name: str = None):
        """异步状态更新"""
        async with self._lock:
            # 更新状态字段
            for key, value in updates.items():
                if hasattr(self._state, key):
                    setattr(self._state, key, value)
            
            # 记录时间戳
            if node_name:
                self._state.state_timestamps[node_name] = datetime.now()
            
            # 通知监听器
            await self._notify_listeners(updates, node_name)
    
    async def get_state(self) -> TradingAgentState:
        """获取当前状态"""
        return self._state
    
    def add_listener(self, listener_func):
        """添加状态监听器"""
        self._state_listeners.append(listener_func)
    
    async def _notify_listeners(self, updates: Dict[str, Any], node_name: str):
        """通知所有监听器"""
        for listener in self._state_listeners:
            await listener(updates, node_name)

3. 迁移实现代码

3.1 核心状态管理器

# tradingagents/agents/utils/agno_state_manager.py

import asyncio
from dataclasses import dataclass, field, asdict
from typing import Dict, List, Any, Optional, Callable
from datetime import datetime
import json
from pathlib import Path

@dataclass
class AgnoAgentState:
    """Agno智能体状态类"""
    messages: List[Dict[str, Any]] = field(default_factory=list)
    company_of_interest: str = ""
    trade_date: str = ""
    
    # 分析师报告
    market_report: Optional[str] = None
    sentiment_report: Optional[str] = None
    news_report: Optional[str] = None
    fundamentals_report: Optional[str] = None
    
    # 辩论状态
    investment_debate_state: Dict[str, Any] = field(default_factory=dict)
    risk_debate_state: Dict[str, Any] = field(default_factory=dict)
    
    # 工具调用统计
    tool_call_count: Dict[str, int] = field(default_factory=dict)
    
    # 决策结果
    final_trade_decision: Dict[str, Any] = field(default_factory=dict)
    investment_plan: Dict[str, Any] = field(default_factory=dict)
    trader_investment_plan: Dict[str, Any] = field(default_factory=dict)
    
    # 性能指标
    performance_metrics: Dict[str, Any] = field(default_factory=dict)
    
    # 状态时间戳
    state_timestamps: Dict[str, datetime] = field(default_factory=dict)
    
    # 状态版本控制
    state_version: str = "1.0.0"
    
    def to_dict(self) -> Dict[str, Any]:
        """转换为字典格式"""
        result = asdict(self)
        # 处理datetime序列化
        for key, value in result['state_timestamps'].items():
            if isinstance(value, datetime):
                result['state_timestamps'][key] = value.isoformat()
        return result
    
    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> 'AgnoAgentState':
        """从字典恢复状态"""
        # 处理时间戳反序列化
        if 'state_timestamps' in data:
            for key, value in data['state_timestamps'].items():
                if isinstance(value, str):
                    data['state_timestamps'][key] = datetime.fromisoformat(value)
        return cls(**data)

class AgnoStateManager:
    """Agno状态管理器 - 替代LangGraph StateGraph"""
    
    def __init__(self, enable_persistence: bool = True, persistence_dir: str = "state_logs"):
        self._state = AgnoAgentState()
        self._state_history = []
        self._state_listeners = []
        self._lock = asyncio.Lock()
        self._enable_persistence = enable_persistence
        self._persistence_dir = Path(persistence_dir)
        
        if self._enable_persistence:
            self._persistence_dir.mkdir(parents=True, exist_ok=True)
    
    async def update_state(self, updates: Dict[str, Any], node_name: str = None):
        """异步状态更新"""
        async with self._lock:
            # 保存状态历史
            self._state_history.append({
                'timestamp': datetime.now(),
                'node_name': node_name,
                'previous_state': self._state.to_dict(),
                'updates': updates
            })
            
            # 更新状态字段
            for key, value in updates.items():
                if hasattr(self._state, key):
                    setattr(self._state, key, value)
            
            # 记录时间戳
            if node_name:
                self._state.state_timestamps[node_name] = datetime.now()
            
            # 持久化状态
            if self._enable_persistence:
                await self._persist_state(node_name)
            
            # 通知监听器
            await self._notify_listeners(updates, node_name)
    
    async def get_state(self) -> AgnoAgentState:
        """获取当前状态"""
        return self._state
    
    async def get_state_history(self, node_name: str = None) -> List[Dict[str, Any]]:
        """获取状态历史"""
        if node_name:
            return [h for h in self._state_history if h['node_name'] == node_name]
        return self._state_history.copy()
    
    def add_listener(self, listener_func: Callable[[Dict[str, Any], str], asyncio.Coroutine]):
        """添加状态监听器"""
        self._state_listeners.append(listener_func)
    
    def remove_listener(self, listener_func: Callable):
        """移除状态监听器"""
        if listener_func in self._state_listeners:
            self._state_listeners.remove(listener_func)
    
    async def reset_state(self):
        """重置状态"""
        async with self._lock:
            self._state = AgnoAgentState()
            self._state_history.clear()
    
    async def _notify_listeners(self, updates: Dict[str, Any], node_name: str):
        """通知所有监听器"""
        tasks = []
        for listener in self._state_listeners:
            try:
                task = asyncio.create_task(listener(updates, node_name))
                tasks.append(task)
            except Exception as e:
                print(f"监听器执行失败: {e}")
        
        if tasks:
            await asyncio.gather(*tasks, return_exceptions=True)
    
    async def _persist_state(self, node_name: str = None):
        """持久化状态"""
        try:
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            filename = f"state_{timestamp}_{node_name or 'general'}.json"
            filepath = self._persistence_dir / filename
            
            state_dict = self._state.to_dict()
            with open(filepath, 'w', encoding='utf-8') as f:
                json.dump(state_dict, f, ensure_ascii=False, indent=2)
                
        except Exception as e:
            print(f"状态持久化失败: {e}")

# 兼容性包装器
class LangGraphToAgnoAdapter:
    """LangGraph到Agno的适配器"""
    
    def __init__(self, state_manager: AgnoStateManager):
        self.state_manager = state_manager
    
    async def update_state(self, state_dict: Dict[str, Any], node_name: str = None):
        """兼容LangGraph的状态更新接口"""
        await self.state_manager.update_state(state_dict, node_name)
    
    async def get_state(self) -> Dict[str, Any]:
        """兼容LangGraph的状态获取接口"""
        state = await self.state_manager.get_state()
        return state.to_dict()

3.2 工作流状态管理集成

# tradingagents/graph/agno_workflow.py

from typing import Dict, Any, List, Optional, Callable
import asyncio
from datetime import datetime
from tradingagents.agents.utils.agno_state_manager import AgnoStateManager, AgnoAgentState

class AgnoWorkflow:
    """Agno工作流管理器 - 替代LangGraph的StateGraph"""
    
    def __init__(self, state_manager: Optional[AgnoStateManager] = None):
        self.state_manager = state_manager or AgnoStateManager()
        self.nodes = {}
        self.edges = {}
        self.conditional_edges = {}
        self.start_node = None
        self.end_nodes = []
        
        # 进度跟踪
        self.progress_callbacks = []
        self.node_timings = {}
        self.current_node = None
    
    def add_node(self, node_name: str, node_func: Callable):
        """添加节点"""
        self.nodes[node_name] = node_func
    
    def add_edge(self, from_node: str, to_node: str):
        """添加普通边"""
        if from_node not in self.edges:
            self.edges[from_node] = []
        self.edges[from_node].append(to_node)
    
    def add_conditional_edges(self, from_node: str, condition_func: Callable, 
                             conditions: Dict[str, str]):
        """添加条件边"""
        self.conditional_edges[from_node] = {
            'condition_func': condition_func,
            'conditions': conditions
        }
    
    def set_entry_point(self, node_name: str):
        """设置入口点"""
        self.start_node = node_name
    
    def add_progress_callback(self, callback_func: Callable[[str], None]):
        """添加进度回调"""
        self.progress_callbacks.append(callback_func)
    
    async def astream(self, initial_state: Dict[str, Any], **kwargs):
        """异步流式执行"""
        # 初始化状态
        await self.state_manager.update_state(initial_state)
        
        # 开始执行
        current_node = self.start_node
        step_count = 0
        max_steps = kwargs.get('max_steps', 100)
        
        while current_node and step_count < max_steps:
            step_count += 1
            self.current_node = current_node
            
            # 记录节点开始时间
            node_start_time = datetime.now()
            
            # 发送进度更新
            await self._send_progress_update(current_node)
            
            # 执行节点
            try:
                node_func = self.nodes.get(current_node)
                if node_func:
                    # 获取当前状态
                    current_state = await self.state_manager.get_state()
                    
                    # 执行节点函数
                    if asyncio.iscoroutinefunction(node_func):
                        result = await node_func(current_state)
                    else:
                        result = node_func(current_state)
                    
                    # 更新状态
                    if isinstance(result, dict):
                        await self.state_manager.update_state(result, current_node)
                    
                    # 记录节点结束时间
                    node_end_time = datetime.now()
                    self.node_timings[current_node] = (node_end_time - node_start_time).total_seconds()
                    
                    # 生成状态更新
                    state_update = await self.state_manager.get_state()
                    yield {current_node: state_update.to_dict()}
                
            except Exception as e:
                print(f"节点 {current_node} 执行失败: {e}")
                raise
            
            # 确定下一个节点
            next_node = await self._get_next_node(current_node)
            current_node = next_node
        
        # 生成最终状态
        final_state = await self.state_manager.get_state()
        yield {'__end__': final_state.to_dict()}
    
    async def _get_next_node(self, current_node: str) -> Optional[str]:
        """获取下一个节点"""
        # 检查条件边
        if current_node in self.conditional_edges:
            cond_info = self.conditional_edges[current_node]
            condition_func = cond_info['condition_func']
            conditions = cond_info['conditions']
            
            # 获取当前状态
            current_state = await self.state_manager.get_state()
            
            # 执行条件函数
            if asyncio.iscoroutinefunction(condition_func):
                condition_result = await condition_func(current_state)
            else:
                condition_result = condition_func(current_state)
            
            # 根据条件结果选择下一个节点
            if condition_result in conditions:
                return conditions[condition_result]
        
        # 检查普通边
        if current_node in self.edges:
            next_nodes = self.edges[current_node]
            if next_nodes:
                return next_nodes[0]  # 返回第一个后续节点
        
        return None
    
    async def _send_progress_update(self, node_name: str):
        """发送进度更新"""
        # 节点名称映射(复用现有的映射逻辑)
        node_mapping = {
            'Market Analyst': "📊 市场分析师",
            'Fundamentals Analyst': "💼 基本面分析师", 
            'News Analyst': "📰 新闻分析师",
            'Social Analyst': "💬 社交媒体分析师",
            'Bull Researcher': "🐂 看涨研究员",
            'Bear Researcher': "🐻 看跌研究员",
            'Research Manager': "👔 研究经理",
            'Trader': "💼 交易员决策",
            'Risky Analyst': "🔥 激进风险评估",
            'Safe Analyst': "🛡️ 保守风险评估",
            'Neutral Analyst': "⚖️ 中性风险评估",
            'Risk Judge': "🎯 风险经理",
        }
        
        message = node_mapping.get(node_name, f"🔍 {node_name}")
        
        # 调用所有进度回调
        for callback in self.progress_callbacks:
            try:
                if asyncio.iscoroutinefunction(callback):
                    await callback(message)
                else:
                    callback(message)
            except Exception as e:
                print(f"进度回调执行失败: {e}")
    
    def compile(self):
        """编译工作流"""
        return self

4. 迁移过程中的问题与解决方案

4.1 主要挑战

#### 4.1.1 状态同步机制差异

  • LangGraph:基于add_messages的累积式更新
  • Agno:基于字段替换的直接更新
解决方案
def convert_langgraph_updates_to_agno(updates: Dict[str, Any], current_state: Dict[str, Any]) -> Dict[str, Any]:
    """转换LangGraph更新格式到Agno格式"""
    agno_updates = {}
    
    for key, value in updates.items():
        if key == 'messages' and isinstance(value, list):
            # 处理消息累积
            if key in current_state:
                agno_updates[key] = current_state[key] + value
            else:
                agno_updates[key] = value
        else:
            # 直接替换其他字段
            agno_updates[key] = value
    
    return agno_updates

#### 4.1.2 异步执行模式

  • LangGraph:同步执行为主,支持异步流
  • Agno:原生异步执行
解决方案
  • 将所有节点函数转换为异步函数
  • 使用asyncio.gather处理并发执行
  • 实现异步状态监听器
#### 4.1.3 工具调用机制
  • LangGraph:ToolNode自动处理工具调用
  • Agno:需要手动实现工具调用逻辑
解决方案
class AgnoToolExecutor:
    """Agno工具执行器"""
    
    def __init__(self, tools: List[Callable]):
        self.tools = {tool.__name__: tool for tool in tools}
    
    async def execute_tool_calls(self, tool_calls: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
        """执行工具调用"""
        results = []
        
        for tool_call in tool_calls:
            tool_name = tool_call.get('name')
            tool_args = tool_call.get('arguments', {})
            
            if tool_name in self.tools:
                tool_func = self.tools[tool_name]
                try:
                    if asyncio.iscoroutinefunction(tool_func):
                        result = await tool_func(**tool_args)
                    else:
                        result = tool_func(**tool_args)
                    
                    results.append({
                        'tool_call_id': tool_call.get('id'),
                        'name': tool_name,
                        'result': result,
                        'status': 'success'
                    })
                except Exception as e:
                    results.append({
                        'tool_call_id': tool_call.get('id'),
                        'name': tool_name,
                        'error': str(e),
                        'status': 'error'
                    })
        
        return results

4.2 性能优化策略

#### 4.2.1 内存优化

class MemoryOptimizedStateManager(AgnoStateManager):
    """内存优化的状态管理器"""
    
    def __init__(self, max_history_size: int = 100, **kwargs):
        super().__init__(**kwargs)
        self.max_history_size = max_history_size
    
    async def update_state(self, updates: Dict[str, Any], node_name: str = None):
        """更新状态并清理旧历史"""
        await super().update_state(updates, node_name)
        
        # 清理旧的历史记录
        if len(self._state_history) > self.max_history_size:
            # 保留最近的历史记录
            self._state_history = self._state_history[-self.max_history_size//2:]

#### 4.2.2 并发执行优化

class ConcurrentAgnoWorkflow(AgnoWorkflow):
    """支持并发执行的Agno工作流"""
    
    async def execute_parallel_nodes(self, node_names: List[str]) -> Dict[str, Any]:
        """并行执行多个节点"""
        tasks = []
        
        for node_name in node_names:
            node_func = self.nodes.get(node_name)
            if node_func:
                current_state = await self.state_manager.get_state()
                
                if asyncio.iscoroutinefunction(node_func):
                    task = asyncio.create_task(node_func(current_state))
                else:
                    # 将同步函数包装为异步
                    task = asyncio.create_task(
                        asyncio.get_event_loop().run_in_executor(None, node_func, current_state)
                    )
                
                tasks.append((node_name, task))
        
        # 等待所有任务完成
        results = {}
        for node_name, task in tasks:
            try:
                result = await task
                results[node_name] = result
                
                # 更新状态
                if isinstance(result, dict):
                    await self.state_manager.update_state(result, node_name)
                    
            except Exception as e:
                print(f"并行节点 {node_name} 执行失败: {e}")
        
        return results

5. 迁移验证与测试

5.1 状态一致性验证

import asyncio
import json
from tradingagents.agents.utils.agent_states import AgentState  # 原LangGraph状态
from tradingagents.agents.utils.agno_state_manager import AgnoAgentState

async def test_state_consistency():
    """测试状态一致性"""
    
    # 创建测试数据
    test_data = {
        'messages': [{'role': 'user', 'content': 'test'}],
        'company_of_interest': 'AAPL',
        'trade_date': '2024-01-01',
        'market_report': '市场分析报告',
        'tool_call_count': {'get_market_data': 1}
    }
    
    # 测试LangGraph状态
    langgraph_state = AgentState(**test_data)
    
    # 测试Agno状态
    agno_state = AgnoAgentState(**test_data)
    
    # 验证字段一致性
    for key in test_data.keys():
        assert hasattr(langgraph_state, key), f"LangGraph状态缺少字段: {key}"
        assert hasattr(agno_state, key), f"Agno状态缺少字段: {key}"
        
        lg_value = getattr(langgraph_state, key)
        ag_value = getattr(agno_state, key)
        assert lg_value == ag_value, f"字段 {key} 值不一致: {lg_value} != {ag_value}"
    
    print("状态一致性验证通过")

if __name__ == "__main__":
    asyncio.run(test_state_consistency())

5.2 性能对比测试

import time
import asyncio
from tradingagents.graph.trading_graph import TradingAgentsGraph  # 原LangGraph实现
from tradingagents.graph.agno_workflow import AgnoWorkflow  # 新Agno实现

async def performance_comparison():
    """性能对比测试"""
    
    # 测试配置
    config = {
        'llm_provider': 'openai',
        'quick_think_llm': 'gpt-3.5-turbo',
        'deep_think_llm': 'gpt-4',
        'memory_enabled': False
    }
    
    # 测试数据
    company_name = "AAPL"
    trade_date = "2024-01-01"
    
    # LangGraph性能测试
    print("测试LangGraph性能...")
    start_time = time.time()
    
    langgraph_graph = TradingAgentsGraph(config=config)
    # 这里需要模拟执行,因为完整执行需要真实的LLM调用
    
    langgraph_time = time.time() - start_time
    print(f"LangGraph初始化时间: {langgraph_time:.4f}秒")
    
    # Agno性能测试
    print("测试Agno性能...")
    start_time = time.time()
    
    agno_workflow = AgnoWorkflow()
    # 添加测试节点
    async def test_node(state):
        return {'test_field': 'test_value'}
    
    agno_workflow.add_node('test_node', test_node)
    agno_workflow.set_entry_point('test_node')
    
    agno_time = time.time() - start_time
    print(f"Agno初始化时间: {agno_time:.4f}秒")
    
    # 性能对比
    print(f"\n性能对比:")
    print(f"LangGraph: {langgraph_time:.4f}秒")
    print(f"Agno: {agno_time:.4f}秒")
    print(f"性能提升: {langgraph_time/agno_time:.2f}x")

if __name__ == "__main__":
    asyncio.run(performance_comparison())

6. 迁移步骤与时间表

6.1 迁移步骤

1. 第1-2周:状态管理器核心实现

  • 实现AgnoStateManager基础功能
  • 完成状态序列化/反序列化
  • 实现状态监听器机制
2. 第3-4周:工作流集成
  • 实现AgnoWorkflow类
  • 集成条件边逻辑
  • 实现进度回调机制
3. 第5-6周:节点适配
  • 将现有节点函数转换为异步格式
  • 实现工具调用适配器
  • 测试节点执行流程
4. 第7-8周:性能优化
  • 实现内存优化策略
  • 添加并发执行支持
  • 完成性能测试

6.2 风险评估

风险项影响程度概率应对措施
状态同步不一致实现严格的状态验证机制
性能提升不达预期实现多级优化策略
异步执行错误完善的错误处理和重试机制
内存泄漏内存监控和自动清理机制

7. 总结

本迁移方案通过深入分析LangGraph和Agno的状态管理机制,设计了一套完整的迁移策略。主要优势包括:

1. 性能提升:利用Agno的零拷贝数据管道,预期内存使用减少80% 2. 并发支持:原生异步执行,支持节点级并发 3. 状态一致性:实现严格的状态验证和版本控制 4. 向后兼容:提供LangGraph API兼容层,降低迁移成本

通过本方案的实施,TradingAgents-CN项目将获得显著的性能提升和更好的可扩展性。

暂无表态