14 KiB
14 KiB
开发文档 · parallel-agents-vue
面向维护 / 二次开发这个前端的同学。讲清楚:它是什么、架构与数据流、目录与各文件职责、 对接的后端契约、样式系统、以及常见扩展怎么改。使用 / 嵌入请看 07-使用说明.md。
1. 它是什么
一个独立、可嵌入的 Vue3 组件:输入「任务名称 + 简介」→ 并行调用宿主指定的多个智能体 →
深色消息列表(1:1 复刻圆桌 Step2:步骤条 / 协作卡片 / 席位状态条 / 点头像切换单显)→
支持保存 / 回显 / 重新分析。只对外暴露一个组件 ParallelAgentsPanel。
- 后端是同仓库的 DeerFlow 网关(
offline-backend-20260512/backend),本前端只消费它的几个 HTTP 接口。 - 并行编排在后端:前端发一条请求,后端扇出 N 个智能体、合并成一条 SSE回来;前端只解析这一条流。
2. 技术栈
| 方面 | 选型 |
|---|---|
| 框架 | Vue 3(<script setup> + Composition API) |
| 构建 | Vite 6(dev app + build:lib 组件库双产物) |
| 语言 | TypeScript(strict + noUnusedLocals/Parameters) |
| 样式 | TailwindCSS v4(仅 utilities,不引 preflight)+ 作用域 .pa-* token |
| Markdown | markdown-it(打进库,宿主无需安装) |
| peer dep | vue ^3.4(宿主提供,库不打包 vue) |
3. 目录结构与各文件职责
src/
├─ index.ts 库入口:导出 ParallelAgentsPanel + install 插件 + 类型;import 样式
├─ main.ts / App.vue dev demo(仅本地调试用,不进库)
├─ config.ts 配置:DEFAULT_BASE_URL / DEFAULT_MODEL / TOKEN_PATH
├─ types.ts 共享类型:Dialogue / SeatView / DialogueStep / ClarificationPayload
│ / AgentInput / AgentResult / SavedTask / StreamEvent(事件协议)
├─ api.ts 后端访问层:鉴权(取 token+缓存+401 重登) / SSE 流 / 任务 CRUD / 模型列表
├─ composables/
│ └─ useParallelRun.ts 运行编排(前端态核心):消费合并 SSE → 维护 dialogues/seats;
│ start / cancel / hydrate(回显) / reset / rerunAgent(单席位重跑)
├─ lib/
│ ├─ avatar-constants.ts 头像配色 + getAvatarTypeFor(轮转)
│ └─ step-display.ts 步骤条:getStepDisplay(图标+中文文案) / getStepArgs(参数子节点)
├─ styles/
│ └─ parallel-agents.css 作用域深色 token(--pa-*) + .pa-* 语义类 + Markdown 样式(无 preflight)
└─ components/
├─ ParallelAgentsPanel.vue 主组件:输入区/操作区/模型切换/历史/席位条/会话区 + 全部状态编排
├─ ParallelSeatStrip.vue 席位状态条(头像 + running/done/error 角标,点击切换单显)
├─ MessageBubble.vue 单条气泡:头像/发言人/徽标/时间/步骤条/正文/光标/协作卡片
├─ MessageStepsCard.vue 步骤条(ChainOfThought 结构,1:1 对齐 Step2)
├─ StepIcon.vue 步骤图标(lucide 风格 inline SVG,按 IconKey)
├─ ClarificationCard.vue 协作 / 澄清卡片(1:1 对齐圆桌第一步 IntentClarificationCard)
├─ MarkdownContent.vue markdown-it 渲染
└─ Avatar.vue 机器人 / 真人 SVG 头像
4. 架构与数据流
一次「开始分析」的端到端链路:
用户输入(任务名+简介) ──onStart──> useParallelRun.start(cfg, {taskName, brief, agents, model})
│ 为每个 agent 建一个 Dialogue(流式气泡) + SeatView(席位)
▼
api.streamParallelAgents(cfg, body, handlers, signal)
│ POST {baseUrl}/api/parallel-agents/run/stream (Authorization: Bearer <token>)
▼
后端扇出:每个 agent 各跑一轮 LangGraph,合并成「一条带 agent_id 的 SSE」
▼ 逐条 StreamEvent(见 §5)
useParallelRun.handleEvent(ev) 按 agent_id 路由:
delta→气泡正文(rAF 节流) · step→步骤条 · agent_clarification→协作卡片
agent_done→收口 · agent_error→错误 · all_done→收尾
▼ 响应式 dialogues / seats 变化
组件渲染:ParallelSeatStrip + 当前选中席位的 MessageBubble(单显,点头像切换)
▼ run 结束
panel emit "completed"(AgentResult[]);可点「保存」(按 taskId upsert) / 「重新分析」(重跑)
关键不变量
- 主区单选显示一个席位的消息(
selected=[agentId],点头像切换);后台 N 个智能体仍并行执行。 - delta 用
requestAnimationFrame合并刷新,避免逐 token reflow。 bubbleByAgent: Map<agentId, bubbleId>把事件路由到对应气泡;hydrate/start 时重建。
5. 统一事件协议(前后端唯一契约)
后端 /run/stream 每条 data: 是一个 JSON(StreamEvent,定义在 types.ts):
| type | 字段 | 前端处理 |
|---|---|---|
agent_start |
agent_id |
该席位置 running |
delta |
agent_id, text |
追加正文(节流) |
step |
agent_id, key(=tool_calling:), label, args |
步骤条加一步(含完整 args) |
agent_clarification |
agent_id, content, question, clarification_type, clarification_context, options, allow_* |
设 dialogue.clarification → 卡片 |
agent_done |
agent_id, content |
收口,正文以终态为准 |
agent_error |
agent_id, message |
该席位置 error |
all_done |
— | 全部结束 |
核心数据模型(types.ts):
Dialogue { id, agentId, sender, avatarType, content, time, confidence, streaming?, steps?, clarification? }DialogueStep { id, key, label, ts, args? }(key形如tool_calling:web_search)SeatView { agentId, name, avatarType, status: pending|running|done|error, bubbleId }ClarificationPayload { question, clarificationType, context, options, allowCustom, allowMultiple }AgentResult { agent_id, name, content, status }(宿主读取用)
改协议时:
types.ts的StreamEvent与后端parallel_agents.py的事件 dict 必须同步。
6. 组件树
ParallelAgentsPanel
├─ ParallelSeatStrip ── Avatar (席位条,点头像切换单显)
└─ MessageBubble (当前选中席位)
├─ Avatar
├─ MessageStepsCard ── StepIcon (步骤条;getStepDisplay/getStepArgs 翻译 key+args)
├─ MarkdownContent (markdown-it)
└─ ClarificationCard (msg.clarification 存在时;提交→panel.onClarify→rerunAgent)
ParallelAgentsPanel 的对外接口(详见 07-使用说明.md):
- props:
agents(必填) /username/baseUrl/token/getToken/taskId/model/dark - emits:
saved(SavedTask)/update:taskId(string)/completed(AgentResult[]) defineExpose:getResults()/getResult(id)/start()/cancel()
7. 鉴权
api.ts 的 token 解析优先级:token > getToken() > 缓存 > 用 username 取 token。
- 取 token:
POST {baseUrl}{TOKEN_PATH}(TOKEN_PATH=/api/parallel-agents/auth/token)—— 本面板 专用的「绕过常规登录」公共端点(内网部署限制了标准/login/username)。传{username}直接签发。 - 取到的 token 按
baseUrl::username缓存;业务请求带Authorization: Bearer(顺带绕过 CSRF)。 authedFetch在 401 时清缓存、用 username 重登并重试一次。- 后端该端点在
auth_middleware._PUBLIC_EXACT_PATHS+csrf_middleware._AUTH_EXEMPT_PATHS白名单里。
8. 对接的后端契约
后端实现:offline-backend-20260512/backend/app/gateway/routers/parallel_agents.py
(任务存储:parallel_agent_tasks.py + deerflow/persistence/parallel_agent_tasks/)。
| 用途 | 接口 |
|---|---|
| 取 token | POST /api/parallel-agents/auth/token {username} |
| 并行流式 | POST /api/parallel-agents/run/stream {task_name, brief, agent_ids[], model?} → SSE |
| 任务 CRUD | GET/POST/GET{id}/PUT{id}/DELETE{id} /api/parallel-agent-tasks |
| 模型列表 | GET /api/models → {models:[{name, display_name}]} |
后端运行策略(重要):每个并行席位是「执行席位」,不是总控。
- 禁用工具
_EXCLUDED_TOOLS = [write_file, str_replace, agent_orchestration, bash]:①禁写文件(并行 不互相打架);②禁agent_orchestration/bash(防弱模型模仿总控派活或绕道脚本逃逸)。 - 保留 只读 / 检索工具 +
ask_clarification→ 步骤条、协作卡片正常。 - 任务消息带角色边界护栏(「执行席位、不要模仿总控调度 / 派活」)。
- 步骤
args通过累积tool_call_chunks+ 终态全量tool_calls补全(流式期间空 args 不发,防钉死)。 ask_clarification从终态 messages 抽成结构化agent_clarification事件。
9. 样式系统(嵌入安全)
styles/parallel-agents.css:
- 不引 Tailwind preflight(
@import "tailwindcss/theme.css"+utilities.css),避免重置宿主全局样式。 - 颜色全用作用域内的
--pa-*深蓝 token(取自《深蓝 UI 规范》,与圆桌 Step2 深色一致),全部包在 根类.parallel-agents下,不污染宿主。 - 语义类
.pa-card / .pa-surface / .pa-border / .pa-text(-muted/-primary) / .pa-input / .pa-btn-* / .pa-badge / .pa-md(Markdown)—— 组件上色用这些类,布局用 Tailwind 工具类。 - 面板自带深色成色,不随宿主明暗变化;根元素
h-full w-full flex flex-col min-h-0自适应容器。
10. 步骤条 / 协作卡片(与圆桌的对应)
- 步骤条
MessageStepsCard.vue← 圆桌MessageStepsCard.tsx+ChainOfThought:图标列(带竖向连接线)- 中文文案 + 时间 + 参数子节点。文案/图标映射在
lib/step-display.ts(getStepDisplay),参数子节点 在getStepArgs(chip / code / text / kv)。加新工具图标:在step-display.ts的 switch +StepIcon.vue各加一条。
- 中文文案 + 时间 + 参数子节点。文案/图标映射在
- 协作卡片
ClarificationCard.vue← 圆桌第一步IntentClarificationCard.tsx:标题/类型标签/背景/选项 chip(单选点击即发、多选可勾)/自定义输入/发送。提交回传格式化回答 → 面板onClarify→rerunAgent带「【用户补充】…」重跑该单个智能体。
11. 构建与产物
pnpm install
pnpm dev # dev demo(连本地后端,App.vue 里填 baseUrl/username/agents)
pnpm typecheck # vue-tsc --noEmit
pnpm build:lib # 组件库:dist/parallel-agents-vue.{js,umd.cjs,css}
vite.config.ts:--mode lib走build.lib,external:['vue'],output.exports:'named',CSS 不拆分。package.jsonexports:.→ESM/UMD + types;./style.css→样式。宿主import "parallel-agents-vue/style.css"。
12. 常见扩展怎么改
| 需求 | 改哪里 |
|---|---|
| 新增一个 SSE 事件类型 | types.ts 的 StreamEvent + useParallelRun.handleEvent + 后端 parallel_agents.py |
| 步骤条支持新工具的图标/文案 | lib/step-display.ts(getStepDisplay/getStepArgs)+ StepIcon.vue |
| 改深色配色 / 加浅色主题 | styles/parallel-agents.css 的 --pa-*(浅色可在 .parallel-agents--light 下覆盖) |
| 改默认后端地址 / 模型 | config.ts(DEFAULT_BASE_URL / DEFAULT_MODEL) |
| 多席位并排显示(取消单选) | ParallelAgentsPanel.vue 的 selected / visibleSeats / toggleSeat 逻辑 |
| 宿主自定义存储(不走后端) | 用 @completed + getResults() 取数据,自行存;不调用 createTask/updateTask |
| 改鉴权方式 | api.ts 的 resolveToken / loginByUsername / authedFetch + config.ts 的 TOKEN_PATH |
13. 注意事项 / 已知约束
- agent_id 必须真实(后端注册过的 id),只填名字不行;可用 id 见 06-可用智能体清单.md。
- 并行席位禁派活/脚本/写文件:本面板里每个智能体只做自己那份分析,产出走正文。
- 协作卡片回答是重跑单个智能体(新线程 + 把回答拼进任务),不是 LangGraph 原地 resume。
- token 取自公共端点(无门槛旁路)—— 适合内网;若网关要暴露公网,建议给取 token 端点加共享密钥校验。
- 跨域:宿主与后端不同源时,后端需放开该来源 CORS +
credentials。