catch-img

GPT-5.3-Codex と Claude Opus 4.6 を比較する

同一プロンプトから見えた設計提案の違い

株式会社divxでエンジニアをしている清水です。

2026年2月5日、OpenAI と Anthropic がそれぞれフラッグシップモデルを発表しました。OpenAI の GPT-5.3-Codex、Anthropic の Claude Opus 4.6 です。

本記事では、両モデルに同一の設計改善プロンプトを入力し、その提案内容の違いを整理します。スペック比較ではなく、「設計改善をどう組み立てるか」という観点での比較です。

なお、この記事はベンチマークではなく、1つの実務的な題材に対する観察記録です。モデルの一般論を断定するものではなく、今回の観察範囲でどういう整理が出たかに焦点を当てます。

比較に使った題材

今回の比較では、実際のプルリクエストで出た TODO を題材にしました。

問題になっていたのは、Viewer コンポーネントが現在の route を直接参照し、特定の画面だけ別 API を使うように分岐していたことです。加えて、route パラメータから ID を取り出して、取引先向けのトークン取得 URL まで組み立てていました。

つまり、表示用コンポーネントが次の3つを同時に知っている状態でした。

  • どの画面文脈で使われているか
  • ルーティング構造がどうなっているか
  • どの API を呼ぶべきか

コード上の TODO も、この責務を呼び出し側へ寄せたい、という趣旨でした。

既存コードのイメージ

実コードは業務固有の命名を含むため、ここでは論点が伝わる粒度にぼかして載せます。

export default function Viewer(props: Props): JSX.Element {
  const router = useRouter();

  const isPartnerContext =
    router.pathname === '/partner-portal/.../related-docs/[docId]';

  const resourceId =
    isPartnerContext && typeof router.query.id === 'string'
      ? router.query.id
      : undefined;

  let tokenUrl: string | null;

  if (isPartnerContext) {
    tokenUrl =
      resourceId === undefined
        ? null
        : `${buildPartnerTokenPath(resourceId)}?fileId=${props.fileId}`;
  } else {
    tokenUrl = `/default_token?fileId=${props.fileId}`;
  }

  // tokenUrl を使って viewer 用の token を取得
}

この構造だと、Viewer が route 文字列、query パラメータ、API 分岐を抱え込むため、URL 変更や利用画面の追加に弱くなります。

比較に使ったプロンプト

両モデルには、次のような依頼をそのまま渡しました。

現在、Viewerコンポーネント内で router.pathname を直接参照し、URLに /partner が含まれているかどうかで API エンドポイントを分岐しています。
TODO:
pathnameベタ書きでの判定は管理が難しいため、将来的には呼び出し側からscopeを渡す設計に寄せたい。
この TODO を解消するための設計改善プランを作成してください。
実装コードは書かず、まず設計方針を整理してください。

余計な条件は付けず、設計プランの組み立て方を見ました。

実際の出力例

ここでは各モデルの応答を、業務固有情報を落としつつ要約して載せます。公式情報ではなく、今回の比較で得た観察ログです。

GPT-5.3-Codex

GPT-5.3-Codex は、Viewer に scopeと必要な ID を props で渡す方向を中心に整理していました。

  • pathnameへの依存を削除する
  • Viewer 側では scopeを見て token URL を組み立てる
  • 呼び出し側の一部ルートだけ partnerPortal用の値を渡す
  • 既存の顧客側はデフォルト扱いにして破壊的変更を避ける
  • 検証項目として、顧客側と取引先側でそれぞれ正しい API が呼ばれることを挙げる

要するに、「今ある構造をあまり崩さずに、route 依存だけ外す」プランです。

この方向性をコードのイメージにすると、次のようになります。

type ViewerProps = {
  fileId: string;
  encodedUrn: string;
  apiScope?: 'default' | 'partner';
  partnerResourceId?: string;
};

export default function Viewer(props: ViewerProps): JSX.Element {
  const tokenUrl =
    props.apiScope === 'partner' && props.partnerResourceId
      ? `${buildPartnerTokenPath(props.partnerResourceId)}?fileId=${props.fileId}`
      : `/default_token?fileId=${props.fileId}`;

  const { data } = useDataFetch(tokenUrl);

  return <ViewerBody token={data} urn={props.encodedUrn} />;
}

呼び出し側では、画面文脈だけを渡します。

function DocumentPage(props: PageProps): JSX.Element {
  return (
    <DocViewer
      fileId={props.fileId}
      encodedUrn={props.encodedUrn}
      apiScope={props.partnerResourceId ? 'partner' : 'default'}
      partnerResourceId={props.partnerResourceId}
    />
  );
}

Claude Opus 4.6

Claude Opus 4.6 は、まず問題点を言語化したうえで、複数案を比較していました。

  • route のハードコードが脆いこと
  • Viewer が routing を知っているのは関心の分離として不自然なこと
  • query パラメータ名への依存も暗黙的であること

そのうえで、次のような案を並べていました。

  • token URL 自体を呼び出し側から渡す案
  • scopeを props で渡す案
  • Context で画面文脈を渡す案

最終的には、既存コードベースに似たパターンがあることを根拠に、token URL を呼び出し側から渡す案を推していました。

要するに、「どこに責務を置くべきか」を先に整理し、その後で実装方針を選ぶプランです。

この方向性をコードのイメージにすると、次のようになります。

type ViewerProps = {
  fileId: string;
  encodedUrn: string;
  tokenUrl: string | null;
};

export default function Viewer(props: ViewerProps): JSX.Element {
  const { data } = useDataFetch(props.tokenUrl);

  return <ViewerBody token={data} urn={props.encodedUrn} />;
}

呼び出し側では、画面文脈に応じて URL を組み立てます。

function DocumentPage(props: PageProps): JSX.Element {
  const tokenUrl = props.partnerResourceId
    ? `${buildPartnerTokenPath(props.partnerResourceId)}?fileId=${props.fileId}`
    : `/default_token?fileId=${props.fileId}`;

  return (
    <DocViewer
      fileId={props.fileId}
      encodedUrn={props.encodedUrn}
      tokenUrl={tokenUrl}
    />
  );
}

出力の違いをどう見るか

両者とも TODO の方向性自体は妥当でしたが、整理のレイヤーが違っていました。

  • GPT-5.3-Codex は、移行ステップと影響範囲の整理に寄っていた
  • Claude Opus 4.6 は、責務配置の再設計と選択肢比較に寄っていた

この違いは、単なる文章スタイルの差というより、「問題をどのレイヤーから解くか」の差に見えます。

実務上の評価

一般的な React 設計では、今回のような分岐は呼び出し側で決めるほうが保守しやすいことが多いです。責務分離が明確になり、分岐条件が増えたときも Viewer 自体が肥大化しにくく、テスト観点も持ちやすくなります。

一方で、URL 生成をコンポーネント内部の実装詳細とみなす設計なら、Viewer 側で scopeを理解する案にも一定の合理性はあります。どちらが正解かではなく、どの層に画面文脈を持たせるかの判断です。

実務での使い分けとしては、既存構造を崩さず安全に直したいなら GPT-5.3-Codex 的な整理が扱いやすく、責務の置き場から再検討したいなら Claude Opus 4.6 的な整理が有効だと感じました。

プロンプトによる明示でそれぞれの差は埋まるのか

ここで気になったのは、今回見えた差がモデル固有のものなのか、それともプロンプトで比較観点や出力順序を明示すればかなり収束するのか、という点です。

最初の比較では、あえて余計な条件を足さず、設計改善プランをどう組み立てるかを見ていました。ただ、この条件だと各モデルがどのレイヤーから問題を切るかに自由度があり、出力の差が
出やすくなります。

そこで次に、比較観点と出力順序を追加で指定し、同じ題材に対して再度出力を比較しました。狙いは、プロンプトによって「検討の枠組み」をどこまで揃えられるかを見ることです。

実際に使用したプロンプト

最初に使ったプロンプトに、以下の条件を追加しました。

現在、Viewerコンポーネント内で router.pathname を直接参照し、URLに /partner が含まれているかどうかで API エンドポイントを分岐しています。
TODO:
pathnameベタ書きでの判定は管理が難しいため、将来的には呼び出し側から scope を渡す設計に寄せたい。
この TODO を解消するための設計改善プランを作成してください。
実装コードは書かず、まず設計方針を整理してください。

追加条件:
以下の順序と観点を必ず守ってください。

  1. 現状の問題点を整理する
  2. Viewer が現在持っている責務を分解する
  3. 設計案を最低3案提示する
  4. 各案を以下の観点で比較する
    • 責務分離
    • Viewer の再利用性
    • 呼び出し側への影響
    • 破壊的変更の大きさ
    • テスト容易性
    • 将来の画面追加への強さ
  5. 推奨案を1つ選び、その理由を説明する
  6. 既存コードからの移行ステップを示す
  7. 想定されるリスクと確認事項を示す

制約:

  • 実装コードは書かない
  • route 判定、ID 解決、token URL 組み立て、token 取得の4つを分けて考える
  • scope を渡す案以外の代替案も必ず含める
  • 最後に、推奨案が既存構造をどこまで崩すかを明記する

今回の題材となる既存コードのイメージは、次のようなものでした。

  export default function Viewer(props: Props): JSX.Element {
    const router = useRouter();

    const isPartnerContext =
      router.pathname === '/partner-portal/.../related-docs/[docId]';

    const resourceId =
      isPartnerContext && typeof router.query.id === 'string'
        ? router.query.id
        : undefined;

    let tokenUrl: string | null;

    if (isPartnerContext) {
      tokenUrl =
        resourceId === undefined
          ? null
          : `${buildPartnerTokenPath(resourceId)}?fileId=${props.fileId}`;
    } else {
      tokenUrl = `/default_token?fileId=${props.fileId}`;
    }

    // tokenUrl を使って viewer 用の token を取得
  }

業務固有の命名はぼかしていますが、論点は変えていません。
Viewer が route 判定、ID 解決、API の切り替え、token 取得を同時に抱えている状態です。

実際の出力例

追加条件を与えると、両モデルの出力の枠組みはかなり近づきました。どちらも次のような流れで整理するようになります。

  • 現状の問題点を列挙する
  • Viewer が抱えている責務を分解する
  • 複数案を提示する
  • 比較表でメリット・デメリットを並べる
  • 推奨案を選び、移行ステップとリスクを書く

この意味では、プロンプトによって差はかなり埋まりました。少なくとも「何を出すか」「どの順番で出すか」という出力の骨格は、かなり揃えられます。

ただし、コードのイメージに落とすと、なお差は残りました。

GPT-5.3-Codex の提案をコードの方向性として要約すると、Viewer をできるだけ表示専用に寄せて、ルーティング依存や token 取得は呼び出し側の Container へ外出しする形になります。

  type ViewerProps = {
    resourceUrn: string;
    accessToken?: string;
  };

  export default function Viewer(props: ViewerProps): JSX.Element {
    if (props.accessToken === undefined) {
      return <Loading />;
    }

    return <ViewerBody urn={props.resourceUrn} token={props.accessToken} />;
  }

  export function ViewerContainer(props: ContainerProps): JSX.Element {
    const router = useRouter();

    const isPartnerContext =
      router.pathname === '/partner-portal/.../related-docs/[docId]';

    const resourceId =
      isPartnerContext && typeof router.query.id === 'string'
        ? router.query.id
        : undefined;

    const tokenUrl = isPartnerContext
      ? resourceId === undefined
        ? null
        : `${buildPartnerTokenPath(resourceId)}?fileId=${props.fileId}`
      : `/default_token?fileId=${props.fileId}`;

    const { data: accessToken } = useDataFetch(tokenUrl);

    return (
      <Viewer
        resourceUrn={props.resourceUrn}
        accessToken={accessToken}
      />
    );
  }

一方、Claude Opus 4.6 の提案をコードの方向性として要約すると、route 判定は呼び出し側へ寄せつつ、token 取得までは Viewer 内に残す形になります。

  const DEFAULT_TOKEN_PATH = '/default_token';

  type ViewerProps = {
    fileId: number;
    resourceUrn: string;
    tokenPath?: string;
  };

  export default function Viewer(props: ViewerProps): JSX.Element {
    const tokenUrl = `${props.tokenPath ?? DEFAULT_TOKEN_PATH}?fileId=${props.fileId}`;
    const { data: accessToken } = useDataFetch(tokenUrl);

    if (accessToken === undefined) {
      return <Loading />;
    }

    return <ViewerBody urn={props.resourceUrn} token={accessToken} />;
  }

  function DetailPage(): JSX.Element {
    const router = useRouter();

    const resourceId =
      typeof router.query.id === 'string' ? router.query.id : undefined;

    const tokenPath =
      resourceId === undefined ? undefined : buildPartnerTokenPath(resourceId);

    return (
      <Viewer
        fileId={123}
        resourceUrn="xxx"
        tokenPath={tokenPath}
      />
    );
  }

どちらも元コードの router.pathname 依存を Viewer から外す方向ですが、どこまで外へ出すかが違っていました。

出力の違いをどう見るのか

ここが面白いところで、プロンプトによって揃いやすかったのは「比較の枠組み」でした。

  • 問題点を先に書く
  • 責務分解をする
  • 複数案を並べる
  • 比較表を作る
  • 推奨案と移行ステップを書く

このレベルでは、かなり差を小さくできます。

一方で、それでも残ったのは「何を設計上の本質とみなすか」という差でした。

  • GPT-5.3-Codex は、token 取得まで外へ出して Viewer をより純粋な表示コンポーネントに寄せる方向を推した
  • Claude Opus 4.6 は、既存構造との整合性を保ちながら、まずは route 依存を外す方向を推した

つまり、プロンプトで埋めやすいのは「検討の手順」や「比較観点」であり、それでも残りやすいのは「どこまで理想構造を優先するか」「どこまで既存コードベースに寄せるか」という設計
判断の重心だった、というのが今回の観察でした。

公式発表から読み取れる方向性

OpenAI の日本語公式ページでは、GPT-5.3-Codex は次のように説明されています。

「GPT-5.3-Codex は、これまでで最も高性能なエージェント型コーディングモデルです。Codex の対応範囲をコンピューター上のあらゆる業務へと広げます。」
出典: https://openai.com/ja-JP/index/introducing-gpt-5-3-codex/

Anthropic のモデル紹介ページでは、Claude Opus 4.6 は次のように紹介されています。

"Claude Opus 4.6 — Our most intelligent model for building agents and coding."
出典: https://platform.claude.com/docs/en/about-claude/models/whats-new-claude-4-6

公式発表だけで今回の差を説明できるわけではありませんが、前者は開発作業へ踏み込むエージェント性、後者は広い推論能力とエージェント構築を前面に出しており、今回観察した出力傾向ともある程度は整合していました。

まとめ

同一の設計改善プロンプトに対して、今回の観察範囲では次の違いが見られました。

  • GPT-5.3-Codex は、実装レイヤーでの移行手順と影響範囲の整理に寄る
  • Claude Opus 4.6 は、構造レイヤーでの責務分離と選択肢比較に寄る

生成AIを実務に組み込むときは、提案内容そのものだけでなく、「どのレイヤーで整理されているか」を見ることが重要です。

GPT-5.4 に関しては、また別の記事で触れる予定です。