基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
AIHT
内容
精选全部 AI 动态热点榜AI 日报收藏
分类
模型行业编程产品技术观点开源
更多
Agent 接入
返回首页开发者导航
AIHT
精选 / 资讯详情
8.0
热点
AI SCORE
开源项目2026-10-30 13:49

用 Temporal 让本地 Agent 崩溃后续跑

dev.to · AI#Agent#Temporal#故障恢复
Editor brief · 编辑速览

Undead Agent 使用 Ollama 运行 Gemma,逐个汇总文本文件并生成报告,将执行步骤交给 Temporal 持久化工作流管理。进程遭 SIGKILL 后,新进程可接管任务,保留已完成步骤,并通过面板展示终止与恢复记录。

文章思维导图
Knowledge map
拖拽缩放
AI deep read

AI 深度解读

把 AI Agent 的进程强制杀掉,它还能接着处理文件,已经完成的模型调用也不用重跑。Undead Agent 做了这样一个演示:Gemma 负责生成摘要,Temporal 保存执行进度,进程死后由新 worker 接手。

它最有价值的地方,是把「模型负责什么」和「任务如何活下来」分开了。对写过批量处理脚本、长任务服务的程序员来说,这个项目提供了一种可以借鉴的实现方式。不过,「从死亡处继续」有边界,重试也不等于恢复模型的思考现场。

进程可以丢,已经完成的工作怎么保住

Undead Agent 的任务很简单:读取目录中的文本文件,逐个生成摘要,再把摘要合成报告。作者选择这个场景,是因为长任务很容易遇到休眠、内存不足导致的进程终止,以及部署重启。普通脚本如果只把进度保存在内存里,执行到第 9 步崩溃,重启后就可能从头再来。

项目把这些操作放进 Temporal 的持久化工作流(durable workflow)。大白话解释,就是由独立的执行系统记住哪些步骤成功了,以及成功时返回了什么。

下面这段代码定义了完整的任务顺序:

export async function undeadAgent(dir: string): Promise<string> {
  const files = await listFiles(dir);
  const summaries: string[] = [];
  for (const f of files) {
    summaries.push(await summarizeFile(f));
  }
  return writeReport(summaries);
}

要看的是 summaries.push(await summarizeFile(f)):每个文件作为独立 activity 执行,已完成 activity 的结果会记录在 Temporal 历史里。这里能恢复,依赖的是 Temporal 的执行机制,普通 JavaScript 函数里的 await 没有这种能力。

新 worker 接手后,会重放工作流,通过历史结果重建 summaries 数组,再继续未完成的步骤。数组没有直接跨进程存活,构建它所需的结果被保留了。

恢复的是任务进度,正在生成的那一次模型回答仍可能需要重做。

Undead Agent 的架构:可替换的 worker、Temporal 工作流、本地模型与事件面板

作者提供了一次真实运行的事件记录:12:22:37,worker 21496 完成 file6.txt,即将处理 file7.txt;随后进程被杀。12:22:53,新 worker 2432 启动;12:23:11,file7.txt 进入 attempt 2;12:23:26,该文件处理完成,之后继续处理文件 8、9。

这次记录里,文件 1 到 6 没有重做。它证明的是这条文件摘要流程能够恢复,不能据此推导所有 Agent 任务都能无损重试。

「复活」靠超时和重试,「鬼魂」靠一次新提示

面板上的 Kill it 按钮发送的是 SIGKILL,worker 没机会收尾。作者最初用 Ctrl+C 测试,但当时 worker 会等待当前步骤完成,因此没有真正验证执行中断的恢复。

负责启动进程的是 keepalive.ts。它在子进程退出后,通过 setTimeout(start, 5000) 等待 5 秒,再执行 npx tsx src/worker.ts。新进程启动,不代表中断的步骤立刻重试:Temporal 还要等待原 activity 超时,文中说重启后可能再等待最多 30 秒。

Temporal 时间线显示中断步骤超时后,以 Attempt 2 重试

「鬼魂知道自己死过」也有一个明确机制:读取 Context.current().info.attempt,当 attempt > 1 时,再调用模型,让它围绕当前文件生成两句阴森的旁白,并要求不使用 emoji。

这里需要拆开两件事。attempt 大于 1 表示发生了重试;在作者刻意杀进程的演示里,它对应一次复活。但重试也可能来自其他失败,不能单靠这个计数确认进程曾被杀。旁白同样是新生成的文本,没有证据表明模型保留了中断前的内部状态。

每个步骤还向 events.jsonl 追加事件,包括 born、step done 和 resurrected,面板读取这些记录,展示生命周期。公开页面由 Render 托管,播放的是一次真实运行的录像,真实 Agent 在作者本地运行。

面板展示 worker 的死亡、复活以及文件处理进度

作者还踩中了一个很实际的坑:最初所有步骤统一设置 30 秒超时,单文件摘要能完成,最终报告却连续超时四次,直到 attempt 5 才成功。后来他把报告步骤单独调整为 120 秒。

短超时让演示恢复得更快,也可能误伤正常的慢请求。超时配置必须跟步骤的实际耗时一起考虑。

本地模型降低了调用门槛,但可靠性由工作流提供

项目默认使用 Ollama 中的 gemma3:4b,模型名称通过一个环境变量配置。Gemma 承担两个任务:生成文件摘要,以及撰写复活旁白。

下面是它调用模型的方式:

async function ask(prompt: string): Promise<string> {
  const res = await fetch('http://localhost:11434/api/generate', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ model: MODEL, prompt, stream: false }),
  });
  if (!res.ok) throw new Error(`Ollama error ${res.status}`);
  const data = (await res.json()) as { response: string };
  return data.response.trim();
}

关键是 stream: false:函数等待完整响应后,才返回 data.response.trim()。这段实现没有保存生成到一半的 token;请求中断后,未完成 activity 会重新执行。

作者看重本地部署的几个好处:不需要 API key,不向托管模型发请求,没有按 token 计费,反复测试重试时只消耗本机资源。文件内容也可以留在本机的处理链路里。严格说,「文件不离开磁盘」并不准确,内容仍会被读取并交给本地模型。

模型与工作流分开,也便于调整 Gemma 的规格,而不改任务编排。但文章没有给摘要质量、运行速度或硬件需求的对照数据。它验证了恢复机制,没有验证默认模型适合哪些文档。

我的判断

我认可这个项目的工程取舍:把完成记录交给进程之外的系统保存,让 worker 可以被替换。作者用 SIGKILL 和 Temporal 时间线展示恢复过程,比只展示一次顺利跑完更有说服力。

但原文的「不丢失任何东西」说得太满。演示覆盖的是 worker 死亡,没有证明 Temporal 服务、历史存储或整台机器故障后会怎样。原文也没有说明输入文件在重试期间发生变化时如何处理。

还有一个关键边界:activity 可能已经产生效果,却没来得及记录成功。对于摘要计算,后果通常是多算一次;如果任务扩展到写文件、发通知或调用外部接口,就需要考虑幂等,也就是重复执行时避免重复产生效果。这个项目没有给出这些场景的验证。

已完成步骤可以跳过,不代表每个外部操作都只会发生一次。

和自行保存 JSON 进度相比,Temporal 把历史、重放、超时和重试集中管理,也增加了服务运行与配置工作。作者自己承认,大部分时间花在周边工程和可视化上。对于短脚本,记录完成文件列表可能已经够用;对于频繁中断、需要查失败原因的长任务,这种投入才更容易体现价值。文章没有给两种方案的成本对照,这部分应当视为经验判断。

能拿来做什么

  • 做 Node 批量文档摘要时,可以按文件划分步骤,保存每份摘要的完成结果,再执行最终汇总。这样一次中断不会迫使整批重跑。
  • 为日常 AI 编程任务加入故障演练:在执行中发送 SIGKILL,检查已完成步骤是否重复、未完成步骤能否恢复,以及最终产物是否完整。
  • 给文件处理和报告生成分别设置超时。原文的 30 秒与 120 秒可以作为理解差异的案例,实际配置仍要根据模型和输入耗时确定。
  • 想复现时,先安装 Ollama 并拉取 Gemma,安装 Temporal CLI 后执行 temporal server start-dev;使用 git clone https://github.com/Khushighosh/undead-agent && cd undead-agent && npm install 获取项目,在 docs/ 放入 .txt 文件,再分别运行 npx tsx src/keepalive.ts、npx tsx src/dashboard.ts 和 npx tsx src/client.ts,打开 http://localhost:3000 测试 Kill it。
Original source

本文由 AI 通读原文后重新编写,观点部分为站内判断,细节以原文为准,来源 dev.to · AI,原文版权归原作者所有。

阅读原文
上一篇
多模态代理答对了,也未必找对证据
下一篇
n8n 工作流也需要锁定模型版本

资讯速览

dev.to · AI · 2026-10-30 13:49

AI 评分 8.0 / 10

相关标签

#Agent#Temporal#故障恢复

继续阅读

  • AI 调用日志如何兼顾排障与数据隐私
  • 多模态代理答对了,也未必找对证据
  • n8n 工作流也需要锁定模型版本
  • 用多智能体串联 Kubernetes 故障排查
  • 长序列建模:状态空间模型如何降低开销