できることを蓄積せよ
Simon Willison氏は、ソフトウェア開発者やAIエージェント活用において「自分ができることの蓄積(hoard)」が問題解決の機会発見や革新的な組み合わせを可能にする重要な資産であると論じ、自身のブログやGitHubリポジトリ、LLM支援ツールを例に具体的な実践方法を紹介している。
キーポイント
「できることの蓄積」の重要性
ソフトウェア開発のスキルの本質は、何が可能で何が不可能かを理解し、その実現方法の知識を蓄積することにある。
知識蓄積の具体的な方法
ブログやTIL(Today I Learned)での記録、GitHubリポジトリでのコード収集、LLMを活用したツール開発など、多様な方法で解決策を蓄積している。
蓄積した資産の再結合による価値創造
蓄積したコードや知識は、AIコーディングエージェントへの強力な入力となり、既存の実例を組み合わせて新しいものを構築するプロンプトパターンが効果的である。
実例に基づく確信の重要性
理論的に可能であることを知っているだけではなく、実際に動作するコードを見たり書いたりした経験が、確信と応用力をもたらす。
PDFを画像に変換する非同期ジェネレーター関数
PDFファイルを入力として受け取り、PDF.jsライブラリを使用して各ページを画像に変換する非同期ジェネレーター関数の実装を示しています。
PDF.jsを使用したPDF処理
URL.createObjectURLを使用してファイルオブジェクトからURLを生成し、pdfjsLib.getDocumentでPDFドキュメントを読み込む方法を説明しています。
PDFページのキャンバスレンダリング
PDFの各ページをキャンバス要素にレンダリングし、指定された幅に合わせて高さを計算して画像データを生成するプロセスを説明しています。
影響分析・編集コメントを表示
影響分析
この記事は、AIエージェント時代の生産性向上において、人間の経験知を体系的に蓄積・構造化することが、単なる個人の学習を超えてAIとの協働の質を高める重要な基盤となることを示している。特に、実証済みのコード断片を組み合わせるプロンプト手法は、エージェント活用の実践的なベストプラクティスとして広く応用可能である。
編集コメント
AIツール活用の「How-to」記事が多い中、本記事は「何を蓄積し、どう活用するか」という根本的な知識管理の哲学に立ち返り、AI時代の新たな実践を示す良質な内容。
*Agentic Engineering Patterns >*
コーディングエージェントと生産的に作業するための私の多くのヒントは、それらなしでも私がキャリアで有用だと見つけたアドバイスからの拡張です。その素晴らしい例の一つがこれです:自分がやり方を知っていることは蓄積しておく。
ソフトウェアを構築する際のスキルの大きな部分は、何が可能で何が不可能かを理解し、少なくともそれらをどのように達成できるかのおおよそのアイデアを持っていることです。
これらの質問は広範なものから非常にニッチなもののまで様々です。ウェブページが JavaScript だけで OCR(光学文字認識)操作を実行できますか?アプリが実行されていない状態でも iPhone アプリが Bluetooth デバイスとペアリングできますか?100GB の JSON ファイルを、まず全体をメモリに読み込まずに Python で処理できますか?
このような質問に対する答えをあなたがどれだけ多く持っていればいるほど、他の人がまだ考えていない方法で技術を展開して問題を解決する機会を見出す可能性が高まります。
何かが理論的に可能であることを知っていることと、実際に自分でそれを成し遂げたのを見ることは同じではありません。ソフトウェア専門家として育むべき重要な資産は、このような質問に対する深い回答のコレクションであり、できれば実行可能なコードによって裏付けられたものが理想的です。
私は、このような解決策をさまざまな方法で収集・蓄積しています。私の blog や TIL blog には、私がやり方を理解した事柄に関するノートがぎっしりと詰め込まれています。また、1000 を超える GitHub リポジトリ を持っており、そこにはさまざまなプロジェクトのために書いたコードや、重要なアイデアを実証する小規模な概念実装(proof-of-concepts)が多く含まれています。
最近では、LLM(大規模言語モデル)を活用して、興味深い問題に対するコード解決策のコレクションを拡大しています。
tools.simonwillison.net は、私が LLM の支援を受けたツールやプロトタイプを収集した最大のコレクションです。ここでは、私は HTML ツール と呼ぶものを集めています。これは JavaScript や CSS を埋め込み、特定の課題を解決する単一の HTML ページのことです。
私の simonw/research リポジトリには、より大規模で複雑な例が収められています。ここではコーディングエージェント(コード生成 AI)に問題調査を命じ、動作するコードと、その発見内容を詳細に記した報告書を作成させるという挑戦を行っています。
蓄積したものから再構成する
なぜこれほど多くのものを収集するのか?自分の能力を構築・拡張するのに役立つことに加え、この過程で生成された資産は、コーディングエージェントにとって極めて強力な入力となります。
私が最も好きなプロンプト(指示)のパターンの一つは、既存の動作する例を 2 つ以上組み合わせて、新しい何かを構築するようにエージェントに命じるというものです。
この手法の有効性を明確に示してくれたプロジェクトの一つが、私のツールコレクションに最初に追加したものであり、ブラウザベースの OCR ツール です。詳細は こちら で解説されています。
私は、PDF ファイルからのページを OCR 処理するための、手軽でブラウザ上で動作するツールを求めていました。特に、テキストバージョンが一切提供されておらず、スキャンされた画像のみで構成されている PDF を対象としていました。
以前、ブラウザ内で Tesseract.js OCR ライブラリ の実行を試みたことがあり、非常に能力が高いと感じました。このライブラリは、成熟した Tesseract OCR エンジンの WebAssembly 版を提供しており、JavaScript から呼び出して画像からテキストを抽出することができます。
しかし、私は画像を扱うのではなく、PDF を扱いたかったのです。そこで思い出したのは、Mozilla の PDF.js ライブラリでも作業したことがあるという点です。このライブラリは、PDF の個々のページをレンダリングされた画像に変換する機能などを持っています。
私はこれらの両方のライブラリに関する JavaScript のスニペットをメモに残していました。
私がモデル(当時 Claude 3 Opus)に入力した完全なプロンプトは以下の通りです。これは私の二つの例を組み合わせて、私が探していた解決策を説明するものです:
このコードは、PDF を開き、ページごとに画像に変換する方法を示しています:
html>
head>
title>PDF to Imagestitle>
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) {
<
原文を表示
*Agentic Engineering Patterns >*
Many of my tips for working productively with coding agents are extensions of advice I've found useful in my career without them. Here's a great example of that: hoard things you know how to do.
A big part of the skill in building software is understanding what's possible and what isn't, and having at least a rough idea of how those things can be accomplished.
These questions can be broad or quite obscure. Can a web page run OCR operations in JavaScript alone? Can an iPhone app pair with a Bluetooth device even when the app isn't running? Can we process a 100GB JSON file in Python without loading the entire thing into memory first?
The more answers to questions like this you have under your belt, the more likely you'll be able to spot opportunities to deploy technology to solve problems in ways other people may not have thought of yet.
Knowing that something is theoretically possible is not the same as having seen it done for yourself. A key asset to develop as a software professional is a deep collection of answers to questions like this, ideally illustrated by running code.
I hoard solutions like this in a number of different ways. My blog and TIL blog are crammed with notes on things I've figured out how to do. I have over a thousand GitHub repos collecting code I've written for different projects, many of them small proof-of-concepts that demonstrate a key idea.
More recently I've used LLMs to help expand my collection of code solutions to interesting problems.
tools.simonwillison.net is my largest collection of LLM-assisted tools and prototypes. I use this to collect what I call HTML tools - single HTML pages that embed JavaScript and CSS and solve a specific problem.
My simonw/research repository has larger, more complex examples where I’ve challenged a coding agent to research a problem and come back with working code and a written report detailing what it found out.
Recombining things from your hoard
Why collect all of this stuff? Aside from helping you build and extend your own abilities, the assets you generate along the way become incredibly powerful inputs for your coding agents.
One of my favorite prompting patterns is to tell an agent to build something new by combining two or more existing working examples.
A project that helped crystallize how effective this can be was the first thing I added to my tools collection - a browser-based OCR tool, described in more detail here.
I wanted an easy, browser-based tool for OCRing pages from PDF files - in particular PDFs that consist entirely of scanned images with no text version provided at all.
I had previously experimented with running the Tesseract.js OCR library in my browser, and found it to be very capable. That library provides a WebAssembly build of the mature Tesseract OCR engine and lets you call it from JavaScript to extract text from an image.
I didn’t want to work with images though, I wanted to work with PDFs. Then I remembered that I had also worked with Mozilla’s PDF.js library, which among other things can turn individual pages of a PDF into rendered images.
I had snippets of JavaScript for both of those libraries in my notes.
Here’s the full prompt I fed into a model (at the time it was Claude 3 Opus), combining my two examples and describing the solution I was looking for:
This code shows how to open a PDF and turn it into an image per page:
html>
head>
title>PDF to Imagestitle>
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) {
<
関連記事
今日のまとめ
AI日報で今日の重要ニュースをまとめ読み