feat(parallel-agents): 子智能体禁派活/脚本(防模仿总控) + 面板能力完善
后端 parallel_agents.py:每个并行席位 excluded_tools 增加 agent_orchestration (派活) 与 bash(脚本逃逸),并在任务消息里加「角色边界」护栏——根治弱模型拿到 带「总控/业务链条」语气的任务后模仿总控输出「总控调度指令…」、绕道 glob/bash 翻找派活脚本的问题。新增 /api/parallel-agents/auth/token 取 token 端点 (auth/csrf 豁免)。 parallel-agents-vue 面板:协作澄清卡片、步骤图标/步骤展示、API 与类型完善等。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
3405795371
commit
c230f7a6c6
18 files changed
+1276
-128
No files matched your search
@@ -14,6 +14,7 @@
|
||||
| [05-嵌入与深色样式.md](./05-嵌入与深色样式.md) | 配置文件接口地址、抽屉/弹窗嵌入、深色自适应、不污染宿主 |
|
||||
| [06-可用智能体清单.md](./06-可用智能体清单.md) | **测试用真实 agent_id 清单(可直接复制粘贴)** + 示例任务 |
|
||||
| [07-使用说明.md](./07-使用说明.md) | **在其它 Vue 项目中引入哪些组件、如何使用**(props/事件/嵌入) |
|
||||
| [08-开发文档.md](./08-开发文档.md) | **二次开发**:架构/数据流/事件协议/目录职责/后端契约/扩展指南 |
|
||||
|
||||
## 测试环境
|
||||
|
||||
|
||||
@@ -102,8 +102,11 @@ app.use(ParallelAgentsVue); // 之后任意模板可直接用 <ParallelAgentsPan
|
||||
|
||||
- **接口地址**:默认读 `src/config.ts` 的 `DEFAULT_BASE_URL`(可改源码,或 dev 设 `VITE_PA_BASE_URL`);
|
||||
也可用 `:base-url` prop 临时覆盖。
|
||||
- **登录**:把宿主的**用户名**用 `:username` 传进来即可——组件自动走 DeerFlow 免密登录端点换 token、
|
||||
缓存、401 自动重登。**宿主不用管 token**。若已有 token,可改传 `:token` / `:get-token`(优先级更高)。
|
||||
- **登录**:把宿主的**用户名**用 `:username` 传进来即可——组件自动走本面板**专用**的「绕过常规登录」
|
||||
公共端点 `POST /api/parallel-agents/auth/token` 换 token、缓存、401 自动重登。**宿主不用管 token**。
|
||||
若已有 token,可改传 `:token` / `:get-token`(优先级更高)。
|
||||
- 为什么专用端点:内网部署对标准 `/api/v1/auth/login/username` 有额外限制(口令门/网关裁剪等),拿不到
|
||||
token;该端点传用户名直接签发(账号不存在自动注册),已在 auth/csrf 中间件白名单里放行。
|
||||
- token 缓存按 `baseUrl::username` 维度,多个实例/多用户互不串。
|
||||
|
||||
---
|
||||
@@ -122,14 +125,121 @@ app.use(ParallelAgentsVue); // 之后任意模板可直接用 <ParallelAgentsPan
|
||||
| `model` | `string` | | 可选模型名(不传走默认 / 后端回退) |
|
||||
| `dark` | `boolean`(默认 `true`) | | 深色面板(自带成色,不依赖宿主主题) |
|
||||
|
||||
`AgentInput`/`SavedTask` 等类型也从包里导出:`import type { AgentInput, SavedTask } from "parallel-agents-vue"`。
|
||||
`AgentInput`/`SavedTask`/`AgentResult` 等类型也从包里导出:
|
||||
`import type { AgentInput, SavedTask, AgentResult } from "parallel-agents-vue"`。
|
||||
|
||||
---
|
||||
|
||||
## 六·一、每个参数怎么传(逐个举例)
|
||||
|
||||
### `agents`(必填)——「智能体参数」怎么传
|
||||
|
||||
一个数组,每项 `{ agent_id, name?, avatarType? }`:
|
||||
|
||||
```ts
|
||||
const agents = [
|
||||
{ agent_id: "roundtable-solution-design", name: "方案设计" }, // 最常用:id + 显示名
|
||||
{ agent_id: "roundtable-risk-review", name: "风险审查", avatarType: "amber" }, // 顺带指定头像配色
|
||||
{ agent_id: "demo-expert" }, // 只给 id(显示名回退成 id)
|
||||
];
|
||||
```
|
||||
```vue
|
||||
<ParallelAgentsPanel :agents="agents" :username="'demo'" />
|
||||
```
|
||||
- `agent_id`**必须是后端真实注册的 id**(只写名字无效);可用 id 见 [06-可用智能体清单.md](./06-可用智能体清单.md)。
|
||||
- `name`:席位/气泡显示名,可省(省了用 id)。
|
||||
- `avatarType`:可选 `blue|purple|emerald|amber|pink|human`;不传按顺序自动轮转配色。
|
||||
|
||||
### `username`——免密登录(推荐)
|
||||
|
||||
```vue
|
||||
<ParallelAgentsPanel :username="currentUser.name" :agents="agents" />
|
||||
```
|
||||
|
||||
### `baseUrl`——后端地址(不传用 config 默认)
|
||||
|
||||
```vue
|
||||
<ParallelAgentsPanel :base-url="'http://10.0.0.5:8001'" :username="'demo'" :agents="agents" />
|
||||
```
|
||||
|
||||
### `token` / `getToken`——已有登录态时(优先于 username)
|
||||
|
||||
```vue
|
||||
<!-- 直接给字符串 token -->
|
||||
<ParallelAgentsPanel :token="hostToken" :agents="agents" />
|
||||
<!-- 或给一个(异步)取 token 的函数 -->
|
||||
<ParallelAgentsPanel :get-token="() => authStore.getAccessToken()" :agents="agents" />
|
||||
```
|
||||
|
||||
### `model`——指定模型(不传走默认/后端回退;也可在面板右上角下拉切)
|
||||
|
||||
```vue
|
||||
<ParallelAgentsPanel :model="'deepseek-chat'" :username="'demo'" :agents="agents" />
|
||||
```
|
||||
|
||||
### `dark`——深色(默认 true,自带成色)
|
||||
|
||||
```vue
|
||||
<ParallelAgentsPanel :dark="true" :username="'demo'" :agents="agents" />
|
||||
```
|
||||
|
||||
### `taskId`——存储主键 + 回显(见 §七·二)
|
||||
|
||||
```vue
|
||||
<ParallelAgentsPanel
|
||||
:task-id="taskId"
|
||||
:username="'demo'"
|
||||
:agents="agents"
|
||||
@update:taskId="(id) => (taskId = id)"
|
||||
/>
|
||||
```
|
||||
|
||||
### 全量示例(所有参数一起)
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import { ParallelAgentsPanel } from "parallel-agents-vue";
|
||||
import "parallel-agents-vue/style.css";
|
||||
import type { AgentInput, AgentResult, SavedTask } from "parallel-agents-vue";
|
||||
|
||||
const agents: AgentInput[] = [
|
||||
{ agent_id: "roundtable-solution-design", name: "方案设计" },
|
||||
{ agent_id: "roundtable-risk-review", name: "风险审查" },
|
||||
];
|
||||
const taskId = ref("task-001");
|
||||
function onSaved(t: SavedTask) {
|
||||
console.log("已保存", t.id);
|
||||
}
|
||||
function onCompleted(results: AgentResult[]) {
|
||||
console.log("各智能体返回内容", results);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div style="height: 86vh">
|
||||
<ParallelAgentsPanel
|
||||
:base-url="'http://localhost:8001'"
|
||||
:username="'demo'"
|
||||
:agents="agents"
|
||||
:task-id="taskId"
|
||||
:model="'deepseek-chat'"
|
||||
:dark="true"
|
||||
@saved="onSaved"
|
||||
@update:taskId="(id) => (taskId = id)"
|
||||
@completed="onCompleted"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
## 七、事件
|
||||
|
||||
| 事件 | 载荷 | 触发时机 |
|
||||
| ----------------- | ------------ | --------------------------------------- |
|
||||
| `saved` | `SavedTask` | 保存成功(新建或更新)后 |
|
||||
| `update:taskId` | `string` | 当前任务 id 变化(保存 / 加载历史时) |
|
||||
| 事件 | 载荷 | 触发时机 |
|
||||
| ----------------- | ------------------ | --------------------------------------- |
|
||||
| `saved` | `SavedTask` | 保存成功(新建或更新)后 |
|
||||
| `update:taskId` | `string` | 当前任务 id 变化(保存 / 加载历史时) |
|
||||
| `completed` | `AgentResult[]` | 一轮并行分析结束时,回传各智能体返回内容 |
|
||||
|
||||
```vue
|
||||
<ParallelAgentsPanel
|
||||
@@ -138,14 +248,86 @@ app.use(ParallelAgentsVue); // 之后任意模板可直接用 <ParallelAgentsPan
|
||||
:task-id="taskId"
|
||||
@saved="(t) => console.log('saved', t.id)"
|
||||
@update:taskId="(id) => (taskId = id)"
|
||||
@completed="(results) => console.log('各智能体返回:', results)"
|
||||
/>
|
||||
```
|
||||
|
||||
## 七·一、如何获取模型返回的内容
|
||||
|
||||
每个智能体(模型)返回的正文是 `AgentResult.content`。两种方式,任选其一:
|
||||
|
||||
**A. 事件**:监听 `@completed`,一轮分析结束时自动回传 `AgentResult[]`:
|
||||
|
||||
```vue
|
||||
<ParallelAgentsPanel
|
||||
:username="'demo'"
|
||||
:agents="agents"
|
||||
@completed="(results) => {
|
||||
// results: [{ agent_id, name, content, status }]
|
||||
for (const r of results) console.log(r.name, '→', r.content);
|
||||
}"
|
||||
/>
|
||||
```
|
||||
|
||||
**B. 方法(模板 ref)**:随时主动取(不必等事件):
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import { ParallelAgentsPanel } from "parallel-agents-vue";
|
||||
import type { AgentResult } from "parallel-agents-vue";
|
||||
|
||||
const panelRef = ref<InstanceType<typeof ParallelAgentsPanel> | null>(null);
|
||||
|
||||
function readAll() {
|
||||
const all: AgentResult[] = panelRef.value!.getResults();
|
||||
// [{ agent_id, name, content, status }]
|
||||
}
|
||||
function readOne(id: string) {
|
||||
const one = panelRef.value!.getResult(id); // AgentResult | null
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ParallelAgentsPanel ref="panelRef" :username="user" :agents="agents" />
|
||||
</template>
|
||||
```
|
||||
|
||||
`AgentResult = { agent_id, name, content, status }`,`content` 即该智能体模型返回的正文(Markdown)。
|
||||
`defineExpose` 还暴露了 `start()` / `cancel()`,可由宿主代替按钮触发开始 / 停止。
|
||||
|
||||
## 七·二、如何保存 + 回显聊天记录
|
||||
|
||||
**保存**:面板里点「保存」即把当前整段聊天记录(各智能体气泡 + 步骤条等)存到后端,按用户隔离。
|
||||
传 `:task-id` 时**以它为存储主键**做 upsert(已存在→更新;不存在→用该 id 新建);不传则首次保存由后端
|
||||
生成 id(通过 `@update:taskId` 回传,下次再保存即更新这条)。
|
||||
|
||||
**回显(两种方式)**:
|
||||
|
||||
```vue
|
||||
<!-- 方式 1:挂载时传一个已存在的 taskId → 自动加载并回显该任务的聊天记录 -->
|
||||
<ParallelAgentsPanel :task-id="'task-001'" :username="'demo'" :agents="agents" />
|
||||
```
|
||||
|
||||
```ts
|
||||
// 方式 1 变体:运行中改 taskId(双向绑定),面板会自动切到并回显那条任务
|
||||
const taskId = ref<string>(""); // 留空=新任务
|
||||
// <ParallelAgentsPanel :task-id="taskId" @update:taskId="id => taskId = id" ... />
|
||||
taskId.value = "task-001"; // 改了之后面板自动回显 task-001
|
||||
```
|
||||
|
||||
> 方式 2:面板**右上角内置「历史」下拉**——点开选一条历史任务即回显,**无需写任何代码**。
|
||||
|
||||
要点:先**保存过**、再用**同一个 taskId**(或从历史里选)才会有内容回显;没存过的新 id 第一次进去是空白
|
||||
(后台会探测一次 `GET /…/{id}` 返回 404,属正常,面板静默处理)。
|
||||
|
||||
---
|
||||
|
||||
## 八、交互要点(与圆桌 Step2 一致)
|
||||
|
||||
- 输入「任务名称 + 简介」→「开始分析」→ 各智能体**并行**输出,运行期间**禁止写文件**(产出走正文)。
|
||||
- 输入「任务名称 + 简介」→「开始分析」→ 各智能体**并行**输出。运行期间禁用**写文件**(`write_file`/
|
||||
`str_replace`)与**派活/脚本**(`agent_orchestration`/`bash`,防子智能体模仿总控调度);检索、查看、
|
||||
`ask_clarification` 等照常 —— 所以**步骤条 / 协作卡片正常显示**,每个智能体只做自己那份分析。
|
||||
- 主区每次只显示**一个**席位的消息列表;**点席位头像切换**要看哪个(单选)。
|
||||
- 顶部席位状态条显示每个智能体的进行中 / 完成 / 出错。
|
||||
- 操作区右侧有**模型切换**下拉(拉取后端 `/api/models`);选「默认」则用 `model` prop / 配置 / 后端回退。
|
||||
|
||||
@@ -0,0 +1,226 @@
|
||||
# 开发文档 · parallel-agents-vue
|
||||
|
||||
面向**维护 / 二次开发**这个前端的同学。讲清楚:它是什么、架构与数据流、目录与各文件职责、
|
||||
对接的后端契约、样式系统、以及常见扩展怎么改。使用 / 嵌入请看 [07-使用说明.md](./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:<tool>`), 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](./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. 构建与产物
|
||||
|
||||
```bash
|
||||
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](./06-可用智能体清单.md)。
|
||||
- 并行席位**禁派活/脚本/写文件**:本面板里每个智能体只做自己那份分析,产出走正文。
|
||||
- 协作卡片回答是**重跑单个智能体**(新线程 + 把回答拼进任务),不是 LangGraph 原地 resume。
|
||||
- token 取自公共端点(无门槛旁路)—— 适合内网;若网关要暴露公网,建议给取 token 端点加共享密钥校验。
|
||||
- 跨域:宿主与后端不同源时,后端需放开该来源 CORS + `credentials`。
|
||||
Reference in new issue
Block a user