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:
2026-04-02 00:55:08 +08:00
parent 0fa413380c
commit 16b54d5ccc
73 changed files with 9454 additions and 904 deletions

View File

@@ -8,7 +8,7 @@ export default function TraderView({
agents,
agentProfilesByAgent,
agentSkillsByAgent,
workspaceFilesByAgent,
runFilesByAgent,
selectedAgentId,
selectedAgentProfile,
selectedAgentSkills,
@@ -16,16 +16,16 @@ export default function TraderView({
localSkillDraftsByKey,
skillDetailLoadingKey,
editableFiles,
selectedWorkspaceFile,
workspaceFileContent,
workspaceDraftContent,
selectedRunFile,
runFileContent,
runDraftContent,
isConnected,
isAgentSkillsLoading,
agentSkillsSavingKey,
agentSkillsFeedback,
isWorkspaceFileLoading,
workspaceFileSavingKey,
workspaceFileFeedback,
isRunFileLoading,
runFileSavingKey,
runFileFeedback,
onAgentChange,
onCreateLocalSkill,
onSkillDetailRequest,
@@ -35,8 +35,8 @@ export default function TraderView({
onRemoveSharedSkill,
onSkillToggle,
onWorkspaceFileChange,
onWorkspaceDraftChange,
onWorkspaceFileSave,
onRunDraftChange,
onRunFileSave,
onUploadExternalSkill
}) {
const srOnlyStyle = {
@@ -133,10 +133,10 @@ export default function TraderView({
}}>
<div style={{ display: 'grid', gap: 4 }}>
<div style={{ fontSize: 12, fontWeight: 800, letterSpacing: '0.5px', color: '#111111' }}>
交易员档案
Agent 运行档案
</div>
<div style={{ fontSize: 11, color: '#6B7280' }}>
聚焦查看每个 Agent 的模型工具组技能编排和工作区记忆不展示交易表现数据
聚焦查看每个 Agent 在当前运行任务中的模型工具组技能编排和运行记忆不展示交易表现数据
</div>
</div>
@@ -549,15 +549,15 @@ export default function TraderView({
gap: 10
}}>
<div style={{ display: 'grid', gap: 4 }}>
<div style={{ fontSize: 12, fontWeight: 800, color: '#111111' }}>工作区文件编辑</div>
<div style={{ fontSize: 12, fontWeight: 800, color: '#111111' }}>运行文件编辑</div>
<div style={{ fontSize: 11, color: '#6B7280' }}>
直接调整该交易员的人设协作方式和长期记忆文件
直接调整该交易员在当前运行任务中的人设协作方式和长期记忆文件
</div>
</div>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}>
{editableFiles.map((filename) => {
const isActive = filename === selectedWorkspaceFile;
const isActive = filename === selectedRunFile;
return (
<button
key={filename}
@@ -581,12 +581,12 @@ export default function TraderView({
</div>
<textarea
id={`workspace-editor-${selectedAgentId}-${selectedWorkspaceFile || 'file'}`}
name={`workspace_editor_${selectedAgentId}_${selectedWorkspaceFile || 'file'}`}
aria-label={`编辑 ${selectedWorkspaceFile || '工作区文件'} 内容`}
value={workspaceDraftContent}
onChange={(e) => onWorkspaceDraftChange(e.target.value)}
placeholder={isWorkspaceFileLoading ? '加载中...' : '输入 markdown 内容'}
id={`workspace-editor-${selectedAgentId}-${selectedRunFile || 'file'}`}
name={`workspace_editor_${selectedAgentId}_${selectedRunFile || 'file'}`}
aria-label={`编辑 ${selectedRunFile || '运行文件'} 内容`}
value={runDraftContent}
onChange={(e) => onRunDraftChange(e.target.value)}
placeholder={isRunFileLoading ? '加载中...' : '输入 markdown 内容'}
style={{
minHeight: 280,
resize: 'vertical',
@@ -603,33 +603,33 @@ export default function TraderView({
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 10, flexWrap: 'wrap' }}>
<span style={{ fontSize: 10, color: '#6B7280', fontFamily: '"Courier New", monospace' }}>
当前文件: {selectedWorkspaceFile}
当前运行文件: {selectedRunFile}
</span>
<button
onClick={onWorkspaceFileSave}
disabled={!isConnected || isWorkspaceFileLoading || workspaceFileSavingKey !== null || workspaceDraftContent === workspaceFileContent}
onClick={onRunFileSave}
disabled={!isConnected || isRunFileLoading || runFileSavingKey !== null || runDraftContent === runFileContent}
style={{
padding: '9px 14px',
borderRadius: 6,
border: '1px solid #1565C0',
background: isConnected && !isWorkspaceFileLoading && workspaceFileSavingKey === null && workspaceDraftContent !== workspaceFileContent ? '#0D47A1' : '#94A3B8',
background: isConnected && !isRunFileLoading && runFileSavingKey === null && runDraftContent !== runFileContent ? '#0D47A1' : '#94A3B8',
color: '#FFFFFF',
fontSize: 11,
fontWeight: 700,
cursor: isConnected && !isWorkspaceFileLoading && workspaceFileSavingKey === null && workspaceDraftContent !== workspaceFileContent ? 'pointer' : 'not-allowed'
cursor: isConnected && !isRunFileLoading && runFileSavingKey === null && runDraftContent !== runFileContent ? 'pointer' : 'not-allowed'
}}
>
{workspaceFileSavingKey ? '保存中' : '保存文件'}
{runFileSavingKey ? '保存中' : '保存文件'}
</button>
</div>
{workspaceFileFeedback && (
{runFileFeedback && (
<span style={{
color: workspaceFileFeedback.type === 'success' ? '#00C853' : '#FF5252',
color: runFileFeedback.type === 'success' ? '#00C853' : '#FF5252',
fontSize: 11,
fontFamily: '"Courier New", monospace'
}}>
{workspaceFileFeedback.text}
{runFileFeedback.text}
</span>
)}
</div>