摘要

1) 一句话总结 通过广泛收集和“囤积”可运行的代码示例,开发者可以将其作为强大的上下文输入,结合 AI 编程智能体高效地重组和构建新的软件工具。

2) 关键要点

  • 核心策略:软件专业人员应积累大量关于“技术上如何实现”的答案,并保存为可运行的代码示例,以便发现新的技术应用机会。
  • 资产存储:作者通过个人博客、TIL(Today I Learned)笔记、1000多个 GitHub 仓库以及专门的单文件 HTML 工具集合(tools.simonwillison.net)来囤积代码。
  • LLM 辅助研究:利用大语言模型去研究复杂问题,并带回可运行的代码和详细报告(如 simonw/research 仓库),以此扩展代码库。
  • 重组提示模式(Recombination):一种极其高效的提示词技巧是:将两个或多个现有的工作示例代码提供给 AI,指令其结合这些代码构建新事物。
  • 具体案例:作者将 PDF.js(PDF转图像)和 Tesseract.js(图像OCR)的 JavaScript 代码片段输入给 Claude 3 Opus,仅用几分钟就成功生成了一个基于浏览器的 PDF OCR 工具。
  • 智能体自动化:对于具备联网或本地文件访问能力的编程智能体,可以直接指令它们通过 URL 获取源码,或克隆特定 GitHub 仓库作为参考上下文。
  • 一次性解决原则:开发者只需将某个有用的技巧攻克并记录一次,智能体就能在未来的类似项目中自动查阅并复用该代码示例。

3) 风险/缺口

  • 工具默认行为限制:Claude Code 默认使用的 WebFetch 工具会总结网页内容而不是返回原始 HTML。在需要获取网页源代码作为智能体输入时,必须明确指令其使用 curl 命令以避免关键代码信息丢失。

正文

赞助商: Augment Code — 智能体编排。动态规范。你最喜欢的智能体。带着意图构建

囤积你掌握的技能

我关于如何与编程智能体高效工作的许多技巧,都是我在没有它们的职业生涯中发现的有用建议的延伸。这里有一个很好的例子:囤积你掌握的技能

构建软件的一项重要技能是了解什么是可能的,什么是不可能的,并且至少对如何实现这些事情有一个粗略的了解。

这些问题可能很宽泛,也可能相当冷门。网页能仅用 JavaScript 运行 OCR 操作吗?iPhone 应用能在未运行的情况下与蓝牙设备配对吗?我们能在不将整个文件加载到内存中的情况下,在 Python 中处理一个 100GB 的 JSON 文件吗?

你掌握的这类问题的答案越多,你就越有可能发现机会,以其他人可能尚未想到的方式部署技术来解决问题。

知道某事在理论上是可能的,与亲眼看到它被实现是不同的。作为一名软件专业人员,需要培养的一项关键资产是收集大量此类问题的答案,最好有可运行的代码作为说明。

我通过多种不同的方式囤积这样的解决方案。我的博客TIL 博客塞满了我弄清楚如何做的事情的笔记。我有一千多个 GitHub 仓库,收集了我为不同项目编写的代码,其中许多是展示关键想法的小型概念验证。

最近,我使用 LLM 来帮助扩展我针对有趣问题的代码解决方案集合。

tools.simonwillison.net 是我最大的 LLM 辅助工具和原型集合。我用它来收集我称之为 HTML 工具的东西——嵌入 JavaScript 和 CSS 并解决特定问题的单个 HTML 页面。

我的 simonw/research 仓库包含更大、更复杂的示例,在这些示例中,我挑战编程智能体去研究一个问题,并带回可运行的代码和一份详细说明其发现的书面报告。

重组你囤积的东西

为什么要收集所有这些东西?除了帮助你建立和扩展自己的能力外,你在此过程中生成的资产将成为你的编程智能体极其强大的输入。

我最喜欢的提示模式之一是告诉智能体通过结合两个或多个现有的工作示例来构建新东西。

一个帮助我明确这种方法有多有效的项目,是我添加到工具集合中的第一件东西——一个基于浏览器的 OCR 工具这里有更详细的描述

我想要一个简单的、基于浏览器的工具,用于对 PDF 文件中的页面进行 OCR——特别是那些完全由扫描图像组成、根本没有提供文本版本的 PDF。

我之前曾尝试在浏览器中运行 Tesseract.js OCR 库,发现它非常强大。该库提供了成熟的 Tesseract OCR 引擎的 WebAssembly 构建版本,并允许你从 JavaScript 调用它以从图像中提取文本。

但我不想处理图像,我想处理 PDF。然后我想起我也曾使用过 Mozilla 的 PDF.js 库(https://mozilla.github.io/pdf.js/),除其他功能外,它可以将 PDF 的各个页面转换为渲染图像。

我的笔记中有这两个库的 JavaScript 代码片段。

这是我输入到模型(当时是 Claude 3 Opus)中的完整提示,结合了我的两个示例并描述了我正在寻找的解决方案:

这段代码展示了如何打开 PDF 并将其每页转换为图像:

<!DOCTYPE html>
<html>
<head>
  <title>PDF to Images</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.9.359/pdf.min.js"></script>
  <style>
    .image-container img {
      margin-bottom: 10px;
    }
    .image-container p {
      margin: 0;
      font-size: 14px;
      color: #888;
    }
  </style>
</head>
<body>
  <input type="file" id="fileInput" accept=".pdf" />
  <div class="image-container"></div>

  <script>
  const desiredWidth = 800;
    const fileInput = document.getElementById('fileInput');
    const imageContainer = document.querySelector('.image-container');

    fileInput.addEventListener('change', handleFileUpload);

    pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.9.359/pdf.worker.min.js';

    async function handleFileUpload(event) {
      const file = event.target.files[0];
      const imageIterator = convertPDFToImages(file);

      for await (const { imageURL, size } of imageIterator) {
        const imgElement = document.createElement('img');
        imgElement.src = imageURL;
        imageContainer.appendChild(imgElement);

        const sizeElement = document.createElement('p');
        sizeElement.textContent = `Size: ${formatSize(size)}`;
        imageContainer.appendChild(sizeElement);
      }
    }

    async function* convertPDFToImages(file) {
      try {
        const pdf = await pdfjsLib.getDocument(URL.createObjectURL(file)).promise;
        const numPages = pdf.numPages;

        for (let i = 1; i <= numPages; i++) {
          const page = await pdf.getPage(i);
          const viewport = page.getViewport({ scale: 1 });
          const canvas = document.createElement('canvas');
          const context = canvas.getContext('2d');
          canvas.width = desiredWidth;
          canvas.height = (desiredWidth / viewport.width) * viewport.height;
          const renderContext = {
            canvasContext: context,
            viewport: page.getViewport({ scale: desiredWidth / viewport.width }),
          };
          await page.render(renderContext).promise;
          const imageURL = canvas.toDataURL('image/jpeg', 0.8);
          const size = calculateSize(imageURL);
          yield { imageURL, size };
        }
      } catch (error) {
        console.error('Error:', error);
      }
    }

    function calculateSize(imageURL) {
      const base64Length = imageURL.length - 'data:image/jpeg;base64,'.length;
      const sizeInBytes = Math.ceil(base64Length * 0.75);
      return sizeInBytes;
    }

    function formatSize(size) {
      const sizeInKB = (size / 1024).toFixed(2);
      return `${sizeInKB} KB`;
    }
  </script>
</body>
</html>

这段代码展示了如何对图像进行 OCR:

async function ocrMissingAltText() {
    // Load Tesseract
    var s = document.createElement("script");
    s.src = "https://unpkg.com/tesseract.js@v2.1.0/dist/tesseract.min.js";
    document.head.appendChild(s);

    s.onload = async () => {
      const images = document.getElementsByTagName("img");
      const worker = Tesseract.createWorker();
      await worker.load();
      await worker.loadLanguage("eng");
      await worker.initialize("eng");
      ocrButton.innerText = "Running OCR...";

      // Iterate through all the images in the output div
      for (const img of images) {
        const altTextarea = img.parentNode.querySelector(".textarea-alt");
        // Check if the alt textarea is empty
        if (altTextarea.value === "") {
          const imageUrl = img.src;
          var {
            data: { text },
          } = await worker.recognize(imageUrl);
          altTextarea.value = text; // Set the OCR result to the alt textarea
          progressBar.value += 1;
        }
      }

      await worker.terminate();
      ocrButton.innerText = "OCR complete";
    };
  }

使用这些示例组合成一个单一的 HTML 页面,嵌入 HTML、CSS 和 JavaScript,提供一个大方块,用户可以将 PDF 文件拖放到上面,当他们这样做时,PDF 的每一页都会转换为 JPEG 并显示在页面下方,然后使用 tesseract 运行 OCR,结果显示在每张图片下方的 textarea 块中。

这完美地奏效了!模型输出了一页概念验证页面,完全满足了我的需求。

我最终与它进行了几次迭代才得到最终结果,但只花了几分钟就构建了一个真正有用的工具,我至今仍从中受益。

编程智能体让这变得更加强大

我在 2024 年 3 月构建了那个 OCR 示例,比 Claude Code 的首次发布早了近一年。编程智能体使得囤积工作示例变得更加有价值。

如果你的编程智能体可以访问互联网,你可以告诉它做这样的事情:

使用 curl 获取 https://tools.simonwillison.net/ocrhttps://tools.simonwillison.net/gemini-bbox 的源代码,并构建一个新工具,让你从 PDF 中选择一页并将其传递给 Gemini,以返回该页面上插图的边界框。

(我在那里指定了 curl,因为 Claude Code 默认使用 WebFetch 工具,该工具会总结页面内容而不是返回原始 HTML。)

~/dev/ecosystem/datasette-oauth 项目添加模拟 HTTP 测试,灵感来自 ~/dev/ecosystem/llm-mistral 的做法。

由于我的许多研究代码都是公开的,我经常会告诉编程智能体将我的仓库克隆到 /tmp 并将它们用作输入:

从 GitHub 将 simonw/research 克隆到 /tmp,找到将 Rust 编译为 WebAssembly 的示例,然后使用它为该项目构建一个演示 HTML 页面。

这里的核心思想是,编程智能体意味着我们只需要弄清楚一个有用的技巧_一次_。如果该技巧随后在某处记录了工作代码示例,我们的智能体就可以查阅该示例并在将来使用它来解决任何类似形状的项目。

现在写代码很便宜

这是指南 智能体工程模式 中的一章。

本指南的章节

  1. 原则 1. 现在写代码很便宜 2. 囤积你掌握的技能

  2. 测试和 QA 1. 红/绿 TDD 2. 首先运行测试

  3. 理解代码 1. 线性演练

coding-agents 164ai-assisted-programming 352generative-ai 1665agentic-engineering 17ai 1879llms 1630 创建时间:2026 年 2 月 26 日

最后修改时间:2026 年 2 月 26 日

上一篇:现在写代码很便宜

下一篇:红/绿 TDD

  • ©

关联主题