TaskForce:我做的一个多Agent协作平台

TaskForce:我做的一个多 Agent 协作平台

项目地址:https://github.com/codejaron/TaskForce

为什么做这个项目

AI 交互范式的演进

回顾 AI 应用的发展,可以看到一条清晰的演进路径:

最早是网页聊天——ChatGPT 横空出世。但它只能动嘴,无法感知和改变外部世界。

然后有了 Function Calling——2023 年 OpenAI 让模型学会了调用工具,AI 开始能「动手」了。查天气、操作数据库、发邮件。

接着是 MCP 协议——2024 年底 Anthropic 推出 Model Context Protocol,让工具集成有了开放标准。不同平台可以共享同一套工具生态,AI 开始能跨服务协作。

再到 Agent 的爆发——Manus 让大家看到了 AI 自主规划执行任务的可能性,不再是一问一答,而是「给一个目标,AI 自己想办法完成」。紧接着 Microsoft 推出 AutoGen 多 Agent 框架,Google 发布 A2A(Agent-to-Agent)协议,行业开始认真思考 Agent 之间如何协作。

到了现在,Claude Code 成了标杆产品——它展示了一个 AI Agent 可以多么好用。有意思的是,Claude Code 选择了极简的单线程架构:一个主循环、一条消息历史、累积式上下文。简单、可控、易调试。


我在使用中遇到了一些问题

平时用网页 AI 或者类似工具时,经常碰到几个让人抓狂的情况:

上下文污染:聊着聊着,前面说的重点被中间的对话冲淡了,模型开始钻牛角尖
历史包袱:对话越长,无关信息越多,模型反而越容易被带偏
很多时候,单开一个新页面,把重点重新发给它,效果反而更好。

这让我开始想:累积式的上下文真的是最优解吗?

另一种思路:分工协作

Anthropic 在《How we built our multi-agent research system》里提到:

“子 Agent 通过各自独立的上下文窗口实现压缩,同时探索问题的不同方面,再将最重要的信息浓缩给主 Agent。每个子 Agent 也提供了关注点分离——不同的工具、提示词、探索轨迹——减少了路径依赖。”

这启发了我:如果每个步骤都有一个「干净」的上下文,只接收必要的信息,会不会更好?

想象一下人类团队是怎么协作的:

领导拆解任务,分配给不同的人
每个人专注于自己的部分,不需要知道所有细节
通过文档、会议纪要传递关键信息,而不是让每个人都参与所有对话
TaskForce 就是这个思路的实践

Planner 分析需求、生成结构化的执行计划
Worker 各自独立执行,每次调用都是干净的上下文
Artifact 系统在步骤间传递关键结果,而不是完整对话历史
每个 Worker 只知道:当前任务是什么 + 需要的上下文数据
这样做的好处:

上下文干净:每个 Worker 不会被无关历史污染
Token 可控:不会像累积式那样越来越长


二、核心架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
┌─────────────────────────────────────────────────────────────────────────────┐
│ 用户界面 (React + SSE) │
└────────────────────────────────┬────────────────────────────────────────────┘

┌────────────────────┼────────────────────┐
│ SSE 连接 │ HTTP 请求 │ SSE 事件推送
│ ↓ │
│ ┌───────────────────────────────┐ │
│ │ GroupChatController │ │
│ │ POST /api/groupchat/submit │ │
│ └───────────────┬───────────────┘ │
│ │ │
│ │ 立即返回 202 │
│ │ (Fire-and-forget) │
│ ↓ │
│ ┌───────────────────────────────┐ │
│ │ EventBus │←───┘
│ │ (事件总线) │
│ └───────────────┬───────────────┘
│ │
↓ ↓
┌─────────────────────────────────────────────────────────────────────────────┐
│ │
│ WorkflowEngine (工作流引擎) │
│ │
│ ┌────────────────────────────────────────────────────────────────────────┐ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │ │
│ │ │ StateManager│ ←──→ │StepExecutor │ ←──→ │ SessionContext │ │ │
│ │ │ 状态管理器 │ │ 步骤执行器 │ │ (会话上下文/ThreadLocal) │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────────────────┘ │ │
│ └────────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────┬───────────────────────────────────────────┘

┌─────────────────────────┼─────────────────────────┐
│ │ │
↓ ↓ ↓
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ PlannerAgent │ │ Worker(s) │ │ReplannerAgent │
│ 任务规划器 │ │ 任务执行器 │ │ 重规划器 │
└───────┬───────┘ └───────┬───────┘ └───────┬───────┘
│ │ │
│ ↓ │
│ ┌───────────────┐ │
│ │ MCP 工具层 │ │
│ └───────────────┘ │
│ │ │
└────────────────────────┼────────────────────────┘


┌─────────────────────────────────────────────────────────────────────────────┐
│ 数据持久层 (MySQL) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
│ │ session │ │execution_plan│ │ message │ │ session_artifact │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘

简单说就是四层:

  • 前端用 React,通过 SSE 实时接收进度
  • WorkflowEngine 是核心调度器,控制整个流程
  • 三种 Agent 各司其职:Planner 规划、Worker 执行、Replanner 兜底
  • 数据都存 MySQL

三、三个核心 Agent

PlannerAgent:规划器

接收用户目标,拆解成具体步骤,分配给 Worker。

它有三种输出:

  • 生成计划(目标清晰时)
  • 追问用户(目标模糊时)
  • 放弃规划(实在搞不定时)

有个小设计:LLM 输出 JSON 经常格式错误,我加了个 Self-Correction 机制,解析失败就把错误信息发回去让它自己改,最多试 3 次。

Worker:执行器

干活的。每个 Worker 只管一个步骤,可以调用 MCP 工具。

执行时可能遇到两种特殊情况:

  • BLOCKED:技术问题,比如服务挂了
  • NEED_USER_INPUT:需要用户补充信息

ReplannerAgent:重规划器

Worker 搞砸了就轮到它出场,分析原因,调整后续计划。


四、Artifact 上下文系统

多步骤执行有个头疼的问题:步骤之间怎么传数据?

全塞进 Prompt 里?上下文会爆炸。

我的方案是 Artifact 系统:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Worker A 执行步骤 1

│ LLM 输出:
│ "搜索结果如下:
│ <artifact key="search_results">
│ 1. 结果A - xxx
│ 2. 结果B - yyy
│ </artifact>"


┌─────────────────┐
│ ArtifactParser │ 正则提取 <artifact> 标签
└────────┬────────┘

│ key: "search_results"
│ value: "1. 结果A - xxx\n2. 结果B - yyy"


┌─────────────────┐
│session_artifact │ 存入数据库
│ 表 │
└────────┬────────┘


Worker B 执行步骤 2

│ Prompt 里只放预览(前200字符)
│ 需要完整数据时调用 query_artifact("search_results")


继续处理...

Worker 用 XML 标签输出结构化数据,存到数据库。后续 Worker 的 Prompt 里只放预览,要完整内容就调工具查。

这样上下文长度可控,数据也不会丢。


五、状态流转

执行计划有几个状态:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
               ┌───────────┐
│ CREATED │
│ (创建) │
└─────┬─────┘

│ PlanGeneratedEvent

┌───────────┐
┌────────→│ EXECUTING │←────────┐
│ │ (执行中) │ │
│ └─────┬─────┘ │
│ │ │
│ ┌──────────┼──────────┐ │
│ ↓ ↓ ↓ │
ReplanEvent StepCompleted UserInputReceived
│ (继续下一步) │
│ │ │
│ ↓ │
│ ┌───────────────────┐ │
│ │ 还有下一步? │ │
│ └─────────┬─────────┘ │
│ YES │ NO │
│ │ │ │
│ │ ↓ │
│ │ ┌───────────┐
│ │ │ COMPLETED │
│ │ │ (完成) │
│ │ └───────────┘
│ │
│ ↓
│ ┌───────────────────┐
│ │ BLOCKED 或 │
│ │ NEED_USER_INPUT │
│ └─────────┬─────────┘
│ │
│ ├─── BLOCKED ──→ ReplannerAgent ───┐
│ │ │
│ └─── NEED_USER_INPUT │
│ │ │
│ ↓ │
│ ┌───────────────────┐ │
│ │ WAITING_FOR_INPUT │ │
│ │ (等待用户) │ │
│ └─────────┬─────────┘ │
│ │ │
│ │ 用户提交回复 │
│ ↓ │
└────────────────────────┴────────────────────────┘

BLOCKED 会触发 Replanner,NEED_USER_INPUT 会暂停等用户回复。


六、完整执行流程

一个请求从头到尾是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
用户: "帮我搜索 AI 最新进展并写一篇总结"

│ POST /api/groupchat/submit

┌─────────────────┐
│ 保存用户消息 │ ──→ 【MessageCreatedEvent】 ──→ 前端显示
└────────┬────────┘


┌─────────────────┐
│ PlannerAgent │ ──→ 【PlanningStartedEvent】 ──→ 前端显示 "规划中..."
└────────┬────────┘

│ 生成计划:
│ Step 1: 搜索AI新闻 → Worker-1
│ Step 2: 整理结果 → Worker-2
│ Step 3: 写总结 → Worker-3


【PlanGeneratedEvent】 ──→ 前端渲染计划卡片


┌─────────────────┐
│ WorkflowEngine │ 开始执行循环
└────────┬────────┘

│ ══════ Step 1 ══════

【StepStartedEvent】 ──→ 高亮步骤1

│ Worker-1 调用搜索工具
│ 输出: <artifact key="search_results">...</artifact>


【StepCompletedEvent】 ──→ 标记完成,Artifact 存库

│ ══════ Step 2 ══════

【StepStartedEvent】 ──→ 高亮步骤2

│ Worker-2 读取 search_results(通过工具查询)
│ 输出: <artifact key="organized_data">...</artifact>


【StepCompletedEvent】

│ ══════ Step 3 ══════

【StepStartedEvent】 ──→ 高亮步骤3

│ Worker-3 读取 organized_data
│ 输出最终文章


【StepCompletedEvent】
【SessionCompleteEvent】 ──→ 前端显示完成

七、目前的问题和不足

说实话这个项目还比较粗糙,有几个明显的问题:

1. Planner 的规划质量不稳定

LLM 生成的计划有时候很合理,有时候很离谱。比如一个简单任务它能给你拆成 10 步,或者分配 Worker 的时候完全不看能力匹配。

目前只能靠 Prompt 工程硬凑,效果一般。

2. 步骤之间只能串行

现在是一个步骤完了才能执行下一个。但有些步骤其实可以并行,比如「搜索 A」和「搜索 B」完全可以同时跑。

这个后面想加,但涉及到依赖分析和并发控制,有点麻烦。

3. 前端比较简陋

功能是有了,但 UI/UX 还很粗糙,很多交互细节没打磨。

4. 没有代码执行反馈

现在 Worker 产出代码后,没法自动运行、拿到执行结果或报错信息再迭代。就是「写完就完了」,不知道写得对不对。

理想情况应该是:Worker 写完代码 → 自动运行 → 拿到报错 → 继续修 → 直到跑通。这个闭环目前没做。

5. 上下文压缩还不够好

虽然 Artifact 系统解决了步骤间传数据的问题(只放预览,按需查完整内容),但整个会话的上下文管理还是比较粗放。

没有像 Claude Code 那样的 Auto Compact 功能——当上下文快满的时候自动总结、开新会话继续。


八、后续想做的

如果有时间的话:

  • 支持步骤并行执行
  • 优化 Planner 的规划质量(可能要引入一些规则约束)
  • 前端重构,做得好看点
  • 代码执行闭环:Worker 写完代码能自动运行、拿到反馈、继续迭代
  • 上下文自动压缩,长对话时自动总结
  • RAG 能力:加入向量检索,让 Agent 能从历史对话、知识库里召回相关内容,支持「记忆」和「知识积累」
  • Skill 机制:类似 Claude Code 的 Skills 系统。每个 Skill 是一个独立的能力模块,包含 SKILL.md 指令文件和相关脚本,Claude 会根据任务自动判断该调用哪个 Skill。比如可以做一个「PDF 处理」Skill,里面有提取文本、填表单、合并文档的脚本,当用户提到 PDF 相关任务时自动激活。这样能让 Agent 的能力模块化、可复用、可分享。

最后

这是一个探索性项目,项目还在早期阶段,很多地方都不完善。

说实话,这个项目主要是为了学习。最近 Agent 很火,想亲手做一下,看看「角色分离 + 上下文隔离」这条路走起来是什么感觉。

整个项目边做边学,很多设计是摸索出来的,肯定有考虑不周的地方,而且前端是纯 AI 生成的。

如果你也对 Agent 架构感兴趣,欢迎一起交流探讨;如果发现 bug 或者有更好的想法,更欢迎提 issue 或 PR。

项目地址:https://github.com/codejaron/TaskForce