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,583 @@
1
+ # chat-helper 与 chat-x 集成指南
2
+
3
+ 本指南详细说明如何将 `@blueking/chat-helper` SDK 与 `@blueking/chat-x` UI 组件库集成,构建完整的 AI 智能体应用。
4
+
5
+ ## 架构概述
6
+
7
+ ```
8
+ ┌──────────────────────────────────────────────────────────────┐
9
+ │ 应用层 (Vue 组件) │
10
+ ├──────────────────────────────────────────────────────────────┤
11
+ │ ┌─────────────────────┐ ┌─────────────────────────────┐ │
12
+ │ │ @blueking/chat-x │ │ @blueking/chat-helper │ │
13
+ │ │ (UI 组件库) │ │ (业务逻辑 SDK) │ │
14
+ │ │ │ │ │ │
15
+ │ │ - ChatInput │◄──►│ - useChatHelper() │ │
16
+ │ │ - MessageContainer │ │ - agent │ │
17
+ │ │ - ShortcutBtns │ │ - session │ │
18
+ │ │ - ShortcutRender │ │ - message │ │
19
+ │ │ - AiSelection │ │ │ │
20
+ │ └─────────────────────┘ └─────────────────────────────┘ │
21
+ ├──────────────────────────────────────────────────────────────┤
22
+ │ 后端 API │
23
+ └──────────────────────────────────────────────────────────────┘
24
+ ```
25
+
26
+ **职责划分**:
27
+ - **chat-x**: 纯 UI 渲染,不包含业务逻辑
28
+ - **chat-helper**: 状态管理、API 调用、流式处理
29
+
30
+ ---
31
+
32
+ ## 状态映射
33
+
34
+ ### MessageStatus 映射
35
+
36
+ | chat-helper 状态 | chat-x MessageStatus | 触发场景 |
37
+ |-----------------|---------------------|----------|
38
+ | `message.status = 'pending'` | `MessageStatus.Pending` | 等待响应 |
39
+ | `message.status = 'streaming'` | `MessageStatus.Streaming` | 流式输出中 |
40
+ | `message.status = 'complete'` | `MessageStatus.Complete` | 响应完成 |
41
+ | `message.status = 'error'` | `MessageStatus.Error` | 发生错误 |
42
+ | `message.status = 'stop'` | `MessageStatus.Stop` | 用户停止 |
43
+ | `agent.isChatting = true` | `MessageStatus.Streaming` | 正在聊天 |
44
+
45
+ ### 状态计算示例
46
+
47
+ ```typescript
48
+ import { computed } from 'vue';
49
+ import { MessageStatus } from '@blueking/chat-x';
50
+ import { useChatHelper } from '@blueking/chat-helper';
51
+
52
+ const { agent, message } = useChatHelper({ /* ... */ });
53
+
54
+ // 方式 1:基于 agent.isChatting
55
+ const messageStatus = computed(() =>
56
+ agent.isChatting.value ? MessageStatus.Streaming : MessageStatus.Complete
57
+ );
58
+
59
+ // 方式 2:基于最后一条消息状态
60
+ const messageStatus = computed(() => {
61
+ const lastMsg = message.list.value.at(-1);
62
+ if (!lastMsg) return MessageStatus.Complete;
63
+
64
+ switch (lastMsg.status) {
65
+ case 'streaming': return MessageStatus.Streaming;
66
+ case 'pending': return MessageStatus.Pending;
67
+ case 'error': return MessageStatus.Error;
68
+ case 'stop': return MessageStatus.Stop;
69
+ default: return MessageStatus.Complete;
70
+ }
71
+ });
72
+ ```
73
+
74
+ ---
75
+
76
+ ## 组件对接
77
+
78
+ ### 1. ChatInput 对接
79
+
80
+ ```vue
81
+ <template>
82
+ <ChatInput
83
+ v-model="userInput"
84
+ v-model:cite="citeContent"
85
+ :message-status="messageStatus"
86
+ :placeholder="placeholder"
87
+ :prompts="prompts"
88
+ :resources="resources"
89
+ :shortcuts="shortcuts"
90
+ :shortcut-id="selectedShortcutId"
91
+ :on-send-message="handleSendMessage"
92
+ :on-stop-sending="handleStopSending"
93
+ @select-shortcut="handleSelectShortcut"
94
+ @delete-shortcut="handleDeleteShortcut"
95
+ />
96
+ </template>
97
+
98
+ <script setup lang="ts">
99
+ import { ref, computed } from 'vue';
100
+ import { ChatInput, MessageStatus, type Shortcut, type TagSchema } from '@blueking/chat-x';
101
+ import { useChatHelper, AGUIProtocol } from '@blueking/chat-helper';
102
+
103
+ const userInput = ref<string | TagSchema>('');
104
+ const citeContent = ref('');
105
+ const selectedShortcutId = ref<string | null>(null);
106
+ const isStreaming = ref(false);
107
+
108
+ const chatHelper = useChatHelper({
109
+ requestData: { urlPrefix: '/api/' },
110
+ protocol: new AGUIProtocol({
111
+ onStart: () => { isStreaming.value = true; },
112
+ onDone: () => { isStreaming.value = false; },
113
+ onError: () => { isStreaming.value = false; },
114
+ }),
115
+ });
116
+
117
+ const { agent, session, message } = chatHelper;
118
+
119
+ // 状态映射
120
+ const messageStatus = computed(() =>
121
+ isStreaming.value ? MessageStatus.Streaming : MessageStatus.Complete
122
+ );
123
+
124
+ // 发送消息
125
+ const handleSendMessage = async (value: string, docSchema: TagSchema) => {
126
+ if (!session.current.value?.sessionCode) return;
127
+
128
+ // 构建消息属性
129
+ const property = citeContent.value
130
+ ? { extra: { cite: citeContent.value } }
131
+ : undefined;
132
+
133
+ // 清空输入
134
+ userInput.value = '';
135
+ citeContent.value = '';
136
+
137
+ // 发送
138
+ await agent.chat(
139
+ value,
140
+ session.current.value.sessionCode,
141
+ undefined,
142
+ undefined,
143
+ property
144
+ );
145
+ };
146
+
147
+ // 停止发送
148
+ const handleStopSending = async () => {
149
+ agent.stopChat();
150
+ };
151
+
152
+ // 快捷指令选择
153
+ const handleSelectShortcut = (shortcut: Shortcut, text?: string) => {
154
+ selectedShortcutId.value = shortcut.id;
155
+ if (text) userInput.value = text;
156
+ };
157
+
158
+ // 删除快捷指令
159
+ const handleDeleteShortcut = () => {
160
+ selectedShortcutId.value = null;
161
+ };
162
+ </script>
163
+ ```
164
+
165
+ ### 2. MessageContainer 对接
166
+
167
+ ```vue
168
+ <template>
169
+ <MessageContainer
170
+ v-model:selected-messages="selectedMessages"
171
+ :messages="messages"
172
+ :message-status="messageStatus"
173
+ :enable-selection="enableSelection"
174
+ :on-agent-action="handleAgentAction"
175
+ :on-user-action="handleUserAction"
176
+ @stop-streaming="handleStopStreaming"
177
+ >
178
+ <!-- 自定义消息渲染(可选) -->
179
+ <template #default="{ message: msg }">
180
+ <ActivityMessage v-if="msg.role === 'activity'" :message="msg" />
181
+ <ReasoningMessage v-else-if="msg.role === 'reasoning'" :message="msg" />
182
+ <MessageRender v-else :message="msg" :on-action="handleAction" />
183
+ </template>
184
+ </MessageContainer>
185
+ </template>
186
+
187
+ <script setup lang="ts">
188
+ import { computed, ref } from 'vue';
189
+ import {
190
+ MessageContainer,
191
+ MessageRender,
192
+ MessageStatus,
193
+ type IToolBtn,
194
+ } from '@blueking/chat-x';
195
+ import { useChatHelper, MessageRole } from '@blueking/chat-helper';
196
+
197
+ const selectedMessages = ref([]);
198
+ const enableSelection = ref(false);
199
+
200
+ const { agent, session, message } = useChatHelper({ /* ... */ });
201
+
202
+ // 消息列表直接使用 chat-helper 的数据
203
+ const messages = computed(() => message.list.value);
204
+
205
+ // AI 消息操作
206
+ const handleAgentAction = async (tool: IToolBtn, msgData: any) => {
207
+ switch (tool.id) {
208
+ case 'copy':
209
+ // 复制由组件内部处理
210
+ break;
211
+ case 'like':
212
+ // 返回点赞原因列表
213
+ const likeReasons = await session.getSessionFeedbackReasons(1);
214
+ return likeReasons || ['回答准确', '信息全面', '解决了问题'];
215
+ case 'unlike':
216
+ // 返回点踩原因列表
217
+ const unlikeReasons = await session.getSessionFeedbackReasons(-1);
218
+ return unlikeReasons || ['信息错误', '回答不相关', '内容重复'];
219
+ case 'rebuild':
220
+ // 重新生成
221
+ await rebuildMessage(msgData);
222
+ break;
223
+ }
224
+ };
225
+
226
+ // 用户消息操作
227
+ const handleUserAction = async (tool: IToolBtn, msgData: any) => {
228
+ switch (tool.id) {
229
+ case 'edit':
230
+ // 编辑消息
231
+ editMessage(msgData);
232
+ break;
233
+ case 'delete':
234
+ // 删除消息
235
+ await message.deleteMessages([msgData]);
236
+ break;
237
+ }
238
+ };
239
+
240
+ // 停止流式
241
+ const handleStopStreaming = () => {
242
+ agent.stopChat();
243
+ };
244
+
245
+ // 重新生成
246
+ const rebuildMessage = async (msgData: any) => {
247
+ // 找到对应的用户消息
248
+ const index = message.list.value.findIndex(m => m.messageId === msgData.messageId);
249
+ if (index > 0) {
250
+ const userMsg = message.list.value[index - 1];
251
+ if (userMsg.role === MessageRole.User) {
252
+ const content = typeof userMsg.content === 'string'
253
+ ? userMsg.content
254
+ : JSON.stringify(userMsg.content);
255
+ await agent.chat(content, session.current.value!.sessionCode);
256
+ }
257
+ }
258
+ };
259
+
260
+ const editMessage = (msgData: any) => {
261
+ // 实现编辑逻辑
262
+ };
263
+ </script>
264
+ ```
265
+
266
+ ### 3. ShortcutBtns + ShortcutRender 对接
267
+
268
+ ```vue
269
+ <template>
270
+ <div class="shortcut-area">
271
+ <!-- 快捷指令按钮(空消息时显示) -->
272
+ <ShortcutBtns
273
+ v-if="showShortcutBtns"
274
+ :shortcuts="shortcuts"
275
+ @select-shortcut="handleSelectShortcut"
276
+ />
277
+
278
+ <!-- 快捷指令表单 -->
279
+ <ShortcutRender
280
+ v-if="selectedShortcut?.components?.length"
281
+ :name="selectedShortcut.name"
282
+ :components="selectedShortcut.components"
283
+ :form-model="selectedShortcut.formModel"
284
+ @close="handleCloseShortcut"
285
+ @submit="handleSubmitShortcut"
286
+ />
287
+ </div>
288
+ </template>
289
+
290
+ <script setup lang="ts">
291
+ import { ref, computed } from 'vue';
292
+ import { ShortcutBtns, ShortcutRender, type Shortcut } from '@blueking/chat-x';
293
+ import { useChatHelper } from '@blueking/chat-helper';
294
+
295
+ const selectedShortcut = ref<Shortcut | null>(null);
296
+
297
+ const { agent, session, message } = useChatHelper({ /* ... */ });
298
+
299
+ // 从 agent 信息获取快捷指令
300
+ const shortcuts = computed<Shortcut[]>(() => {
301
+ const commands = agent.info.value?.conversationSettings?.commands;
302
+ if (!commands) return [];
303
+
304
+ return commands.map(cmd => ({
305
+ id: cmd.id,
306
+ name: cmd.name,
307
+ icon: cmd.icon,
308
+ components: cmd.components?.map(comp => ({
309
+ type: comp.type,
310
+ key: comp.key,
311
+ name: comp.name,
312
+ props: {
313
+ placeholder: comp.placeholder,
314
+ options: comp.options,
315
+ rows: comp.rows,
316
+ },
317
+ fillBack: comp.fillBack,
318
+ required: comp.required,
319
+ })),
320
+ }));
321
+ });
322
+
323
+ // 是否显示快捷指令按钮
324
+ const showShortcutBtns = computed(() =>
325
+ message.list.value.length === 0 && shortcuts.value.length > 0
326
+ );
327
+
328
+ // 选择快捷指令
329
+ const handleSelectShortcut = (shortcut: Shortcut, text?: string) => {
330
+ selectedShortcut.value = {
331
+ ...shortcut,
332
+ formModel: text ? { content: text } : {},
333
+ };
334
+ };
335
+
336
+ // 关闭快捷指令
337
+ const handleCloseShortcut = () => {
338
+ selectedShortcut.value = null;
339
+ };
340
+
341
+ // 提交快捷指令表单
342
+ const handleSubmitShortcut = async (formModel: Record<string, unknown>) => {
343
+ if (!session.current.value?.sessionCode) return;
344
+
345
+ // 构建消息内容
346
+ const content = (formModel.content as string) || selectedShortcut.value?.name || '';
347
+
348
+ // 构建消息属性
349
+ const property = {
350
+ extra: {
351
+ command: selectedShortcut.value?.id,
352
+ ...formModel,
353
+ },
354
+ };
355
+
356
+ selectedShortcut.value = null;
357
+
358
+ // 发送
359
+ await agent.chat(
360
+ content,
361
+ session.current.value.sessionCode,
362
+ undefined,
363
+ undefined,
364
+ property
365
+ );
366
+ };
367
+ </script>
368
+ ```
369
+
370
+ ### 4. AiSelection 对接
371
+
372
+ ```vue
373
+ <template>
374
+ <AiSelection
375
+ v-model:visible="aiSelectionVisible"
376
+ :shortcuts="selectionShortcuts"
377
+ :max-shortcut-count="3"
378
+ :offset="10"
379
+ @select-shortcut="handleSelectionShortcut"
380
+ @selection-change="handleSelectionChange"
381
+ />
382
+ </template>
383
+
384
+ <script setup lang="ts">
385
+ import { ref, computed } from 'vue';
386
+ import { AiSelection, type Shortcut } from '@blueking/chat-x';
387
+ import { useChatHelper } from '@blueking/chat-helper';
388
+
389
+ const aiSelectionVisible = ref(false);
390
+ const selectedText = ref('');
391
+
392
+ const { agent, session } = useChatHelper({ /* ... */ });
393
+
394
+ // 划词快捷指令(通常是子集)
395
+ const selectionShortcuts = computed<Shortcut[]>(() => [
396
+ { id: 'ask', name: '问问小鲸' },
397
+ { id: 'translate', name: '翻译' },
398
+ { id: 'explain', name: '解释' },
399
+ ]);
400
+
401
+ // 选区变化
402
+ const handleSelectionChange = (text: string) => {
403
+ selectedText.value = text;
404
+ };
405
+
406
+ // 选择快捷指令
407
+ const handleSelectionShortcut = async (shortcut: Shortcut, text: string) => {
408
+ if (!session.current.value?.sessionCode) return;
409
+
410
+ // 根据快捷指令构建内容
411
+ let content = text;
412
+ if (shortcut.id === 'translate') {
413
+ content = `翻译以下内容:\n${text}`;
414
+ } else if (shortcut.id === 'explain') {
415
+ content = `解释以下内容:\n${text}`;
416
+ }
417
+
418
+ // 发送
419
+ await agent.chat(
420
+ content,
421
+ session.current.value.sessionCode,
422
+ undefined,
423
+ undefined,
424
+ { extra: { command: shortcut.id, cite: text } }
425
+ );
426
+ };
427
+ </script>
428
+ ```
429
+
430
+ ---
431
+
432
+ ## 数据流转示例
433
+
434
+ ### 完整对话流程
435
+
436
+ ```
437
+ 1. 用户输入 → ChatInput.onSendMessage
438
+ 2. 调用 agent.chat(userInput, sessionCode)
439
+ ├─ 创建用户消息 → message.list 更新
440
+ └─ 发起 SSE 请求
441
+ 3. Protocol.onStart → isStreaming = true → MessageStatus.Streaming
442
+ 4. 流式事件处理:
443
+ ├─ TextMessageStart → 创建 AI 消息占位
444
+ ├─ TextMessageChunk → 更新 AI 消息内容
445
+ ├─ ThinkingStart → 创建推理消息
446
+ ├─ ToolCallStart → 创建工具调用消息
447
+ └─ TextMessageEnd → 标记消息完成
448
+ 5. Protocol.onDone → isStreaming = false → MessageStatus.Complete
449
+ 6. MessageContainer 自动更新渲染
450
+ ```
451
+
452
+ ### 会话切换流程
453
+
454
+ ```
455
+ 1. 用户点击会话 → session.chooseSession(sessionCode)
456
+ ├─ agent.stopChat() → 停止当前聊天
457
+ ├─ session.current = 目标会话
458
+ ├─ message.getMessages(sessionCode) → 加载消息
459
+ └─ agent.resumeStreamingChat() → 恢复流式(如果需要)
460
+ 2. MessageContainer 更新显示新会话消息
461
+ 3. ChatInput 清空输入状态
462
+ ```
463
+
464
+ ---
465
+
466
+ ## 错误处理
467
+
468
+ ### 统一错误处理
469
+
470
+ ```typescript
471
+ import { useChatHelper, AGUIProtocol } from '@blueking/chat-helper';
472
+ import { Message } from 'bkui-vue';
473
+
474
+ const chatHelper = useChatHelper({
475
+ requestData: { urlPrefix: '/api/' },
476
+ interceptors: {
477
+ response: (response) => {
478
+ // API 业务错误
479
+ if (response.data.code !== 0) {
480
+ Message({ theme: 'error', message: response.data.message });
481
+ }
482
+ return response;
483
+ },
484
+ },
485
+ protocol: new AGUIProtocol({
486
+ onError: (error) => {
487
+ // 流式错误
488
+ Message({ theme: 'error', message: `AI 响应错误: ${error.message}` });
489
+ },
490
+ }),
491
+ });
492
+ ```
493
+
494
+ ### 重试机制
495
+
496
+ ```typescript
497
+ const retryChat = async (retries = 3) => {
498
+ for (let i = 0; i < retries; i++) {
499
+ try {
500
+ await agent.chat(userInput, sessionCode);
501
+ return; // 成功则退出
502
+ } catch (error) {
503
+ if (i === retries - 1) {
504
+ throw error; // 最后一次仍失败则抛出
505
+ }
506
+ await new Promise(r => setTimeout(r, 1000 * (i + 1))); // 递增延迟
507
+ }
508
+ }
509
+ };
510
+ ```
511
+
512
+ ---
513
+
514
+ ## 性能优化
515
+
516
+ ### 1. 消息虚拟滚动
517
+
518
+ 对于大量消息,使用虚拟滚动:
519
+
520
+ ```vue
521
+ <template>
522
+ <VirtualScroll
523
+ :items="message.list.value"
524
+ :item-height="80"
525
+ v-slot="{ item }"
526
+ >
527
+ <MessageRender :message="item" />
528
+ </VirtualScroll>
529
+ </template>
530
+ ```
531
+
532
+ ### 2. 防抖发送
533
+
534
+ ```typescript
535
+ import { useDebounceFn } from '@vueuse/core';
536
+
537
+ const debouncedSend = useDebounceFn((value: string) => {
538
+ agent.chat(value, session.current.value!.sessionCode);
539
+ }, 300);
540
+ ```
541
+
542
+ ### 3. 消息分页加载
543
+
544
+ ```typescript
545
+ const loadMoreMessages = async () => {
546
+ if (message.isListLoading.value) return;
547
+
548
+ // 获取更早的消息
549
+ const oldestMsg = message.list.value[0];
550
+ if (oldestMsg?.messageId) {
551
+ await message.getMessages(sessionCode, { before: oldestMsg.messageId });
552
+ }
553
+ };
554
+ ```
555
+
556
+ ---
557
+
558
+ ## 最佳实践检查清单
559
+
560
+ ### 初始化
561
+ - [ ] `onMounted` 中初始化 agent 和 session
562
+ - [ ] 有会话则选择第一个,无则创建
563
+ - [ ] 处理初始化错误
564
+
565
+ ### 清理
566
+ - [ ] `onUnmounted` 中调用 `agent.stopChat()`
567
+ - [ ] 清理定时器和事件监听
568
+
569
+ ### 状态管理
570
+ - [ ] 使用 computed 映射 messageStatus
571
+ - [ ] 正确处理 loading 状态
572
+ - [ ] 使用枚举而非字符串
573
+
574
+ ### 错误处理
575
+ - [ ] 配置拦截器处理 API 错误
576
+ - [ ] 配置 Protocol.onError 处理流式错误
577
+ - [ ] 提供友好的错误提示
578
+
579
+ ### 用户体验
580
+ - [ ] 发送后清空输入框
581
+ - [ ] 流式响应时禁用发送按钮
582
+ - [ ] 提供停止按钮
583
+ - [ ] 自动滚动到最新消息