离线AI任务不中断:断网与重启后的自愈方案实测 2026年9月我把手头几台能跑AI的机器翻出来从零测了一遍“离线任务不中断”的整套组合方案。起因很简单手上有几批批量生成任务白天要带着笔记本出门晚上机器还得自己把活干完中间赶上一次断网云端服务直接罢工任务全卡在半截返工成本高到肉疼。于是就有了这个实测项目——目标就一句话断网情况下AI能继续跑机器关机重启后任务能自己续上。这轮实测我覆盖了本地模型推理层、任务编排层、Agent调度层把Ollama、llama.cpp、n8n、Dify、CrewAI、自写Python队列全部拉出来跑了一遍。如果你也有类似需求——比如内网环境部署AI应用、夜班批量推理、AI Agent无人值守跑批这篇文章应该能帮你少踩一半的坑。1. 场景拆解为什么“离线关机后继续跑”会成为刚需1.1 三类典型用户场景先说最现实的场景。第一类是夜班批量任务。很多AI任务其实不需要人盯比如批量生成商品描述、批量翻译文档、批量做OCR识别、批量给图片打标签。白天机器要干别的晚上人走了机器能不能自己把活干完这时候如果工具不支持离线运行或者断网就罢工基本就废了。第二类是内网隔离环境。很多企业、研究机构的生产网络是物理隔离的外网根本访问不了。数据不能出内网AI模型只能在本地机器上跑。我在实测中遇到一个朋友他们在内网做专利辅助检索模型不能联网任务队列不能依赖云服务所有环节都得本地化。这时候“离线”不是可选功能是硬性要求。第三类是资源受限的移动办公。笔记本白天带去开会晚上回家插电中间人不在任务想继续跑怎么办只能靠“任务状态持久化关机续跑”。这要求工具必须把任务状态写到磁盘而不是存在内存里重启之后还能认领未完成的任务。1.2 需求背后的技术逻辑任务不中断的三层含义很多人一听“AI离线运行”就以为只要本地装个大模型就行实测下来远没那么简单。“任务不中断”其实分三层缺一层都会出问题。第一层断网时模型依然可推理。这层解决的是“推理能力离线化”需要本地权重文件、本地推理框架、本地接口服务。Ollama能干的就是这层的事模型权重都存在本机推理时只在本地调用CPU/GPU不需要任何外部请求。但只做到这层只能保证“单次请求能出结果”任务调度、排队、失败重试全都没有。第二层任务状态可持久化。任务不能只活在进程里。进程一退任务状态全丢那关机续跑就是空谈。必须有一个东西记录“任务清单哪些跑完了、哪些还在排队、哪些跑失败了”最简单的方案是写JSON文件稍微正式一点用SQLite再重一点用Redis或者PostgreSQL。实测下来个人项目JSON就够多机协同才需要上数据库。第三层机器重启后任务能自己续上。这层解决的是“无人值守可靠性”。机器意外断电、系统更新自动重启、人为关机进程起来了之后能不能自动把队列消费端拉起来继续处理还没完成的任务这需要三个配合开机自启机制systemd/Task Scheduler/launchd、队列状态恢复、消费端的幂等设计。我在实测中见过太多项目死在第三层——任务队列是在内存里写的重启一次全没了或者消费端没有幂等重启后同一批任务被重复执行结果数据直接翻倍。这三层是层层递进的关系只做第一层只能说“能离线跑”全做到才配叫“任务不中断”。1.3 方案选型的核心评判维度我选工具的时候不看宣传功能多全只看五个维度硬不硬模型加载速度与资源占用同模型在不同推理框架下显存占用和加载时间差别能到30%以上。Ollama做了显存自适应调度llama.cpp更轻但配置费手vLLM吞吐高但部署重。个人单机场景Ollama最省心。队列容错能力任务跑一半进程崩了重试之后是重新跑还是从断点续这个问题直接决定任务成本。断点恢复机制有没有显式的任务状态持久化状态记录在内存还是磁盘启动时能不能自动扫描未完成任务服务重启恢复速度机器重启后多久能自动进入正常工作状态实测中有的工具要手动敲命令有的秒级自愈。离线安装与模型迁移成本是不是能完全离线部署模型包、依赖包能不能拷贝迁移这轮实测里我特意测了“A机下载、B机离线导入”的完整流程确实是不少人问得最多的点。2. 实测工具清单与选型依据谁真正扛住了断电和断网2.1 本地推理层Ollama、llama.cpp、vLLM的取舍本地推理层是整个方案的底座我分别测了Ollama、llama.cpp、vLLM三个框架。Ollama是我这轮的主推。它对个人用户最友好一条命令就能启动服务默认监听11434端口API兼容OpenAI格式。实测中我先把模型在联网机器上拉取到本地缓存再把整个模型目录拷贝到目标机器离线导入后直接运行完全不碰网络。Ollama的模型文件采用分层存储导入时只要保证目录结构完整、Manifest文件里的路径正确就能加载成功。它的显存调度也做得不错同一张卡上可以按上下文长度动态换入换出模型。llama.cpp的优势在极轻量纯CPU机器上也能跑量化支持更细。但它的部署门槛高一些需要自己编译、自己写服务封装、自己管理并发。如果你的场景是老旧笔记本、小内存设备llama.cpp值得研究但如果只是普通桌面机Ollama更省心。vLLM适合高并发批量推理连续批处理能把GPU利用率拉满但它依赖更重显存碎片管理、分页KV Cache这些概念对新手不太友好离线打包迁移也比较折腾。我在这次实测中没有把它作为主要测试对象只在对比表里列了数据——单机每日任务量超过几万条才值得上vLLM否则运维成本反而拖后腿。2.2 任务编排层n8n、Dify、自写Python队列模型有了任务怎么排队、怎么调度、怎么记录状态这一层我测了三种方式。n8n是可视化工作流工具支持把AI请求编排成多步骤流程每一步的执行状态会持久化到它的SQLite数据库里。实测中n8n的Workflow可以设定定时触发即使中间某一步失败也能自动重试。但它的短板是n8n内部的队列机制在处理超长任务时表现一般任务执行中如果整个Docker容器重启正在运行的那个执行节点可能卡在“running”状态需要手动标记为失败或重置。对短期任务还行对长跑任务要小心。Dify更适合做AI应用的后端编排它有完整的Prompt管理、知识库、工作流日志任务状态和日志都落库。但Dify本身不是任务队列它适合在线请求式调用不适合“挂一晚上批量跑几千条”的场景。我实测过用Dify的API批量调用请求一旦超过它的超时限制客户端断开后任务状态就不太好追踪。自写Python队列是我这轮实测下来最稳的方案。别听到“自写”就发怵核心代码不超过200行就是把任务状态写进SQLite消费端按状态轮询失败重试重启后自动扫描未完成任务。这个方案的优势是状态完全可控、断点恢复逻辑自己说了算、没有任何平台依赖。劣势是没有可视化界面运维得靠命令行和日志。我的建议很简单——任务形态复杂、需要可视化编排就选n8n需要做完整AI应用就选Dify批量任务追求“稳”和“可控”自写队列是底线方案也是这轮实测中唯一在断电模拟测试里零丢失的方案。2.3 Agent层CrewAI离线跑通的经验前两层解决的是“单次推理可靠”和“批量任务不丢”Agent层解决的是“多步骤自主决策不中断”。我实测了CrewAI搭配本地模型的方案。CrewAI这类Agent框架默认调的是OpenAI、Anthropic这类云端模型想离线跑必须做两件事。一是把LLM后端切到本地Ollama通过自定义BaseLLM接口指向http://localhost:11434/v1/chat/completions二是把Agent里面所有依赖联网的工具调用全部替换成本地实现比如把“搜索最新资料”换成“读取本地知识库文档”把“调外部API”换成“调用本地脚本处理数据”。实测中遇到的最大坑是Agent在跑多步骤协作时子任务执行到一半突然报错整个Crew直接终止而且没有状态恢复机制。解决办法是在Agent的task回调里把每一步执行结果实时写入SQLite在Crew的step_callback里记录当前执行步骤编号重启后从上次中断的步骤续跑而不是从头再来。实测下来离线CrewAI处理一批50个文档的分类任务断点续跑最多只重复执行最后一步不会把整批任务推倒重来。2.4 关机续跑的关键任务队列的状态恢复机制“关机后继续运行”这个需求的本质是任务队列的状态恢复。我在这轮实测里对比了三种状态存储方案存储方案恢复能力并发支持复杂度适用场景JSON文件弱需手动合并低最低单机几十条任务个人调试SQLite强重启自动恢复中低单机几百到几千条推荐Redis队列强支持多消费端高中多机协同、高并发任务实测结论个人项目无脑选SQLite。它就是一个文件备份迁移都方便重启后连数据都不用管天然持久化。Redis的优点是消费速度快但多了一个中间件部署离线环境下还得额外打包Redis性价比不高。JSON文件只适合任务量特别小的情况一旦任务中间状态多了文件并发读写会出问题。3. 实操部署三步实现离线任务不中断3.1 模型离线打包与导入先说模型怎么离线装到目标机器。很多人在内网环境卡在最开始这一步——没有外网模型文件拉不下来。我的做法是提前在联网机器上把模型拉取到本地缓存然后把整个模型目录打包拷贝过去。以Ollama为例模型文件默认存放在~/.ollama/models目录下Linux和macOS路径相同Windows在C:\Users\用户名\.ollama\models。操作步骤如下在联网机器上执行ollama pull qwen2.5:14b-instruct-q4_K_M把目标模型拉取到本地。找到模型目录整体打包tar -czf ollama_models.tar.gz ~/.ollama/models。拷贝压缩包到离线机器解压到相同路径。在离线机器上启动Ollama服务ollama serve执行ollama list确认模型可见。这里有个细节Ollama的模型通常带有量化版本文件名里的q4_K_M表示4-bit量化。实测下来14B模型用Q4量化后大约9GB显存需求降一半推理质量损失在可接受范围内。如果你的GPU显存只有8GB老老实实用7B模型Q4量化有24GB显存14B甚至32B的Q4都能跑起来。如果模型在联网机器上拉取时有问题也可以直接下载官方提供的GGUF格式文件放到models/manifests/registry.ollama.ai/library/模型名/标签对应的目录下再手写Manifest文件。这个方法适合从HuggingFace这类模型仓库直接下载的场景整个导入过程不需要Ollama参与联网只要文件结构和哈希值对得上就行。3.2 任务队列与断点续跑实现我直接给出我实测验证过的Python任务队列方案。先建SQLite表记录任务核心表结构是这样的import sqlite3 import json import time import requests from datetime import datetime DB_PATH tasks.db def init_db(): conn sqlite3.connect(DB_PATH) conn.execute( CREATE TABLE IF NOT EXISTS tasks ( id TEXT PRIMARY KEY, status TEXT DEFAULT pending, payload TEXT, result TEXT, error TEXT, created_at TIMESTAMP, updated_at TIMESTAMP ) ) conn.commit() conn.close() def add_task(task_id, payload): conn sqlite3.connect(DB_PATH) conn.execute( INSERT INTO tasks (id, status, payload, created_at, updated_at) VALUES (?, pending, ?, ?, ?), (task_id, json.dumps(payload), datetime.now(), datetime.now()) ) conn.commit() conn.close()消费端永真循环扫描pending状态的任务把任务发给本地Ollama服务执行成功写回result并标记done失败标记failed且记录错误信息并在任务超过最大重试次数前允许重试def process_tasks(): while True: conn sqlite3.connect(DB_PATH) rows conn.execute( SELECT id, payload FROM tasks WHERE statuspending ORDER BY created_at LIMIT 1 ).fetchall() conn.close() if not rows: time.sleep(5) continue task_id, payload_str rows[0] payload json.loads(payload_str) try: resp requests.post( http://localhost:11434/v1/chat/completions, json{model: qwen2.5:14b-instruct-q4_K_M, messages: [{role: user, content: payload[prompt]}], stream: False} ) result resp.json()[choices][0][message][content] update_task(task_id, done, resultresult) except Exception as e: update_task(task_id, failed, errorstr(e)) time.sleep(1)这里有几个关键设计必须说明白。第一状态更新必须显式写库不能只放在内存里等程序结束再写否则断电就丢。第二任务ID用确定性ID比如用输入数据的内容哈希值作为任务ID这样即使重复提交INSERT重复会报错天然避免重复任务。第三消费端轮询间隔不要设太短实测中SQLite的写入性能足够支撑每秒几十条任务更新轮询1秒足够不用设成50毫秒白烧CPU。任务状态机一共四个状态pending排队中、running执行中、done完成、failed失败。重启后消费端扫描时如果发现任务状态是running说明进程在上次执行中途死掉了这时候直接把状态重置为pending重新执行如果任务本身支持幂等重复执行也只会覆盖同一条结果不会产生脏数据。3.3 开机自启与看门狗脚本任务队列有了最后一步是让机器重启后自动把整套服务拉起来。我分别说一下三个平台的配置方法。Windows平台用任务计划程序。创建一个“系统启动时”触发的任务操作选“启动程序”程序填ollama.exe或你的Python脚本路径。注意要勾选“不管用户是否登录都要运行”这样才能在无人工干预的情况下自动拉起服务。我实测中踩过一个坑——任务计划程序默认以最高权限运行时Python脚本的工作目录会变成C:\Windows\System32如果你在脚本里用了相对路径读数据库会直接报文件找不到。解决办法是在脚本开头强制os.chdir(os.path.dirname(os.path.abspath(__file__)))或者把工作目录参数显式填好。Linux平台直接写systemd服务文件路径/etc/systemd/system/ai-worker.service[Unit] DescriptionAI Task Worker Afternetwork.target ollama.service [Service] Typesimple WorkingDirectory/opt/ai-worker ExecStart/usr/bin/python3 /opt/ai-worker/worker.py Restartalways RestartSec5 EnvironmentOLLAMA_HOST127.0.0.1:11434 [Install] WantedBymulti-user.targetRestartalways是关键参数进程异常退出后5秒自动拉起。实测中配合Typesimple只要主进程不退干净systemd就不会误判失活。如果你把Ollama也装在同一台机器上建议在After里写上ollama.service避免worker比模型服务先启动、请求直接打空。macOS平台用launchd写一个~/Library/LaunchAgents/com.example.aiworker.plist配置KeepAlive为true即可。我还在worker脚本外加了一层看门狗作用是定时检查任务积压数和服务存活情况。看门狗本身不处理任务只负责发现问题并及时拉起服务#!/bin/bash # watchdog.sh 每5分钟执行一次检查worker进程和服务端口 if ! pgrep -f worker.py /dev/null; then nohup python3 /opt/ai-worker/worker.py /opt/ai-worker/worker.log 21 fi if ! curl -s http://localhost:11434/api/tags /dev/null; then systemctl restart ollama fi看门狗脚本的检查逻辑不能太复杂核心就是一个发现进程挂了就拉起发现端口不通就重启服务。实测中很多人想在一行脚本里把所有异常状态都判断完结果脚本本身比主程序还容易出问题。4. 实测中的坑与排查实录4.1 离线模型加载失败的常见原因离线导入模型看起来很简单实际操作容易翻车。我实测中遇到最多的问题是这三类模型路径层级不对。Ollama的models目录有严格的层级结构manifests目录里按registry.ollama.ai/library/模型名/标签存放索引文件blobs目录存放实际权重分片。手动拷贝时如果只拷贝了blobs、漏了manifests或者Manifest文件里的digest和实际blob不一致Ollama会直接报错说找不到模型。排查方法很简单先把原来的models目录完全备份再替换启动后执行ollama list确认。量化文件和推理框架不匹配。同一个模型名的GGUF文件可能被不同的量化工具处理过不同版本的llama.cpp或Ollama对量化格式的兼容性不完全一致。实测中我遇到过一次Q8量化的模型在旧版Ollama上无法加载的情况升级到新版后正常。显存溢出后加载卡死。模型尺寸超过显存容量时Ollama通常会把部分层offload到CPU但如果内存也不够整个加载过程会卡住不动而且日志没有明显报错。排查方法是看ollama ps输出如果模型一直在加载状态且显存占用接近上限直接换更小尺寸的量化版别硬扛。生产任务对稳定性要求高跑不动的模型趁早换别把时间耗在调参上。4.2 任务中断后状态丢失的排查状态丢失是“关机续跑”方案里最隐蔽的坑。我这轮实测出现过一次诡异现象任务执行完数据库里也写了done但重启之后部分任务又变回了pending状态。排查后发现是写入事务没有提交——Python的sqlite3模块默认在INSERT后需要显式commit()但我有个分支忘了提交程序异常退出时那部分状态就回滚了。另一个容易踩的是任务重复消费。消费端从数据库取到一条pending任务后如果同一个worker进程被systemd拉起两次旧进程还没死透新进程又起来了就会同时处理同一任务导致结果重复写入。我的解决办法是在任务表里加一个worker_token字段消费端拿到任务后先更新statusrunning同时写入自己的随机令牌带WHERE statuspending条件做原子更新受影响行数为0就说明被别的worker抢走了跳过即可。幂等设计的核心只有一句话给每条任务一个唯一ID重复执行时以ID为准做覆盖或跳过。不要依赖“任务名称唯一”或者“结果内容相同”这类业务层面的判断会被数据格式问题坑到。4.3 资源占用与性能调优实测这轮实测的硬件环境是三台机器一台24GB显存的4090工作站一台16GB内存的Mac mini一台8GB显存的Windows笔记本。跑下来的数据很直观模型配置24GB GPU16GB纯CPU8GB GPU7B Q4约4.5GB秒级响应可跑32K上下文慢单条约20秒可跑建议限流14B Q4约9GB流畅单条约3秒基本跑不动超显存不推荐32B Q4约19GB可用但上下文受限不建议完全不可用如果你的机器显存只有8GB别硬塞14B模型。实测中我试过用8GB显存跑14B Q4模型的一部分层被offload到CPU之后单条请求耗时是24GB机器上的10倍以上任务队列积压严重。这种情况下不如用7B或者8B模型推理速度翻好几倍单条质量差距其实可以靠更好的Prompt补偿。性能调优方面Ollama有两个环境变量值得调。OLLAMA_NUM_PARALLEL控制模型并行请求数默认值偏低批量任务时可以设到4实测并发吞吐提升明显OLLAMA_MAX_LOADED_MODELS控制同时加载的模型数量如果任务队列里混合用了多个模型这个参数决定了几张卡的分工策略。上下文长度方面批量任务大多不需要超长上下文我实测把num_ctx从默认的4096砍到2048显存占用能省出一截速度也有提升。4.4 常见问题速查表问题现象可能原因排查方向ollama list看不到离线导入的模型Manifest路径错误或blob缺失检查models目录层级结构与原机器是否一致加载模型时卡住不动显存超限部分层offload到CPU执行ollama ps查显存占用换小模型任务重启后回到pending状态状态提交未commit进程被杀回滚检查代码中所有数据库写操作是否显式提交任务重复执行结果重复worker进程重复拉起无幂等处理加唯一任务ID和运行中状态锁定系统重启后worker未自动启动自启服务配置的WorkingDirectory错误检查systemd配置确保路径绝对化批量任务越跑越慢积压严重模型并行度太低或上下文混乱调OLLAMA_NUM_PARALLEL缩短num_ctx任务失败后一直pending不重试消费端没有失败重试逻辑加failed状态和重试计数超限后告警我实测下来最大的感触是离线任务不中断这套方案真正的核心不在模型选多大而在于任务状态管理是否稳妥。模型只是推理引擎任务队列、状态持久化、自启动、幂等消费这些“脏活累活”才是决定整个系统能不能扛住无人值守的关键。最后再分享一个小技巧。我习惯把Ollama的models目录单独放到一块独立硬盘或者独立分区不跟系统盘混在一起。这样系统重装不丢模型迁移到新机器时直接拔盘拷贝就行。同时数据库文件建议开启SQLite的WAL模式并发读写性能比默认模式好很多。这套方案我已经稳定跑了一段时间中途经历过三次断电重启和一次系统更新自动重启任务队列全部自动恢复没丢过一条任务。