构建分析网页的本地智能体

我和浏览器标签页的关系有点"有毒"。

一切始于一个周一的早晨。我在Hacker News上看到一个有趣的标题,我按下Command键点击了它,并告诉自己,"我五分钟后就看"。快进到周五下午,我打开了47个标签页。网站图标已经缩小到单个像素的大小。而我一个都没读。

我意识到我需要的不是更多时间;而是一个过滤器。我需要一种方式来预览文章内容,然后问自己:

"这真的值得我花15分钟吗?"

当然,我可以用ChatGPT或Claude来做这件事。但我想,能不能用这个做个有趣的东西呢?

所以,我决定把这当作一个周末的宠物项目。目标是什么?构建一个本地AI代理,它运行在我的机器上,零成本,帮助我更快地浏览文章。

1、"本地"这个兔子洞

我们常常把AI想象成一个存在于数据中心的巨大东西。但像Llama 3Qwen 3.5这样的模型已经变得如此高效,以至于它们在现代笔记本电脑上运行得出奇地好。虽然可能有点慢。

我想要的很简单:

  1. 我需要一种方式来获取文章内容(一个Chrome扩展?)
  2. 我需要一个后端来接收这个内容(嗯,Python/Flask应用)
  3. 最后,我需要一个本地LLM来为我总结文章。

没有云端,没有成本,没有数据离开我的电脑。

2、设置大脑(Ollama)

在本地运行LLM曾经很困难。然后Ollama出现了,让一切变得简单。

如果你会用终端,你就能运行模型。

我决定尝试llama3.2。为什么?嗯,它不是我常用的模型。但当我尝试其他模型时,这个在总结质量与总结时间之间的平衡是最好的。更多基准测试稍后再说。

以下是我需要做的全部操作:

# 1. 安装Ollama (Mac/Linux)
curl -fsSL https://ollama.com/install.sh | sh

# 2. 拉取模型
ollama pull llama3.2

# 3. 启动服务(这会在11434端口打开一个本地API)
ollama serve

就是这样。现在我们有了一个运行在localhost:11434上的REST API,我们可以与之对话了。

3、Chrome扩展

我最初想的是直接把URL发送给我的Python脚本,让Python去抓取页面。

但问题是: 动态JavaScript网站、Cookie弹窗,有时Cloudflare验证会阻止我这样做。

谁已经可以访问渲染好的文本?浏览器。

所以,我写了一个极其简单的Chrome扩展。它不需要很花哨。它只需要说:"嘿Python,这是这个页面上的文本!"

3.1 清单文件(manifest.json)

这告诉Chrome我们需要与活动标签页通信的权限。

{
  "manifest_version": 3,
  "name": "Local Article Summarizer",
  "version": "1.0.0",
  "description": "Send the current page to a local Flask app for summarization with Ollama.",
  "permissions": [
    "activeTab",
    "scripting"
  ],
  "host_permissions": [
    "http://127.0.0.1:5001/*"
  ],
  "action": {
    "default_popup": "popup.html"
  }
}

3.2 逻辑(popup.js)

当我点击扩展按钮时,它获取document.body.innerText并将其发送到我机器上运行的本地服务器。

chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
  // 1. 注入脚本来获取文本

  chrome.scripting.executeScript(
    {
      target: { tabId: tabs[0].id },

      func: () => document.body.innerText,
    },
    (results) => {
      if (results && results[0]) {
        const articleText = results[0].result;

        // 2. 发送到我们的Python后端

        fetch("http://127.0.0.1:5000/summarize", {
          method: "POST",

          headers: { "Content-Type": "application/json" },

          body: JSON.stringify({
            text: articleText,

            url: tabs[0].url,
          }),
        });
      }
    },
  );
});

为什么这有效: 不同的网站使用不同的HTML结构。通过尝试多种方法,我可以从约90%的文章中提取内容。此外,在后端,我可以使用Python的newspaper库,它特别擅长移除广告、导航和其他干扰内容。

3.3 与Ollama对话

def summarize_with_ollama(article_text, model=DEFAULT_MODEL, prompt_type="standard"):
    trimmed = article_text.strip()

    prompts = {
        "standard": f"""你是一位文章总结专家。

用3-5个清晰的句子总结这篇文章。
然后列出恰好5个关键要点作为项目符号。
要具体直接,避免废话。

文章:
{trimmed}

总结:""",
        "detailed": f"""你是一位专业的技术作家。

彻底分析这篇文章并提供:
1. 一个2段的总结,涵盖主题和作者的主要论点
2. 恰好8个关键要点作为项目符号
3. 对文章可信度的评估
4. 1-10分的质量评分,附一句话解释
5. 对清晰度、原创性、实用价值和证据的评分说明

评分要严格,避免重复相同的观点。

文章:
{trimmed}

分析:""",
        "quick": f"""用恰好2句话和恰好3个要点总结这篇文章。

文章:
{trimmed}

快速总结:""",
    }

    prompt = prompts.get(prompt_type, prompts["standard"])

    response = requests.post(
        OLLAMA_URL,
        json={
            "model": model,
            "prompt": prompt,
            "stream": False,
            "options": {
                "temperature": 0.3,
                "num_ctx": 16384,
            },
        },
        timeout=3000,
    )
    response.raise_for_status()
    return response.json().get("response", "").strip()

关键洞察: 不同的提示会产生不同的结果。我有三种模式:

  • quick(快速):当我只想了解大意时
  • standard(标准):适用于大多数文章
  • detailed(详细):对于我想深入分析的重要文章

3.4 最重要的部分:提示工程

最大的质量提升不是来自切换模型。而是来自更具体地说明我想要什么结果。

我早期的提示很模糊:

总结这篇文章。

这通常产生泛泛的输出:一段平淡的文字,没有结构,通常也没有清晰的要点。

效果更好的做法是约束任务:

你是一位专业的技术作家。
阅读下面的文章并做4件事:
1. 用4个简洁的句子总结它。
2. 列出恰好5个要点。
3. 给它打1-10分。
4. 用清晰度、原创性、实用价值和证据来解释这个分数。
要具体。避免废话。不要重复相同的观点。
评分要严格。

这个改变立刻让输出更加犀利。模型不再漫无边际,开始更好地组织信息,并给了我一些真正有用的东西。

我的教训很简单:更好的提示胜过随意切换模型。在下载另一个模型之前,先收紧任务,定义输出格式,告诉模型什么是"好"的结果。根据我的经验,4B模型在这个特定用例中的表现和9B的qwen3.2一样好。

3.5 Flask接口

Flask后端只有一个目的:处理来自Chrome扩展的API请求。当请求带着文章文本和URL进来时,Flask应用将其传递给一个函数来清理内容,然后将清理后的文本发送给Ollama。响应被格式化为JSON并立即发送回Chrome扩展或Web界面。

4、哪些本地模型对我有效?

我在几篇文章上测试了一些本地模型。目标不是找一个全能冠军,而是弄清楚哪些足够好,可以继续使用。

我在同一篇文章上测试了几个模型来比较运行时性能:

我的机器是MacBook Air M1(16GB RAM)

相同文本的不同模型输出

主要的权衡很简单:更小的模型更快,但它们遗漏了更多细微差别,比如lfm2.5-thinking:latest。我不确定这是否因为它是为了不同目的(比如思考)而训练的。它们对于粗略的总结来说还不错,但当我需要结构或判断时就不那么可靠了。

对于我的机器来说, llama3.2 (3b)是本地总结开始感觉始终不错的临界点。比这更大的模型可以产生非常相似的输出,但需要更多时间。qwen模型花了5分钟以上!有些文章的阅读时间可能还不到5分钟。

虽然Ollama可以在本地运行,但你可以使用他们的云端服务来运行需要快速输出的任务。对于更快的响应,我依赖minimax-m2.5:cloud。出于显而易见的原因,响应本身比任何其他本地模型都要好。

5、结束语

我的下一个目标是给这些总结提供永久存储,这样模型就可以慢慢在后台总结,我可以在有时间的时候回来看。如果我认为值得,我可以选择阅读整篇文章。

我还想让文章评分更加稳健,这样我就能知道文章不是AI垃圾,而是有扎实内容的。

我不认为这个设置能替代Claude、ChatGPT或其他强大的托管模型。这不是真正的目标。

目标是回答一个更小的问题:我能构建一个能产生不错结果的本地文章总结器吗? 对我来说,答案是肯定的。

让这个实验有用的不是本地模型赢了。而是它们变得足够好,值得用于一个特定的任务,而且足够有趣,让我想继续探索。

如果你对本地模型好奇,这是一个很好的入门项目。它实用,易于评估,而且开放性足够,一旦你看到结果比预期好,就会把你拉得更深。我基本上是用AI辅助编码完成了整个东西。你也可以试试。


原文链接: Building a Local AI Agent to Summarise Web Articles

汇智网翻译整理,转载请标明出处