feat(coordinator): 添加团队节点默认最大工具迭代次数配置

添加 DEFAULT_TEAM_NODE_MAX_TOOL_ITERATIONS 配置项以控制团队节点的最大工具迭代次数,
并修改 LocalAgentRunner 中的逻辑来使用此默认值当 envelope 中未指定时。

fix(runtime): 修复团队节点运行成功判断逻辑

更新运行成功判断条件,将 finish_reason 为 "max_tool_iterations_finalized" 的情况
视为运行失败,并添加对原始工具调用输出的检测,避免将其误判为成功完成。

feat(mcp): 添加团队工作流MCP工具类别支持

增加新的本地MCP工具类别 "team_workflow" 及其对应的工具创建功能,
为团队工作流提供本地工具支持。

refactor(engine): 调整AgentLoop最大工具迭代次数设置

将 AgentProfile 中的默认 max_tool_iterations 从 30 增加到 100,
同时移除 TaskExecutionPlanner 构造函数中的重复参数传递。

perf(mcp): 优化MCP连接管理避免重复连接

添加 mcp_connected 标志来跟踪MCP连接状态,确保 connect_all 只执行一次,
提高性能并避免不必要的重复连接。

refactor(skills): 移除技能团队模板相关功能

移除与技能团队模板相关的代码,包括解析、存储和处理逻辑,
简化技能记录结构和加载流程。

feat(process): 增强会话过程投影器功能

添加技能激活快照事件处理,改进团队运行完成消息显示,
并增强技能激活事件的时间戳记录功能。

refactor(tasks): 简化任务尝试编排器团队执行逻辑

移除团队执行相关代码,将所有任务统一按单步执行处理,
简化任务编排器的复杂度并提升执行效率。

fix(evidence): 修复节点证据评估中需求验证逻辑

更新节点证据评估逻辑,跳过自然语言证据需求的确定性验证,
只执行机器可读的需求验证,避免因自然语言需求导致的节点失败。
This commit is contained in:
2026-06-26 16:36:29 +08:00
parent 53b13e8eac
commit 520a21a027
360 changed files with 13271 additions and 1848 deletions

View File

@ -0,0 +1,304 @@
import { describe, expect, it } from 'vitest';
import { buildTaskUiModel } from '@/lib/task-ui-model';
import type { BackendTask, ProcessEvent, ProcessRun, SessionProcessProjection, TaskTimelineCard } from '@/types';
function task(overrides: Partial<BackendTask> = {}): BackendTask {
return {
task_id: 'task-1',
session_id: 'web:default',
description: 'Build MGM report',
goal: 'Build MGM report',
constraints: [],
priority: 0,
status: 'awaiting_acceptance',
creator: 'user',
created_at: '2026-06-24T00:00:00.000Z',
updated_at: '2026-06-24T00:01:00.000Z',
run_ids: ['main-run'],
skill_names: ['mgm-galaxy-financial-chart-report-safe'],
feedback: [],
metadata: {},
...overrides,
};
}
function process(overrides: Partial<SessionProcessProjection> = {}): SessionProcessProjection {
return {
runs: [],
events: [],
artifacts: [],
...overrides,
};
}
describe('buildTaskUiModel', () => {
it('keeps single-agent tasks out of the Agent Team tree and summarizes tool results', () => {
const cards: TaskTimelineCard[] = [
{
id: 'tool-start',
taskId: 'task-1',
runId: 'main-run',
type: 'tool_call',
title: 'Calling tool: web_search',
actorName: 'web_search',
status: 'running',
createdAt: '2026-06-24T00:00:10.000Z',
details: {
tool_name: 'web_search',
tool_call_id: 'call-1',
arguments: '{"query":"MGM China annual results"}',
},
},
{
id: 'tool-result',
taskId: 'task-1',
runId: 'main-run',
type: 'tool_result',
title: 'Tool result: web_search',
actorName: 'web_search',
status: 'done',
createdAt: '2026-06-24T00:00:20.000Z',
summary: '{"success":true,"query":"MGM China annual results","quality":"low","results":[{"title":"bad"}]}',
details: {
tool_name: 'web_search',
tool_call_id: 'call-1',
result_summary: '{"success":true,"query":"MGM China annual results","quality":"low","results":[{"title":"bad"}]}',
},
},
];
const model = buildTaskUiModel({
task: task(),
process: process({
runs: [
{
run_id: 'main-run',
parent_run_id: 'task:task-1:attempt:1',
session_id: 'web:default',
actor_type: 'agent',
actor_id: 'main-agent',
actor_name: 'Main Agent',
title: 'Final synthesis',
source: 'task_synthesis',
status: 'done',
started_at: '2026-06-24T00:00:00.000Z',
},
],
}),
cards,
locale: 'zh-Hans',
});
expect(model.executionMode).toBe('single');
expect(model.team.hasTeam).toBe(false);
expect(model.agentTree).toEqual([]);
expect(model.tools).toHaveLength(1);
expect(model.tools[0].status).toBe('done');
expect(model.tools[0].quality).toBe('low');
expect(model.tools[0].summary).toContain('MGM China annual results');
});
it('models real task team nodes separately from the main agent', () => {
const cards: TaskTimelineCard[] = [
{
id: 'team',
taskId: 'task-1',
runId: 'task:task-1:attempt:1',
type: 'agent_team',
title: 'Agent Team',
actorName: 'Task Team',
status: 'error',
createdAt: '2026-06-24T00:00:10.000Z',
details: {
task_outcome: 'incomplete',
node_ids: ['collect', 'report'],
incomplete_node_ids: ['collect'],
},
},
];
const runs: ProcessRun[] = [
{
run_id: 'main-run',
parent_run_id: 'task:task-1:attempt:1',
session_id: 'web:default',
actor_type: 'agent',
actor_id: 'main-agent',
actor_name: 'Main Agent',
title: 'Final synthesis',
source: 'task_synthesis',
status: 'done',
started_at: '2026-06-24T00:00:00.000Z',
},
{
run_id: 'collect-run',
parent_run_id: 'task:task-1:attempt:1',
session_id: 'web:default:team:collect',
actor_type: 'agent',
actor_id: 'collect',
actor_name: 'collect',
title: 'collect',
source: 'task_team',
status: 'error',
started_at: '2026-06-24T00:00:10.000Z',
metadata: { node_id: 'collect' },
},
];
const model = buildTaskUiModel({
task: task(),
process: process({ runs, events: [] as ProcessEvent[] }),
cards,
locale: 'zh-Hans',
});
expect(model.executionMode).toBe('team');
expect(model.team.hasTeam).toBe(true);
expect(model.team.outcome).toBe('incomplete');
expect(model.agentTree.map((node) => node.name)).toEqual(['collect']);
});
it('groups tools and result versions by task attempt', () => {
const cards: TaskTimelineCard[] = [
{
id: 'run-1-tool-call',
taskId: 'task-1',
runId: 'main-run-1',
type: 'tool_call',
title: 'Calling tool: web_fetch',
actorName: 'web_fetch',
status: 'running',
createdAt: '2026-06-24T00:00:10.000Z',
details: { tool_name: 'web_fetch', tool_call_id: 'call-1' },
},
{
id: 'run-1-tool-result',
taskId: 'task-1',
runId: 'main-run-1',
type: 'tool_result',
title: 'Tool result: web_fetch',
actorName: 'web_fetch',
status: 'error',
createdAt: '2026-06-24T00:00:15.000Z',
details: { tool_name: 'web_fetch', tool_call_id: 'call-1' },
},
{
id: 'run-1-result',
taskId: 'task-1',
runId: 'main-run-1',
type: 'result',
title: 'Result ready',
actorName: 'Main Agent',
status: 'error',
createdAt: '2026-06-24T00:00:20.000Z',
summary: 'First result',
},
{
id: 'run-2-tool-call',
taskId: 'task-1',
runId: 'main-run-2',
type: 'tool_call',
title: 'Calling tool: web_search',
actorName: 'web_search',
status: 'running',
createdAt: '2026-06-24T00:02:10.000Z',
details: { tool_name: 'web_search', tool_call_id: 'call-2' },
},
{
id: 'run-2-tool-result',
taskId: 'task-1',
runId: 'main-run-2',
type: 'tool_result',
title: 'Tool result: web_search',
actorName: 'web_search',
status: 'done',
createdAt: '2026-06-24T00:02:30.000Z',
details: { tool_name: 'web_search', tool_call_id: 'call-2' },
},
{
id: 'run-2-result',
taskId: 'task-1',
runId: 'main-run-2',
type: 'result',
title: 'Result ready',
actorName: 'Main Agent',
status: 'done',
createdAt: '2026-06-24T00:03:00.000Z',
summary: 'Second result',
},
];
const runs: ProcessRun[] = [
{
run_id: 'task:task-1:attempt:1',
session_id: 'web:default',
actor_type: 'system',
actor_id: 'task',
actor_name: 'Task Planner',
title: 'single plan',
source: 'task_mode',
status: 'error',
started_at: '2026-06-24T00:00:00.000Z',
metadata: { attempt_index: 1 },
},
{
run_id: 'main-run-1',
parent_run_id: 'task:task-1:attempt:1',
session_id: 'web:default',
actor_type: 'agent',
actor_id: 'main-agent',
actor_name: 'Main Agent',
title: 'Final synthesis',
source: 'task_synthesis',
status: 'error',
started_at: '2026-06-24T00:00:05.000Z',
finished_at: '2026-06-24T00:00:20.000Z',
metadata: { attempt_index: 1 },
},
{
run_id: 'task:task-1:attempt:2',
session_id: 'web:default',
actor_type: 'system',
actor_id: 'task',
actor_name: 'Task Planner',
title: 'single plan',
source: 'task_mode',
status: 'done',
started_at: '2026-06-24T00:02:00.000Z',
finished_at: '2026-06-24T00:03:00.000Z',
metadata: { attempt_index: 2 },
},
{
run_id: 'main-run-2',
parent_run_id: 'task:task-1:attempt:2',
session_id: 'web:default',
actor_type: 'agent',
actor_id: 'main-agent',
actor_name: 'Main Agent',
title: 'Final synthesis',
source: 'task_synthesis',
status: 'done',
started_at: '2026-06-24T00:02:05.000Z',
finished_at: '2026-06-24T00:03:00.000Z',
metadata: { attempt_index: 2 },
},
];
const model = buildTaskUiModel({
task: task({ run_ids: ['main-run-1', 'main-run-2'] }),
process: process({ runs }),
cards,
locale: 'zh-Hans',
});
expect(model.tools).toHaveLength(2);
expect(model.attempts).toHaveLength(2);
expect(model.attempts[0].index).toBe(1);
expect(model.attempts[0].tools.map((tool) => tool.toolName)).toEqual(['web_fetch']);
expect(model.attempts[0].result?.summary).toBe('First result');
expect(model.attempts[1].index).toBe(2);
expect(model.attempts[1].tools.map((tool) => tool.toolName)).toEqual(['web_search']);
expect(model.attempts[1].result?.summary).toBe('Second result');
});
});