在企业级智能体系统中,真正困难的并不是"调用一次大模型",而是如何让一个复杂任务具备可规划、可暂停、可恢复、可审计、可治理的完整生命周期。本文基于一套 Python + FastAPI 实现的多智能体运行时平台,系统拆解其核心设计:任务 API、执行计划、状态机、运行时快照、事件信封、任务仓储与执行引擎。该系统通过 POST /tasks 创建任务,通过 POST /tasks/{task_id}/resume 恢复任务,并在执行过程中完成"规划---执行---人工补参---工具调用---结果汇总"的闭环。
一、系统定位:不是聊天机器人,而是 Agent Runtime
传统聊天机器人通常围绕单轮问答展开,用户输入一句话,系统调用模型后直接返回结果。但企业级任务往往不是一次模型调用可以完成的,例如"分析支付系统最近的告警并给出处理建议",系统需要先理解任务,再判断缺少哪些参数,然后等待用户补充业务系统、时间范围等信息,最后再调用工具并汇总结果。
因此,这套系统的定位更接近一个 Agent Runtime Platform,也就是智能体运行时平台。它关注的核心不是"模型怎么回答",而是"任务如何被可靠执行"。
在当前实现中,一个任务会经历以下流程:
text
创建任务
-> 生成执行计划
-> 执行任务理解节点
-> 暂停等待人工输入
-> 用户补参恢复
-> 执行工具调用
-> 汇总结果
-> 任务完成
这一流程由 EngineService 统一驱动,状态转换由 TaskStateMachine 约束,任务数据由仓储保存,执行过程由事件日志记录。
二、整体架构:分层清晰的任务操作系统
系统整体可以划分为六层:
text
FastAPI 接入层
-> TaskService 应用服务层
-> EngineService 执行引擎层
-> PlannerService 任务规划层
-> TaskStateMachine 状态机层
-> Repository 仓储层
其中,API 层只负责 HTTP 请求接入和 DTO 转换,不直接处理复杂业务。TaskService 负责协调任务创建、查询和恢复;EngineService 负责真正的运行时调度;PlannerService 负责生成执行计划;仓储层负责保存任务对象。这样的分层让系统具备良好的可替换性,例如当前使用内存仓储,后续可以无缝替换为 PostgreSQL。
三、API 层:任务生命周期的外部入口
任务管理 API 提供了四个核心端点:
text
POST /tasks
GET /tasks
GET /tasks/{task_id}
POST /tasks/{task_id}/resume
其中,POST /tasks 用于创建任务,创建后系统会自动启动执行引擎,并暂停在人工输入节点;POST /tasks/{task_id}/resume 用于提交用户补充参数,让任务从暂停点继续执行。API 层通过 to_task_response() 和 to_task_detail_response() 将领域对象转换为前端友好的响应 DTO。
这种设计有一个很重要的工程价值:前端不需要理解引擎内部细节,只需要根据返回的状态和 requiredUserFields 判断下一步该展示什么表单。例如任务创建后返回:
json
{
"status": "WAIT_USER_INPUT",
"requiredUserFields": ["businessSystem", "timeRange"]
}
前端即可动态提示用户补充业务系统和时间范围。
四、执行计划:把自然语言转成可调度节点
系统中的执行计划由 ExecutionPlan 和 PlanNode 表示。一个 PlanNode 就是任务执行中的最小调度单元,包含节点 ID、节点类型、依赖关系、配置、重试策略和超时时间等信息。节点之间通过 depends_on 形成 DAG 结构。
当前示例中,PlannerService 生成了一个线性计划:
text
understand_task
-> collect_required_fields
-> execute_mock_tool
-> summarize_result
这四个节点分别对应:
text
LLM_REASONING:理解用户任务
HUMAN_INPUT:等待人工补参
TOOL_CALL:调用工具
SUMMARY:汇总结果
虽然当前规划逻辑是硬编码的,但这个抽象非常关键。因为一旦后续接入 LLM,就可以让大模型根据用户输入动态生成执行计划,从而支持故障分析、变更审批、巡检报告、知识检索等不同业务流程。
五、状态机:让智能体系统具备确定性
大模型是概率系统,但企业业务系统必须是确定性系统。因此,任务状态不能随意流转,必须由状态机统一约束。
系统定义了完整的任务状态:
text
CREATED
PLANNING
PLAN_CREATED
RUNNING
WAIT_USER_INPUT
WAIT_USER_CONFIRM
WAIT_APPROVAL
PAUSED
RETRYING
FAILED
CANCELLED
FINISHED
典型流程如下:
text
CREATED -> PLANNING -> PLAN_CREATED -> RUNNING -> WAIT_USER_INPUT
WAIT_USER_INPUT -> RUNNING -> FINISHED
所有合法状态转换都定义在 _ALLOWED_TRANSITIONS 中。例如,CREATED 只能进入 PLANNING,PLAN_CREATED 只能进入 RUNNING,FINISHED 和 CANCELLED 是终态,不允许继续转换。
这种设计的价值在于:
text
防止非法状态跳转
保证任务生命周期可控
为前端提供明确操作边界
为失败重试和人工恢复提供基础
对于多智能体系统来说,状态机就是运行时秩序的核心。
六、RuntimeTask:任务运行时的核心领域实体
RuntimeTask 是整个系统最重要的领域对象。它不仅保存任务的基础信息,例如 task_id、tenant_id、user_id、input_text,还保存执行过程中的动态状态,例如当前节点、下一个节点、上下文、执行计划、事件日志和最终结果。
其中比较关键的是:
python
context: dict[str, Any]
plan: list[dict[str, Any]]
event_log: list[dict[str, Any]]
result: dict[str, Any] | None
context 用来在节点之间传递数据。例如任务理解节点会写入 taskUnderstanding,工具调用节点会写入 mockToolResult。最终汇总节点会基于上下文生成 result。
这使得任务不再只是一个简单请求,而是一个可持续演进的运行时对象。
七、运行时快照:实现暂停与恢复
企业级 Agent 系统必须支持人机协同,而人机协同的本质就是"任务暂停"和"任务恢复"。
系统通过 RuntimeSnapshot 描述运行时快照,记录当前节点、下一个节点、上下文变量、必填字段、已完成节点、失败节点、重试状态和乐观锁版本号等信息。
这意味着当任务暂停时,系统不是简单地"等一下",而是保存完整执行现场。用户补参后,系统可以从暂停点继续执行,而不是重新开始。
这非常像操作系统里的进程上下文保存,也类似工作流引擎中的 checkpoint 机制。
八、事件信封:为审计、追踪和异步化做准备
系统还定义了统一的 EventEnvelope,用于封装任务创建、状态变更、工具调用、人工交互等事件。事件中包含:
text
eventId
eventType
taskId
tenantId
userId
correlationId
causationId
traceId
source
payload
其中,correlationId 用于关联同一业务流程中的事件,causationId 用于描述事件因果关系,traceId 可对接 OpenTelemetry 等链路追踪系统。
这说明系统从一开始就不是面向 Demo 设计,而是面向生产级事件驱动架构设计。后续接入 Kafka 后,就可以实现:
text
任务事件异步投递
工具调用异步消费
失败事件进入重试队列
超限失败进入死信队列
基于事件日志做审计和回放
九、EngineService:任务运行时调度核心
EngineService 是整个系统的心脏。它负责启动任务、推进状态、执行节点、暂停任务和恢复任务。启动任务时,它会先进入规划阶段,调用 PlannerService 生成执行计划,然后模拟执行 understand_task 节点,再暂停到 collect_required_fields 节点等待用户输入。
恢复任务时,它会完成三类校验:
text
任务是否存在
任务是否处于 WAIT_USER_INPUT 状态
用户输入是否包含所有 requiredUserFields
校验通过后,系统会把用户输入合并到上下文,继续执行工具调用节点,最后进入结果汇总节点并将任务状态置为 FINISHED。
这个执行过程虽然是模拟实现,但已经具备了生产级 Runtime 的骨架:
text
状态机驱动
节点化调度
上下文传递
人工暂停恢复
事件日志记录
结果汇总输出
十、仓储模式:让存储实现可替换
当前系统使用 InMemoryTaskRepository 保存任务,内部通过 dict[UUID, RuntimeTask] 存储数据。虽然这是一个内存实现,但所有方法都定义为异步接口,方便后续替换为真实数据库。
例如:
python
async def save(self, task: RuntimeTask) -> RuntimeTask:
task.touch()
self._tasks[task.task_id] = task
return task
这种设计体现了仓储模式的价值:
text
业务层不关心数据存在哪里
内存仓储可以用于测试和演示
PostgreSQL 仓储可以用于生产
Redis 可以用于缓存和分布式锁
如果后续要生产化,只需要实现同样接口的数据库仓储,而无需修改 API 层和 Engine 层。
十一、DTO 设计:隔离前后端数据模型
API 层定义了独立的 DTO,包括:
text
CreateTaskRequest
ResumeTaskRequest
TaskResponse
TaskDetailResponse
TaskListResponse
这些模型通过 Pydantic 的 alias 实现 camelCase 和 snake_case 的自动转换。例如后端字段是 input_text,前端请求字段是 inputText。
这是一种非常实用的工程设计:
text
后端保持 Python 风格
前端使用 JSON 常见 camelCase
领域模型与接口模型解耦
响应体可按场景区分轻量版和详情版
十二、系统启动与服务入口
FastAPI 应用入口在 main.py 中完成应用实例创建、路由注册、静态文件挂载和健康检查接口定义。系统提供 /health 接口,适合后续接入 Kubernetes 存活探针或负载均衡健康检查。
这说明系统虽然是一个原型,但已经具备基础服务化意识:
text
API 文档
静态资源挂载
健康检查
模块化路由注册
配置集中管理
十三、从原型到生产级平台的演进方向
当前系统已经跑通了最核心的任务闭环,但要演进为生产级平台,还需要进一步增强:
text
1. 将内存仓储替换为 PostgreSQL,实现任务、计划、事件持久化。
2. 引入 Redis 分布式锁,防止同一任务被并发恢复或重复执行。
3. 接入 Kafka,将事件日志升级为真正的事件总线。
4. 接入真实 LLM,让 PlannerService 动态生成执行计划。
5. 接入工具注册中心,实现 TOOL_CALL 节点的真实工具调用。
6. 引入子智能体注册与调度机制,实现 SUB_AGENT 节点派发。
7. 增加 OpenTelemetry 链路追踪,打通 traceId、eventId 和 taskId。
8. 增加任务看板,展示任务状态、执行节点、事件日志和失败原因。
这样一来,系统就可以从一个单机原型演进为真正的企业级多智能体任务编排平台。
结语
这套系统最有价值的地方,不在于它当前调用了多强的大模型,而在于它抽象出了企业级 Agent Runtime 的核心骨架:
text
任务是长期存在的
执行是状态机驱动的
节点是可编排的
上下文是可传递的
人工交互是可暂停恢复的
事件是可审计追踪的
存储是可替换的
未来的企业智能体平台,竞争重点不会只是谁的 Prompt 更好,也不会只是模型参数更大,而是谁能构建更可靠的 Runtime。
因为真正的企业级智能体系统,本质上不是一个聊天框,而是一个面向复杂任务执行的智能操作系统。