模块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 状态流转机制
在
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处理并发执行
- 实现异步状态监听器
- 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基础功能
- 完成状态序列化/反序列化
- 实现状态监听器机制
- 实现AgnoWorkflow类
- 集成条件边逻辑
- 实现进度回调机制
- 将现有节点函数转换为异步格式
- 实现工具调用适配器
- 测试节点执行流程
- 实现内存优化策略
- 添加并发执行支持
- 完成性能测试
6.2 风险评估
| 风险项 | 影响程度 | 概率 | 应对措施 |
|---|---|---|---|
| 状态同步不一致 | 高 | 中 | 实现严格的状态验证机制 |
| 性能提升不达预期 | 中 | 低 | 实现多级优化策略 |
| 异步执行错误 | 高 | 中 | 完善的错误处理和重试机制 |
| 内存泄漏 | 中 | 低 | 内存监控和自动清理机制 |
7. 总结
本迁移方案通过深入分析LangGraph和Agno的状态管理机制,设计了一套完整的迁移策略。主要优势包括:
1. 性能提升:利用Agno的零拷贝数据管道,预期内存使用减少80% 2. 并发支持:原生异步执行,支持节点级并发 3. 状态一致性:实现严格的状态验证和版本控制 4. 向后兼容:提供LangGraph API兼容层,降低迁移成本
通过本方案的实施,TradingAgents-CN项目将获得显著的性能提升和更好的可扩展性。