feat: Add evaluation hooks, skill adaptation and team pipeline config

- Add EvaluationHook for post-execution agent evaluation
- Add SkillAdaptationHook for dynamic skill adaptation
- Add team/ directory with team coordination logic
- Add TEAM_PIPELINE.yaml for smoke_fullstack pipeline config
- Update RuntimeView, TraderView and RuntimeSettingsPanel UI
- Add runtimeApi and websocket services
- Add runtime_state.json to smoke_fullstack state

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-19 18:52:12 +08:00
parent f4a2b7f3af
commit 4b5ac86b83
87 changed files with 5042 additions and 744 deletions

View File

@@ -5,7 +5,7 @@ import { AGENTS, INITIAL_TICKERS } from './config/constants';
// Services
import { ReadOnlyClient } from './services/websocket';
import { startRuntime } from './services/runtimeApi';
import { startRuntime, uploadAgentSkillZip } from './services/runtimeApi';
// Hooks
import { useFeedProcessor } from './hooks/useFeedProcessor';
@@ -98,6 +98,8 @@ export default function LiveTradingApp() {
const [ohlcHistoryByTicker, setOhlcHistoryByTicker] = useState({});
const [explainEventsByTicker, setExplainEventsByTicker] = useState({});
const [newsByTicker, setNewsByTicker] = useState({});
const [insiderTradesByTicker, setInsiderTradesByTicker] = useState({});
const [technicalIndicatorsByTicker, setTechnicalIndicatorsByTicker] = useState({});
const [selectedExplainSymbol, setSelectedExplainSymbol] = useState('');
const [historySourceByTicker, setHistorySourceByTicker] = useState({});
@@ -127,6 +129,11 @@ export default function LiveTradingApp() {
const [initialCashDraft, setInitialCashDraft] = useState('100000');
const [marginRequirementDraft, setMarginRequirementDraft] = useState('0');
const [enableMemoryDraft, setEnableMemoryDraft] = useState(false);
const [modeDraft, setModeDraft] = useState('live');
const [pollIntervalDraft, setPollIntervalDraft] = useState('10');
const [startDateDraft, setStartDateDraft] = useState('');
const [endDateDraft, setEndDateDraft] = useState('');
const [enableMockDraft, setEnableMockDraft] = useState(false);
const [runtimeConfigFeedback, setRuntimeConfigFeedback] = useState(null);
const [isRuntimeConfigSaving, setIsRuntimeConfigSaving] = useState(false);
const [selectedSkillAgentId, setSelectedSkillAgentId] = useState(AGENTS[0]?.id || 'portfolio_manager');
@@ -602,7 +609,11 @@ export default function LiveTradingApp() {
initial_cash: initialCash,
margin_requirement: marginRequirement,
enable_memory: Boolean(enableMemoryDraft),
mode: serverMode || 'live'
mode: modeDraft || 'live',
poll_interval: Number(pollIntervalDraft) || 10,
start_date: startDateDraft || null,
end_date: endDateDraft || null,
enable_mock: Boolean(enableMockDraft)
});
setIsRuntimeConfigSaving(false);
@@ -630,9 +641,13 @@ export default function LiveTradingApp() {
initialCashDraft,
marginRequirementDraft,
enableMemoryDraft,
modeDraft,
pollIntervalDraft,
startDateDraft,
endDateDraft,
enableMockDraft,
watchlistDraftSymbols,
watchlistInputValue,
serverMode,
addSystemMessage
]);
@@ -644,6 +659,11 @@ export default function LiveTradingApp() {
setInitialCashDraft('100000');
setMarginRequirementDraft('0');
setEnableMemoryDraft(false);
setModeDraft('live');
setPollIntervalDraft('10');
setStartDateDraft('');
setEndDateDraft('');
setEnableMockDraft(false);
setRuntimeConfigFeedback(null);
}, []);
@@ -862,6 +882,38 @@ export default function LiveTradingApp() {
}
}, [selectedSkillAgentId, selectedWorkspaceFile, workspaceDraftContent]);
const handleUploadExternalSkill = useCallback(async (file) => {
if (!(file instanceof File)) {
setAgentSkillsFeedback({ type: 'error', text: '请选择 zip 文件后再上传' });
return;
}
if (!selectedSkillAgentId) {
setAgentSkillsFeedback({ type: 'error', text: '未选择目标 Agent' });
return;
}
setAgentSkillsSavingKey(`${selectedSkillAgentId}:__upload__`);
setAgentSkillsFeedback(null);
try {
const result = await uploadAgentSkillZip({
agentId: selectedSkillAgentId,
file,
activate: true
});
setAgentSkillsFeedback({
type: 'success',
text: `已上传并安装技能 ${result.skill_name || ''}`.trim()
});
requestAgentSkills(selectedSkillAgentId);
} catch (error) {
setAgentSkillsFeedback({
type: 'error',
text: `上传失败: ${error.message || '未知错误'}`
});
} finally {
setAgentSkillsSavingKey(null);
}
}, [requestAgentSkills, selectedSkillAgentId]);
useEffect(() => {
setWorkspaceDraftContent(selectedWorkspaceContent);
}, [selectedWorkspaceContent]);
@@ -967,6 +1019,31 @@ export default function LiveTradingApp() {
});
}, []);
const requestStockInsiderTrades = useCallback((symbol, startDate = null, endDate = null) => {
const normalized = typeof symbol === 'string' ? symbol.trim().toUpperCase() : '';
if (!normalized || !clientRef.current) {
return false;
}
return clientRef.current.send({
type: 'get_stock_insider_trades',
ticker: normalized,
start_date: startDate,
end_date: endDate,
limit: 50
});
}, []);
const requestStockTechnicalIndicators = useCallback((symbol) => {
const normalized = typeof symbol === 'string' ? symbol.trim().toUpperCase() : '';
if (!normalized || !clientRef.current) {
return false;
}
return clientRef.current.send({
type: 'get_stock_technical_indicators',
ticker: normalized
});
}, []);
const requestStockRangeExplain = useCallback((symbol, startDate, endDate, articleIds = []) => {
const normalized = typeof symbol === 'string' ? symbol.trim().toUpperCase() : '';
if (!normalized || !startDate || !endDate || !clientRef.current) {
@@ -1050,13 +1127,15 @@ export default function LiveTradingApp() {
}, [isLiveEnabled, chartTab]);
useEffect(() => {
if (!isWatchlistPanelOpen || !isWatchlistDraftDirty) {
// Only reset when watchlist panel is closed AND runtime settings is also closed
// This prevents reset when user is editing in RuntimeSettingsPanel
if ((!isWatchlistPanelOpen && !isRuntimeSettingsOpen) || !isWatchlistDraftDirty) {
setWatchlistDraftSymbols(runtimeWatchlistSymbols);
if (!isWatchlistPanelOpen) {
if (!isWatchlistPanelOpen && !isRuntimeSettingsOpen) {
setWatchlistInputValue('');
}
}
}, [isWatchlistDraftDirty, isWatchlistPanelOpen, runtimeWatchlistSymbols]);
}, [isWatchlistDraftDirty, isWatchlistPanelOpen, isRuntimeSettingsOpen, runtimeWatchlistSymbols]);
useEffect(() => {
isWatchlistSavingRef.current = isWatchlistSaving;
@@ -1084,6 +1163,8 @@ export default function LiveTradingApp() {
requestStockNews,
requestStockNewsCategories,
requestStockNewsTimeline,
requestStockInsiderTrades,
requestStockTechnicalIndicators,
requestStockStory,
selectedExplainSymbol
]);
@@ -1682,6 +1763,32 @@ export default function LiveTradingApp() {
}));
},
stock_insider_trades_loaded: (e) => {
const symbol = typeof e.ticker === 'string' ? e.ticker.trim().toUpperCase() : '';
if (!symbol) {
return;
}
setInsiderTradesByTicker((prev) => ({
...prev,
[symbol]: {
trades: Array.isArray(e.trades) ? e.trades : [],
startDate: e.start_date || null,
endDate: e.end_date || null
}
}));
},
stock_technical_indicators_loaded: (e) => {
const symbol = typeof e.ticker === 'string' ? e.ticker.trim().toUpperCase() : '';
if (!symbol) {
return;
}
setTechnicalIndicatorsByTicker((prev) => ({
...prev,
[symbol]: e.indicators || null
}));
},
stock_range_explain_loaded: (e) => {
const symbol = typeof e.ticker === 'string' ? e.ticker.trim().toUpperCase() : '';
if (!symbol) {
@@ -2388,6 +2495,11 @@ export default function LiveTradingApp() {
initialCash={initialCashDraft}
marginRequirement={marginRequirementDraft}
enableMemory={enableMemoryDraft}
mode={modeDraft}
pollInterval={pollIntervalDraft}
startDate={startDateDraft}
endDate={endDateDraft}
enableMock={enableMockDraft}
watchlistSymbols={watchlistDraftSymbols}
watchlistInputValue={watchlistInputValue}
watchlistSuggestions={watchlistSuggestions}
@@ -2400,6 +2512,11 @@ export default function LiveTradingApp() {
onInitialCashChange={setInitialCashDraft}
onMarginRequirementChange={setMarginRequirementDraft}
onEnableMemoryChange={setEnableMemoryDraft}
onModeChange={setModeDraft}
onPollIntervalChange={setPollIntervalDraft}
onStartDateChange={setStartDateDraft}
onEndDateChange={setEndDateDraft}
onEnableMockChange={setEnableMockDraft}
onWatchlistInputChange={handleWatchlistInputChange}
onWatchlistInputKeyDown={handleWatchlistInputKeyDown}
onWatchlistAdd={() => commitWatchlistInput(watchlistInputValue)}
@@ -2539,6 +2656,7 @@ export default function LiveTradingApp() {
onWorkspaceFileChange={handleWorkspaceFileChange}
onWorkspaceDraftChange={setWorkspaceDraftContent}
onWorkspaceFileSave={handleWorkspaceFileSave}
onUploadExternalSkill={handleUploadExternalSkill}
/>
</Suspense>
</div>
@@ -2573,9 +2691,13 @@ export default function LiveTradingApp() {
selectedHistorySource={historySourceByTicker[selectedExplainSymbol] || null}
explainEventsSnapshot={explainEventsByTicker[selectedExplainSymbol] || null}
newsSnapshot={newsByTicker[selectedExplainSymbol] || null}
insiderTradesSnapshot={insiderTradesByTicker[selectedExplainSymbol] || null}
technicalIndicatorsSnapshot={technicalIndicatorsByTicker[selectedExplainSymbol] || null}
onRequestRangeExplain={requestStockRangeExplain}
onRequestNewsForDate={requestStockNewsForDate}
onRequestStory={requestStockStory}
onRequestInsiderTrades={requestStockInsiderTrades}
onRequestTechnicalIndicators={requestStockTechnicalIndicators}
currentDate={currentDate}
onRequestSimilarDays={requestStockSimilarDays}
onRequestStockEnrich={requestStockEnrich}