返回 AiToEarn
README.md
根目录 / project / aitoearn-web / src / store / agent / README.md
1 # Agent Store
2
3 全局 AI Agent 任务状态管理模块。
4
5 ## 目录结构
6
7 ```text
8 src/store/agent/
9 ├── index.ts # 主入口,组装 store 并导出
10 ├── agent.types.ts # 类型定义
11 ├── agent.constants.ts # 常量定义
12 ├── agent.state.ts # 初始状态
13 ├── agent.methods.ts # 核心方法
14 ├── handlers/ # 处理器目录
15 │ ├── index.ts # 处理器导出
16 │ └── action.handlers.ts # Action 处理器
17 ├── utils/ # 工具函数目录
18 │ ├── index.ts # 工具导出
19 │ ├── refs.ts # Refs 管理
20 │ ├── message.ts # 消息工具
21 │ └── progress.ts # 进度工具
22 ├── task-instance/ # 任务实例目录
23 │ ├── index.ts # 模块导出
24 │ ├── task-instance.types.ts # 类型定义
25 │ ├── TaskInstance.ts # 核心类(代理层)
26 │ ├── message.handler.ts # 消息处理逻辑
27 │ ├── workflow.handler.ts # 工作流处理逻辑
28 │ └── sse.handler.ts # SSE 消息处理逻辑
29 └── README.md # 本文档
30 ```
31
32 ## 模块说明
33
34 ### index.ts - 主入口
35
36 组装 store 并导出所有模块:
37
38 ```typescript
39 import { useAgentStore } from '@/store/agent'
40
41 const { isGenerating, messages, createTask, stopTask } = useAgentStore()
42 ```
43
44 ### handlers/ - 处理器目录
45
46 #### Action 处理器 (action.handlers.ts)
47
48 使用策略模式处理任务结果:
49
50 ```typescript
51 import { ActionRegistry } from '@/store/agent'
52
53 // 注册自定义 Action
54 ActionRegistry.register({
55 type: 'customAction',
56 canHandle: (taskData) => taskData.action === 'customAction',
57 execute: async (taskData, context) => {
58 // 处理逻辑
59 },
60 })
61 ```
62
63 内置 Action:
64
65 - `navigateToPublish` - 导航到发布页面
66 - `navigateToDraft` - 导航到草稿箱
67 - `saveDraft` - 保存草稿
68 - `updateChannel` - 更新频道授权
69 - `loginChannel` - 登录频道
70
71 ### utils/ - 工具目录
72
73 #### refs.ts - Refs 管理
74
75 管理内部引用变量,避免闭包问题:
76
77 ```typescript
78 import { createAgentRefs, resetAgentRefs } from '@/store/agent'
79
80 const refs = createAgentRefs()
81 resetAgentRefs(refs) // 重置 refs
82 ```
83
84 #### message.ts - 消息工具
85
86 消息创建和状态管理:
87
88 ```typescript
89 import { createMessageUtils } from '@/store/agent'
90
91 const messageUtils = createMessageUtils({ refs, set, get })
92 const userMsg = messageUtils.createUserMessage('Hello', [])
93 messageUtils.markMessageDone()
94 ```
95
96 #### progress.ts - 进度工具
97
98 进度计算和状态配置(内部使用)。
99
100 ### task-instance/ - 任务实例目录
101
102 TaskInstance 是解决多任务消息混乱问题的核心架构。每个 Agent 任务对应一个独立的 TaskInstance 实例,确保消息状态完全隔离。
103
104 #### 模块拆分结构
105
106 | 文件 | 行数 | 职责 |
107 | ------------------------ | ---- | ---------------------------------- |
108 | `task-instance.types.ts` | ~100 | 类型定义(上下文接口、回调接口等) |
109 | `message.handler.ts` | ~250 | 消息处理(创建、更新、状态管理) |
110 | `workflow.handler.ts` | ~260 | 工作流处理(步骤管理、工具调用) |
111 | `sse.handler.ts` | ~290 | SSE 消息分发和处理 |
112 | `TaskInstance.ts` | ~415 | 核心类(代理层,整合各模块) |
113
114 #### TaskInstance.ts - 核心类
115
116 **核心设计:**
117
118 - **instanceId**: 实例唯一标识(创建时生成,不可变)
119 - **taskId**: 任务ID(可从临时ID迁移到真实ID)
120 - **实例级 refs**: 每个实例有独立的 `currentAssistantMessageId`、`streamingText`、`currentStepWorkflow` 等
121 - **SSE 回调绑定**: SSE 回调绑定到具体 TaskInstance,消除多任务切换时的竞态条件
122 - **代理模式**: TaskInstance 作为门面,代理到各个 handler 模块
123
124 #### message.handler.ts - 消息处理
125
126 提供消息创建和状态更新功能:
127
128 ```typescript
129 // 消息创建
130 createUserMessage(content, medias?) // 创建用户消息
131 createAssistantMessage(ctx) // 创建 AI 回复消息
132
133 // 消息状态更新
134 markMessageDone(ctx) // 标记完成
135 markMessageError(ctx, error) // 标记错误
136 updateMessageContent(ctx, content) // 更新内容
137 updateMessageWithActions(ctx, content, actions) // 更新带 actions
138 ```
139
140 #### workflow.handler.ts - 工作流处理
141
142 处理工作流步骤和工具调用:
143
144 ```typescript
145 startNewStep(ctx) // 开始新步骤
146 addWorkflowStep(ctx, step) // 添加工作流步骤
147 updateLastWorkflowStep(ctx, updater) // 更新最后一步
148 handleToolCallComplete(ctx, name, input) // 处理工具调用完成
149 handleToolResult(ctx, resultText) // 处理工具结果
150 ```
151
152 #### sse.handler.ts - SSE 消息处理
153
154 处理来自服务端的 SSE 事件:
155
156 ```typescript
157 handleSSEMessage(ctx, msg, callbacks?) // SSE 消息处理主入口
158 // 内部处理: stream_event, assistant, user, result, error, done
159 ```
160
161 **使用示例:**
162
163 ```typescript
164 import { TaskInstance, getTaskInstance, getOrCreateTaskInstance } from '@/store/agent'
165
166 // 创建任务实例上下文
167 const instanceContext: ITaskInstanceContext = {
168 syncToStore: (taskId, updater) => updateTaskData(taskId, updater),
169 getData: (taskId) => getTaskData(taskId),
170 migrateTaskData: (fromTaskId, toTaskId) => {
171 /* 迁移数据 */
172 },
173 setCurrentTaskId: (taskId) => set({ currentTaskId: taskId }),
174 }
175
176 // 创建新的任务实例
177 const instance = new TaskInstance(tempTaskId, instanceContext)
178
179 // 设置翻译函数和 Action 上下文
180 instance.setTranslation(t)
181 instance.setActionContext(actionContext)
182
183 // 通过实例添加消息
184 const userMessage = instance.createUserMessage(prompt, medias)
185 instance.addMessage(userMessage)
186
187 // 通过实例处理 SSE 消息
188 instance.handleSSEMessage(sseMessage, callbacks)
189
190 // 获取或创建任务实例
191 const instance = getOrCreateTaskInstance(taskId, context)
192
193 // 获取现有实例
194 const existing = getTaskInstance(taskId)
195 ```
196
197 **核心 API:**
198
199 | 方法 | 说明 |
200 | --------------------------------------- | -------------------------------- |
201 | `createUserMessage(content, medias?)` | 创建用户消息 |
202 | `createAssistantMessage()` | 创建 AI 回复消息 |
203 | `addMessage(message)` | 添加消息到当前任务 |
204 | `handleSSEMessage(message, callbacks?)` | 处理 SSE 消息 |
205 | `markMessageDone()` | 标记当前消息完成 |
206 | `markMessageError(error)` | 标记当前消息错误 |
207 | `setIsGenerating(value)` | 设置生成状态 |
208 | `setProgress(value)` | 设置进度 |
209 | `migrateToRealTaskId(newTaskId)` | 更新任务ID(临时ID迁移到真实ID) |
210 | `resetForNewRound()` | 重置实例状态(新一轮对话) |
211 | `abort()` | 中止 SSE 连接 |
212
213 ## 使用示例
214
215 ### 基础使用
216
217 ```tsx
218 import { useAgentStore } from '@/store/agent'
219 import { useShallow } from 'zustand/react/shallow'
220
221 function ChatPage() {
222 const { messages, isGenerating, createTask, setActionContext } = useAgentStore(
223 useShallow((state) => ({
224 messages: state.messages,
225 isGenerating: state.isGenerating,
226 createTask: state.createTask,
227 setActionContext: state.setActionContext,
228 }))
229 )
230
231 const router = useRouter()
232 const { t } = useTranslation()
233
234 // 设置 action 上下文
235 useEffect(() => {
236 setActionContext({ router, lng: 'zh-CN', t })
237 }, [router, t])
238
239 const handleSend = async (prompt: string) => {
240 await createTask({ prompt, t })
241 }
242
243 return <div>{/* ... */}</div>
244 }
245 ```
246
247 ### 继续对话
248
249 ```tsx
250 const { continueTask } = useAgentStore()
251
252 const handleContinue = async (prompt: string, taskId: string) => {
253 await continueTask({ prompt, taskId, t })
254 }
255 ```
256
257 ## 扩展指南
258
259 ### 添加 Action 处理器
260
261 1. 在 `handlers/action.handlers.ts` 中添加:
262
263 ```typescript
264 const customActionHandler: IActionHandler = {
265 type: 'customAction',
266 canHandle: (taskData) => taskData.action === 'customAction',
267 execute: async (taskData, context) => {
268 // 处理逻辑
269 },
270 }
271 ```
272
273 2. 添加到处理器数组或运行时注册:
274
275 ```typescript
276 ActionRegistry.register(customActionHandler)
277 ```
278
279 ### 添加工具函数
280
281 在 `utils/` 目录下创建新文件,并在 `utils/index.ts` 中导出。
282
283 ## 注意事项
284
285 1. **消息 ID 匹配**:消息更新使用 ID 精确匹配,避免更新错误消息。
286 2. **Refs vs State**:频繁更新的数据使用 ref,避免闭包问题。
287 3. **Action 上下文**:使用 action 前需要调用 `setActionContext`。
288 4. **插件发布**:小红书/抖音需要浏览器插件支持。
289
290 ## ⚠️ 重要:避免 `this` 上下文丢失问题
291
292 ### 问题描述
293
294 在使用工厂函数(如 `createStoreMethods`)返回对象字面量时,如果方法内部使用 `this` 来调用其他方法,当这些方法被作为回调函数传递时,`this` 的上下文会丢失。
295
296 ### 错误示例
297
298 ```typescript
299 // ❌ 错误:在对象字面量中使用 this
300 function createMethods() {
301 return {
302 methodA() {
303 console.log('A')
304 },
305 methodB() {
306 // 当 methodB 作为回调传递时,this 会丢失
307 this.methodA() // TypeError: Cannot read properties of undefined
308 },
309 }
310 }
311
312 // 使用场景(会出错)
313 const methods = createMethods()
314 someAsyncFunction((data) => {
315 methods.methodB() // 正常工作
316 })
317
318 // 或者
319 someAsyncFunction(methods.methodB) // this 丢失!
320 ```
321
322 ### 正确做法
323
324 将方法定义为独立的闭包函数,然后在返回对象中引用它们:
325
326 ```typescript
327 // ✅ 正确:使用闭包引用
328 function createMethods() {
329 // 先定义为独立函数
330 function methodA() {
331 console.log('A')
332 }
333
334 function methodB() {
335 // 直接调用闭包中的函数,不依赖 this
336 methodA()
337 }
338
339 // 返回方法对象
340 return {
341 methodA,
342 methodB,
343 }
344 }
345 ```
346
347 ### 检查清单
348
349 编写新方法时,请检查:
350
351 1. 方法是否会作为回调函数传递?
352 2. 方法内部是否调用了同一对象的其他方法?
353 3. 如果是,是否使用了 `this`?
354
355 如果满足以上条件,请使用闭包模式而非 `this`。
356
356 lines MARKDOWN