agent-configs 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/README.md +223 -0
  2. package/agents/architect.md +211 -0
  3. package/agents/code-reviewer.md +104 -0
  4. package/agents/planner.md +119 -0
  5. package/agents/refactor-cleaner.md +306 -0
  6. package/agents/security-reviewer.md +545 -0
  7. package/agents/tdd-guide.md +280 -0
  8. package/bundles/bk-chat-bundle/README.md +48 -0
  9. package/bundles/bk-chat-bundle/manifest.json +10 -0
  10. package/bundles/continuous-learning/.claude/commands/evolve.md +190 -0
  11. package/bundles/continuous-learning/.claude/commands/instinct-status.md +64 -0
  12. package/bundles/continuous-learning/.claude/commands/learn.md +83 -0
  13. package/bundles/continuous-learning/.claude/hooks/learning-end.js +85 -0
  14. package/bundles/continuous-learning/.claude/hooks/observe.js +131 -0
  15. package/bundles/continuous-learning/.claude/lib/learning.js +559 -0
  16. package/bundles/continuous-learning/.claude/lib/utils.js +312 -0
  17. package/bundles/continuous-learning/.claude/skills/continuous-learning/SKILL.md +200 -0
  18. package/bundles/continuous-learning/.cursor/hooks/learning-end.js +102 -0
  19. package/bundles/continuous-learning/.cursor/rules/continuous-learning.mdc +34 -0
  20. package/bundles/continuous-learning/.cursor/skills/continuous-learning/SKILL.md +77 -0
  21. package/bundles/continuous-learning/README.md +159 -0
  22. package/bundles/continuous-learning/manifest.json +51 -0
  23. package/bundles/planning-bundle/README.md +34 -0
  24. package/bundles/planning-bundle/manifest.json +10 -0
  25. package/bundles/review-bundle/README.md +43 -0
  26. package/bundles/review-bundle/manifest.json +11 -0
  27. package/bundles/shared-memory/.claude/commands/list-sessions.md +124 -0
  28. package/bundles/shared-memory/.claude/commands/load-session.md +169 -0
  29. package/bundles/shared-memory/.claude/commands/save-session.md +137 -0
  30. package/bundles/shared-memory/.claude/hooks/memory-compact.js +43 -0
  31. package/bundles/shared-memory/.claude/hooks/memory-end.js +42 -0
  32. package/bundles/shared-memory/.claude/hooks/memory-start.js +59 -0
  33. package/bundles/shared-memory/.claude/lib/memory.js +416 -0
  34. package/bundles/shared-memory/.claude/lib/utils.js +209 -0
  35. package/bundles/shared-memory/.claude/skills/shared-memory/SKILL.md +183 -0
  36. package/bundles/shared-memory/.cursor/hooks/memory-start.js +42 -0
  37. package/bundles/shared-memory/.cursor/rules/shared-memory.mdc +37 -0
  38. package/bundles/shared-memory/.cursor/skills/shared-memory/SKILL.md +183 -0
  39. package/bundles/tdd-bundle/README.md +33 -0
  40. package/bundles/tdd-bundle/manifest.json +10 -0
  41. package/cli.js +978 -0
  42. package/commands/build-fix.md +29 -0
  43. package/commands/code-review.md +40 -0
  44. package/commands/e2e.md +363 -0
  45. package/commands/learn.md +114 -0
  46. package/commands/plan.md +113 -0
  47. package/commands/refactor-clean.md +28 -0
  48. package/commands/tdd.md +326 -0
  49. package/commands/test-coverage.md +27 -0
  50. package/commands/update-codemaps.md +17 -0
  51. package/commands/update-docs.md +31 -0
  52. package/configs.json +158 -0
  53. package/hooks/hooks.json +101 -0
  54. package/package.json +58 -0
  55. package/rules/agents.md +49 -0
  56. package/rules/coding-style.md +70 -0
  57. package/rules/git-workflow.md +45 -0
  58. package/rules/hooks.md +46 -0
  59. package/rules/patterns.md +55 -0
  60. package/rules/performance.md +47 -0
  61. package/rules/security.md +36 -0
  62. package/rules/testing.md +30 -0
  63. package/skills/ai-config-architect/SKILL.md +59 -0
  64. package/skills/ai-config-architect/references/agents.md +77 -0
  65. package/skills/ai-config-architect/references/commands.md +66 -0
  66. package/skills/ai-config-architect/references/hooks.md +70 -0
  67. package/skills/ai-config-architect/references/patterns.md +66 -0
  68. package/skills/ai-config-architect/references/platforms.md +82 -0
  69. package/skills/ai-config-architect/references/rules.md +66 -0
  70. package/skills/ai-config-architect/references/skills.md +67 -0
  71. package/skills/bk-chat-helper/SKILL.md +398 -0
  72. package/skills/bk-chat-helper/references/api-reference.md +606 -0
  73. package/skills/bk-chat-helper/references/examples.md +789 -0
  74. package/skills/bk-chat-helper/references/integration-guide.md +583 -0
  75. package/skills/bk-chat-x/SKILL.md +400 -0
  76. package/skills/bk-chat-x/references/components-api.md +340 -0
  77. package/skills/bk-chat-x/references/examples.md +386 -0
  78. package/skills/bk-chat-x/references/shortcuts-guide.md +375 -0
  79. package/skills/coding-standards/SKILL.md +523 -0
  80. package/skills/security-review/SKILL.md +497 -0
  81. package/skills/security-review/references/cloud-infrastructure-security.md +361 -0
  82. package/skills/strategic-compact/SKILL.md +66 -0
  83. package/skills/strategic-compact/scripts/suggest-compact.sh +52 -0
  84. package/skills/tdd-workflow/SKILL.md +412 -0
  85. package/skills/verification-loop/SKILL.md +128 -0
@@ -0,0 +1,789 @@
1
+ # 完整示例代码
2
+
3
+ ## 基础聊天应用
4
+
5
+ ```vue
6
+ <template>
7
+ <div class="chat-app">
8
+ <!-- 会话列表侧边栏 -->
9
+ <aside class="session-sidebar">
10
+ <button @click="createNewSession" :disabled="session.isCreateLoading.value">
11
+ {{ session.isCreateLoading.value ? '创建中...' : '新建会话' }}
12
+ </button>
13
+
14
+ <div v-if="session.isListLoading.value" class="loading">加载中...</div>
15
+ <ul v-else class="session-list">
16
+ <li
17
+ v-for="item in session.list.value"
18
+ :key="item.sessionCode"
19
+ :class="{ active: session.current.value?.sessionCode === item.sessionCode }"
20
+ @click="session.chooseSession(item.sessionCode)"
21
+ >
22
+ <span class="name">{{ item.sessionName }}</span>
23
+ <button @click.stop="session.deleteSession(item.sessionCode)">删除</button>
24
+ </li>
25
+ </ul>
26
+ </aside>
27
+
28
+ <!-- 聊天主区域 -->
29
+ <main class="chat-main">
30
+ <!-- Agent 信息 -->
31
+ <header v-if="agent.info.value" class="agent-header">
32
+ <h2>{{ agent.info.value.agentName }}</h2>
33
+ <p v-if="agent.info.value.conversationSettings?.openingRemark">
34
+ {{ agent.info.value.conversationSettings.openingRemark }}
35
+ </p>
36
+ </header>
37
+
38
+ <!-- 消息列表 -->
39
+ <div v-if="message.isListLoading.value" class="loading">加载消息...</div>
40
+ <div v-else class="message-list">
41
+ <div
42
+ v-for="msg in message.list.value"
43
+ :key="msg.messageId || msg.id"
44
+ :class="['message', msg.role]"
45
+ >
46
+ <div class="role">{{ msg.role }}</div>
47
+ <div class="content">
48
+ <template v-if="msg.role === 'user'">
49
+ {{ typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content) }}
50
+ </template>
51
+ <template v-else-if="msg.role === 'assistant'">
52
+ {{ msg.content }}
53
+ <div v-if="msg.toolCalls?.length" class="tool-calls">
54
+ <div v-for="call in msg.toolCalls" :key="call.id" class="tool-call">
55
+ 调用工具: {{ call.function.name }}
56
+ </div>
57
+ </div>
58
+ </template>
59
+ <template v-else-if="msg.role === 'reasoning'">
60
+ <div v-for="(step, idx) in msg.content" :key="idx" class="thinking-step">
61
+ {{ step }}
62
+ </div>
63
+ <span v-if="msg.duration" class="duration">耗时: {{ msg.duration }}ms</span>
64
+ </template>
65
+ <template v-else-if="msg.role === 'tool'">
66
+ <div class="tool-result">{{ msg.content }}</div>
67
+ <span v-if="msg.duration" class="duration">耗时: {{ msg.duration }}ms</span>
68
+ </template>
69
+ <template v-else>
70
+ {{ msg.content }}
71
+ </template>
72
+ </div>
73
+ <div v-if="msg.status === 'streaming'" class="streaming-indicator">●</div>
74
+ </div>
75
+ </div>
76
+
77
+ <!-- 输入区域 -->
78
+ <div class="input-area">
79
+ <textarea
80
+ v-model="userInput"
81
+ @keydown.enter.prevent="handleSend"
82
+ placeholder="输入消息,按 Enter 发送..."
83
+ :disabled="agent.isChatting.value"
84
+ />
85
+ <button @click="handleSend" :disabled="!canSend">
86
+ {{ agent.isChatting.value ? '发送中...' : '发送' }}
87
+ </button>
88
+ <button @click="agent.stopChat()" :disabled="!agent.isChatting.value">
89
+ 停止
90
+ </button>
91
+ </div>
92
+ </main>
93
+ </div>
94
+ </template>
95
+
96
+ <script setup lang="ts">
97
+ import { ref, computed, onMounted, onUnmounted } from 'vue';
98
+ import { useChatHelper, AGUIProtocol } from '@blueking/chat-helper';
99
+
100
+ // 用户输入
101
+ const userInput = ref('');
102
+
103
+ // 创建 chatHelper
104
+ const chatHelper = useChatHelper({
105
+ requestData: {
106
+ urlPrefix: 'https://your-api.com/api/',
107
+ headers: () => ({
108
+ Authorization: `Bearer ${localStorage.getItem('token')}`,
109
+ }),
110
+ },
111
+ protocol: new AGUIProtocol({
112
+ onStart: () => console.log('开始响应'),
113
+ onDone: () => console.log('响应完成'),
114
+ onError: (error) => {
115
+ console.error('错误:', error);
116
+ alert('发生错误,请重试');
117
+ },
118
+ }),
119
+ });
120
+
121
+ const { agent, session, message } = chatHelper;
122
+
123
+ // 能否发送
124
+ const canSend = computed(() =>
125
+ !agent.isChatting.value &&
126
+ userInput.value.trim() &&
127
+ session.current.value?.sessionCode
128
+ );
129
+
130
+ // 初始化
131
+ onMounted(async () => {
132
+ try {
133
+ await agent.getAgentInfo();
134
+ await session.getSessions();
135
+ if (session.list.value.length > 0) {
136
+ await session.chooseSession(session.list.value[0].sessionCode);
137
+ }
138
+ } catch (error) {
139
+ console.error('初始化失败:', error);
140
+ }
141
+ });
142
+
143
+ // 清理
144
+ onUnmounted(() => {
145
+ agent.stopChat();
146
+ });
147
+
148
+ // 发送消息
149
+ const handleSend = async () => {
150
+ if (!canSend.value) return;
151
+
152
+ const input = userInput.value.trim();
153
+ userInput.value = '';
154
+
155
+ await agent.chat(input, session.current.value!.sessionCode);
156
+ };
157
+
158
+ // 创建新会话
159
+ const createNewSession = async () => {
160
+ await session.createSession({
161
+ sessionCode: `session_${Date.now()}`,
162
+ sessionName: `会话 ${new Date().toLocaleString()}`,
163
+ });
164
+ };
165
+ </script>
166
+
167
+ <style scoped>
168
+ .chat-app {
169
+ display: flex;
170
+ height: 100vh;
171
+ }
172
+
173
+ .session-sidebar {
174
+ width: 250px;
175
+ border-right: 1px solid #ddd;
176
+ padding: 16px;
177
+ }
178
+
179
+ .session-list {
180
+ list-style: none;
181
+ padding: 0;
182
+ }
183
+
184
+ .session-list li {
185
+ padding: 8px;
186
+ cursor: pointer;
187
+ border-radius: 4px;
188
+ display: flex;
189
+ justify-content: space-between;
190
+ }
191
+
192
+ .session-list li.active {
193
+ background: #e3f2fd;
194
+ }
195
+
196
+ .chat-main {
197
+ flex: 1;
198
+ display: flex;
199
+ flex-direction: column;
200
+ padding: 16px;
201
+ }
202
+
203
+ .message-list {
204
+ flex: 1;
205
+ overflow-y: auto;
206
+ }
207
+
208
+ .message {
209
+ margin-bottom: 16px;
210
+ padding: 12px;
211
+ border-radius: 8px;
212
+ }
213
+
214
+ .message.user {
215
+ background: #e3f2fd;
216
+ margin-left: 20%;
217
+ }
218
+
219
+ .message.assistant {
220
+ background: #f5f5f5;
221
+ margin-right: 20%;
222
+ }
223
+
224
+ .message.reasoning {
225
+ background: #fff3e0;
226
+ font-size: 0.9em;
227
+ }
228
+
229
+ .message.tool {
230
+ background: #e8f5e9;
231
+ font-family: monospace;
232
+ }
233
+
234
+ .input-area {
235
+ display: flex;
236
+ gap: 8px;
237
+ padding-top: 16px;
238
+ border-top: 1px solid #ddd;
239
+ }
240
+
241
+ .input-area textarea {
242
+ flex: 1;
243
+ resize: none;
244
+ padding: 8px;
245
+ }
246
+
247
+ .streaming-indicator {
248
+ color: #2196f3;
249
+ animation: blink 1s infinite;
250
+ }
251
+
252
+ @keyframes blink {
253
+ 50% { opacity: 0; }
254
+ }
255
+ </style>
256
+ ```
257
+
258
+ ---
259
+
260
+ ## 与 chat-x 组件完整集成
261
+
262
+ ```vue
263
+ <template>
264
+ <div class="chat-page">
265
+ <!-- 预设问题(空消息时显示) -->
266
+ <ShortcutBtns
267
+ v-if="message.list.value.length === 0 && predefinedQuestions.length > 0"
268
+ :shortcuts="predefinedQuestions.map((q, i) => ({ id: String(i), name: q }))"
269
+ @select-shortcut="handleQuestionClick"
270
+ />
271
+
272
+ <!-- 消息容器 -->
273
+ <MessageContainer
274
+ v-else
275
+ v-model:selected-messages="selectedMessages"
276
+ :messages="message.list.value"
277
+ :message-status="messageStatus"
278
+ :enable-selection="enableSelection"
279
+ :on-agent-action="handleAgentAction"
280
+ :on-user-action="handleUserAction"
281
+ @stop-streaming="handleStop"
282
+ />
283
+
284
+ <!-- 快捷指令表单 -->
285
+ <ShortcutRender
286
+ v-if="selectedShortcut?.components?.length"
287
+ :name="selectedShortcut.name"
288
+ :components="selectedShortcut.components"
289
+ :form-model="selectedShortcut.formModel"
290
+ @close="selectedShortcut = null"
291
+ @submit="handleShortcutSubmit"
292
+ />
293
+
294
+ <!-- 输入框 -->
295
+ <ChatInput
296
+ v-else
297
+ v-model="userInput"
298
+ v-model:cite="citeContent"
299
+ :message-status="messageStatus"
300
+ :placeholder="placeholder"
301
+ :prompts="prompts"
302
+ :resources="resources"
303
+ :shortcuts="shortcuts"
304
+ :shortcut-id="selectedShortcut?.id"
305
+ :on-send-message="handleSend"
306
+ :on-stop-sending="handleStop"
307
+ @select-shortcut="handleSelectShortcut"
308
+ @delete-shortcut="selectedShortcut = null"
309
+ />
310
+
311
+ <!-- 划词选择 -->
312
+ <AiSelection
313
+ v-model:visible="aiSelectionVisible"
314
+ :shortcuts="shortcuts"
315
+ @select-shortcut="handleSelectShortcut"
316
+ />
317
+ </div>
318
+ </template>
319
+
320
+ <script setup lang="ts">
321
+ import { ref, computed, onMounted, onUnmounted } from 'vue';
322
+ import {
323
+ ChatInput,
324
+ MessageContainer,
325
+ ShortcutBtns,
326
+ ShortcutRender,
327
+ AiSelection,
328
+ MessageStatus,
329
+ type Shortcut,
330
+ type IToolBtn,
331
+ } from '@blueking/chat-x';
332
+ import { useChatHelper, AGUIProtocol, MessageRole } from '@blueking/chat-helper';
333
+
334
+ // 状态
335
+ const userInput = ref('');
336
+ const citeContent = ref('');
337
+ const selectedShortcut = ref<Shortcut | null>(null);
338
+ const selectedMessages = ref([]);
339
+ const enableSelection = ref(false);
340
+ const aiSelectionVisible = ref(false);
341
+ const isStreaming = ref(false);
342
+
343
+ // 配置
344
+ const placeholder = ref('请输入消息...');
345
+ const prompts = ref(['帮我写一篇文章', '解释这段代码']);
346
+ const resources = ref([
347
+ { id: 'file', name: '上传文件', type: 'file' },
348
+ { id: 'image', name: '上传图片', type: 'image' },
349
+ ]);
350
+ const shortcuts = ref<Shortcut[]>([
351
+ { id: 'ask', name: '问问小鲸' },
352
+ { id: 'translate', name: '翻译' },
353
+ { id: 'explain', name: '解释' },
354
+ ]);
355
+
356
+ // chatHelper
357
+ const chatHelper = useChatHelper({
358
+ requestData: {
359
+ urlPrefix: '/api/',
360
+ headers: () => ({
361
+ Authorization: `Bearer ${getToken()}`,
362
+ }),
363
+ },
364
+ protocol: new AGUIProtocol({
365
+ onStart: () => { isStreaming.value = true; },
366
+ onDone: () => { isStreaming.value = false; },
367
+ onError: (error) => {
368
+ isStreaming.value = false;
369
+ showError(error.message);
370
+ },
371
+ }),
372
+ });
373
+
374
+ const { agent, session, message } = chatHelper;
375
+
376
+ // 计算属性
377
+ const messageStatus = computed(() =>
378
+ isStreaming.value ? MessageStatus.Streaming : MessageStatus.Complete
379
+ );
380
+
381
+ const predefinedQuestions = computed(() =>
382
+ agent.info.value?.conversationSettings?.predefinedQuestions || []
383
+ );
384
+
385
+ // 初始化
386
+ onMounted(async () => {
387
+ await agent.getAgentInfo();
388
+ await session.getSessions();
389
+
390
+ if (session.list.value.length > 0) {
391
+ await session.chooseSession(session.list.value[0].sessionCode);
392
+ } else {
393
+ // 没有会话则创建一个
394
+ await session.createSession({
395
+ sessionCode: `session_${Date.now()}`,
396
+ sessionName: '新会话',
397
+ });
398
+ }
399
+ });
400
+
401
+ onUnmounted(() => {
402
+ agent.stopChat();
403
+ });
404
+
405
+ // 发送消息
406
+ const handleSend = async (value: string) => {
407
+ if (!session.current.value?.sessionCode || !value.trim()) return;
408
+
409
+ // 清空输入
410
+ userInput.value = '';
411
+
412
+ // 构建消息属性
413
+ const property = citeContent.value
414
+ ? { extra: { cite: citeContent.value } }
415
+ : undefined;
416
+ citeContent.value = '';
417
+
418
+ // 发送
419
+ await agent.chat(
420
+ value,
421
+ session.current.value.sessionCode,
422
+ undefined,
423
+ undefined,
424
+ property
425
+ );
426
+ };
427
+
428
+ // 停止
429
+ const handleStop = () => {
430
+ agent.stopChat();
431
+ };
432
+
433
+ // 预设问题点击
434
+ const handleQuestionClick = (shortcut: Shortcut) => {
435
+ userInput.value = shortcut.name;
436
+ handleSend(shortcut.name);
437
+ };
438
+
439
+ // 快捷指令选择
440
+ const handleSelectShortcut = (shortcut: Shortcut, text?: string) => {
441
+ selectedShortcut.value = shortcut;
442
+ if (text) {
443
+ userInput.value = text;
444
+ }
445
+ };
446
+
447
+ // 快捷指令表单提交
448
+ const handleShortcutSubmit = (formModel: Record<string, unknown>) => {
449
+ // 根据快捷指令构建消息
450
+ const command = selectedShortcut.value?.id;
451
+ const content = formModel.content as string || userInput.value;
452
+
453
+ selectedShortcut.value = null;
454
+
455
+ agent.chat(
456
+ content,
457
+ session.current.value!.sessionCode,
458
+ undefined,
459
+ undefined,
460
+ { extra: { command, ...formModel } }
461
+ );
462
+ };
463
+
464
+ // AI 消息操作
465
+ const handleAgentAction = async (tool: IToolBtn) => {
466
+ switch (tool.id) {
467
+ case 'copy':
468
+ // 复制由组件内部处理
469
+ break;
470
+ case 'like':
471
+ // 返回点赞原因
472
+ return await session.getSessionFeedbackReasons(1) || ['回答准确', '信息全面'];
473
+ case 'unlike':
474
+ // 返回点踩原因
475
+ return await session.getSessionFeedbackReasons(-1) || ['信息错误', '回答不相关'];
476
+ case 'rebuild':
477
+ // 重新生成
478
+ const lastUserMsg = message.list.value.findLast(m => m.role === MessageRole.User);
479
+ if (lastUserMsg) {
480
+ const content = typeof lastUserMsg.content === 'string'
481
+ ? lastUserMsg.content
482
+ : JSON.stringify(lastUserMsg.content);
483
+ await agent.chat(content, session.current.value!.sessionCode);
484
+ }
485
+ break;
486
+ }
487
+ };
488
+
489
+ // 用户消息操作
490
+ const handleUserAction = async (tool: IToolBtn, msgData: any) => {
491
+ switch (tool.id) {
492
+ case 'edit':
493
+ userInput.value = typeof msgData.content === 'string'
494
+ ? msgData.content
495
+ : '';
496
+ break;
497
+ case 'delete':
498
+ await message.deleteMessages([msgData]);
499
+ break;
500
+ }
501
+ };
502
+
503
+ // 辅助函数
504
+ function getToken() {
505
+ return localStorage.getItem('token') || '';
506
+ }
507
+
508
+ function showError(msg: string) {
509
+ console.error(msg);
510
+ // 显示错误提示
511
+ }
512
+ </script>
513
+
514
+ <style scoped>
515
+ .chat-page {
516
+ display: flex;
517
+ flex-direction: column;
518
+ height: 100vh;
519
+ padding: 16px;
520
+ }
521
+ </style>
522
+ ```
523
+
524
+ ---
525
+
526
+ ## 自定义 Protocol 示例
527
+
528
+ ```typescript
529
+ import {
530
+ AGUIProtocol,
531
+ type ITextMessageChunkEvent,
532
+ type IThinkingStartEvent,
533
+ type IToolCallStartEvent,
534
+ type IRunErrorEvent,
535
+ } from '@blueking/chat-helper';
536
+
537
+ class EnhancedProtocol extends AGUIProtocol {
538
+ private analytics: Analytics;
539
+ private audioPlayer: AudioPlayer;
540
+
541
+ constructor(options?: any) {
542
+ super(options);
543
+ this.analytics = new Analytics();
544
+ this.audioPlayer = new AudioPlayer();
545
+ }
546
+
547
+ // 文本消息 - 实时语音播报
548
+ handleTextMessageChunkEvent(event: ITextMessageChunkEvent) {
549
+ // 语音播报
550
+ if (this.audioPlayer.isEnabled()) {
551
+ this.audioPlayer.speak(event.delta || '');
552
+ }
553
+
554
+ // 记录统计
555
+ this.analytics.trackTextChunk(event.messageId);
556
+
557
+ // 调用父类
558
+ super.handleTextMessageChunkEvent(event);
559
+ }
560
+
561
+ // 思考开始 - 显示动画
562
+ handleThinkingStartEvent(event: IThinkingStartEvent) {
563
+ showThinkingAnimation(event.title);
564
+ super.handleThinkingStartEvent(event);
565
+ }
566
+
567
+ // 思考结束 - 统计时间
568
+ handleThinkingEndEvent(event: any) {
569
+ hideThinkingAnimation();
570
+ this.analytics.trackThinkingTime(event.duration);
571
+ super.handleThinkingEndEvent(event);
572
+ }
573
+
574
+ // 工具调用 - 显示通知
575
+ handleToolCallStartEvent(event: IToolCallStartEvent) {
576
+ showNotification(`正在调用工具: ${event.toolCallName}`);
577
+ this.analytics.trackToolUsage(event.toolCallName);
578
+ super.handleToolCallStartEvent(event);
579
+ }
580
+
581
+ // 错误处理 - 上报
582
+ handleRunErrorEvent(event: IRunErrorEvent) {
583
+ // 上报错误
584
+ this.analytics.reportError({
585
+ code: event.code,
586
+ message: event.message,
587
+ timestamp: Date.now(),
588
+ });
589
+
590
+ // 显示友好提示
591
+ showErrorToast(event.message);
592
+
593
+ super.handleRunErrorEvent(event);
594
+ }
595
+ }
596
+
597
+ // 使用
598
+ const chatHelper = useChatHelper({
599
+ requestData: { urlPrefix: '/api/' },
600
+ protocol: new EnhancedProtocol({
601
+ onStart: () => showLoadingOverlay(),
602
+ onDone: () => hideLoadingOverlay(),
603
+ }),
604
+ });
605
+ ```
606
+
607
+ ---
608
+
609
+ ## 多租户/多应用场景
610
+
611
+ ```typescript
612
+ import { ref, watch } from 'vue';
613
+ import { useChatHelper, AGUIProtocol } from '@blueking/chat-helper';
614
+
615
+ // 当前应用和租户
616
+ const currentApp = ref('app-1');
617
+ const currentTenant = ref('tenant-1');
618
+
619
+ // 创建 chatHelper
620
+ const chatHelper = useChatHelper({
621
+ requestData: {
622
+ urlPrefix: '/api/',
623
+ // 动态获取
624
+ data: () => ({
625
+ app_id: currentApp.value,
626
+ tenant_id: currentTenant.value,
627
+ }),
628
+ headers: () => ({
629
+ Authorization: `Bearer ${getToken()}`,
630
+ 'X-App-ID': currentApp.value,
631
+ 'X-Tenant-ID': currentTenant.value,
632
+ }),
633
+ },
634
+ });
635
+
636
+ // 切换应用时重置
637
+ watch([currentApp, currentTenant], async () => {
638
+ // 停止当前聊天
639
+ chatHelper.agent.stopChat();
640
+
641
+ // 重新加载数据
642
+ await chatHelper.session.getSessions();
643
+ if (chatHelper.session.list.value.length > 0) {
644
+ await chatHelper.session.chooseSession(
645
+ chatHelper.session.list.value[0].sessionCode
646
+ );
647
+ }
648
+ });
649
+ ```
650
+
651
+ ---
652
+
653
+ ## 消息本地缓存
654
+
655
+ ```typescript
656
+ import { watch } from 'vue';
657
+ import { useChatHelper } from '@blueking/chat-helper';
658
+
659
+ const chatHelper = useChatHelper({
660
+ requestData: { urlPrefix: '/api/' },
661
+ });
662
+
663
+ const { session, message } = chatHelper;
664
+
665
+ // 缓存 key
666
+ const getCacheKey = (sessionCode: string) => `messages_${sessionCode}`;
667
+
668
+ // 监听消息变化并缓存
669
+ watch(
670
+ () => message.list.value,
671
+ (newList) => {
672
+ if (session.current.value?.sessionCode) {
673
+ localStorage.setItem(
674
+ getCacheKey(session.current.value.sessionCode),
675
+ JSON.stringify(newList)
676
+ );
677
+ }
678
+ },
679
+ { deep: true }
680
+ );
681
+
682
+ // 加载缓存
683
+ const loadCachedMessages = (sessionCode: string) => {
684
+ const cached = localStorage.getItem(getCacheKey(sessionCode));
685
+ if (cached) {
686
+ try {
687
+ message.list.value = JSON.parse(cached);
688
+ } catch (e) {
689
+ console.error('缓存解析失败:', e);
690
+ }
691
+ }
692
+ };
693
+
694
+ // 在选择会话前先加载缓存
695
+ const chooseSessionWithCache = async (sessionCode: string) => {
696
+ loadCachedMessages(sessionCode);
697
+ await session.chooseSession(sessionCode);
698
+ };
699
+ ```
700
+
701
+ ---
702
+
703
+ ## 状态指示器组件
704
+
705
+ ```vue
706
+ <template>
707
+ <div v-if="status" class="status-bar" :class="status.type">
708
+ <span class="icon">{{ statusIcon }}</span>
709
+ <span class="message">{{ status.message }}</span>
710
+ </div>
711
+ </template>
712
+
713
+ <script setup lang="ts">
714
+ import { ref, computed } from 'vue';
715
+ import {
716
+ useChatHelper,
717
+ AGUIProtocol,
718
+ type IThinkingStartEvent,
719
+ type IToolCallStartEvent,
720
+ } from '@blueking/chat-helper';
721
+
722
+ const status = ref<{ type: string; message: string } | null>(null);
723
+
724
+ const statusIcon = computed(() => {
725
+ switch (status.value?.type) {
726
+ case 'thinking': return '🤔';
727
+ case 'tool': return '🔧';
728
+ case 'streaming': return '📝';
729
+ case 'error': return '❌';
730
+ case 'success': return '✅';
731
+ default: return '';
732
+ }
733
+ });
734
+
735
+ // 自定义 Protocol
736
+ class StatusProtocol extends AGUIProtocol {
737
+ handleThinkingStartEvent(event: IThinkingStartEvent) {
738
+ status.value = { type: 'thinking', message: `思考中: ${event.title || ''}` };
739
+ super.handleThinkingStartEvent(event);
740
+ }
741
+
742
+ handleThinkingEndEvent(event: any) {
743
+ status.value = null;
744
+ super.handleThinkingEndEvent(event);
745
+ }
746
+
747
+ handleToolCallStartEvent(event: IToolCallStartEvent) {
748
+ status.value = { type: 'tool', message: `调用工具: ${event.toolCallName}` };
749
+ super.handleToolCallStartEvent(event);
750
+ }
751
+
752
+ handleToolCallEndEvent(event: any) {
753
+ status.value = null;
754
+ super.handleToolCallEndEvent(event);
755
+ }
756
+ }
757
+
758
+ const chatHelper = useChatHelper({
759
+ requestData: { urlPrefix: '/api/' },
760
+ protocol: new StatusProtocol({
761
+ onStart: () => {
762
+ status.value = { type: 'streaming', message: '正在生成回复...' };
763
+ },
764
+ onDone: () => {
765
+ status.value = { type: 'success', message: '回复完成' };
766
+ setTimeout(() => { status.value = null; }, 2000);
767
+ },
768
+ onError: (error) => {
769
+ status.value = { type: 'error', message: error.message };
770
+ },
771
+ }),
772
+ });
773
+ </script>
774
+
775
+ <style scoped>
776
+ .status-bar {
777
+ padding: 8px 16px;
778
+ border-radius: 4px;
779
+ display: flex;
780
+ align-items: center;
781
+ gap: 8px;
782
+ }
783
+ .status-bar.thinking { background: #e3f2fd; }
784
+ .status-bar.tool { background: #fff3e0; }
785
+ .status-bar.streaming { background: #e8f5e9; }
786
+ .status-bar.error { background: #ffebee; color: #c62828; }
787
+ .status-bar.success { background: #e8f5e9; }
788
+ </style>
789
+ ```