Files
evotraders/scripts/test_sandbox.py
cillin 16b54d5ccc 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
2026-04-02 00:55:08 +08:00

204 lines
5.9 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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()