refactor: classify gateway task load

This commit is contained in:
Haitao Pan 2026-05-29 14:30:19 +08:00
parent 191ddc6ca4
commit 71bae611fc
2 changed files with 160 additions and 2 deletions

View File

@ -451,6 +451,11 @@ extension AppControllerDesktopThreadActions on AppController {
final capturedLocalAttachments = List<CollaborationAttachment>.unmodifiable(
localAttachments,
);
final taskLoadClass = classifyGatewayTaskLoadInternal(message);
final taskMetadata = Map<String, dynamic>.unmodifiable(<String, dynamic>{
...dispatch.metadata,
'taskLoadClass': taskLoadClass,
});
final executionWorkingDirectory = gatewayExecutionWorkingDirectoryInternal(
target: currentTarget,
workingDirectory: workingDirectory,
@ -475,7 +480,7 @@ extension AppControllerDesktopThreadActions on AppController {
model: model,
routing: routing,
agentId: dispatch.agentId ?? '',
metadata: Map<String, dynamic>.unmodifiable(dispatch.metadata),
metadata: taskMetadata,
resumeSessionHint: resumeSessionHint,
appendUserTurn: appendUserTurn,
),
@ -500,7 +505,7 @@ extension AppControllerDesktopThreadActions on AppController {
model: model,
routing: routing,
agentId: dispatch.agentId ?? '',
metadata: Map<String, dynamic>.unmodifiable(dispatch.metadata),
metadata: taskMetadata,
resumeSessionHint: resumeSessionHint,
appendUserTurn: appendUserTurn,
),
@ -672,11 +677,84 @@ extension AppControllerDesktopThreadActions on AppController {
'6. The app syncs final artifacts from currentTaskWorkspace back into localWorkspace.',
)
..writeln()
..writeln('Task load classification:')
..writeln('- class: ${classifyGatewayTaskLoadInternal(requestText)}')
..writeln(
'- Gateway owns execution decomposition, scheduling, retries, and resumability for this class.',
)
..writeln()
..writeln(
'Available classes: short_task, long_task, complex_long_chain_task.',
)
..writeln();
buffer
..writeln('User request:')
..write(requestText);
return buffer.toString();
}
String classifyGatewayTaskLoadInternal(String requestText) {
final normalized = requestText.trim().toLowerCase();
if (normalized.isEmpty) {
return 'short_task';
}
final hasChapterSplit =
normalized.contains('拆章节') ||
normalized.contains('chapter') ||
normalized.contains('章节');
final hasAgentStage =
normalized.contains('codex') ||
normalized.contains('agent') ||
normalized.contains('调用');
final hasImageStage =
normalized.contains('gpt images') ||
normalized.contains('images2') ||
normalized.contains('生成图') ||
normalized.contains('图片');
final hasPackagingStage =
normalized.contains('汇总排版') ||
normalized.contains('排版') ||
normalized.contains('制作视频') ||
normalized.contains('视频') ||
normalized.contains('mp4');
final hasChainArrows =
normalized.contains('->') || normalized.contains('');
if (hasChapterSplit &&
hasAgentStage &&
hasImageStage &&
hasPackagingStage &&
hasChainArrows) {
return 'complex_long_chain_task';
}
const longTaskMarkers = <String>[
'生成文件',
'产物',
'附件',
'图片提示词',
'完整调研ppt',
'markdown格式',
'输出markdown',
'ppt',
'pptx',
'powerpoint',
'word',
'docx',
'png',
'mp4',
'jpg',
'markdown',
'.md',
'image prompt',
'artifacts',
'downloadurl',
];
if (requestText.length >= 1200 ||
longTaskMarkers.any(normalized.contains)) {
return 'long_task';
}
return 'short_task';
}
bool usesOpenClawGatewayQueueInternal(
AssistantExecutionTarget target,
SingleAgentProvider provider,

View File

@ -1143,6 +1143,86 @@ void main() {
);
});
test(
'sendChatMessage classifies complex artifact chains for Gateway',
() async {
final fakeGoTaskService = _RecordingGoTaskServiceClient();
final controller = _connectedGatewayController(fakeGoTaskService);
addTearDown(controller.dispose);
await controller.ensureActiveAssistantThreadInternal();
await controller.setAssistantExecutionTarget(
AssistantExecutionTarget.gateway,
);
await controller.sendChatMessage(
'围绕\n\n'
'从单机权限 → 网络边界 → Web安全 → 云身份 → Zero Trust → AI Agent 身份 → AI模型与知识保护 演进\n\n'
'拆章节 -> 每章调用 Codex -> 每章 GPT images2 生成图 -> 汇总排版 -> 制作视频',
);
expect(fakeGoTaskService.requests, hasLength(1));
final request = fakeGoTaskService.requests.single;
expect(request.metadata['taskLoadClass'], 'complex_long_chain_task');
expect(request.prompt, contains('Task load classification:'));
expect(request.prompt, contains('- class: complex_long_chain_task'));
expect(
request.prompt,
contains(
'Gateway owns execution decomposition, scheduling, retries, and resumability for this class.',
),
);
expect(
request.prompt,
isNot(contains('First write the chapter breakdown')),
);
expect(
request.prompt,
isNot(contains('Run heavyweight stages in order')),
);
expect(
request.prompt,
contains('User request:\n围绕\n\n从单机权限 → 网络边界 → Web安全'),
);
},
);
test(
'sendChatMessage classifies simple Gateway prompts as short tasks',
() async {
final fakeGoTaskService = _RecordingGoTaskServiceClient();
final controller = _connectedGatewayController(fakeGoTaskService);
addTearDown(controller.dispose);
await controller.ensureActiveAssistantThreadInternal();
await controller.setAssistantExecutionTarget(
AssistantExecutionTarget.gateway,
);
await controller.sendChatMessage('写一段普通说明');
expect(fakeGoTaskService.requests, hasLength(1));
final request = fakeGoTaskService.requests.single;
expect(request.metadata['taskLoadClass'], 'short_task');
expect(request.prompt, contains('- class: short_task'));
},
);
test('sendChatMessage classifies artifact output as a long task', () async {
final fakeGoTaskService = _RecordingGoTaskServiceClient();
final controller = _connectedGatewayController(fakeGoTaskService);
addTearDown(controller.dispose);
await controller.ensureActiveAssistantThreadInternal();
await controller.setAssistantExecutionTarget(
AssistantExecutionTarget.gateway,
);
await controller.sendChatMessage('生成 Markdown 和 PNG 产物');
expect(fakeGoTaskService.requests, hasLength(1));
final request = fakeGoTaskService.requests.single;
expect(request.metadata['taskLoadClass'], 'long_task');
expect(request.prompt, contains('- class: long_task'));
});
test(
'sendChatMessage runs Gateway task with remote workspace when local workspace is unavailable',
() async {