Ollama + n8n 工作流实战:从抓取网页 → 总结 → 分类存档 → 推送提醒,全程本地零云端

# Ollama + n8n 工作流实战:从抓取网页 → 总结 → 分类存档 → 推送提醒,全程本地零云端

这篇不讲故事,直接上干货。把一个完整的工作流从 0 搭到能用的状态,每一步的命令和配置都列出来。


最终效果

跑起来之后,这个工作流会:

1. 定时抓取你指定的网页/RSS
2. AI 自动总结内容
3. 判断内容分类(科技/财经/工具/其他)
4. 存档到 SQLite 数据库
5. 如果命中你关心的关键词,额外推送通知

全程本地,不依赖任何云端 API。


前置环境

已经装好的:

  • Ollama(qwen2.5:7b)
  • n8n(Docker)
  • 还需要加两个服务:

    BASH
    mkdir -p /opt/n8n-services && cd /opt/n8n-services

    cat > docker-compose.yml << 'EOF' version: "3.8" services: # SQLite 不需要单独容器,n8n 内置支持 # 但如果想外部访问数据,可以加一个 sqlite-web sqlite-web: image: coleifer/sqlite-web container_name: sqlite-web restart: unless-stopped ports: - "8002:8080" volumes: - n8n-data:/data environment: - SQLITE_DATABASE=/data/n8n-sqlite.db volumes: n8n-data: external: true EOF

    docker compose up -d

    SQLite Web 只是方便查看数据,不是必须的。


    第 1 步:创建数据库表

    在 n8n 里加一个 Execute Command 节点,初始化数据库:

    BASH
    sqlite3 /home/node/.n8n/database.sqlite << 'SQL'
    CREATE TABLE IF NOT EXISTS content_archive (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        title TEXT NOT NULL,
        summary TEXT,
        category TEXT,
        source_url TEXT,
        keywords_matched INTEGER DEFAULT 0,
        created_at DATETIME DEFAULT CURRENT_TIMESTAMP
    );

    CREATE INDEX IF NOT EXISTS idx_category ON content_archive(category); CREATE INDEX IF NOT EXISTS idx_created ON content_archive(created_at); SQL

    或者直接在 n8n 里用 SQLite 节点的 “Execute Query” 功能创建。


    第 2 步:搭建工作流

    打开 n8n,新建 Workflow,按顺序添加节点。

    节点 1:Schedule(定时触发)

  • 触发模式:Every Day
  • 时间:09:00(或者你喜欢的时间)
  • 节点 2:RSS Feed Read(抓取内容)

    添加 3~5 个你常看的源。我用的几个:

    https://feeds.feedburner.com/36kr/news
    https://rsshub.app/36kr/news/latest
    https://rsshub.app/ithome/news
    

    RSSHub 可以自己部署,docker run -d -p 1200:1200 diygod/rsshub

    节点 3:Code(数据清洗)

    RSS 抓回来的数据需要整理:

    JAVASCRIPT
    // 合并所有 RSS 条目,去重
    const items = $input.all();
    const seen = new Set();
    const unique = items.filter(item => {
      const url = item.json.link || item.json.guid || '';
      if (seen.has(url)) return false;
      seen.add(url);
      return true;
    });

    // 每条提取关键字段 const articles = unique.map(item => ({ title: item.json.title || '无标题', content: item.json.contentSnippet || item.json.content || '', link: item.json.link || item.json.guid || '', date: item.json.pubDate || item.json.isoDate || '' }));

    return [{ json: { articles, count: articles.length } }];

    节点 4:Ollama Chat(AI 处理)

    这是核心节点。System Prompt:

    你是一个内容处理助手。请对以下文章列表进行处理:

    1. 为每篇文章生成 50 字以内的中文摘要 2. 判断分类:科技/财经/工具/其他 3. 提取 3 个关键词

    输出格式为 JSON 数组,不要其他内容: [ { "title": "文章标题", "summary": "摘要", "category": "分类", "keywords": ["关键词1", "关键词2", "关键词3"], "link": "原文链接" } ]

    User Message:

    以下是需要处理的文章列表:
    {{ JSON.stringify($node["Code"].json.articles) }}
    

    注意: 如果文章太多,一次性处理可能会超时。建议在 Code 节点里限制数量(比如取最新的 10 条)。

    节点 5:Code(解析 AI 输出)

    AI 返回的是 JSON 字符串,需要解析成 n8n 能处理的格式:

    JAVASCRIPT
    const raw = $input.first().json.content || $input.first().json.message?.content;
    let articles;

    try { // 有时候 AI 会在 JSON 外面包一些废话,用正则提取 const jsonMatch = raw.match(/[[sS]*]/); articles = jsonMatch ? JSON.parse(jsonMatch[0]) : []; } catch (e) { articles = []; }

    // 拆分成多条,每条是一个文章 return articles.map(a => ({ json: { title: a.title || '', summary: a.summary || '', category: a.category || '其他', keywords: a.keywords || [], link: a.link || '' } }));

    节点 6:IF(关键词匹配)

    检查文章是否包含你关心的关键词:

    条件:keywords 数组包含以下任意一个
      - "AI" 或 "人工智能" 或 "Ollama" 或 "Docker" 或 "Linux"
    

    匹配到的文章走”是”分支,否则走”否”分支。

    节点 7:SQLite(存档 – 所有文章)

    “否”分支和”是”分支都连一个 SQLite 插入:

    SQL
    INSERT INTO content_archive (title, summary, category, source_url, keywords_matched)
    VALUES ('{{ $json.title }}', '{{ $json.summary }}', '{{ $json.category }}', '{{ $json.link }}', 0);
    

    “是”分支的 keywords_matched 设为 1。

    节点 8:Telegram Bot(推送 – 仅命中关键词的文章)

    只有命中关键词的文章才推送:

    📌 关键词命中提醒

    标题:{{ $json.title }} 分类:{{ $json.category }} 摘要:{{ $json.summary }}

    {{ $json.link }}


    完整工作流结构

    Schedule (每天 9:00)
      ↓
    RSS Feed Read (3~5 个源)
      ↓
    Code (去重 + 提取字段)
      ↓
    Ollama Chat (总结 + 分类 + 关键词)
      ↓
    Code (解析 JSON 输出)
      ↓
         IF (关键词匹配?)
        / 
      是   否
      ↓     ↓
    SQLite  SQLite
    (标记1) (标记0)
      ↓
    Telegram 推送
    

    优化点

    1. 分页处理

    如果 RSS 源很多(20+ 条),建议分批处理:

    JAVASCRIPT
    // Code 节点里加一个分页器
    const articles = $input.first().json.articles;
    const PAGE_SIZE = 5;
    const page = $execution.resumeData?.page || 0;
    const slice = articles.slice(page * PAGE_SIZE, (page + 1) * PAGE_SIZE);

    if (slice.length === 0) { return []; // 处理完毕 }

    // 设置下次继续 $execution.resumeData = { page: page + 1 };

    return [{ json: { articles: slice, hasMore: (page + 1) * PAGE_SIZE < articles.length } }];

    2. 错误重试

    n8n 支持节点级别的错误重试。在 Ollama Chat 节点设置里:

  • On ErrorRetry
  • Max Retries3
  • Retry Interval30s
  • 模型加载慢或者超时时自动重试,不用手动重跑。

    3. 导出工作流

    搭好的工作流可以导出备份:

    n8n → Workflow → 右上角三点 → Download

    导出的 JSON 文件就是你的工作流模板,换机器直接导入。


    验证

    跑完之后,打开 SQLite Web(http://IP:8002),应该能看到 content_archive 表里有记录了。

    每条记录包含:

  • 标题、摘要、分类
  • 是否命中关键词
  • 抓取时间
  • 可以按分类筛选,看看 AI 分得准不准。


    资源占用

    一次完整执行(10 篇文章):

  • Ollama 推理:约 30~60 秒
  • n8n 内存峰值:约 400MB
  • 磁盘占用:每天 10 条约 50KB(纯文本)
  • 如果是 8G 内存的机器,建议:

  • 文章数量限制在 5 条以内
  • 模型换成 qwen2.5:3b
  • 执行间隔拉到 12 小时一次

扩展方向

这个工作流是基础版,后续可以:

1. 加去重逻辑——用 SQLite 的 title 做 UNIQUE 约束,避免重复存档
2. 加 Webhook 触发——不只是定时,有新内容时通过 Webhook 立即触发
3. 加邮件摘要——每周日把当周存档汇总成一封邮件
4. 接入多模态——用 LLaVA 模型处理网页里的截图/图片

工作流 JSON 模板就不贴了(太长),有需要的可以自己按上面的节点配置搭。每一步都不难,主要是把流程想清楚再动手。

发表评论