737 lines
25 KiB
Markdown
737 lines
25 KiB
Markdown
# Agent 组件开发问答(Q&A)
|
||
|
||
> 目录范围:`src/components/agent`
|
||
>
|
||
> 涵盖文件:
|
||
> - `AgentExpertCenterPage.tsx`
|
||
> - `AgentExpertEditPage.tsx`
|
||
> - `AgentChatPage.tsx`
|
||
> - `SkillCreatorPage.tsx`
|
||
> - `SkillsWorkshop.tsx`
|
||
> - `SkillCreatorThreadSidebar.tsx`
|
||
> - `AgentConversationList.tsx`
|
||
> - `SkillCreatorConversationItem.tsx`
|
||
> - `ChatInputComposer.tsx`
|
||
> - `AgentChatPage.css`
|
||
|
||
---
|
||
|
||
## Q1:这个目录整体是干什么的?
|
||
|
||
**A:这是“专家 Agent + 技能工坊 + 技能创建对话”的前端 UI 组件集合。**
|
||
|
||
- `AgentExpertCenterPage`:专家列表入口(我的专家/发布专家)。
|
||
- `AgentExpertEditPage`:专家配置编辑页(左侧对话预览 + 右侧配置)。
|
||
- `AgentChatPage`:某个专家的真实聊天页(含线程、文件上传、流式回答)。
|
||
- `SkillsWorkshop`:技能资产中心(搜索/筛选/接入/发布/删除/编辑)。
|
||
- `SkillCreatorPage`:技能创建/改写专用对话页(支持文件树预览和保存弹窗)。
|
||
- `SkillCreatorThreadSidebar`:复用的会话侧边栏(获取线程、新建、删除、切换)。
|
||
- `AgentConversationList` + `SkillCreatorConversationItem`:统一消息列表与消息卡片渲染。
|
||
- `ChatInputComposer`:统一输入框(发送/停止/上传)。
|
||
- `AgentChatPage.css`:`AgentChatPage` 专用动画和滚动条样式。
|
||
|
||
---
|
||
|
||
## Q2:核心共享数据模型有哪些?
|
||
|
||
**A:对话层主要围绕 `ConversationMessage`。**
|
||
|
||
- 定义位置:`AgentConversationList.tsx`
|
||
- 字段:
|
||
- `id`: 消息唯一标识。
|
||
- `role`: `'user' | 'assistant'`
|
||
- `content`: 文本内容(markdown 渲染)。
|
||
- `steps`: 思考步骤列表(数据来自 `data-thinking-step`)。
|
||
- `thinkings`: 推理内容列表(来自 `reasoning-*` 事件)。
|
||
- `toolCalls`: 工具调用(输入输出)列表。
|
||
|
||
**A:文件树使用 `FileNode`。**
|
||
|
||
- 在 `AgentChatPage`、`SkillCreatorPage` 内部定义并使用。
|
||
- 字段:`name/path/type/size/extension/children`。
|
||
|
||
**A:线程侧边栏使用 `SkillCreatorThread`。**
|
||
|
||
- 定义位置:`SkillCreatorThreadSidebar.tsx`
|
||
- 字段:`id/title/archived/expert_agent_id/created_at/updated_at`。
|
||
|
||
---
|
||
|
||
## Q3:整个目录的“数据流主干”怎么走?
|
||
|
||
**A:可以看成 3 条主链路。**
|
||
|
||
### 1) 专家链路(Center -> Edit -> Chat)
|
||
|
||
1. `AgentExpertCenterPage` 拉专家列表(我的/发布)。
|
||
2. 点击“编辑”跳 `AgentExpertEditPage`,加载专家详情 + 工具 + 技能绑定状态。
|
||
3. 点击“进行对话”跳 `AgentChatPage`,加载该专家详情和对应会话消息。
|
||
|
||
### 2) 技能链路(Workshop -> SkillCreator)
|
||
|
||
1. `SkillsWorkshop` 拉技能列表(official/mine),本地映射成 UI `Skill`。
|
||
2. 新建技能:创建会话后跳 `SkillCreatorPage?id=threadId`。
|
||
3. AI 辅助编辑已有技能:先创建线程,再 copy skill 到线程,再跳转 `SkillCreatorPage`。
|
||
|
||
### 3) 对话链路(输入 -> SSE -> UI -> 持久化)
|
||
|
||
1. `ChatInputComposer` 触发 `onSend`。
|
||
2. 页面组件(`AgentChatPage` 或 `SkillCreatorPage`)执行 `handleSendMessage`。
|
||
3. 先写入用户消息 + assistant 占位消息。
|
||
4. 调流式接口读取 SSE:
|
||
- `data-thinking-step` -> 更新 `steps`
|
||
- `reasoning-start/delta/end` -> 更新 `thinkings`
|
||
- `tool-input-available/tool-output-available` -> 更新 `toolCalls`
|
||
- `text-delta` -> 追加 `content`
|
||
5. 完成后将 assistant 结果再次 POST 到消息接口持久化。
|
||
|
||
---
|
||
|
||
## Q4:`AgentExpertCenterPage` 组件是干什么的?
|
||
|
||
**A:专家资产“总览入口”,负责展示列表和基础操作入口。**
|
||
|
||
### 组件职责
|
||
|
||
- 展示“我的专家”和“发布专家”两种视图。
|
||
- 打开专家详情弹窗(支持发布状态切换)。
|
||
- 提供“编辑”和“进行对话”跳转入口。
|
||
|
||
### 数据来源与接口
|
||
|
||
- 列表查询:
|
||
- `POST /api/v1/expert-agents/list`(按 `is_draft`)
|
||
- 我的专家场景还会额外 `GET /api/v1/expert-agents` 做合并去重。
|
||
- 状态切换:
|
||
- `PATCH /api/v1/expert-agents/{id}`(更新 `is_draft`)
|
||
|
||
### 数据流
|
||
|
||
- `activeTab` 改变 -> `fetchExpertList` 触发 -> 更新 `agentList`。
|
||
- `agentList` -> `cards(useMemo)` -> 渲染卡片。
|
||
- 卡片按钮:
|
||
- 编辑 -> `navigate('/qa-center/agent-expert-edit?id=...')`
|
||
- 对话 -> `navigate('/qa-center/agent-chat?id=...')`
|
||
|
||
### 维护注意
|
||
|
||
- “创建专家”按钮目前是 UI 壳子,未接创建接口。
|
||
- “删除”按钮当前仅 `console.log`,未接真实删除逻辑。
|
||
|
||
---
|
||
|
||
## Q5:`AgentExpertEditPage` 组件是干什么的?
|
||
|
||
**A:专家配置编辑工作台,同时内置一个“测试对话区”。**
|
||
|
||
### 组件职责
|
||
|
||
- 左侧:和该专家进行流式对话(非真实聊天线程,使用 no_real 线程)。
|
||
- 右侧:编辑专家基础配置(名称、模型、描述、系统提示词)。
|
||
- 管理工具绑定、技能绑定(弹窗操作)。
|
||
|
||
### 关键状态
|
||
|
||
- 对话相关:`messages/inputValue/threadId/isStreaming`
|
||
- 配置相关:`formValues`
|
||
- 工具相关:`toolOptions/selectedTools/pendingTools/boundToolIdMap`
|
||
- 技能相关:`skillOptions/selectedSkills/boundSkillMap/skillKeyword`
|
||
|
||
### 主要接口
|
||
|
||
- 专家详情:`GET /api/v1/expert-agents/{agentId}`
|
||
- 保存专家:`PUT /api/v1/expert-agents/{agentId}`
|
||
- 绑定工具:
|
||
- 查询:`GET /api/v1/expert-agents/{agentId}/tools`
|
||
- 添加:`POST /api/v1/expert-agents/{agentId}/tools`
|
||
- 删除:`DELETE /api/v1/expert-agents/{agentId}/tools/{toolId}`
|
||
- 绑定技能:
|
||
- 查询:`GET /api/v1/expert-agents/{agentId}/skills`
|
||
- 添加:`POST /api/v1/expert-agents/{agentId}/skills`
|
||
- 删除:`DELETE /api/v1/expert-agents/{agentId}/skills/{skillId}`
|
||
- 测试对话:
|
||
- 线程:`GET /api/v1/expert-agent-chat/threads/no_real/{agentId}`
|
||
- 用户消息:`POST /api/v1/expert-agent-chat/messages`
|
||
- SSE:`POST /api/v1/expert-agent-chat/run/stream/help_expert`
|
||
|
||
### 数据流(对话)
|
||
|
||
- 用户发送 -> append user msg -> 保证 threadId -> 保存用户消息 -> append assistant 占位 -> SSE 增量更新。
|
||
|
||
### 维护注意
|
||
|
||
- 右侧 `preview` 区底部 `ChatInputComposer` 是只读展示,不参与真实请求。
|
||
- 工具与技能弹窗是“前端状态 + 后端同步”双层状态,调试时优先检查 refresh 查询是否成功。
|
||
|
||
---
|
||
|
||
## Q6:`AgentChatPage` 组件是干什么的?
|
||
|
||
**A:专家“真实聊天页”,并带线程管理、文件上传和文件预览。**
|
||
|
||
### 组件职责
|
||
|
||
- 根据 URL `id` 读取专家详情。
|
||
- 管理当前线程(localStorage 持久化)。
|
||
- 获取历史消息并转换为统一消息结构。
|
||
- 流式发送消息并渲染思考过程。
|
||
- 管理会话文件(上传、树形结构、预览、侧栏缩放)。
|
||
|
||
### 关键状态
|
||
|
||
- 会话:`currentThreadId/messages/inputValue/isStreaming/isLoading`
|
||
- UI:`isThreadSidebarOpen/autoScrollEnabled/showScrollButton`
|
||
- 文件:`fileList/fileListThreadId/isFileSidebarOpen/expandedPaths/selectedFile/filePreview*`
|
||
|
||
### 主要接口
|
||
|
||
- 专家详情:`GET /api/v1/expert-agents/{agentId}`
|
||
- 会话消息:
|
||
- 历史:`GET /api/v1/expert-agent-chat/threads/{id}/messages`
|
||
- 保存用户:`POST /api/v1/expert-agent-chat/messages`
|
||
- SSE:`POST /api/v1/expert-agent-chat/run/stream`
|
||
- 保存助手:`POST /api/v1/expert-agent-chat/messages`
|
||
- 文件:
|
||
- 列表:`GET /api/v1/expert-agent-chat/threads/{id}/files/all`
|
||
- 上传:`POST /api/v1/expert-agent-chat/threads/{id}/files/upload`
|
||
- 预览:`GET /api/v1/expert-agent-chat/threads/{id}/files/preview?path=...`
|
||
|
||
### 关键行为细节
|
||
|
||
- **发送即出现 assistant 占位**:点击发送后立即插入 assistant 空消息,保证用户立刻看到“连接中”态。
|
||
- **连接中显示规则**:由 `AgentConversationList` 判断“最后一条 assistant 且还没有任何生成内容”才显示连接状态。
|
||
- **失败回滚**:发送或流式失败时移除该 assistant 占位,避免残留空卡片。
|
||
- **停止流式**:`AbortController` 中断请求,并清理 loading 态字段。
|
||
|
||
### 维护注意
|
||
|
||
- `threadStorageKey` 与 `agentId` 强绑定,切换专家会切换独立 thread 记忆。
|
||
- `normalizeHistoryContent` 是历史消息兼容核心,后端 content 结构变更时先改这里。
|
||
|
||
---
|
||
|
||
## Q7:`SkillCreatorPage` 组件是干什么的?
|
||
|
||
**A:技能创建/重构专用对话页,结构上是 `AgentChatPage` 的“技能场景定制版”。**
|
||
|
||
### 组件职责
|
||
|
||
- 从 query/localStorage 同步当前线程 ID。
|
||
- 加载技能会话历史与会话文件。
|
||
- 走技能专属流式接口输出思考步骤/推理/tool call/文本。
|
||
- 提供“保存技能”弹窗(当前主要是 UI + 本地状态)。
|
||
|
||
### 与 `AgentChatPage` 的主要差异
|
||
|
||
- 线程 ID 同步到 URL(`useSearchParams`)。
|
||
- 有独立 `isConnecting` 状态,用于连接中阶段展示。
|
||
- 流式接口是:`POST /api/v1/expert-agent-chat/skills-agent`
|
||
- 页面内注入了 dot/shimmer 动画样式(供消息连接中态使用)。
|
||
|
||
### 主要接口
|
||
|
||
- 会话消息:`GET /threads/{id}/messages`
|
||
- 流式:`POST /skills-agent`
|
||
- 保存 assistant:`POST /messages`
|
||
- 文件:`GET all` / `POST upload` / `GET preview`
|
||
|
||
### 数据流
|
||
|
||
- 发消息 -> append 用户 + assistant 占位 -> `isConnecting=true` -> 收到首个流块后 `isConnecting=false` -> 增量刷新 assistant -> 持久化。
|
||
|
||
### 维护注意
|
||
|
||
- 保存技能按钮目前仅 `console.log` 输出技能名和描述,未接正式后端保存接口。
|
||
|
||
---
|
||
|
||
## Q8:`SkillsWorkshop` 组件是干什么的?
|
||
|
||
**A:技能资产管理页(发现、筛选、接入、发布、删除、进入编辑)。**
|
||
|
||
### 组件职责
|
||
|
||
- 拉取 `official` 与 `mine` 技能,映射成统一 UI 结构。
|
||
- 支持搜索、类型筛选、分类筛选。
|
||
- 支持官方技能接入到“我的技能”。
|
||
- 支持发布、删除、编辑(编辑弹窗支持 AI 编辑和上传覆盖)。
|
||
- 支持“新建技能体”:Agent 对话创建 / 上传文件创建。
|
||
|
||
### 主要接口
|
||
|
||
- 列表:`GET /api/v1/skills?type=official|mine`
|
||
- 发布:`POST /api/v1/expert-agents/skills/publish`
|
||
- 删除我的技能:`DELETE /api/v1/skills/mine/{skillId}`
|
||
- 上传覆盖:`PUT /api/v1/skills/{skillId}/upload`
|
||
- 接入官方技能:`POST /api/v1/skills/mine/add`
|
||
- 上传创建:`POST /api/v1/skills/upload`
|
||
- Agent 创建线程:`POST /api/v1/expert-agent-chat/threads`
|
||
- AI 编辑复制技能:`POST /threads/{threadId}/copy-skill/{skillId}`
|
||
|
||
### 数据流
|
||
|
||
- filter/search 改变 -> `useEffect` 重新拉取技能 -> 本地去重并缓存到 `skills`。
|
||
- 卡片编辑 -> `editingSkill` 打开弹窗 -> 选择 AI 编辑时创建线程后跳 `SkillCreatorPage`。
|
||
|
||
### 维护注意
|
||
|
||
- `inferCategory` 是关键词规则映射,新增技能类型时可优先扩展该规则。
|
||
- “进入”按钮目前使用 `skill.id` 跳 `skill-creator`,若后端语义改成 threadId 需调整。
|
||
|
||
---
|
||
|
||
## Q9:`SkillCreatorThreadSidebar` 组件是干什么的?
|
||
|
||
**A:会话线程管理的复用侧边栏。**
|
||
|
||
### 组件职责
|
||
|
||
- 打开时加载会话列表。
|
||
- 新建会话并自动选中。
|
||
- 删除会话并在当前会话被删时自动回退到其它会话。
|
||
- 支持线程过滤(如仅展示当前专家线程)。
|
||
|
||
### 输入输出(Props 视角)
|
||
|
||
- 输入:`authedFetch/currentThreadId/isOpen/isBusy/threadFilter/createThreadPayload...`
|
||
- 输出:
|
||
- `onThreadSelect(threadId)`
|
||
- `onClose()`
|
||
|
||
### 数据流
|
||
|
||
- `isOpen` 从 false -> true:触发 `refreshThreads`。
|
||
- 新建成功:更新本地列表并触发 `onThreadSelect`。
|
||
- 删除成功:本地移除;若删的是当前线程,自动选择 fallback。
|
||
|
||
### 维护注意
|
||
|
||
- `createThreadPayload` 是高复用扩展点,场景页可注入 `expert_agent_id/title` 等字段。
|
||
|
||
---
|
||
|
||
## Q10:`AgentConversationList` 组件是干什么的?
|
||
|
||
**A:统一遍历消息并决定“连接中状态”是否显示。**
|
||
|
||
### 核心逻辑
|
||
|
||
- 逐条渲染 `SkillCreatorConversationItem`。
|
||
- 只在以下条件满足时给最后一条 assistant 传 `isConnecting=true`:
|
||
- 页面处于连接/流式阶段;
|
||
- 当前消息是最后一条 assistant;
|
||
- 且还没有任何实际生成内容(`content/steps/thinkings/toolCalls` 都为空)。
|
||
|
||
### 价值
|
||
|
||
- 将“连接中占位显示时机”从页面组件抽离,统一控制,减少重复判断代码。
|
||
|
||
---
|
||
|
||
## Q11:`SkillCreatorConversationItem` 组件是干什么的?
|
||
|
||
**A:单条消息卡片渲染器(用户消息 + 助手多面板消息)。**
|
||
|
||
### 渲染结构
|
||
|
||
- 用户消息:右侧气泡 + user avatar。
|
||
- 助手消息:左侧 avatar + 多段内容:
|
||
- `StepsSummary`
|
||
- `ThinkingPanel[]`
|
||
- `ToolCallPanel[]`
|
||
- 连接中动画文案(`正在连接技能服务...`)
|
||
- Markdown 正文
|
||
|
||
### 数据来源
|
||
|
||
- 完全由父级传入 `message` 和 `isConnecting` 控制,不直接请求后端。
|
||
|
||
### 维护注意
|
||
|
||
- 此组件是所有对话页的统一呈现层;若改动 message schema,要同步更新这里的各面板。
|
||
|
||
---
|
||
|
||
## Q12:`ChatInputComposer` 组件是干什么的?
|
||
|
||
**A:通用输入组件,封装输入、发送/停止、上传文件。**
|
||
|
||
### 输入输出(Props)
|
||
|
||
- 输入:`value/disabled/readOnly/isSending/isDarkMode/showUpload/placeholder...`
|
||
- 回调:
|
||
- `onChange(value)`
|
||
- `onSend()`
|
||
- `onStop()`
|
||
- `onUploadFile(file)`
|
||
|
||
### 关键交互
|
||
|
||
- Enter(非 Shift)触发发送(前提:`submitEnabled` 且非 `isSending`)。
|
||
- 发送按钮在发送中切换为停止按钮(`onClick={isSending ? onStop : onSend}`)。
|
||
- 文件上传通过隐藏 `input[type=file]` + 按钮触发。
|
||
|
||
### 维护注意
|
||
|
||
- 当前实现里文本框只受 `disabled` 控制,不再因为 `isSending` 禁止输入。
|
||
- 上传按钮仍受 `disabled || !onUploadFile` 控制。
|
||
|
||
---
|
||
|
||
## Q13:`AgentChatPage.css` 主要做了什么?
|
||
|
||
**A:提供 `AgentChatPage` 的辅助样式。**
|
||
|
||
- `.agent-resizing`:拖拽文件侧栏时全局 cursor/user-select 控制。
|
||
- `.agent-file-tree-scroll` / `.agent-file-preview-scroll`:滚动条样式统一。
|
||
- `.agent-file-markdown-preview`:markdown 首尾边距和图片样式收敛。
|
||
- `.animate-breathing` 等动画类:预留呼吸动效。
|
||
|
||
---
|
||
|
||
## Q14:典型业务流程:发送一条消息时,数据如何传输?
|
||
|
||
**A:以 `AgentChatPage` 为例。**
|
||
|
||
1. 用户在 `ChatInputComposer` 输入后点击发送。
|
||
2. `handleSendMessage` 校验 `text/threadId/isStreaming`。
|
||
3. 立即写入:
|
||
- user message
|
||
- assistant 占位 message(空内容)
|
||
4. 调 `POST /messages` 落用户消息。
|
||
5. 调 `POST /run/stream` 开始 SSE。
|
||
6. 每个 SSE event 增量更新 assistant message:
|
||
- step -> `steps`
|
||
- reasoning -> `thinkings`
|
||
- tool -> `toolCalls`
|
||
- text -> `content`
|
||
7. 流结束后将聚合结果组装成 `persistedContent`,再 `POST /messages` 落库。
|
||
8. 刷新文件列表(对可能新增输出文件做同步)。
|
||
|
||
---
|
||
|
||
## Q15:典型业务流程:从技能工坊进入技能创建页,数据如何传输?
|
||
|
||
**A:两种入口。**
|
||
|
||
### 入口 A:新建技能(Agent 构建)
|
||
|
||
1. `SkillsWorkshop.handleCreateSkillByAgent` 创建线程。
|
||
2. 将 `threadId` 写入 localStorage。
|
||
3. 跳转 `navigate('/qa-center/skill-creator?id=threadId')`。
|
||
4. `SkillCreatorPage` 用 query/localStorage 同步 `currentThreadId`,然后拉消息和文件。
|
||
|
||
### 入口 B:AI 辅助编辑已有技能
|
||
|
||
1. 创建线程。
|
||
2. `POST /threads/{threadId}/copy-skill/{skillId}` 把技能内容复制到线程工作区。
|
||
3. 跳转 SkillCreator 页面,后续在线程中对话编辑。
|
||
|
||
---
|
||
|
||
## Q16:如果要新增一个“对话页”,建议复用哪些组件?
|
||
|
||
**A:建议最小复用组合如下。**
|
||
|
||
- 消息列表:`AgentConversationList`
|
||
- 单消息卡片:`SkillCreatorConversationItem`(自动包含 steps/thinking/tool 等面板)
|
||
- 输入框:`ChatInputComposer`
|
||
- 线程侧边栏:`SkillCreatorThreadSidebar`
|
||
|
||
**页面层只负责:**
|
||
|
||
- 状态管理(`messages/input/isStreaming/currentThreadId`)
|
||
- 接口请求(历史 + SSE + 持久化)
|
||
- 事件映射(把后端事件转为 `ConversationMessage` 结构)
|
||
|
||
---
|
||
|
||
## Q17:维护时最容易踩坑的点有哪些?
|
||
|
||
**A:建议优先关注这 8 个点。**
|
||
|
||
1. **SSE 事件兼容性**:后端 event type 或字段名变化会直接导致 UI 不更新。
|
||
2. **历史内容兼容**:`normalizeHistoryContent` 没跟上 content schema 会出现历史消息空白。
|
||
3. **线程切换中断**:切换线程时要记得 abort 流并清状态,避免串流到新线程。
|
||
4. **占位消息回收**:发送失败/流失败需移除 assistant 占位,避免残留空卡片。
|
||
5. **threadId 语义一致性**:某些页面参数传 skillId/agentId,需避免误当 threadId 使用。
|
||
6. **工具/技能绑定状态双源**:本地状态和后端状态不同步时,优先以 refresh 查询结果为准。
|
||
7. **上传入口 accept 限制**:上传文件类型修改时要同步后端校验和 UI 提示。
|
||
8. **深色主题样式**:该目录深色/浅色分支很多,改样式需双主题回归。
|
||
|
||
---
|
||
|
||
## Q18:后续文档扩展建议
|
||
|
||
**A:当前文档适合开发入门,下一步可补充“接口契约版”。**
|
||
|
||
- 补完整 SSE event JSON 示例(每种事件一例)。
|
||
- 增加错误码到 UI 提示映射表。
|
||
- 补一份“页面状态机图”(Idle / Connecting / Streaming / Stopped / Error)。
|
||
- 给每个页面补“可测点清单”(单测/联调/回归)。
|
||
|
||
---
|
||
|
||
## Q19:能否给一份可联调的“接口契约附录”?
|
||
|
||
**A:可以,以下按模块整理。**
|
||
|
||
### 1) 专家模块(Expert)
|
||
|
||
- 查询专家列表
|
||
- `POST /api/v1/expert-agents/list`
|
||
- 请求体:
|
||
- `is_draft: boolean`
|
||
- 响应(数组项):
|
||
- `id/name/avatar_url/description/model_name/is_draft/user_id/created_at`
|
||
|
||
- 查询专家详情
|
||
- `GET /api/v1/expert-agents/{agentId}`
|
||
- 响应关键字段:
|
||
- `id/name/description/system_prompt/model_name`
|
||
|
||
- 更新专家配置
|
||
- `PUT /api/v1/expert-agents/{agentId}`
|
||
- 请求体关键字段:
|
||
- `name`
|
||
- `description`
|
||
- `system_prompt`
|
||
- `model_name`
|
||
- `avatar_url`(当前传 `null`)
|
||
|
||
- 切换草稿/发布状态
|
||
- `PATCH /api/v1/expert-agents/{agentId}`
|
||
- 请求体:
|
||
- `is_draft: boolean`
|
||
|
||
### 2) 线程与消息模块(Chat Thread + Message)
|
||
|
||
- 查询线程列表
|
||
- `GET /api/v1/expert-agent-chat/threads`
|
||
- 响应:
|
||
- `threads: SkillCreatorThread[]`
|
||
- `archived_threads: SkillCreatorThread[]`
|
||
|
||
- 新建线程
|
||
- `POST /api/v1/expert-agent-chat/threads`
|
||
- 请求体常见字段:
|
||
- `title`
|
||
- `archived`
|
||
- `is_real_chat`
|
||
- `expert_agent_id`(专家聊天场景)
|
||
|
||
- 删除线程
|
||
- `DELETE /api/v1/expert-agent-chat/threads/{threadId}`
|
||
|
||
- 查询线程消息历史
|
||
- `GET /api/v1/expert-agent-chat/threads/{threadId}/messages`
|
||
- 响应消息项关键字段:
|
||
- `id/thread_id/role/content/created_at`
|
||
|
||
- 保存消息(用户或助手)
|
||
- `POST /api/v1/expert-agent-chat/messages`
|
||
- 请求体(用户消息):
|
||
- `thread_id`
|
||
- `role: "user"`
|
||
- `content: { type: "text", text: string }`
|
||
- 请求体(助手消息):
|
||
- `thread_id`
|
||
- `role: "assistant"`
|
||
- `content: persistedContent[]`
|
||
|
||
### 3) 流式回复模块(SSE)
|
||
|
||
- 专家聊天流式
|
||
- `POST /api/v1/expert-agent-chat/run/stream`
|
||
- 请求体关键字段:
|
||
- `chat_id`
|
||
- `expert_agent_id`
|
||
- `user_query`
|
||
- `messages`
|
||
|
||
- 专家编辑页测试流式
|
||
- `POST /api/v1/expert-agent-chat/run/stream/help_expert`
|
||
|
||
- 技能创建流式
|
||
- `POST /api/v1/expert-agent-chat/skills-agent`
|
||
- 请求体关键字段:
|
||
- `chat_id`
|
||
- `user_query`
|
||
|
||
### 4) 文件模块(线程文件)
|
||
|
||
- 查询线程文件树
|
||
- `GET /api/v1/expert-agent-chat/threads/{threadId}/files/all`
|
||
|
||
- 上传文件
|
||
- `POST /api/v1/expert-agent-chat/threads/{threadId}/files/upload`
|
||
- form-data:
|
||
- `file`
|
||
|
||
- 文件预览
|
||
- `GET /api/v1/expert-agent-chat/threads/{threadId}/files/preview?path=...`
|
||
- 兼容响应:
|
||
- 文本
|
||
- JSON 字符串
|
||
- JSON 对象中的 `content`
|
||
|
||
### 5) 技能模块(Skills)
|
||
|
||
- 查询技能列表
|
||
- `GET /api/v1/skills?type=official|mine|all&keyword=&page=1&page_size=100`
|
||
|
||
- 发布技能
|
||
- `POST /api/v1/expert-agents/skills/publish`
|
||
- 请求体:
|
||
- `skill_id`
|
||
|
||
- 添加官方技能到我的技能
|
||
- `POST /api/v1/skills/mine/add`
|
||
- 请求体:
|
||
- `official_skill_id`
|
||
|
||
- 删除我的技能
|
||
- `DELETE /api/v1/skills/mine/{skillId}`
|
||
|
||
- 上传技能文件创建
|
||
- `POST /api/v1/skills/upload`
|
||
|
||
- 上传覆盖已有技能
|
||
- `PUT /api/v1/skills/{skillId}/upload`
|
||
|
||
### 6) 专家绑定工具/技能
|
||
|
||
- 工具
|
||
- 查询:`GET /api/v1/expert-agents/{agentId}/tools`
|
||
- 绑定:`POST /api/v1/expert-agents/{agentId}/tools`(`tool_name`)
|
||
- 解绑:`DELETE /api/v1/expert-agents/{agentId}/tools/{toolId}`
|
||
|
||
- 技能
|
||
- 查询:`GET /api/v1/expert-agents/{agentId}/skills`
|
||
- 绑定:`POST /api/v1/expert-agents/{agentId}/skills`(`skill_id`)
|
||
- 解绑:`DELETE /api/v1/expert-agents/{agentId}/skills/{skillId}`
|
||
|
||
---
|
||
|
||
## Q20:SSE 事件规范可以补全吗?
|
||
|
||
**A:可以,以下是前端当前实现依赖的事件集合。**
|
||
|
||
### 1) 事件封包格式
|
||
|
||
- 每行以 `data: ` 开头
|
||
- 正文为 JSON 字符串
|
||
- 结束标记:`data: [DONE]`
|
||
|
||
### 2) 前端识别的事件类型
|
||
|
||
- `data-thinking-step`
|
||
- 用途:更新执行步骤面板
|
||
- 关键字段:
|
||
- `data.id`
|
||
- `data.title`
|
||
- `data.status`(`completed` 或其它)
|
||
- `data.items[]`
|
||
|
||
- `reasoning-start`
|
||
- 用途:开始一段思考
|
||
- 行为:创建新的 thinking 段,记录开始时间
|
||
|
||
- `reasoning-delta`
|
||
- 用途:思考内容增量
|
||
- 关键字段:
|
||
- `delta`
|
||
|
||
- `reasoning-end`
|
||
- 用途:结束当前思考段
|
||
- 行为:计算该段耗时并写入 `duration`
|
||
|
||
- `tool-input-available`
|
||
- 用途:记录工具调用入参
|
||
- 关键字段:
|
||
- `toolCallId`
|
||
- `toolName`
|
||
- `input`
|
||
|
||
- `tool-output-available`
|
||
- 用途:记录工具调用输出
|
||
- 关键字段:
|
||
- `toolCallId`
|
||
- `output`
|
||
|
||
- `text-delta`
|
||
- 用途:助手正文 token 增量
|
||
- 关键字段:
|
||
- `delta`
|
||
|
||
### 3) 推荐后端保证
|
||
|
||
- 同一次回复里的 `toolCallId` 全局唯一。
|
||
- `reasoning-start` 与 `reasoning-end` 成对(允许无 `delta`)。
|
||
- `data-thinking-step` 同一个 `id` 可重复更新状态。
|
||
- `text-delta` 在结束前允许为空片段,但尽量减少空片段。
|
||
|
||
### 4) 前端容错策略(已实现)
|
||
|
||
- JSON parse 失败:跳过该行,不中断整流。
|
||
- 未识别 event.type:忽略。
|
||
- `output` 非字符串:尝试 `result` 字段,否则 `JSON.stringify`。
|
||
- `AbortError`:按“用户主动停止”处理,不弹致命错误。
|
||
|
||
---
|
||
|
||
## Q21:可以给一份页面级测试清单吗?
|
||
|
||
**A:可以,以下清单按页面拆分,适合联调和回归。**
|
||
|
||
### A. `AgentChatPage` 测试清单
|
||
|
||
- 打开页面后,能加载专家信息(名称、描述)。
|
||
- 未选线程时,空态正确显示;选中线程后历史消息正确渲染。
|
||
- 点击发送后立即出现 assistant 占位卡片,显示连接中状态。
|
||
- 首个有效流式事件到达后,连接中占位消失,转为正常内容增量渲染。
|
||
- 发送失败/流失败时,assistant 占位会被回收,不留空白卡片。
|
||
- 点击停止后,流式中断,按钮状态恢复,后续不再追加内容。
|
||
- 上传文件后文件树刷新;文件预览支持 text/json 两类返回。
|
||
- 切换线程时会中断当前流,且不会把旧线程流内容写到新线程。
|
||
|
||
### B. `SkillCreatorPage` 测试清单
|
||
|
||
- URL `id` 与 localStorage 线程 ID 同步正确。
|
||
- 新线程空态和快捷提示按钮显示正常。
|
||
- 发送后 `isConnecting=true`,收到首包后 `isConnecting=false`。
|
||
- steps/thinking/tool/text 四类流式内容都能正确出现。
|
||
- 线程切换、停止流、文件上传/预览路径行为与 `AgentChatPage` 一致。
|
||
- 保存技能弹窗开关正常(当前为 UI 行为)。
|
||
|
||
### C. `AgentExpertEditPage` 测试清单
|
||
|
||
- 进入页面后基础信息(name/model/description/system_prompt)能回填。
|
||
- 保存专家配置后接口成功,按钮 loading 状态恢复。
|
||
- 工具弹窗:勾选新增 -> 确认添加 -> 列表刷新一致。
|
||
- 技能弹窗:绑定/解绑/一键绑定后状态与后端一致。
|
||
- 左侧测试对话能正常流式返回,滚动与发送状态正确。
|
||
|
||
### D. `SkillsWorkshop` 测试清单
|
||
|
||
- `all`/`mine` 切换结果正确,搜索词可生效。
|
||
- 分类筛选(writing/analysis/decision/validation/workflow)正确过滤。
|
||
- 官方技能“+”接入后卡片状态转“已接入”。
|
||
- 我的技能菜单中的发布/删除操作正确执行。
|
||
- 编辑弹窗中 AI 编辑路径可成功创建线程并跳转 SkillCreator。
|
||
- 上传创建、上传覆盖两条路径成功后状态正确恢复(含 input 清空)。
|
||
|
||
### E. `AgentExpertCenterPage` 测试清单
|
||
|
||
- mine/published 标签切换后列表正确变化。
|
||
- mine 模式会按当前用户过滤,且与全量接口合并去重后正确展示。
|
||
- 详情弹窗中的发布开关可切换并刷新列表。
|
||
- 编辑、对话跳转路径参数正确。
|
||
|
||
### F. 主题与可用性回归
|
||
|
||
- 深色/浅色主题下对话气泡、按钮、边框、hover 都可读。
|
||
- 长会话场景滚动性能可接受,自动滚动按钮行为符合预期。
|
||
- 键盘发送(Enter)与换行(Shift+Enter)行为正确。
|
||
|