mirror of
https://github.com/ThearchyHelios/AgentLab.git
synced 2026-11-15 17:22:00 +00:00
协作团队真的能并行了,界面上也看得出来
先说一个和名字不符的事实:这个节点叫「多 Agent 协作」,但在此之前它是一条
严格的串行流水线。调度协议写死成 `next: string`——"一次只能派一个"被编进了
协议本身,模型再想并行也表达不出来。三个专家各花 10 秒,总是 30 秒。
(真并行一直在图级 fan-out:实测 3 个各 sleep 2 秒的节点连在同一个上游,
全部 +0.00s 开始、+2.37s 结束,墙钟 2.6 秒。只是协作团队走不到那条路。)
**协议改成 assignments 数组**,一轮可以派多个互不依赖的专家,asyncio.gather
并发执行,总耗时按最慢的那个算。max_parallel 可配,设成 1 即退回严格串行。
**并发只在任务真的互不依赖时才成立**,这件事的成本必须说清楚:同一轮发出去的
专家拿到的是**同一份进展快照**,谁也看不见谁这一轮干了什么。派错了不报错,
只会让两个人基于一样的旧信息重复劳动,而这在结果里很难看出来。所以判断责任
压在调度者身上,提示词里把反例写出来:
可以同时派:「查 A 的资料」和「查 B 的资料」
不能同时派:「查资料」和「根据资料写报告」
不能同时派:「写初稿」和「校对初稿」
并明确告诉它拿不准就分两轮——代价只是慢一点。
**每个人单独计时**,而不是事后拿整轮墙钟去摊。界面要显示"并行省下多少",
那个数必须是量出来的:按 N×墙钟 推算等于假设每个人都跑满了最慢那条,
而实际上快的那个可能只花了三分之一。测试里专门有个快慢不对称的用例守这条。
**调度日志改成结构化字段**(agents / done / reason / parallel)。以前只有一句
中文消息串,解码层得拿正则去拆「调度 → X(理由)」——后端文案一改就散架,
而这次正是改了文案。老运行的事件没有这些字段,正则那条路留着兜底。
**界面加泳道。** 每个成员一行、每轮一格,条形宽度按耗时归一化,所以一眼看得出
谁和谁是同时干的、谁是这一轮的瓶颈。顶上一行汇总"3 名成员 · 3 轮 · 2 轮并行,
省下 11.8s";点任意一格展开那个人这一轮的任务和回复。全程串行时不装作有并行,
而是说"本次任务互相依赖,逐轮进行"。
泳道排在子步骤**前面**:先给一眼看清的形状,再往下是每一步的内容。反过来的话,
得把十几行步骤读完才知道这个团队是怎么分工的。窄栏(画布右栏 360px)下隐去
"N 人同时"的文字——并行与否看条形本来就一目了然,而缩写成"并2"没人看得懂。
验证:后端 276 passed(新增 test_supervisor.py 7 条,核心那条用**墙钟**判定
并发而不是看事件顺序——顺序只能说明"发出去了",说明不了"同时跑");
check-stream 新增 10 条泳道断言,含"同一轮里慢的那个条更长"和两种栏宽不溢出;
tsc 干净,三个 check 不回归。
This commit is contained in:
@@ -136,6 +136,50 @@ const REVIEW_DEGRADED_TURN: StreamTurn = {
|
||||
rawOutput: { answer: "平台管理员可以修改他人权限。" },
|
||||
}
|
||||
|
||||
/** 协作团队的泳道:前两轮并行、第三轮汇总 */
|
||||
const TEAM_TURN: StreamTurn = {
|
||||
id: "team", question: "比较三种向量数据库在中文检索上的取舍", phase: "done", status: "完成",
|
||||
steps: [{
|
||||
id: "n-team", seq: 1, kind: "node", status: "done", title: "调研团队", meta: "24.1s",
|
||||
team: {
|
||||
members: ["researcher", "analyst", "writer"],
|
||||
savedMs: 11800,
|
||||
finished: true,
|
||||
rounds: [
|
||||
{
|
||||
round: 0, parallel: 2, wallMs: 9600, sumMs: 16400,
|
||||
reason: "两边资料互不相干,可以同时查",
|
||||
members: [
|
||||
{ agent: "researcher", instruction: "查 Milvus 与 Qdrant 的中文分词支持", ms: 9600,
|
||||
status: "done", result: "Milvus 2.4 起内置 jieba…" },
|
||||
{ agent: "analyst", instruction: "查 pgvector 的中文检索方案", ms: 6800,
|
||||
status: "done", result: "pgvector 本身不分词,需配 zhparser…" },
|
||||
],
|
||||
},
|
||||
{
|
||||
round: 1, parallel: 2, wallMs: 7400, sumMs: 12400,
|
||||
reason: "两组基准测试互不依赖",
|
||||
members: [
|
||||
{ agent: "researcher", instruction: "跑召回率基准", ms: 7400, status: "done",
|
||||
result: "hit@1:Milvus 0.91 / Qdrant 0.89 / pgvector 0.84" },
|
||||
{ agent: "analyst", instruction: "跑写入吞吐基准", ms: 5000, status: "done",
|
||||
result: "10 万条:Milvus 42s / Qdrant 51s / pgvector 88s" },
|
||||
],
|
||||
},
|
||||
{
|
||||
round: 2, parallel: 1, wallMs: 7100, sumMs: 7100,
|
||||
reason: "两份基准都出来了,可以汇总",
|
||||
members: [
|
||||
{ agent: "writer", instruction: "根据前面的结论写选型建议", ms: 7100,
|
||||
status: "done", result: "## 结论\n中文为主且要开箱即用 → Milvus…" },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
}],
|
||||
output: { 建议: "中文为主且要开箱即用选 Milvus;已有 Postgres 且数据量在百万以内,pgvector + zhparser 的运维成本最低。" },
|
||||
}
|
||||
|
||||
const LONG_TURN: StreamTurn = {
|
||||
id: "long", question: "需要能够显示总结内容", phase: "done", status: "完成",
|
||||
steps: [], output: { result: longReport() },
|
||||
@@ -185,6 +229,9 @@ function Preview() {
|
||||
if (params.get('review') === '1') {
|
||||
return <AssistantStream turns={[REVIEW_BROKEN_TURN, REVIEW_DEGRADED_TURN]} />
|
||||
}
|
||||
if (params.get('team') === '1') {
|
||||
return <AssistantStream turns={[TEAM_TURN]} dense={params.get('dense') === '1'} />
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex h-full flex-col">
|
||||
|
||||
Reference in New Issue
Block a user