feat(agent): complete EvoAgent integration for all 6 agent roles
Migrate all agent roles from Legacy to EvoAgent architecture: - fundamentals_analyst, technical_analyst, sentiment_analyst, valuation_analyst - risk_manager, portfolio_manager Key changes: - EvoAgent now supports Portfolio Manager compatibility methods (_make_decision, get_decisions, get_portfolio_state, load_portfolio_state, update_portfolio) - Add UnifiedAgentFactory for centralized agent creation - ToolGuard with batch approval API and WebSocket broadcast - Legacy agents marked deprecated (AnalystAgent, RiskAgent, PMAgent) - Remove backend/agents/compat.py migration shim - Add run_id alongside workspace_id for semantic clarity - Complete integration test coverage (13 tests) - All smoke tests passing for 6 agent roles Constraint: Must maintain backward compatibility with existing run configs Constraint: Memory support must work with EvoAgent (no fallback to Legacy) Rejected: Separate PM implementation for EvoAgent | unified approach cleaner Confidence: high Scope-risk: broad Directive: EVO_AGENT_IDS env var still respected but defaults to all roles Not-tested: Kubernetes sandbox mode for skill execution
This commit is contained in:
203
scripts/test_sandbox.py
Normal file
203
scripts/test_sandbox.py
Normal file
@@ -0,0 +1,203 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
沙盒执行器测试脚本
|
||||
|
||||
测试多模式技能沙盒执行器的基本功能。
|
||||
默认使用 none 模式(无沙盒)。
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# 确保后端目录在路径中
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "backend"))
|
||||
|
||||
|
||||
def test_sandbox_initialization():
|
||||
"""测试沙盒初始化"""
|
||||
print("=" * 60)
|
||||
print("测试 1: 沙盒初始化")
|
||||
print("=" * 60)
|
||||
|
||||
from backend.tools.sandboxed_executor import get_sandbox, SkillSandbox
|
||||
|
||||
# 重置单例(确保测试干净)
|
||||
SkillSandbox._instance = None
|
||||
|
||||
# 默认应该使用 none 模式
|
||||
sandbox = get_sandbox()
|
||||
|
||||
assert sandbox.current_mode == "none", f"期望模式 'none',实际 '{sandbox.current_mode}'"
|
||||
print(f"✓ 沙盒模式: {sandbox.current_mode}")
|
||||
print(f"✓ 后端类型: {type(sandbox._backend).__name__}")
|
||||
|
||||
return sandbox
|
||||
|
||||
|
||||
def test_no_sandbox_warning():
|
||||
"""测试无沙盒模式的安全警告"""
|
||||
print("\n" + "=" * 60)
|
||||
print("测试 2: 无沙盒模式安全警告")
|
||||
print("=" * 60)
|
||||
|
||||
import warnings
|
||||
|
||||
from backend.tools.sandboxed_executor import NoSandboxBackend
|
||||
|
||||
backend = NoSandboxBackend()
|
||||
|
||||
# 捕获警告
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
warnings.simplefilter("always")
|
||||
|
||||
# 执行会触发警告
|
||||
try:
|
||||
backend.execute(
|
||||
skill_name="builtin/valuation_review",
|
||||
function_name="build_dcf_report",
|
||||
function_args={"rows": [], "current_date": "2024-01-01"},
|
||||
)
|
||||
except Exception:
|
||||
pass # 我们不关心执行结果,只关心警告
|
||||
|
||||
# 检查是否产生了警告
|
||||
runtime_warnings = [x for x in w if issubclass(x.category, RuntimeWarning)]
|
||||
if runtime_warnings:
|
||||
print("✓ 安全警告已触发")
|
||||
print(f" 警告内容: {runtime_warnings[0].message}")
|
||||
else:
|
||||
print("⚠ 未触发安全警告(可能已缓存)")
|
||||
|
||||
|
||||
def test_docker_config():
|
||||
"""测试 Docker 模式配置解析"""
|
||||
print("\n" + "=" * 60)
|
||||
print("测试 3: Docker 模式配置解析")
|
||||
print("=" * 60)
|
||||
|
||||
# 设置环境变量
|
||||
os.environ["SKILL_SANDBOX_MODE"] = "docker"
|
||||
os.environ["SKILL_SANDBOX_MEMORY_LIMIT"] = "1g"
|
||||
os.environ["SKILL_SANDBOX_CPU_LIMIT"] = "2.0"
|
||||
|
||||
from backend.tools.sandboxed_executor import SkillSandbox
|
||||
|
||||
# 重置单例
|
||||
SkillSandbox._instance = None
|
||||
|
||||
try:
|
||||
sandbox = SkillSandbox()
|
||||
print(f"✓ 沙盒模式: {sandbox.current_mode}")
|
||||
print(f"✓ 后端类型: {type(sandbox._backend).__name__}")
|
||||
|
||||
# 检查配置
|
||||
backend = sandbox._backend
|
||||
assert backend.config["memory_limit"] == "1g"
|
||||
assert backend.config["cpu_limit"] == 2.0
|
||||
print(f"✓ 内存限制: {backend.config['memory_limit']}")
|
||||
print(f"✓ CPU 限制: {backend.config['cpu_limit']}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"⚠ Docker 后端创建失败(预期,可能未安装 agentscope-runtime): {e}")
|
||||
|
||||
# 恢复环境变量
|
||||
os.environ["SKILL_SANDBOX_MODE"] = "none"
|
||||
SkillSandbox._instance = None
|
||||
|
||||
|
||||
def test_analysis_tools_import():
|
||||
"""测试分析工具导入"""
|
||||
print("\n" + "=" * 60)
|
||||
print("测试 4: 分析工具导入")
|
||||
print("=" * 60)
|
||||
|
||||
try:
|
||||
from backend.tools.analysis_tools import (
|
||||
TOOL_REGISTRY,
|
||||
_sandbox,
|
||||
dcf_valuation_analysis,
|
||||
)
|
||||
|
||||
print(f"✓ TOOL_REGISTRY 包含 {len(TOOL_REGISTRY)} 个工具")
|
||||
print(f"✓ _sandbox 实例模式: {_sandbox.current_mode}")
|
||||
print(f"✓ dcf_valuation_analysis 函数可用")
|
||||
|
||||
# 检查估值分析工具是否都在
|
||||
valuation_tools = [
|
||||
"dcf_valuation_analysis",
|
||||
"owner_earnings_valuation_analysis",
|
||||
"ev_ebitda_valuation_analysis",
|
||||
"residual_income_valuation_analysis",
|
||||
]
|
||||
|
||||
for tool in valuation_tools:
|
||||
if tool in TOOL_REGISTRY:
|
||||
print(f" ✓ {tool}")
|
||||
else:
|
||||
print(f" ✗ {tool} 缺失")
|
||||
|
||||
except Exception as e:
|
||||
print(f"✗ 导入失败: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
def test_skill_execution_mock():
|
||||
"""测试技能执行(模拟)"""
|
||||
print("\n" + "=" * 60)
|
||||
print("测试 5: 技能执行(无沙盒模式)")
|
||||
print("=" * 60)
|
||||
|
||||
from backend.tools.sandboxed_executor import get_sandbox
|
||||
|
||||
sandbox = get_sandbox()
|
||||
|
||||
# 使用模拟参数调用
|
||||
try:
|
||||
# 注意:这需要实际的技能模块存在
|
||||
result = sandbox.execute_skill(
|
||||
skill_name="builtin/valuation_review",
|
||||
function_name="build_dcf_report",
|
||||
function_args={
|
||||
"rows": [{"ticker": "AAPL", "current_fcf": 100000000}],
|
||||
"current_date": "2024-01-01",
|
||||
},
|
||||
)
|
||||
print(f"✓ 技能执行成功")
|
||||
print(f" 结果类型: {type(result)}")
|
||||
print(f" 结果预览: {str(result)[:100]}...")
|
||||
except Exception as e:
|
||||
print(f"⚠ 技能执行失败(可能缺少依赖或数据): {e}")
|
||||
|
||||
|
||||
def main():
|
||||
"""主测试函数"""
|
||||
print("\n" + "=" * 60)
|
||||
print("技能沙盒执行器测试")
|
||||
print("=" * 60)
|
||||
print(f"当前 SKILL_SANDBOX_MODE: {os.getenv('SKILL_SANDBOX_MODE', '未设置(默认 none)')}")
|
||||
|
||||
# 确保使用默认模式测试
|
||||
os.environ["SKILL_SANDBOX_MODE"] = "none"
|
||||
|
||||
try:
|
||||
test_sandbox_initialization()
|
||||
test_no_sandbox_warning()
|
||||
test_docker_config()
|
||||
test_analysis_tools_import()
|
||||
test_skill_execution_mock()
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("测试完成")
|
||||
print("=" * 60)
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n✗ 测试失败: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user