Files
deerflow-code/parallel-agents-vue-offline-test/parallel-agents-vue-offline-pack/docs/08-开发文档.md
T
2026-06-09 19:41:01 +08:00

14 KiB
Raw Blame History

开发文档 · 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.json exports:.→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。