← ブログ一覧
技術ブログ

AIエージェントにCMSのメディアを運用させる ― 画像のバイト列はLLMに通さない

平原 典彦 — Tesseraを開発しているエンジニア。ヘッドレスCMSとAI連携に関心があります。

AIエージェント(Claude CodeやCursor)にブログ記事を書かせるところまでは、もう普通にできるようになりました。ではその記事に載せる画像――アイキャッチや説明図――は、誰がアップロードするのか。ここもエージェントに任せたくなりますが、意外な落とし穴があります。

結論から言うと、Tesseraはメディアの投入経路を2つ用意し、**「大きな画像のバイト列は、LLMのコンテキストに通さない」**という一線で使い分けています。この記事は、AIエージェントにCMSのメディアを運用させるときの、その設計の話です。

まず素直な道:MCPでBase64アップロード

TesseraのMCPサーバーには upload_asset というツールがあります。ファイル名・MIMEタイプ・Base64エンコードした中身を渡すと、アセットが作られる。エージェントから見れば、ツールを1回呼ぶだけ。いちばん手軽な経路です。

小さい画像や、テキスト/CSVのような軽いファイルなら、これで十分です。実際 upload_asset は image/*・PDF・mp4/webm・text/plain・text/csv を受け付けます。

問題は、サイズが大きくなったときです。

Base64は「LLMのコンテキストを通る」

upload_asset はBase64文字列を引数に取ります。つまり画像のバイト列が、エージェント(LLM)のコンテキストを一度通過するということです。

ここが厄介です。1MBの画像はBase64にすると約1.3MBの文字列になります。この文字列がツール呼び出しの引数としてLLMのコンテキストに載る。すると――

  • トークンを大量に消費する(画像1枚で数十万トークン規模になり得る)
  • コンテキストウィンドウを圧迫し、進行中の他の作業を押し出す
  • そもそも大きすぎてツール呼び出しが失敗する

アイキャッチ用のPNGは、平気で数百KB〜数MBになります。つまり「エージェントにアイキャッチを上げさせる」という、いちばんやりたい用途で、Base64経路はいちばん詰まりやすい。Tesseraの upload_asset約5MBを上限にしているのも、この経路の性質を踏まえた歯止めです。

だからもう1本、HTTPの投入口を用意した

そこでTesseraは、MCPとは別にHTTPのメディアAPIを持たせました。

POST https://tesseracms.com/api/assets

これは multipart/form-data でファイルをストリームとしてそのまま送るエンドポイントです。ポイントは、バイト列がLLMのコンテキストを一切通らないこと。エージェントは、手元のファイルを curl で直接投げればいい。

curl -X POST https://tesseracms.com/api/assets \
  -H "Authorization: Bearer $TESSERA_API_KEY" \
  -F "file=@cover.png" \
  -F "visibility=public"

返ってくるのは、作られたアセットの情報です。

{
  "asset": {
    "id": "109bdc45-…",
    "fileName": "cover.png",
    "mimeType": "image/png",
    "sizeBytes": 1015772,
    "visibility": "public",
    "url": "https://media.tesseracms.com/<workspaceId>/<assetId>.png",
    "createdAt": "…"
  }
}
  • 認証はAPIキー(read_write スコープ)。MCPと同じキーで、HTTPからも書き込める
  • 上限は20MB(Base64経路の約5MBより大きい)
  • visibility公開/非公開を選べる。公開なら永続の直リンク、非公開なら短命の署名付きURLが返る

エージェントは、この url(や id)を受け取って、記事のMarkdownに画像として差し込めます。バイト列は curl のプロセスからTesseraへ直接流れ、LLMは「このファイルを送れ」という指示と、返ってきたURLしか見ていない。 これが狙いです。

(種明かしをすると、この記事のアイキャッチも、同じ /api/assets 経由で入れています。ドッグフーディングです。)

「投入」だけでなく「発見」もエージェントに

メディア運用は、上げて終わりではありません。「さっき上げた画像のURLは?」「前に使ったあのロゴ、もう一度貼りたい」――**発見(discovery)**が要ります。

ここはMCPの list_assets が担います。ワークスペースのアセットを新しい順に一覧し、ファイル名で部分一致の絞り込みができ、各アセットの配信URL(公開なら公開URL、非公開なら署名付きURL)を返します。

つまりエージェントの中では、こういう運用が回ります。

  1. 画像を用意して curl/api/assets へ投入(コンテキストを通さない)
  2. 記事本文に貼る画像URLは list_assets で引いて差し込む
  3. 使い回したい既存アセットも list_assets で名前から探す

投入はコンテキストを迂回するHTTP、発見はMCP。役割で経路を分けています。

使い分けの整理

  • 小さい画像・テキスト/CSVを手軽に → MCP upload_asset(Base64)。ツール1回で済むが、中身がLLMのコンテキストを通る・約5MB上限
  • アイキャッチ等の大きい画像・動画 → HTTP POST /api/assets(multipart・curl)。バイト列がLLMのコンテキストを通らない・20MB・公開/非公開を選べる
  • アップ済みアセットを探す / URLを得る → MCP list_assets。新しい順・ファイル名で絞り込み・配信URLを返す

設計の考え方:何をLLMに通すか

AIにCMSを運用させるとき、地味に効いてくるのが「何をLLMのコンテキストに通すか」の線引きです。

スキーマやエントリのテキストは、LLMを通す価値があります。エージェントが構造を理解して提案・生成するための材料だからです。一方で、画像のバイト列をLLMに通す必要はありません。エージェントが判断に使うのは「どのファイルを・どこに・公開/非公開で置くか」であって、ピクセルの中身そのものではない。

だからTesseraは、判断に関わる軽いもの・発見はMCP(upload_asset の軽量ケース・list_assets)で、重いバイト列はコンテキストを迂回するHTTP導線/api/assets)に逃がしました。「AIがCMSを運用する」を現実的に回すには、この“通す/通さない”の設計がいる、というのが今回の話です。

まとめ

  • AIエージェントに画像を上げさせるとき、Base64経路(MCP upload_asset)はバイト列がLLMのコンテキストを通る。大きな画像ではトークン浪費・コンテキスト圧迫・失敗につながる(約5MB上限)
  • そこでHTTPのメディアAPI POST /api/assets(multipart・curl・APIキー read_write)を用意。バイト列はLLM文脈を通らず、20MB・公開/非公開に対応。返る url をそのまま本文に差せる
  • 発見はMCP list_assets:新しい順・ファイル名絞り込み・配信URLを返す。投入と発見で経路を分ける
  • 本質は「何をLLMに通すか」の線引き。テキスト(構造)は通し、画像のバイト列は迂回させる

エージェントにブログを書かせるなら、画像もエージェントに運用させたい。そのときに詰まらないための、地味だけれど効く導線の話でした。

無料で始めるドキュメントを見る

AIエージェントにCMSのメディアを運用させる ― 画像のバイト列はLLMに通さない | Tessera