ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Termux + SQLite 搭建 10 层 Agent Mesh:手机端多Agent编排与热节流实践

2026/8/31 1:29:36 拓冰建站 浏览量
Termux + SQLite 搭建 10 层 Agent Mesh:手机端多Agent编排与热节流实践 在手机上用 Termux 跑一套 10 层 SQLite Agent Mesh还要尽量不触发热节流这件事乍看像炫技实际更像一次资源规划训练。SQLite 负责状态存储和任务协调Agent 各自消费任务、更新状态、向下游输出结果10 层表示处理链路至少有 10 个逻辑阶段而不是真的有 10 台机器在组网。手机端真正做主的是人不是算力。如果你有一台还能用的 Android 手机想验证多 Agent 编排、任务队列、失败重试、低频自动化和调度的基本思路这套方案确实可以低成本跑通。但“without thermal throttling”只能当作目标不是默认状态什么都不做直接开 10 个 Worker十几分钟后温度就会教你重新规划。下面按我实际会用到的顺序把这件事拆开讲先确认架构再准备环境然后搭最小骨架接着处理热节流最后给出验证方式和边界判断。1. 先搞清楚 10 层 Agent Mesh 到底在跑什么1.1 Agent Mesh 不是分布式数据库而是任务编排方式先说一个容易误解的点Agent Mesh 不是把 SQLite 变成分布式数据库而是用 SQLite 作为多个 Agent 之间的共享状态和协调层。每个 Agent 是一个独立进程它做的事情可以抽象成三步从数据库里领取一个属于自己的任务。执行任务。把结果写回数据库然后领取下一个任务。10 层的意思就是任务要经过 10 个逻辑阶段才最终完成。比如第 0 层负责数据采集第 1 层负责格式校验第 2 层标准化第 3 层加工处理然后逐层传递直到最后一层导出结果。Mesh 和普通流水线的区别在于Agent 之间的连接不是写死的。每个 Agent 可以只知道自己负责哪个层级、处理什么类型的任务至于上游是谁、下游是谁都由任务路由字段决定。这样好处是灵活坏处是排错时要多查一层路由信息所以表结构设计反而比代码更重要。1.2 为什么是 SQLite而不是 MySQL 或者 Redis在 Termux 里跑 MySQL 或者 Redis 不是不行但没必要。手机上资源本来就紧张再额外维持一个常驻数据库服务只会增加内存占用和温度压力。SQLite 的优势非常匹配这个场景单文件数据库整个库就是一个.db文件备份、迁移都很方便。不需要单独启动数据库服务进程直接读写文件。支持 WAL 模式读和写可以并发。同一个设备上多个进程访问同一个库文件只要控制好写频率完全够用。SQLite 的短板也很明确同一时刻只有一个写事务能成功。所以多 Agent 同时更新状态时很可能出现database is locked报错。解决办法不是换数据库而是控制写入频率、设置busy_timeout、减少高频 UPDATE。1.3 10 层怎么拆才不像硬凑分层不是越多越好而是每一层都要有独立职责。常见的 10 层可以这样设计层级职责举例tier 0数据接入接收原始文本或文件路径tier 1格式校验检查字段是否完整tier 2数据清洗去掉空值和异常字符tier 3标准化统一时间格式、大小写等tier 4富化补充额外信息tier 5分类或打标签tier 6聚合按某个维度汇总tier 7生成结果例如渲染报告tier 8质检检查结果是否满足规则tier 9导出和清理如果业务根本不需要聚合和质检硬拆 10 层只会增加延迟。更合理的做法是先用 3 层验证流程再逐步扩到 5 层、8 层、10 层。10 层在这里更应该理解成“架构扩展目标”而不是第一次跑就必须全部实现。2. Termux 环境准备和热节流前置判断2.1 Termux 怎么装、基础包装哪些Termux 是 Android 上的终端模拟器和 Linux 环境。社区通常建议从 F-Droid 或 GitHub Releases 获取安装包避免渠道包更新不及时的问题。进入 Termux 后先做基础更新pkg update pkg upgrade -y然后安装本次需要的基础包pkg install python sqlite tmux htop -y需要文件导出到手机存储时可以再执行termux-setup-storagetmux 在这里比较重要。它能让进程在 Termux 会话关闭后继续运行相当于给 Agent 进程做了最简单的守护。如果不想用 tmux也可以用nohup ... 但输出日志管理会麻烦一些。2.2 先量化手机的资源水平不要凭感觉判断“我的手机能不能跑”。先看几项硬指标nproc free -h df -hnproc查看 CPU 核心数free -h查看内存df -h查看磁盘空间。对于 10 个轻量 Python Agent 来说6 核 CPU、4GB 内存、剩余 2GB 磁盘通常能启动但能不能稳定跑取决于任务本身的 CPU 占用和并发调度策略。还要注意手机 SoC 的不同核心型号可能不同。有的手机有大核和小核默认调度器会优先用大核。如果你的 Agent 全都是计算密集型任务大核长时间满载热节流几乎是必然的。2.3 热节流是怎么发生的手机没有主动风扇散热主要靠机身外壳和内部均热板。当某个核心高负载运行时间一长SoC 温度上升系统会通过降低 CPU 频率来保护硬件这就是热节流。热节流不是一次到位而是阶梯式的。温度越高频率越保守。表现出来就是任务量没变但每个任务耗时越来越长整体吞吐下降。想观察温度可以直接读系统提供的 thermal zonefor z in /sys/class/thermal/thermal_zone*/; do echo $(basename $z) type$(cat $z/type) temp$(cat $z/temp) done不同设备的温度单位不完全一致很多以毫摄氏度为单位也就是说读到60000代表 60 摄氏度左右。但也有的设备直接读出来就是摄氏度。判断时要先确认单位不要拿一个阈值套所有设备。2.4 热节流的典型表现热节流发生后你最先看到的不是报错而是变慢。常见表现包括top里 CPU 占用不低但单任务耗时明显增加。数据库的database is locked概率变高因为任务执行时间变长写事务排队更严重。机身背面明显发烫电池温度升高。多个 Worker 同时变慢而不是某一个卡住。如果你发现跑着跑着吞吐掉了一半先去查温度不要急着怀疑代码有问题。3. 搭一套最小可运行的 10 层 Agent Mesh3.1 表结构任务表、心跳表、日志表第一版表结构不要设计得太复杂。任务表是核心至少要能看出这条任务在哪一层、是什么状态、被谁处理过、失败几次、下一次重试时间是什么时候。PRAGMA journal_modeWAL; PRAGMA busy_timeout5000; CREATE TABLE IF NOT EXISTS tasks ( id INTEGER PRIMARY KEY AUTOINCREMENT, tier INTEGER NOT NULL, payload TEXT NOT NULL, status TEXT NOT NULL DEFAULT pending, attempts INTEGER NOT NULL DEFAULT 0, max_attempts INTEGER NOT NULL DEFAULT 3, next_retry_at TEXT, created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, result TEXT, error TEXT ); CREATE INDEX IF NOT EXISTS idx_tasks_queue ON tasks(tier, status, next_retry_at);状态字段建议只用几个固定值pending、running、done、failed。重试中的任务也可以叫retry但重点是next_retry_at要能决定它何时重新可见。心跳表用来记录每个 Agent 还在不在线CREATE TABLE IF NOT EXISTS agents ( name TEXT PRIMARY KEY, tier INTEGER NOT NULL, last_heartbeat TEXT NOT NULL );如果某个 Agent 超过一定时间没有更新心跳调度逻辑就可以把它的running任务重新放回pending。3.2 Agent 通用工作循环怎么写一个 Worker 最核心的循环是领任务、执行、写结果、休眠。这里最需要小心的是“领任务”这一步如果两个 Worker 同时读到同一条任务就会重复处理。用事务加状态判断可以避免这个问题import sqlite3 import time DB_PATH agent.db def connect(): conn sqlite3.connect(DB_PATH, timeout10) conn.execute(PRAGMA journal_modeWAL) conn.execute(PRAGMA busy_timeout5000) return conn def claim_task(conn, tier): conn.execute(BEGIN IMMEDIATE) row conn.execute( SELECT id, payload FROM tasks WHERE tier ? AND status pending AND (next_retry_at IS NULL OR next_retry_at CURRENT_TIMESTAMP) ORDER BY id LIMIT 1 , (tier,) ).fetchone() if row: conn.execute( UPDATE tasks SET status running, updated_at CURRENT_TIMESTAMP WHERE id ?, (row[0],) ) conn.commit() return row def handle(payload): # 这里替换成你的实际处理逻辑 return {ok: True, length: len(payload)} def worker(tier): conn connect() while True: task claim_task(conn, tier) if task is None: time.sleep(2) continue task_id, payload task try: result handle(payload) conn.execute( UPDATE tasks SET status done, result ?, updated_at CURRENT_TIMESTAMP WHERE id ?, (str(result), task_id) ) except Exception as exc: conn.execute( UPDATE tasks SET attempts attempts 1, status failed, error ?, next_retry_at datetime(now, 30 seconds), updated_at CURRENT_TIMESTAMP WHERE id ? , (str(exc), task_id) ) conn.commit() time.sleep(0.2) if __name__ __main__: import sys tier_no int(sys.argv[1]) if len(sys.argv) 1 else 0 worker(tier_no)BEGIN IMMEDIATE是关键。它会让当前连接在写操作开始时就申请写锁避免两个连接同时把同一条任务改成running。这段代码只适合本地验证。真正用于生产环境还要把日志落盘、支持优雅退出、处理进程被杀后遗留的running状态。3.3 10 层之间的数据流转怎么控制任务从 tier 0 进入处理完成后进入 tier 1依次传递。具体有两种做法第一种是 Worker 自己往下游写。比如 tier 0 的 Worker 处理完直接把结果插入一条新任务到 tier 1。这种方式链路清晰但要在代码里显式指定下游层级。第二种是单独一个调度器进程轮询找到所有statusdone并且尚未转发到下一层的任务然后插入下游任务。多一层调度灵活但也要额外维护“是否已经转发”的标记。我更建议第一版用第一种直接把下游插入放在处理成功后conn.execute( INSERT INTO tasks(tier, payload) VALUES (?, ?) , (next_tier, result_text) )这样只要在一个事务里更新当前任务状态并插入下游任务就不会丢数据。比如当前任务更新为done如果插入下游失败整个事务回滚当前任务仍然停留在running或pending之后还能重试。3.4 启动多个 Worker 时要注意什么想启动 10 个 Worker可以手动开多个 tmux 窗口或者用脚本后台启动for i in $(seq 0 9); do python worker.py $i worker_$i.log 21 done但第一次跑的时候不要一下开满 10 个。我一般会先开 2 个 Worker分别处理 tier 0 和 tier 1确认数据库读写和日志都正常再逐步增加到完整链路。还要注意日志文件位置。手机存储空间有限如果 Worker 把大量内容打印到日志几天不清理磁盘就会满。SQLite 写不进去但表面看起来像“数据库卡死”。4. 防热节流的实操手段监控、降载、调度4.1 用 thermal_zone 直接读温度防热第一步是能看到温度。把一段只读脚本放到~/bin/check_temp.sh#!/data/data/com.termux/files/usr/bin/bash for z in /sys/class/thermal/thermal_zone*/; do type$(cat $z/type 2/dev/null) temp$(cat $z/temp 2/dev/null) echo $type : $temp done配合watch持续观察watch -n 5 ./check_temp.sh如果你发现温度在任务跑起来后持续上涨不要等它触发系统级热节流再处理。先降低 Worker 数量或者把任务切小让温度回落。4.2 错峰、限流和随机抖动手机高负载下最容易出问题的不是 CPU而是所有 Agent 同时去写 SQLite。比如每个 Agent 每处理完一条任务都写一次heartbeat10 个 Agent 同时写锁冲突就会明显上升。所以写入要主动错峰心跳更新不要每次循环都写改成每隔 30 秒写一次。两个任务之间加随机延迟比如time.sleep(random.uniform(0.1, 0.5))。大批任务入库时不要一次性插入一万条可以分 100 条一批提交。随机延迟看起来是“浪费”实际是在给热量和数据库锁留缓冲。4.3 CPU 优先级与核心绑定能做多少做多少Linux 环境下可以用nice降低进程优先级。Termux 里没有 root 时权限有限但nice一般可用nice -n 10 python worker.py 2 worker_2.log 21 taskset可以把进程绑定到指定核心但在部分 Android 设备上会受权限限制。能绑就绑不能绑就不要强求。更稳妥的降载思路是直接减少 Worker 进程数把每个层级 2 个 Worker 降成 1 个整体负载基本减半。还有个常见误区不要用nice -n -20把某个 Agent 优先级拉满。手机系统本身有后台管理机制你拉高优先级不仅可能无效还可能把系统进程挤掉反而触发系统强制杀任务。4.4 物理散热和 Android 后台策略到了这一层软件调优已经接近极限。接下来是物理和系统策略把手机壳摘掉让机身直接接触金属支架或桌面。避免放在被子上、枕头边这些地方散热极差。不要长时间放在阳光直射或高温环境。充电时继续高负载跑发热会更严重如果只是偶尔跑一下最好不要边快充边满载。Termux 进程需要保持唤醒否则手机进入深度休眠后 Worker 会被挂起。安装 Termux:API 后可以使用termux-wake-lock跑完再释放termux-wake-unlock同时记得在系统设置里关闭对 Termux 的电池优化否则几十分钟后进程可能被系统杀掉。4.5 温度过高时的暂停与降级逻辑更完整的做法是让调度逻辑感知温度温度超过阈值就暂停领取新任务只让已经在跑的任务结束。def read_temp(): try: with open(/sys/class/thermal/thermal_zone0/temp) as f: return int(f.read().strip()) except Exception: return NoneWorker 在每次领取任务前读一次温度如果超过阈值就睡眠 30 秒再试temp read_temp() if temp and temp THERMAL_LIMIT: print(thermal limit reached, sleeping) time.sleep(30) continue阈值不能写死。不同手机的 thermal_zone 编号和单位都不一样建议先跑一段只有 1 个 Worker 的测试观察稳定温度大约在多少再留一点余量设置阈值。5. 验证链路从单任务到批量任务5.1 先跑一条确认流转完整第一轮验证的目标不是速度而是链路完整。插入一条 tier 0 任务sqlite3 agent.db INSERT INTO tasks(tier, payload) VALUES(0, test payload);只启动 tier 0 的 Workerpython worker.py 0然后查询状态sqlite3 agent.db SELECT id, tier, status, attempts, error FROM tasks;正常情况是这条任务从pending变成running再变成done。如果变成failed先看error字段不要继续加并发。5.2 再跑 10 层流水线确认单层没问题后把 10 个层级的 Worker 都启动。这里不要手动在终端里开 10 个前台进程建议用 tmuxtmux new -s mesh在 tmux 里启动脚本输出重定向到日志文件然后按前缀键退出会话让终端空闲。之后再通过tmux attach -t mesh查看输出。验证时重点看两件事任务是否逐层到达 tier 9。同一层任务有没有被重复处理。sqlite3 agent.db SELECT tier, status, count(*) FROM tasks GROUP BY tier, status;如果所有 tier 都有done记录最终层也有结果说明流转链路基本正常。5.3 批量并发冲到什么程度算安全先单个 Worker 记录一条任务的平均耗时再按耗时推算并发上限。比如一条任务耗时 0.2 秒单 Worker 每秒能处理 5 条10 层同时各开一个 Worker理论吞吐大约是每秒 5 条。如果想达到每秒 50 条就需要把每层并发拉到 10这时手机温度会明显上升。安全的做法是阶梯式加压每层 1 个 Worker跑 5 分钟记录温度和吞吐。每层升到 2 个 Worker跑 5 分钟再记录。温度稳定且没有database is locked再继续加。个人建议手机端把整体吞吐定在每秒几条到十几条这个区间追求更高吞吐还是交给服务器更合理。5.4 常见问题排查顺序如果任务执行异常按下面顺序查现象先查什么database is lockedbusy_timeout是否设置写事务是不是太多是否有长事务没提交任务一直停在runningWorker 是否被杀上次进程是不是没退出先确认没有存活 Worker 再重置状态任务处理速度越来越慢温度、CPU 频率、内存占用、日志文件是否过大Worker 无故消失Android 电池优化、内存不足、Termux 会话被系统回收任务没有进入下一层看当前层代码是否成功执行了下游插入事务是否正确提交如果确认没有 Worker 还在运行可以用下面语句清理残留的running状态UPDATE tasks SET status pending, updated_at CURRENT_TIMESTAMP WHERE status running;执行前一定要确认否则可能把正在进行中的任务也重置掉。6. 手机跑 10 层 Agent Mesh 的边界6.1 能跑通不代表适合长期服务在手机上把 10 层 Agent Mesh 跑通可以证明你对任务队列、状态机、并发控制和资源规划有一定理解。但手机不是服务器。SQLite 本身是本地数据库适合单机多进程读写的场景。如果多个手机通过网络共享同一个.db文件这个方案基本不可行因为 SQLite 不提供网络共享协议。跨设备协作应该把核心调度放到一台服务器上手机端只作为客户端提交任务或拉取结果。长期跑还涉及维护问题日志轮转、数据库备份、磁盘空间清理、依赖升级、Worker 崩溃自动重启这些在服务器上都有成熟工具在手机上只能靠脚本堆。6.2 什么时候该换到服务器或桌面环境出现以下任何一个信号就说明该换环境了温度持续处于热节流边缘任务吞吐不稳定。并发写入一高database is locked频繁出现。需要 7x24 小时稳定运行。多台设备需要共享任务队列。任务本身需要更大的内存或更高算力。迁移成本不高因为核心数据都在 SQLite 里。把整个agent.db文件复制到服务器或桌面 Linux 环境去掉 Termux 相关依赖用同样的 Python 代码继续跑就行。桌面端也可以用 DB Browser for SQLite 这类工具直接打开同一个库文件方便检查状态。6.3 给新手的最终建议如果想复现这个标题我的建议顺序是先跑 3 层确认流程再加到 5 层观察热节流最后才冲 10 层。10 层不是起点而是目标。最该盯住的几个点表结构要能表达任务状态写入要控制频率温度要能实时看到日志要落盘Worker 数量从少到多逐步加。把这五件事做好10 层 Agent Mesh 在 Termux 上跑通是大概率事件。如果跳过这些只想着一次性把所有 Worker 拉满大概率会在热节流和数据库锁之间反复折腾。