本页目录
本地大模型能帮上什么忙 —— 边界、场景与自动化
把模型跑起来并常驻只是起点。真正的问题是:一张消费级显卡上的 35B 小模型,到底该拿它干嘛?本文的答案很克制——它干不了生产端的复杂逻辑(那是 Claude Code + Opus 的活),它的位置很窄:日常的、非复杂逻辑的、可容错的杂活。但在这个窄位置里,它有云替代不了的两个底色:离线自足、隐私不出机。想清楚这条边界,才知道哪些活值得为它折腾。
一、先划边界:本地模型不干什么
装完本地模型,第一反应往往是"拿它替代 ChatGPT / Copilot"——然后失望。因为在纯能力上,消费级显卡跑得动的 35B 模型打不过前沿云模型,尤其在多步推理、长链路 agent、精确代码这些"生产端"的活上。
一句话定位:
复杂逻辑、生产交付、要质量的活 → Claude Code + Opus;本地模型只接日常、非复杂逻辑、可容错的杂活。
为什么不能让本地干生产:小参数模型在需要精确和多步推理的地方会翻车,而生产端翻车的代价很高(错误代码、跑偏的 agent)。本地的省钱和隐私,不足以抵消这个代价。把复杂活留给 Opus,是给本地模型划清边界的第一条,也是最重要的一条。
二、本地的立身之本:离线 + 隐私
既然能力上打不过云,为什么还要本地?因为有两件事云再强也做不到:
- 离线自足:断网、上不了云、云被墙、云涨价或限流——本地照跑。它不依赖任何外部服务在线。
- 隐私不出机:私人数据喂给它不上传、不进任何第三方的日志和训练集。
这两条,才是"有些活就该在本地"的真正理由。
关于云端 fallback 的矛盾
一个流行做法是"本地优先、云端兜底":本地停了(如打游戏时 gamemode 停服)自动回退云端,使用不中断。听起来很美,但要清醒——
一旦配了云端 fallback,本地就不再离线、也不再纯隐私。 只要发生过一次回退,那一次的数据就上了云。fallback 和本地的两个底色天然矛盾。
所以不能无脑给所有本地任务都挂 fallback。诚实的做法是按场景分两类:
| 场景性质 | 本地为什么被选中 | 配 fallback? | 例子 |
|---|---|---|---|
| 可用性优先 | 本地只是"省钱的默认",离线/隐私不是硬需求 | ✅ 可以 | openclaw 日常助手:查信息、排日程,本地停了回退云端,保证助手不掉线 |
| 纯粹性优先 | 本地是"因为离线/隐私才选的" | ❌ 绝不 | daily-harvest 扫含私人代码和密钥的会话,回退云端 = 把这些数据传出去 |
这条线正是本机配置里划出来的:openclaw 的 primary = 本地 / fallbacks = deepseek(可用性优先),而 llm-jobs 的 harvest 本地不可用时直接 exit 3 等待补跑、绝不上云(纯粹性优先)。判断标准很简单:如果这个任务"上一次云"就违背了你用本地的初衷,那它就不该有 fallback。
三、那它到底能帮上什么
在"日常杂活 + 离线/隐私"这个窄位置里,本地模型的三个属性各解锁一类场景。下面 ✅ 是本机真实在跑的,💡 是可延伸的设想。
零边际成本 → 敢让日常杂活常态化
按量计费的云模型,你会下意识"算着用";本地零成本,才敢让低价值但高频的杂活天天跑。
- ✅ openclaw 日常问答:查信息、排日程、随手一问,问多少次都不心疼。这类"单次没多大价值、但架不住频繁"的活,只有零成本养得起。
- ✅ 日报每天合成:
daily-report每天 09:00 把昨天的碎条目去重、合并成人话,连同系统巡检、待接续、周五的「本周视角」拼成四节推微信。每天必跑、一次多调,换云端按次计费就显得肉疼。 - ✅ 本周视角:周五日报多出一节——模型读过去 7 天的完成条目,综述精力去向、点名停滞的事、给下周一条建议。跨文档综合这种活,人工做很烦,模型零成本周周做。
- 💡 延伸:给每篇收藏的文章 / RSS 自动写一句摘要;月报滚动汇总。
隐私不出机 → 敢把私人数据喂进去
有一类数据"很想用 AI 处理,但不敢交给云"。本地是唯一敢喂的地方。
- ✅ daily-harvest:扫当天所有 Claude Code 项目会话(含私人项目、代码、可能的密钥)+ openclaw 记忆,提炼"实际干了什么"回填日报。这些数据绝不能上云,只有本地敢这么喂。
- 💡 延伸:私人日记 / 聊天记录 / 财务票据做本地 RAG 检索;照片 OCR 归档。
常驻可嵌入 → 敢把它焊进自动化
模型常驻在 localhost:18080,任何脚本、定时器随时能调,无需唤醒任何云服务、无网络往返顾虑。
- ✅ llm-jobs:systemd timer 到点就调本地模型,采集→提炼→投递,全程本地闭环(下一节详解)。
- ✅ openclaw 默认模型:日常 agent 的
primary指向本地。 - ✅ 系统巡检降噪:
health-snapshot只读采集原始信号(failed units、备份结果、ZFS 水位、24h 上万条 err 级日志按来源聚合、SSH 认证失败),模型负责降噪和轻重排序,产出日报的「🩺 系统」一节。"radicale 把 INFO 打成 err 属长期噪声""SSH 有 2FA、爆破未遂不必报"这类判断,规则写不出来,模型一句话搞定;全绿时只输出「✅ 一切正常」。 - 💡 延伸:记忆的周期性蒸馏与维护。
四、把想法落成常驻任务:llm-jobs 框架
上面那些自动化,落地都长一个样:采集数据 → 调一次本地模型 → 写文件 / 投递。与其每个任务各写一遍(各自 hand-roll curl 打模型、各自发微信),不如抽成统一框架。~/.local/lib/llm-jobs/ 就是这个框架——「周期性调用本地模型」这一类会不断长出来的任务(日报、harvest、将来的周报/月报/记忆维护)的统一落点,加新任务只写一个采集脚本。
为什么要这层抽象
日报原本是个孤立脚本,唯一数据源 ~/doc/daily/,而这个目录靠 Claude Code 和 openclaw(Miko)两个 agent「想起来才调 doc-daily-log」的尽力而为——谁忘了记,完成的活就整块从日报消失(例:2026-07-03 日报原本是空的)。问题拆两块:
- 网罗性:干的活没可靠地进 daily。→ 用
daily-harvest从真实作业痕迹(会话记录)里捞。 - 质量:纯字符串解析,重复项、粒度不齐原样透传。→ 用本地模型合成。
而这俩本质都是「定时调本地模型」,于是抽成统一框架。
架构决策:systemd vs openclaw cron
同样是"定时",两条路差别很大:
| systemd timer + 脚本 | openclaw cron | |
|---|---|---|
| 任务模型 | 确定性 pipeline(采集→调模型→投递) | 唤醒一个 agent 会话(带工具/记忆/channel 上下文) |
| 可观测性 | journald → Loki,可 Grafana 告警 | openclaw 内部日志,游离监控栈外 |
| 依赖 | 只依赖 llama-server(也是 systemd) | 依赖 openclaw gateway 在线 |
| 调试 | --dry-run、systemctl start、纯脚本可版本化 | 需在 agent 语境复现,非确定 |
规矩:pipeline 类走 systemd,agent 类才用 openclaw cron。 日报和 harvest 是「读文件→调一次模型→写文件/发消息」的管道,不需要工具循环,走 systemd。openclaw cron 只留给「Miko 真的要动脑动手」的定时任务(目前无)。
结构:三层解耦
~/.local/lib/llm-jobs/
bin/llm-call 统一模型入口:POST localhost:18080;默认按请求关 thinking(--think 开),
空 content 视为失败并升 token 重试,本地挂了 exit 3
bin/notify-weixin 统一投递:发微信 bot;--spool 把 token 失效(ret=-2)的消息落盘排队
bin/health-snapshot 只读采集巡检信号(failed units/备份结果/ZFS/journal err 聚合/SSH 认证),
供 daily-report 的「系统」一节喂模型降噪
jobs/daily-harvest 凌晨:扫昨天的会话 → map-reduce 提炼完成项 + 待接续 → 补进 daily
jobs/daily-report 晨间:读 daily + 实时巡检 → 合成四节 → 投递
jobs/notify-flush 每小时:补发 spool 里排队的微信消息
README.md 约定 + 任务登记表
bin/llm-call是唯一模型入口 —— 「定时调本地模型」只有一处经测试的实现,模型名/端点/回退策略集中管理。换模型只改这一处。bin/notify-weixin投递独立 —— 将来加 bark/邮件/多渠道只动这一层。- 模板单元
llm-job@.service——ExecStart=%h/.local/lib/llm-jobs/jobs/%i,加任务不用新建 service 文件。
约定:任务脚本只调 bin/ 里的共享原语,不要各自 hand-roll curl 打模型或发微信。
完整闭环
harvest 的定时实例经 systemd drop-in 传 --date 昨天(手动跑仍默认今天);04:00 时昨天已彻底结束,补全的正好是 09:00 要报的。机器夜里睡眠错过触发也没关系,Persistent=true 会在唤醒后补跑。
daily-harvest(04:00):找昨天所有 Claude Code 会话 ~/.claude/projects/*/*.jsonl,每个压成 USER:/AI: 纯文本(去思考和工具噪声、截断到限额)喂模型 map 提炼两类东西——「实际完成的有意义工作」和「明确说要做但没做完的」,外加 openclaw 自己蒸馏过的记忆。完成项候选 + 当天 daily 已有的人工条目一起 reduce,只挑真正完成、且人工未记的,输出 JSON,用 hv_* tag 逐条写回,每次先物理删旧 hv_ 块再重算(幂等,可反复跑不叠加)。待接续候选参照完成项去重后写 daily/<日期>.pending(整条链路 best-effort,失败只丢这一节)。全程本地模型 + 本地写。
daily-report(09:00):合成四节推微信——①「🩺 系统」:health-snapshot 采实时巡检信号,模型降噪成简报(模型不可用降级为只列 failed units,巡检永不阻塞发报);②「📝 昨日完成」:读昨天 daily 全部条目,模型去重合并写成人话,模型不可用时回退「原始分组」(不上云,只是不做 LLM 合成),日报一定发得出去;③「🔜 待接续」:直接读 .pending 文件,没有则整节不出现;④「📊 本周视角」:仅周五,模型综述近 7 天条目。拼上日期/天气(wttr.in),所有节经统一的确定性排版后处理(条目间空行、要点全角缩进——不指望模型排版稳定),notify-weixin --spool 投递。
兜底(纯粹性优先的体现):harvest 完成后写 ~/.local/state/llm-jobs/harvest-<日期>.ok 戳记;若本地模型不可用(被 gamemode 停了),写 .deferred 并 exit 3——不写假的空结果、也不上云,systemd 标记失败进 Loki。09:00 report 出报前若发现昨日没有 .ok 戳记,就先补跑一次 daily-harvest --date <昨天>(此时模型已恢复)再读 daily。覆盖"凌晨机器在睡眠 / 模型被 gamemode 停了"全场景。注意这条兜底链的前提是 .ok 戳记必须诚实——2026-07-10 的事故(见下一节"血泪")正是空结果被误写成 .ok,把整条兜底链短路了。
加一个新任务
- 写
~/.local/lib/llm-jobs/jobs/<name>(可执行,支持--dry-run,只调bin/里的共享原语) - 建
~/.config/systemd/user/llm-job@<name>.timer(照抄 daily-report,改OnCalendar) systemctl --user daemon-reload && systemctl --user enable --now llm-job@<name>.timer
运维
# 手动跑(不发送/不写入)
# 手动触发真实任务
# 看日志(journald → 也进 Loki)
|
微信 notify-weixin 若返回 ret=-2(context token 过期),--spool 调用方的消息会落盘排队,notify-flush 每小时自动补发;在微信给 bot 发一条消息刷新 token 可加速送达。
五、把本地模型用好的几条实操
都是从上面这些真实任务里踩出来的,可迁移到任何"调本地模型"的场景:
- 日常任务关掉 thinking,且要在请求侧关:整理、总结、归类这类活不需要思维链,关掉省一大截时延。关键是用请求级
chat_template_kwargs: {"enable_thinking": false}(可覆盖服务端预设),不要依赖服务端预设——预设是共享的,可能哪天为别的场景改掉(见下条血泪)。bin/llm-call现在默认请求侧关思考,需要时--think显式开。 - 善用长上下文,别自己切碎:本地能开到很长的窗口(本机 64K–192K),喂整个会话、整份文档即可。harvest 曾把单会话截断上限从 10k 提到 40k——截断会切掉会话中段的完成小结,导致漏掉。
- 结构化输出要校验 + 重试:让模型出 JSON 就要容错解析、失败重试(
bin/llm-call内置重试 2 次)。小模型在 temp 下偶尔会跑格式。 - 判断类提示词用正向抽取,别给"没有就输出无"的轻易出口(血泪):harvest 的 map 早期用否定框架("不算讨论就输出无"),在 temp 下会随机把有产出的会话整段判成「无」,丢掉一整天的活(同输入第 1 次出 6 条、第 2 次「无」)。改成"编程会话通常有产出,通读全文再抽取,仅纯咨询才输出无"后 5/5 稳定。LLM 判断类提示词别给"什么都不输出"的便捷出口,它会在采样里不稳定地丢数据。
- 别过度合并:reduce 阶段"合并同类项"太激进,会让同输入时而 19 条、时而塌成 4 条空泛大标题。保粒度,合并留给下游(日报生成)那一步。
- HTTP 200 也可能是假成功——空 content 必须当失败(血泪,2026-07-10):为修 codex 卡顿把服务端预设
qwen3.6-mtp-thinking从enable_thinking:false改成reasoning = on后,思维链被分离进reasoning_content,批任务的小max_tokens(512)全烧在思考上——返回 200 但content=""、finish_reason=length。harvest 把空输出当"该会话没活",17 个有真实工作的会话全判「无」,还写了.ok成功戳记,晨间兜底被短路,日报发出「昨日无记录」。而且给思考加预算赢不了:实测 512→2048 它思考得更久照样烧光。修法双管齐下:①llm-call把空 content 视为失败(length时翻倍 max_tokens 立即重试,耗尽exit 3走 deferred);②批任务默认请求侧关思考(上一条)。更深一层的教训:共享推理实例的服务端预设是所有调用方的隐式契约——为某一个场景改预设,要把其它调用方都过一遍。 - 别指望模型排版稳定,展示层用确定性后处理:prompt 里写"每条空一行"这类排版要求,小模型执行不稳定。日报的做法是模型只管内容,脚本统一重排(条目间空行、要点全角空格缩进),模型输出格式再怎么飘,读者看到的排版恒定。
- 结构化输出的 token 要给足,且失败绝不能伪装成功(血泪):忙碌的一天候选可达上百条(54 会话→163 候选),reduce 输出 JSON 很长,
max_tokens=1500直接截断→ JSON 非法 → 解析失败。早期把解析失败静默当成"无新增"、还写了.ok成功戳记,于是晨间兜底也不触发,整天工作静默蒸发(2026-07-06)。三条铁律:①输出 token 给足并升档重试(6000→9000);②判空要区分None(失败)与[](真无新增);③失败要有降级而非丢弃——本机 harvest 在 reduce 反复失败时降级为"按项目分组直写原始候选"(下游日报再合并),只有模型真不可用才.deferred+exit 3。任何"失败被当成空结果"的路径,都会在自动化里悄悄吞掉数据。 - agent / 工具调用要开
--jinja:否则模型"能吐 toolcall 标签却不执行"——llama-server 的原生工具解析(把输出解析成结构化tool_calls)只在--jinja开启时生效。纯对话不受影响,但当 agent 用必开。
相关文档
- local-llm.md — 部署:显存预算、量化选型、MTP、常驻潮汐调度、完整 systemd 配置
- timers-and-crons.md — 全部 systemd timer 总览(含 llm-job@ 两个)
- monitoring.md — journald → Loki 日志聚合(llm-jobs 失败可在此告警)