docs: 添加 AG-UI 协议规则约束到 apps/AGENTS.md

This commit is contained in:
qzl
2026-03-02 11:17:20 +08:00
parent 99d540a18d
commit 87b8727ca4
5 changed files with 279 additions and 1 deletions
@@ -7,6 +7,7 @@ import '../ai/ai_decision_engine.dart';
import '../models/ag_ui_event.dart';
import '../models/tool_result.dart';
import '../tools/tool_registry.dart';
import 'mock_history_service.dart';
/// Mock ID 前缀常量
const _threadIdPrefix = 'thread_';
@@ -25,10 +26,12 @@ typedef EventCallback = void Function(AgUiEvent event);
class AgUiService {
EventCallback onEvent;
final AiDecisionEngine _decisionEngine;
final MockHistoryService _historyService;
AgUiService({EventCallback? onEvent})
: onEvent = onEvent ?? ((_) {}),
_decisionEngine = AiDecisionEngine();
_decisionEngine = AiDecisionEngine(),
_historyService = MockHistoryService();
Future<void> sendMessage(String content) async {
if (Env.isMockApi) {
@@ -38,6 +41,43 @@ class AgUiService {
}
}
Future<void> loadHistory({DateTime? beforeDate}) async {
if (Env.isMockApi) {
await _mockLoadHistory(beforeDate: beforeDate);
} else {
throw UnimplementedError('Real API not implemented');
}
}
bool hasEarlierHistory(DateTime fromDate) {
return _historyService.hasEarlierHistory(fromDate);
}
Future<void> _mockLoadHistory({DateTime? beforeDate}) async {
final threadId = '$_threadIdPrefix${DateTime.now().millisecondsSinceEpoch}';
final runId = '$_runIdPrefix${DateTime.now().millisecondsSinceEpoch}';
onEvent(RunStartedEvent(threadId: threadId, runId: runId));
DateTime targetDate;
if (beforeDate != null) {
final prevDate = _historyService.getPreviousDay(beforeDate);
if (prevDate == null) {
onEvent(RunFinishedEvent(threadId: threadId, runId: runId));
return;
}
targetDate = prevDate;
} else {
targetDate = _historyService.getLatestHistoryDate() ?? DateTime.now();
}
final messages = _historyService.getHistoryForDay(targetDate);
onEvent(MessagesSnapshotEvent(messages: messages));
onEvent(RunFinishedEvent(threadId: threadId, runId: runId));
}
Future<void> _mockEventStream(String content) async {
final threadId = '$_threadIdPrefix${DateTime.now().millisecondsSinceEpoch}';
final runId = '$_runIdPrefix${DateTime.now().millisecondsSinceEpoch}';
@@ -0,0 +1,149 @@
import '../models/ag_ui_event.dart';
import '../models/tool_result.dart';
class MockHistoryService {
static final MockHistoryService _instance = MockHistoryService._internal();
factory MockHistoryService() => _instance;
MockHistoryService._internal();
List<SnapshotMessage> getHistoryForDay(DateTime date) {
final dayStart = DateTime(date.year, date.month, date.day);
final allHistory = _generateAllHistory();
return allHistory.where((msg) {
if (msg.ui != null) {
final data = msg.ui!.data;
final startAtStr = data['startAt'] as String?;
if (startAtStr != null) {
try {
final startAt = DateTime.parse(startAtStr);
final msgDate = DateTime(startAt.year, startAt.month, startAt.day);
return msgDate == dayStart;
} catch (_) {}
}
}
return false;
}).toList();
}
DateTime? getLatestHistoryDate() {
final now = DateTime.now();
return DateTime(now.year, now.month, now.day);
}
DateTime? getPreviousDay(DateTime currentDate) {
final allDates = _getAllHistoryDates();
final sortedDates = allDates.toList()..sort((a, b) => b.compareTo(a));
final currentDateOnly = DateTime(
currentDate.year,
currentDate.month,
currentDate.day,
);
for (final date in sortedDates) {
if (date.isBefore(currentDateOnly)) {
return date;
}
}
return null;
}
bool hasEarlierHistory(DateTime fromDate) {
final allDates = _getAllHistoryDates();
final fromDateOnly = DateTime(fromDate.year, fromDate.month, fromDate.day);
return allDates.any((date) => date.isBefore(fromDateOnly));
}
Set<DateTime> _getAllHistoryDates() {
final now = DateTime.now();
final today = DateTime(now.year, now.month, now.day);
final yesterday = today.subtract(const Duration(days: 1));
return {today, yesterday};
}
List<SnapshotMessage> _generateAllHistory() {
final now = DateTime.now();
final today = DateTime(now.year, now.month, now.day);
final yesterday = today.subtract(const Duration(days: 1));
return [
SnapshotMessage(id: 'hist-m1', role: 'user', content: '明天提醒我开会'),
SnapshotMessage(
id: 'hist-t1',
role: 'tool',
toolCallId: 'hist-tc1',
ui: UiCard(
cardType: 'calendar_card.v1',
data: CalendarCardData(
id: 'hist-s1',
title: '产品评审会议',
description: '讨论Q2产品路线图',
startAt: today
.add(const Duration(days: 1, hours: 10))
.toIso8601String(),
endAt: today
.add(const Duration(days: 1, hours: 11))
.toIso8601String(),
timezone: 'Asia/Shanghai',
location: '会议室A / 在线',
color: '#4F46E5',
sourceType: 'ai_generated',
).toJson(),
actions: [
CardAction(
type: 'link',
label: '查看详情',
target: '/calendar/hist-s1',
),
],
),
),
SnapshotMessage(
id: 'hist-m2',
role: 'assistant',
content: '已为你创建日程"产品评审会议",明天上午10:00。我还会提前15分钟提醒你。',
),
SnapshotMessage(id: 'hist-m3', role: 'user', content: '下周一之前提交项目报告'),
SnapshotMessage(
id: 'hist-t2',
role: 'tool',
toolCallId: 'hist-tc2',
ui: UiCard(
cardType: 'calendar_card.v1',
data: CalendarCardData(
id: 'hist-s2',
title: '提交项目报告',
description: '完成并提交Q2项目报告',
startAt: yesterday
.subtract(const Duration(days: 3))
.toIso8601String(),
endAt: null,
timezone: 'Asia/Shanghai',
location: null,
color: '#F59E0B',
sourceType: 'ai_generated',
).toJson(),
actions: [
CardAction(
type: 'link',
label: '查看详情',
target: '/calendar/hist-s2',
),
],
),
),
SnapshotMessage(
id: 'hist-m4',
role: 'assistant',
content: '好的,我已帮你创建待办事项"提交项目报告",截止日期为下周一。我还会提醒你完成这项任务。',
),
SnapshotMessage(
id: 'hist-m5',
role: 'assistant',
content: '你好,我有什么可以帮你的?',
),
];
}
}