03-囤积那些你“知道怎么做”的事情

By 刘志军 , 2026-03-21, 分类: agentic-patterns-translation

我关于如何高效使用编程智能体的许多建议,其实都是我职业生涯中那些行之有效的经验之谈,即便没有 AI 参与时也同样适用。一个绝佳的例子就是:囤积那些你“知道怎么做”的事情。

构建软件的一项核心能力,在于理解什么是可能的,什么是不可能的,并且至少对如何实现这些目标有一个大致的思路。

这些问题可能宏大,也可能非常冷门。例如:网页能否仅靠 JavaScript 运行 OCR(光学字符识别)?iPhone 应用在未运行时能否与蓝牙设备配对?我们能否在 Python 中处理 100GB 的 JSON 文件而不将其全部加载到内存?

你掌握这类问题的答案越多,就越有可能发现别人尚未想到的、利用技术解决问题的机会。

要对这些答案充满信心,最好的办法就是亲眼看到运行中的代码。知道某事在理论上可行,与亲眼见证它被实现完全是两回事。作为软件专业人士,一项关键资产就是收集大量此类问题的答案,并附带相关的证明代码。

我通过多种方式“囤积”这些方案。我的博客和“TIL(今天学到了)”博客里塞满了关于各种技术实现的研究笔记。我在 GitHub 上有一千多个仓库,收集了为不同项目编写的代码,其中很多都是演示核心理念的小型原型(PoC)。

最近,我开始利用 LLM 来帮助我扩展这一代码方案库。

重新组合你的“宝库”

为什么要收集这些东西?除了提升个人能力外,你在这个过程中生成的资产会成为编程智能体的强大输入

我最喜欢的提示词模式之一,就是让智能体通过组合两个或多个现有的运行示例来构建新东西。

一个让我深刻意识到这种方式多么有效的项目,是我工具集里的第一个成员:一个基于浏览器的 OCR 工具

当时,我想做一个简单的浏览器工具,用来对 PDF 页面进行 OCR 处理——尤其是那些完全由扫描图像组成、没有文本层的 PDF。

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

但我不想处理单张图像,我想处理 PDF。接着我想到,我以前用过 Mozilla 的 PDF.js 库,它可以将 PDF 的每一页渲染成图像。

在我的笔记里,正好有这两个库的 JavaScript 代码片段。于是,我把这两个示例结合起来,向模型(当时是 Claude 3 Opus)发出了如下提示词:

这段代码展示了如何打开 PDF 并将其转换为每页一张的图像:
```html
<!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:
```javascript
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、CSS 和 JS 的单页面。提供一个大方框,用户可以将 PDF 文件拖放进去。操作后,将 PDF 的每一页转换为 JPEG 显示在页面上,然后使用 Tesseract 运行 OCR,并将结果显示在每个图像下方的文本框中。

结果非常完美!模型直接吐出了一个完全符合我要求的原型页面。我只进行了几次简单的迭代就完成了最终版本。这只花了几分钟时间,却打造出了一个让我受益至今的实用工具。

编程智能体让这一模式更强大

我构建那个 OCR 示例是在 2024 年 3 月,比 Claude Code 发布早了近一年。而编程智能体的出现,让囤积运行示例的价值翻倍了。

如果你的编程智能体可以访问网络,你可以告诉它

使用 `curl` 获取 `https://tools.simonwillison.net/ocr`  `https://tools.simonwillison.net/gemini-bbox` 的源码,构建一个新工具,让我能选择 PDF 的一页并传给 Gemini,返回该页插图的检测框。” _

(这里指定 curl 是因为 Claude Code 默认会总结页面,而我们需要原始 HTML。)

编程智能体也非常擅长搜索,这意味着你可以在本地运行它们,并告诉它们去哪里找示例:

参考 `~/dev/ecosystem/llm-mistral` 的实现方式,为 `~/dev/ecosystem/datasette-oauth` 项目添加模拟 HTTP 请求的测试。

通常这就足够了——智能体会启动一个搜索子智能体去调研,并提取完成任务所需的细节。

由于我的研究代码大多是公开的,我经常让智能体将仓库克隆到 /tmp 目录作为输入:

从 GitHub 克隆 `simonw/research` 到 `/tmp`,找到将 Rust 编译为 WebAssembly 的示例,并以此为本项目构建一个演示 HTML 页面。

核心理念在于:有了编程智能体,任何有用的技巧我们都只需要解决一次。 只要这个技巧被记录在某处并配有可运行的代码示例,我们的智能体就能在未来查阅它,并用它来解决任何类似的问题


原文:Agentic Engineering Patterns by Simon Willison


关注公众号「Python之禅」,回复「1024」免费获取Python资源

python之禅