---
title: 本地大模型能帮上什么忙 —— 边界、场景与自动化
url: https://doc.liz6.com/homelab/local-llm-usage
locale: zh
area: homelab
tags:
- homelab
date: 2026-07-10
modified: 2026-07-11
description: 本地大模型跑起来之后拿它干嘛。核心是先划边界:35B 级的本地小模型干不了生产端的复杂逻辑——那是 Claude Code + Opus 的活;本地只接日常、非复杂逻辑、可容错的杂活。但在这个窄位置里,它有云替代不了的两个底色——离线自足、隐私不出机。本文讲清楚边界在哪、能帮上什么(以真实在跑的 openclaw 日常助手 + llm-jobs 日报/harvest 为例)、以及怎么把一个想法落成常驻任务,并正面回答"配了云端 fallback 是否自相矛盾"。部署见 local-llm.md。
---

# 本地大模型能帮上什么忙 —— 边界、场景与自动化

> 把模型[跑起来并常驻](local-llm.md)只是起点。真正的问题是:一张消费级显卡上的 35B 小模型,到底该拿它干嘛?本文的答案很克制——它干不了生产端的复杂逻辑(那是 Claude Code + Opus 的活),它的位置很窄:**日常的、非复杂逻辑的、可容错的杂活**。但在这个窄位置里,它有云替代不了的两个底色:**离线自足、隐私不出机**。想清楚这条边界,才知道哪些活值得为它折腾。

## 一、先划边界:本地模型不干什么

装完本地模型,第一反应往往是"拿它替代 ChatGPT / Copilot"——然后失望。因为在纯能力上,消费级显卡跑得动的 35B 模型**打不过前沿云模型**,尤其在多步推理、长链路 agent、精确代码这些"生产端"的活上。

一句话定位:

> **复杂逻辑、生产交付、要质量的活 → Claude Code + Opus;本地模型只接日常、非复杂逻辑、可容错的杂活。**

<svg viewBox="0 0 720 300" xmlns="http://www.w3.org/2000/svg" font-family="-apple-system,'Source Han Sans CN','Microsoft YaHei',sans-serif" role="img" aria-label="生产端交给 Opus,日常杂活交给本地 35B">
  <rect width="720" height="300" fill="#ffffff"/>
  <text x="360" y="30" text-anchor="middle" font-size="18" font-weight="700" fill="#1f2933">谁干什么:按「逻辑复杂度 + 交付要求」分</text>
  <rect x="40" y="56" width="315" height="200" rx="12" fill="#eef2ff" stroke="#c7d2fe"/>
  <text x="197" y="86" text-anchor="middle" font-size="15" font-weight="700" fill="#3730a3">Claude Code + Opus</text>
  <text x="197" y="108" text-anchor="middle" font-size="12" fill="#4f46e5">复杂逻辑 · 生产交付 · 要质量</text>
  <line x1="64" y1="124" x2="331" y2="124" stroke="#c7d2fe" stroke-width="1"/>
  <text x="64" y="150" font-size="12.5" fill="#3730a3">· 写功能代码 / 重构 / 调试</text>
  <text x="64" y="176" font-size="12.5" fill="#3730a3">· 架构设计 / 深度分析</text>
  <text x="64" y="202" font-size="12.5" fill="#3730a3">· 多步推理 / 长链路 agent</text>
  <text x="64" y="228" font-size="12" fill="#6366f1">翻车代价高的活,都在这一边</text>
  <rect x="365" y="56" width="315" height="200" rx="12" fill="#f0fdfa" stroke="#99f6e4"/>
  <text x="522" y="86" text-anchor="middle" font-size="15" font-weight="700" fill="#115e59">本地 35B(小模型)</text>
  <text x="522" y="108" text-anchor="middle" font-size="12" fill="#0d9488">日常 · 非复杂逻辑 · 可容错</text>
  <line x1="389" y1="124" x2="656" y2="124" stroke="#99f6e4" stroke-width="1"/>
  <text x="389" y="150" font-size="12.5" fill="#115e59">· 整理 / 总结 / 归类 / 提取</text>
  <text x="389" y="176" font-size="12.5" fill="#115e59">· 日报合成 / 会话回捞</text>
  <text x="389" y="202" font-size="12.5" fill="#115e59">· 日常问答 / 查信息 / 排日程</text>
  <text x="389" y="228" font-size="12" fill="#0d9488">错了无所谓、能重跑的活,放这边</text>
  <text x="360" y="284" text-anchor="middle" font-size="12.5" fill="#64748b">别拿「省钱 / 隐私」当借口把复杂活塞给本地——省下的钱不够填翻车的坑。</text>
</svg>

为什么不能让本地干生产:小参数模型在需要精确和多步推理的地方**会翻车**,而生产端翻车的代价很高(错误代码、跑偏的 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](monitoring.md),可 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 打模型或发微信。

### 完整闭环

<svg viewBox="0 0 720 420" xmlns="http://www.w3.org/2000/svg" font-family="-apple-system,'Source Han Sans CN','Microsoft YaHei',sans-serif" role="img" aria-label="凌晨 harvest 补全昨日 daily,清晨 report 读取合成推送">
  <defs><marker id="arrHarvestFlow" markerWidth="10" markerHeight="8" refX="8" refY="3" orient="auto"><path d="M0,0 L8,3 L0,6 Z" fill="#475569"/></marker></defs>
  <rect width="720" height="420" fill="#ffffff"/>
  <text x="360" y="28" text-anchor="middle" font-size="17" font-weight="700" fill="#1f2933">两个定时任务共享同一份 daily:先补全,后报告</text>
  <rect x="120" y="48" width="480" height="26" rx="6" fill="#4f46e5"/>
  <text x="360" y="66" text-anchor="middle" font-size="13" font-weight="700" fill="#ffffff">04:00 · daily-harvest</text>
  <rect x="132" y="80" width="456" height="34" rx="6" fill="#eef2ff" stroke="#c7d2fe"/>
  <text x="360" y="101" text-anchor="middle" font-size="11.5" fill="#4f46e5">扫【昨天】的 CC 会话 + openclaw 记忆</text>
  <rect x="132" y="120" width="456" height="34" rx="6" fill="#eef2ff" stroke="#c7d2fe"/>
  <text x="360" y="141" text-anchor="middle" font-size="11.5" fill="#4f46e5">提炼完成项 + 待接续</text>
  <rect x="132" y="160" width="456" height="30" rx="6" fill="#e0e7ff"/>
  <text x="360" y="180" text-anchor="middle" font-size="12" font-weight="600" fill="#3730a3">补进昨天的 daily</text>
  <line x1="360" y1="190" x2="360" y2="210" stroke="#475569" stroke-width="1.8" marker-end="url(#arrHarvestFlow)"/>
  <rect x="120" y="212" width="480" height="26" rx="6" fill="#0d9488"/>
  <text x="360" y="230" text-anchor="middle" font-size="13" font-weight="700" fill="#ffffff">09:00 · daily-report</text>
  <rect x="132" y="244" width="456" height="34" rx="6" fill="#f0fdfa" stroke="#99f6e4"/>
  <text x="360" y="265" text-anchor="middle" font-size="11.5" fill="#0f766e">读昨天的 daily(已被 harvest 补全)+ 实时巡检</text>
  <rect x="132" y="284" width="456" height="34" rx="6" fill="#f0fdfa" stroke="#99f6e4"/>
  <text x="360" y="305" text-anchor="middle" font-size="11.5" fill="#0f766e">合成四节</text>
  <rect x="132" y="324" width="456" height="30" rx="6" fill="#ccfbf1"/>
  <text x="360" y="344" text-anchor="middle" font-size="12" font-weight="600" fill="#115e59">推微信</text>
  <rect x="60" y="372" width="600" height="40" rx="8" fill="#eef2ff"/>
  <text x="360" y="388" text-anchor="middle" font-size="12" fill="#3730a3">两个任务共享同一份 daily:harvest 先补全,report 才读得到完整数据。</text>
  <text x="360" y="405" text-anchor="middle" font-size="12" fill="#3730a3">机器夜里睡眠错过触发也没关系,Persistent=true 会在唤醒后补跑。</text>
</svg>

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`,把整条兜底链短路了。

### 加一个新任务

1. 写 `~/.local/lib/llm-jobs/jobs/<name>`(可执行,支持 `--dry-run`,只调 `bin/` 里的共享原语)
2. 建 `~/.config/systemd/user/llm-job@<name>.timer`(照抄 daily-report,改 `OnCalendar`)
3. `systemctl --user daemon-reload && systemctl --user enable --now llm-job@<name>.timer`

### 运维

```bash
# 手动跑(不发送/不写入)
~/.local/lib/llm-jobs/jobs/daily-report  --dry-run
~/.local/lib/llm-jobs/jobs/daily-harvest --dry-run [--date YYYY-MM-DD]

# 手动触发真实任务
systemctl --user start llm-job@daily-report.service

# 看日志(journald → 也进 Loki)
journalctl --user -u llm-job@daily-report.service -n 50
systemctl --user list-timers | grep llm-job
```

微信 `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](local-llm.md) — 部署:显存预算、量化选型、MTP、常驻潮汐调度、完整 systemd 配置
- [timers-and-crons.md](timers-and-crons.md) — 全部 systemd timer 总览(含 llm-job@ 两个)
- [monitoring.md](monitoring.md) — journald → Loki 日志聚合(llm-jobs 失败可在此告警)
