Agent长任务状态持久化,中断后继续执行数据库设计
很多 Agent 应用在运行长任务时,一旦服务重启、进程被杀或网络闪断,任务就从头再来。
要解决这个问题,核心思路是把任务进度持久化到数据库,让 Agent 在中断后能定位到上次的断点并继续执行。
本文直接给出可落地的表结构、执行流程和恢复策略,适合零基础开发者照着建表、改代码和验证效果。
设计之前先想清楚:长任务到底要保存什么
在设计数据库之前,先明确 Agent 长任务需要持久化的三类信息:
- 任务本身的元数据:任务编号、任务类型、创建时间、状态。
- 执行进度:当前执行到哪个步骤、步骤是否成功、重试次数。
- 断点上下文:例如已经获取的数据分页游标、已处理的消息 ID、步骤的中间输出。
这三类数据分别对应三张表:任务表、步骤表、上下文表。
不要试图把所有内容塞进一张大表,后续扩展和排查都会很痛苦。
第一步:设计任务表和步骤表
任务表负责记录一次完整请求的生命周期。
CREATE TABLE agent_task (
task_id VARCHAR(64) PRIMARY KEY,
task_type VARCHAR(50) NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'pending',
current_step INT NOT NULL DEFAULT 0,
max_step INT NOT NULL DEFAULT 0,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
finished_at DATETIME NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
其中 status 建议使用 pending、running、paused、success、failed 这几种状态。current_step 表示当前执行到的步骤序号,
恢复执行时直接从这里继续。
步骤表记录每个子步骤的执行细节:
CREATE TABLE agent_step (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
task_id VARCHAR(64) NOT NULL,
step_name VARCHAR(100) NOT NULL,
step_order INT NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'pending',
retry_count INT NOT NULL DEFAULT 0,
output TEXT NULL,
started_at DATETIME NULL,
finished_at DATETIME NULL,
INDEX idx_task_step (task_id, step_order)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
step_order 和任务表的 current_step 对应。
每次执行一个步骤,先更新步骤表,再更新任务表,确保两边数据一致。
第二步:使用上下文表保存断点信息
有些长任务并不是简单的一步步执行,而是需要记录中间数据。
比如循环抓取多页数据,需要保存当前抓到的页码;
或者处理一批文件,需要记录已经处理到的文件路径。
这部分数据不适合放进任务表,单独建一张上下文表更清晰。
CREATE TABLE agent_context (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
task_id VARCHAR(64) NOT NULL,
context_key VARCHAR(100) NOT NULL,
context_val TEXT NOT NULL,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_task_key (task_id, context_key)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
写入时使用 INSERT ... ON DUPLICATE KEY UPDATE 保证幂等:
INSERT INTO agent_context (task_id, context_key, context_val)
VALUES ('task_001', 'last_page', '5')
ON DUPLICATE KEY UPDATE context_val = VALUES(context_val);
恢复执行时,先查任务表拿到 current_step,再读上下文表拿到断点数据,就能精确续跑。
第三步:中断后如何恢复执行
恢复逻辑按以下顺序实现,避免出现重复执行或跳步:
- 根据
task_id查询任务表,判断status是否为paused或running。 - 从
agent_task读取current_step。 - 从
agent_context读取该任务的全部上下文。 - 从
agent_step查询step_order >= current_step且状态不是success的步骤列表。 - 从断点步骤开始重新执行,每完成一步更新三张表。
伪代码示例:
task_info = db.query("SELECT * FROM agent_task WHERE task_id = %s", task_id)
context = db.query("SELECT context_key, context_val FROM agent_context WHERE task_id = %s", task_id)
steps = db.query("SELECT * FROM agent_step WHERE task_id = %s AND step_order >= %s AND status != 'success' ORDER BY step_order", task_id, task_info.current_step)
for step in steps:
result = execute_step(step, context)
if result.success:
update_step_success(step)
update_task_current_step(step.step_order)
else:
update_step_retry(step)
break
常见疑问与避坑说明
任务中断时状态不一致怎么办?
先更新步骤表,再更新任务表的 current_step。
如果更新完步骤表后进程崩溃,恢复时会发现当前步骤已经标记成功,自动跳到下一步;
如果先更新任务表再更新步骤表,就可能出现跳过步骤的问题。
是否需要引入消息队列?
如果任务量不大,直接使用数据库轮询即可。
只有任务执行频率很高或需要分布式协调时,才建议引入队列,避免数据库连接被打满。
状态字段不要用中文或自由文本
建议用固定英文枚举值,避免程序判断时大小写不一致或空格问题。
不要依赖数据库事务跑长任务
事务只适合短小操作。
每个步骤独立提交,才能让中断恢复时看到真实进度。
验证设计是否可用
写完代码后,按以下步骤做一次完整的断点续跑测试:
- 启动一个包含 10 步的 Agent 任务,执行到第 4 步时手动杀掉进程。
- 检查
agent_task表中current_step是否为 3 或 4,status是否为running。 - 检查
agent_context是否保存了断点数据。 - 重新启动服务,触发恢复流程。
- 观察日志,确认任务从第 4 步继续执行,而不是从头开始。
- 执行完成后检查
agent_step所有步骤均为success,任务表status为success。
如果第 5 步出现重复执行,优先检查步骤表的状态判断条件;
如果恢复后跳步,优先检查 current_step 的更新顺序。
本文介绍的这套设计直接解决了 Agent 长任务中断后继续执行的核心问题。
按照这个思路建表、写恢复逻辑并完成验证,你的 Agent 服务在重启或崩溃后就能自动回到断点续跑。
遇到状态不一致时,通过核对任务表和步骤表的数据就能快速定位原因。