[{"data":1,"prerenderedAt":55},["ShallowReactive",2],{"article-c_p__7mzq":3,"prev-article-c_p__7mzq":16,"$f3cdc7wqr1aipx":40,"next-article-c_p__7mzq":46,"$fkmi5szdr8agx":48},{"contents":4,"totalCount":14,"offset":15,"limit":14},[5],{"id":6,"createdAt":7,"updatedAt":8,"publishedAt":9,"revisedAt":8,"title":10,"content":11,"tags":12,"is_no_index":13},"c_p__7mzq","2026-08-13T09:19:06.202Z","2026-08-13T12:10:56.451Z","2026-08-13T10:54:50.763Z","Cloudflareスタックで非同期ジョブをいい感じに管理したい","\u003Ch1 id=\"h8d027c8ed3\">はじめに\u003C\u002Fh1>\u003Cp>どうも、わたしです。\u003C\u002Fp>\u003Cp>ここ数年で、Cloudflare Workersを取り巻く状況はかなり変わっているようで、Workersそのものだけでなく、QueuesやDurable Objects、D1、Analytics Engine等、Cloudflareスタックの中で使えるサービスが結構増えました。加えて、比較的安価に使えることもあり、Workersを本格的なバックエンドとして採用するケースも(わたしの周りでは)かなり増えたように思います。\u003Cbr>利用者が増えれば当然知見も増えるもので、Durable Objectsをどう使うと嬉しいのか、D1に何を任せるべきなのか、Containersで何が解決可能なのか…といった話を見かけることが増えました。\u003C\u002Fp>\u003Cp>そんな中で、Workersスイートで構築されたアプリケーションを運用していると、バックグラウンドジョブ周りでいくつか困ることが出てきました。\u003Cbr>そのあたりをどうにかしようとして作り始めたのが、今回紹介する\u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fchan-mai\u002FTsumugi\" target=\"_blank\" rel=\"noopener noreferrer\">Tsumugi\u003C\u002Fa>です。\u003C\u002Fp>\u003Cp>\u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fchan-mai\u002FTsumugi\" target=\"_blank\" rel=\"noopener noreferrer\">https:\u002F\u002Fgithub.com\u002Fchan-mai\u002FTsumugi\u003C\u002Fa>\u003C\u002Fp>\u003Cp>\u003Ca href=\"https:\u002F\u002Ftsumugi.mq1.dev\" target=\"_blank\" rel=\"noopener noreferrer\">https:\u002F\u002Ftsumugi.mq1.dev\u003C\u002Fa>\u003C\u002Fp>\u003Cp>Cloudflare Workers \u002F Durable Objects \u002F Queues \u002F D1を中心に、バックグラウンドジョブの投入、遅延実行、再試行、優先度、同時実行数制御、定期実行、ジョブ同士の依存関係などをまとめてブラックボックスとしていい感じに扱うためのジョブ管理システムです。最初から汎用的なジョブシステムを作ろうと思って始めたわけではないのですが、気がついたらそこそこ大きくなっていました。\u003C\u002Fp>\u003Cp>そしてせっかく公開したのに思っていた以上に反応をもらえず、たいへん寂しいのでこの記事を書いています。\u003C\u002Fp>\u003Cp>これは宣伝です。笑ってください。\u003C\u002Fp>\u003Ch1 id=\"hd92040d0df\">きっかけ\u003C\u002Fh1>\u003Cp>Tsumugiを作る直接のきっかけは、以前作ったMisskey向け匿名質問箱サービスの\u003Ca href=\"https:\u002F\u002Fmewk.app\u002F\" target=\"_blank\" rel=\"noopener noreferrer\">Mewk\u003C\u002Fa>です。\u003C\u002Fp>\u003Cp>Mewkでは、バックグラウンドジョブの管理に\u003Ca href=\"https:\u002F\u002Fkiribi.pages.dev\u002F\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">Kiribi\u003C\u002Fa>というCloudflare Queues向けのジョブ管理システムを採用していました。Queuesのインターフェースを隠して単純な同期処理らしく扱えるようにしてくれるライブラリで、状態管理や再試行まで含めて面倒を見てくれるため、かなり便利に使っていました。\u003C\u002Fp>\u003Cp>一方で、実際のサービスに載せて運用していると、少しずつKiribiの外側に欲しいものが増えていきました。特定の種類のジョブだけ同時実行数を厳密に制限したかったり、待機中のジョブに優先順位を付けたかったり、同じ対象に対するジョブを重複して積みたくなかったり、複数の処理に依存関係を持たせたかったり、みたいなやつです。\u003C\u002Fp>\u003Cp>どれも個別には実装できますし、Kiribi自体も使いづらいライブラリだったわけではありません。が、必要な機能をアプリケーション側で補っていくと、Kiribiを使いながら別のジョブスケジューラを作っているような微妙な状態になってきました。\u003C\u002Fp>\u003Cp>KiribiへContributeする方向も当然考えたものの、2年以上ほどメンテナンスが続いていませんでしたし、必要としていた変更も単純な機能追加というより、スケジューリングや状態管理の責務をどこに置くかという設計そのものに関わるものであったことも踏まえ、既存の設計へ無理に差し込むよりも現在のCloudflareスタックを前提に一度作り直した方がよいのではないか、と考えました。\u003C\u002Fp>\u003Cp>有難いことに、Kiribiが登場した当時からCloudflare側の状況もかなり変わっているようで、Cloudflareスタックの中だけでより複雑な状態管理を成立させるための選択肢も増えており、その結果として作り始めたのがTsumugiです。\u003C\u002Fp>\u003Ch1 id=\"hf2b8efe0f7\">Queuesだけでは少し足りない\u003C\u002Fh1>\u003Cp>Cloudflare Queuesは、consumerへの配送, 失敗時の再試行まで面倒を見てくれる便利なものです。単純な非同期処理であれば、(希に揮発することもありますが)Queueを直接使うだけで十分なことも多いです。\u003C\u002Fp>\u003Cp>しかし、実際に使っていると、Queuesはあくまで配送のためのものであって、次にどのジョブを実行するべきかを判断するスケジューラとして使えない部分がつらくなってきます。\u003Cbr>既にn件のジョブが実行されている間はn+1件目をまだ動かしたくないだとか、待機中のジョブの中でもpriorityの高いものを先に実行したいだとか、あるジョブは別のジョブが成功するまで開始させたくないみたいな場合ですね。\u003C\u002Fp>\u003Cp>こういう細かな制御をしたい場合、今どういう状態なのかを見ながら次に何を実行するかを判別する実装を書いてあげる必要があります。\u003C\u002Fp>\u003Ch1 id=\"ha960b4c985\">Durable Objectで判別, Queuesで実行\u003C\u002Fh1>\u003Cp>Tsumugiでは、bindingごとにDurable Objectを置き、それをスケジューラ兼コーディネータとして利用しています。\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-mermaid\">flowchart LR\n    A[Application] --&gt;|enqueue| B[Durable Object]\n    B --&gt;|dispatch| C[Cloudflare Queues]\n    C --&gt; D[Performer]\n\n    B --&gt; E[Outbox]\n    E --&gt;|projection| F[D1]\n    F --&gt; G[Dashboard \u002F REST API]\n\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>Durable Objectは、待機中のジョブ、現在実行中のジョブ、実行可能時刻、priority、concurrency、rate limitなどを見ながら、次に何をdispatchするかを決定しています。\u003C\u002Fp>\u003Cp>この部分をDurable Objectに委ねた主な理由として、整合性が欲しかったためです。たとえば「このジョブは最大3件まで同時実行する」と決めたのであれば、4件目は本当に実行されてほしくありません。D1上のカウンタを何となく増減させながら判定するよりも、ひとつのDurable Objectが状態を持ち、そこで判断を直列化した方がはるかに分かりやすいはずです。\u003C\u002Fp>\u003Cp>ただし、Durable Object自身にジョブ本体を実行させているわけではありません。\u003Cbr>たとえば10分かかる処理をDurable Objectから直接\u003Ccode>await\u003C\u002Fcode>した場合、その10分間ずっとDurable Object側のリクエストを生かしておくことになり、それはあまり嬉しくありません。\u003C\u002Fp>\u003Cp>なのでTsumugiでは、Durable Objectは判断するだけで、実際の実行はQueuesに任せる、という形にしています。\u003C\u002Fp>\u003Ch1 id=\"hf9050c2e7f\">理解せず単純に扱える\u003C\u002Fh1>\u003Cp>内部の話をすると、Durable Object, SQLite, Queues, D1, outbox, projectionなど色々なものが出てきます。\u003C\u002Fp>\u003Cp>とはいえ、利用者側までその事情を意識する必要がないようにしたかったので、表側はなるべく普通のジョブライブラリっぽくしています。\u003C\u002Fp>\u003Cp>たとえばメールを送るジョブならこんな感じで書けます。\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-ts\">import { Performer } from &apos;tsumugi\u002Fperformer&apos;;\n\nexport class SendMail extends Performer&lt;{ to: string }, void, {}, Env&gt; {\n  async perform(payload: { to: string }): Promise&lt;void&gt; {\n    await this.env.MAILER.send(payload.to);\n  }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>これをWorkerのトップレベルからexport\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-ts\">export * from &apos;.\u002Fperformers\u002Findex.js&apos;;\n\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>あとは\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-ts\">await tsumugi.enqueue(env, {\n  binding: &apos;SendMail&apos;,\n  payload: {\n    to: &apos;a@example.com&apos;,\n  },\n});\n\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>とすればenqueueできます。\u003C\u002Fp>\u003Cp>ちなみに\u003Ccode>binding\u003C\u002Fcode>に\u003Ccode>SendMail\u003C\u002Fcode>を指定すると、\u003Ccode>payload\u003C\u002Fcode>もPerformer側から型推論されます。\u003C\u002Fp>\u003Cp>同じ情報を複数箇所に書くのは冗長ですごく嫌いなので、TsumugiではWorkerからexportされているPerformerをruntime上のregistryとして使いつつ、型も同じ場所から取るようにしています。\u003C\u002Fp>\u003Ch1 id=\"hf5d5b8f0fc\">ジョブらしいことは一通りできる(はず)\u003C\u002Fh1>\u003Cp>基本のenqueueに加えて、遅延実行も指定できます\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-ts\">await tsumugi.enqueue(env, {\n  binding: &apos;SendMail&apos;,\n  payload,\n  delayMs: 60_000,\n});\n\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>priorityを付けることもできます\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-ts\">await tsumugi.enqueue(env, {\n  binding: &apos;SendMail&apos;,\n  payload,\n  priority: 10,\n});\n\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>同じ対象に対するジョブを重複して作りたくない場合は、\u003Ccode>uniqueKey\u003C\u002Fcode>も明示可能です\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-ts\">await tsumugi.enqueue(env, {\n  binding: &apos;SendMail&apos;,\n  payload,\n  uniqueKey: &apos;a@example.com&apos;,\n});\n\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>このほかにも、bindingごとの同時実行数制御, rate limit, retry, 定期実行, remote performer, retry\u002Fcancellation, 実行履歴の追跡などがあります。また、複数のジョブに依存関係を持たせるFlowという仕組みもありますが、これは少し毛色が違うので後述します。が、全部ここで説明し始めるとそのままREADMEになってしまうので、細かい使い方はドキュメントを見てください。\u003C\u002Fp>\u003Cp>\u003Ca href=\"https:\u002F\u002Ftsumugi.mq1.dev\" target=\"_blank\" rel=\"noopener noreferrer\">https:\u002F\u002Ftsumugi.mq1.dev\u003C\u002Fa>\u003C\u002Fp>\u003Cp>ちゃんと書きました。\u003C\u002Fp>\u003Ch1 id=\"hc234659312\">ジョブ同士の依存関係も扱える\u003C\u002Fh1>\u003Cp>Tsumugiには、複数のジョブを依存関係ごとまとめて扱うFlowという仕組みがあります。\u003C\u002Fp>\u003Cp>これは、ジョブを頂点、ジョブ同士の依存関係を辺とした有向非巡回グラフ(DAG)として処理全体を表現するものです。\u003C\u002Fp>\u003Cp>たとえば画像を受け取って、まず解析を行い、その結果を使ってサムネイルの生成とメタデータの保存を並列に実行し、両方が終わったあとに公開処理を行う場合は、概念的には次のようなグラフになります。\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-mermaid\">graph TD\n    A[画像を取得する] --&gt; B[画像を解析する]\n    B --&gt; C[サムネイルを生成する]\n    B --&gt; D[メタデータを保存する]\n    C --&gt; E[公開処理をする]\n    D --&gt; E\n\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>この場合、\u003Ccode>公開処理をする\u003C\u002Fcode>は、\u003Ccode>サムネイルを生成する\u003C\u002Fcode>と\u003Ccode>メタデータを保存する\u003C\u002Fcode>の両方が完了するまで実行されません。\u003C\u002Fp>\u003Cp>こうした処理自体は、Aの処理が成功したらBをenqueueし、Bが終わったらCとDをenqueueし、それぞれの完了状態をどこかに記録して、両方が終わったらEをenqueueすることでQueues単体でも一応実現可能ではあります。しかし、どのジョブが完了したのか、次に実行可能になるジョブはどれなのか、途中のジョブが失敗した場合に後続をどう扱うのか、といったことまで自分で管理しなければなりません。\u003C\u002Fp>\u003Cp>Tsumugiでは、この依存関係そのものをジョブシステム側の状態として管理します。あるジョブが完了すると、そのジョブを依存先としている後続ジョブについて、すべての依存関係が解決したかをTsumugi側で判定し、実行可能になったものだけを順次スケジューリングします。\u003C\u002Fp>\u003Cp>Temporalのような汎用的で大規模なworkflow engineを目指しているわけではありませんが、バックグラウンド処理を書いていると「AとBが終わったらCを動かしたい」くらいの依存関係はわりと普通に出てきます。そうした関係を個々のPerformerの中へ書き散らすのではなく、ジョブ同士の関係そのものをTsumugiに管理させられる、というのがFlowです。\u003C\u002Fp>\u003Ch1 id=\"h2c993c5785\">Cloudflareスタック\u003C\u002Fh1>\u003Cp>TsumugiはCloudflare専用です。\u003C\u002Fp>\u003Cp>少なくとも今のところ、AWSでも動くとかGCPでも動くとか、そういう方向はあまり考えていません。Durable ObjectsもQueuesもD1も普通に使います。\u003C\u002Fp>\u003Cp>これは意図的なもので、どこでも動くライブラリを作ろうとすると、どうしても各環境の一番弱い共通部分へ寄せた抽象化になってしまいます。折角CloudflareにはDurable Objectsのような特殊で便利なプリミティブがあるのに、それを使わず一般化するのはもったいないと思っています。\u003C\u002Fp>\u003Cp>逆に言うとTsumugiは、Cloudflareでしか動かなくていいので、Cloudflareではちゃんと気持ちよく動くものを目指しています。\u003C\u002Fp>\u003Cp>こういうことばかり言うと気持ち悪いのでデメリットっぽいものも紹介しておくと、Workers Paidが必要ですし、D1やQueuesの準備も必要です。Kiribiと比べて依存するサービス数も増えるのでコスト的には劣っています。単純に「重要度の低いWebhookを受け取って後で1回だけ処理したい」程度であれば、Tsumugiを入れる必要はありませんし、なんならQueuesを直接使った方がたぶんしあわせになれます。\u003C\u002Fp>\u003Ch1 id=\"h6bddf1fb83\">Kiribiとの違い\u003C\u002Fh1>\u003Cp>TsumugiはKiribiからかなり影響を受けていますが、結果として設計はだいぶ違うものになりました。\u003C\u002Fp>\u003Cp>Kiribiは、(わたしの解釈に誤りがなければ)Cloudflare Queuesにジョブ管理の仕組みを足すという方向のライブラリです。それに対してTsumugiでは、Queuesをジョブシステムそのものではなく、実行時の配送手段として扱っています。\u003C\u002Fp>\u003Cp>Kiribiの方が構成は単純ですし、Queues自身が持っているスケーラビリティの恩恵を素直に受けることができます。一方でTsumugiは、スケジューリングの判断を自分で握っている分、priorityや正確なconcurrency制御、Flow、rate limitなどをひとつの状態モデルの上で扱いやすくなっています。\u003C\u002Fp>\u003Cp>ですので、TsumugiがKiribiの完全な上位互換だとは思っていません。そもそも、単純なジョブ管理ならKiribiの方が軽量ですし、Queuesをなるべくそのまま使いたいならKiribiの設計の方が自然に見えます。Tsumugiは、その先でもう少し制御したくなったときの選択肢として使っていただけると嬉しいです。\u003C\u002Fp>\u003Ch1 id=\"ha4ae1149d6\">分散システムは嫌\u003C\u002Fh1>\u003Cp>ここまで色々と書いていますが、Tsumugiの内部は割とちゃんと分散システムです。もうなんか嫌ですね。\u003C\u002Fp>\u003Cp>Tsumugiの内部では、Durable Object, Queues, Performer, D1と複数のコンポーネントを跨いでジョブを処理しています。そのため、正常系だけでなく、それぞれの境界で処理が失敗した場合に状態をどう収束させるかはかなり意識して設計しました。\u003C\u002Fp>\u003Cp>たとえば、Durable Object側ではジョブをdispatchする状態まで進んだもののQueueへの送信に失敗した場合や、Performerの処理自体は成功したものの、その完了をDurable Objectへ通知する段階で失敗した場合などがあります。D1へのprojectionについても同様で、途中で失敗したからといってジョブそのものの状態まで不整合になってしまうのは避けるべきです。\u003C\u002Fp>\u003Cp>Tsumugiでは、こうした問題を扱いやすくするために、ジョブのauthoritativeな状態と実際の配送、実行、read modelへの反映をそれぞれ分けています。\u003C\u002Fp>\u003Cp>例として、retryについてはQueues側へ判断を任せるのではなく、Durable Object側をauthorityとして扱っています。また、D1はあくまでread modelなので、projectionが一時的に遅れたり失敗したりしても、それによってジョブの実行状態そのものが変わることはないようにしています。配送保証についてもジョブ単位で扱えるようにしていて、at-most-onceを必要とするジョブについては、実行前のclaimを含めて通常のジョブとは異なる扱いをしています。\u003C\u002Fp>\u003Cp>このあたりは機能だけを見るとあまり目立たない部分ですが、ジョブ管理システムとして実際に使ううえではかなり重要だと考えていて、正常系だけでなく、Queueへの配送やWorkerの実行、状態更新のどこかが途中で失敗した場合にも、最終的に説明可能な状態へ戻せることを意識しました。\u003C\u002Fp>\u003Ch1 id=\"hb7ef8b25a6\">導入周りもまとめて\u003C\u002Fh1>\u003Cp>Tsumugiはnpm packageとして公開しているほか、初期セットアップ用のCLI, D1のmigration, 管理画面, REST API, ドキュメント, 実装例等もまとめて提供しています。\u003C\u002Fp>\u003Cp>導入自体は、packageを追加したあとに\u003Ccode>tsumugi init\u003C\u002Fcode>を実行するところから始められます。\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-bash\">pnpm add tsumugi\nnpx tsumugi init\n\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>\u003Ccode>tsumugi init\u003C\u002Fcode>では、Tsumugiが利用するD1 DatabaseやQueueの作成、wrangler設定やソースコードの雛形生成、migrationの適用などを行います。\u003C\u002Fp>\u003Cp>Tsumugiは内部でDurable Objects、Queues、D1と複数のCloudflareサービスを利用するため、これらをすべて手作業で設定する形にすると導入時に確認する項目がかなり増えてしまいます。そのため、最初に必要になる構成についてはできるだけCLI側で生成するようにしました。\u003C\u002Fp>\u003Cp>加えて、ジョブの状態を確認するためのダッシュボードもpackageに含まれており、別途管理画面を用意しなくとも、ジョブの一覧や実行履歴, エラーの確認, retry\u002Fcancelなどの操作が可能です。REST APIも提供されるため、必要であれば独自の管理ツールなどから操作することもできるかと思います。\u003C\u002Fp>\u003Cp>内部構成はそれなりに複雑ですが、Tsumugiを利用するにあたっては、その中身を意識せず、ブラックボックスとして容易に扱えることを意識しています。\u003C\u002Fp>\u003Ch1 id=\"h9be0c3393d\">おわりに\u003C\u002Fh1>\u003Cp>そんなわけで、Cloudflareスタック向けのジョブ管理システム、Tsumugiを作りました。\u003C\u002Fp>\u003Cp>もともとはKiribiを使っていて、実運用の中で「もう少しここを制御したい」と思ったことが始まりでした。Kiribi自体は便利でしたが、必要な機能が増えるにつれて既存の設計の外側へはみ出す部分が増え、さらに当時すでに2年以上メンテナンスが続いていなかったこともあり、今のCloudflareスタックを前提に一度作り直してみることにしました。\u003C\u002Fp>\u003Cp>Kiribiが登場した頃から時間が経ち、Cloudflareのサービスも増え、Workersだけでシステムを組むこと自体もずいぶん現実的になりました。であれば、今あるプリミティブを前提に、もう一度ジョブ管理システムを考えてみよう。というのがTsumugiです。\u003C\u002Fp>\u003Cp>Cloudflare Queuesを使っていて、「配送はQueuesでいいけれど、ジョブの実行順や状態はもう少し自分で管理したい」だとか、「同時実行数やpriority、依存関係まで含めて扱いたい」みたいなものにあたりに心当たりがあれば、試してもらえると嬉しいです。\u003C\u002Fp>\u003Cp>まだ利用者もほとんどいないので、設計上変なところ、不具合、使いづらいAPI、こういうものが欲しい、などあれば雑にでもIssueを投げてもらえると助かります。\u003C\u002Fp>\u003Cp>ここまで読んで、まあちょっと面白いことをやっているじゃん くらいに思っていただけたのであれば、Starをひとつ置いていってもらえるとわたしが喜びます。\u003C\u002Fp>\u003Cp>\u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fchan-mai\u002FTsumugi\" target=\"_blank\" rel=\"noopener noreferrer\">https:\u002F\u002Fgithub.com\u002Fchan-mai\u002FTsumugi\u003C\u002Fa>\u003C\u002Fp>\u003Cp>それでは〜\u003C\u002Fp>",[],false,1,0,{"contents":17,"totalCount":39,"offset":15,"limit":14},[18],{"id":19,"createdAt":20,"updatedAt":21,"publishedAt":21,"revisedAt":21,"title":22,"content":23,"tags":24,"is_no_index":37,"summary":38},"0s5wahzwf4","2026-07-13T20:36:41.461Z","2026-07-13T20:40:28.103Z","知性","\u003Cp>嫌な時代になったもので、わたしが知性を感じていたもの達は、軒並みLLMによって6割くらいの完成度で再現可能になってしまいました。\u003C\u002Fp>\u003Cp>文章も、コードも、設計も、映像も。おかげさまで世の中はslopまみれです。検索結果を開けばLLM製のまとめ記事、技術記事サイトを開けばどこかで見たような解説の劣化版焼き直し。SEO汚染で瀕死だったインターネットに、とどめの一撃が入った格好です。\u003C\u002Fp>\u003Cp>この6割という数字が、また嫌らしい。2割なら誰も相手にしませんし、10割なら諦めもつく。6割は、素人目には十分に見えて、よく見ると粗が見える感じの、ちょうど厄介なラインです。そして残念なことに、世の中の大半の場面は6割で十分だったようです。残念で仕方がありません。\u003C\u002Fp>\u003Cp>極端な話、LLMの出力する任意の成果物は単なるパターンマッチに過ぎません。確率的に尤もらしいトークンを並べているだけで、そこに理解と呼べるものがあるのかは怪しいところです。\u003C\u002Fp>\u003Cp>ただ、残念なことに、わたし達が知的作業と呼んでいたモノの多くは、そこまで崇高なものではなく、パターンマッチで代替可能でした。とりあえず動くものをつくる、目の前のエラーに場当たり的なパッチを当てる。そういうある種の短絡的な思考で解決できる問題は殆どLLMで解決可能になっているように感じています。認めたくないですがね。\u003C\u002Fp>\u003Cp>正直、コーディングや設計で現行のLLMに負ける気は更々ありません。が、これも今だけでしょう。そのうち負ける気がしています。というか、Fable 5相手ですら怪しいですからね。1年前のモデルには余裕を感じていたというのに。\u003C\u002Fp>\u003Cp>本当に負けた日には、わたしも「オデ、Fableノウデ、クウ。オデ、ツヨイエンジニアニ、ナル」とか言い出しかねません。まぁ、相手に腕は無いんですが。\u003C\u002Fp>\u003Cp>以前、LLMがどれだけ優れていても、つまらない人間はつまらないという記事を書きました。今回は、その矛先を自分に向けてみようと思います。\u003C\u002Fp>\u003Cp>私は幼い頃から気持ちの悪いコンピュータのオタクでした。それ自体はただただ気持ち悪いだけなのでいいのですが、いつの日からか、技術は好奇心の対象というより、無価値な自分を価値あるものに見せるための粉飾の材料になっていたようにも思います。技術の話をしている間だけは、何者かでいられるためです。\u003C\u002Fp>\u003Cp>念のため書いておくと、これは私が特別高度な事を理解していることを示唆するものではありません。\u003C\u002Fp>\u003Cp>とは言え、世の中の大半は(詳しいとされている人でさえ)基礎的な部分を碌に理解していません。TCPの輻輳制御を説明できないままインフラを語り、ベジェの制御点の意味も知らないままモーションを売っています。だから、尤もらしいことを言うだけで相対的に浮上できてしまう。ただそれは、周囲の無理解に支えられた非本質的な虚構でしかないとも思うのです。\u003C\u002Fp>\u003Cp>白状すると、私は自分を大きく見せようとする無知な人が苦手です。それは私自身がそういう側面を持ち合わせているからなのかもしれませんし、そうではないのかもしれません。実際のところ、周りが私のことをどう思っているかなんて知りようがないので、どちらとも言えないのですが。\u003C\u002Fp>\u003Cp>そして今、尤もらしいことを言う能力なら人間よりよほど上手い計算機が現れてしまいました。虚構で嵩上げしていた部分から順に価値が剥がれ落ちていきます。これは困ったことになりました。\u003C\u002Fp>\u003Cp>では、何が残るのでしょうか。\u003C\u002Fp>\u003Cp>私は、分からないことを分からないと自分で認識して、批判的に内省できることだと考えています。視座が高い話をしたいわけではありません。むしろ逆で、それすら出来ないメタ認知で、現代社会に適応できるのか？という純粋な疑問です。\u003C\u002Fp>\u003Cp>LLMは自分が何を分かっていないのかを分かっていません。知識の穴に差し掛かっても速度を落とさず、同じ自信で虚偽を出力してくる。あれだけ流暢に喋る計算機が、自分の限界の認識だけは絶望的に下手なわけです。裏を返せば、自分の理解の輪郭を把握していること, どこまでが検証済みでどこからが受け売りかを区別できること, 分からないと言えること。今のところ、ここあたりだけは辛うじて人間の役割として残っています。\u003C\u002Fp>\u003Cp>むしろ、それを持たない人間には価値がないので、LLMでいいです。分かっていないことを分かっていないまま断言する人間は、品質でも再現性でもコストでもその辺の計算機に劣ります。存在意義がありません。\u003C\u002Fp>\u003Cp>偉そうなことを書きましたが、真っ先に価値が剥がれる側は多分わたしです。コーディングで負ける日が来たとき、身の程を知る知性とやらが飯の種になる保証はどこにもありません。\u003C\u002Fp>\u003Cp>とはいえ、他にやれることも特に思いつかないので、当面は自分がどこから分かっていないのかの確認だけ続けることになりそうです。それすら粉飾の一部だと言われたら、返す言葉もありませんが。\u003C\u002Fp>",[25,31],{"id":26,"createdAt":27,"updatedAt":28,"publishedAt":27,"revisedAt":28,"slug":29,"name":30},"wia4slp55q","2025-04-28T07:14:14.541Z","2025-12-03T16:08:13.798Z","thoughts","独り言",{"id":32,"createdAt":33,"updatedAt":34,"publishedAt":33,"revisedAt":34,"slug":35,"name":36},"qfey3yw0z1","2025-04-29T08:44:41.687Z","2025-12-03T16:07:14.622Z","technology","テクノロジー",true,"嫌な時代になったもので、わたしが知性を感じていたもの達は、軒並みLLMによって6割くらいの完成度で再現可能になってしまいました。文章も、コードも、設計も、映像も。おかげさまで世の中はslopまみれです。検索結果を開けばLLM製のまとめ記事、技術記事サイトを開けばどこかで見たような解説の劣化版焼き直し",69,{"url":41,"domain":42,"title":43,"description":44,"type":45},"https:\u002F\u002Ftsumugi.mq1.dev\u002F","tsumugi.mq1.dev","Tsumugi","Cloudflareスタック向けに設計されたジョブ管理システム","GENERAL",{"contents":47,"totalCount":15,"offset":15,"limit":14},[],{"url":49,"domain":50,"title":51,"description":52,"image":53,"favicon":54,"type":45},"https:\u002F\u002Fgithub.com\u002Fchan-mai\u002FTsumugi","github.com","GitHub - chan-mai\u002FTsumugi: A job management system designed for the Cloudflare stack.","A job management system designed for the Cloudflare stack. - chan-mai\u002FTsumugi","https:\u002F\u002Fopengraph.githubassets.com\u002F5b776cadef085ed8abeb6d55ee10521c377329d5fa4d02f365e7b038efa81535\u002Fchan-mai\u002FTsumugi","https:\u002F\u002Fgithub.githubassets.com\u002Ffavicons\u002Ffavicon.svg",1786623118735]