LLMのモデル廃止に耐える~TypeScriptで型安全なモデルカタログを作成~
本文の状態
日本語全文を表示中
詳細モードで約9分の本文を読めます。
同じ出来事の情報源
6媒体で確認
CyberAgent Developers Blog · InfoQ · AWS Machine Learning Blog · TechCrunch AI · Simon Willison Blog · The Decoder
各社の報じ方を比較 ↓著者がLLMプロバイダー(Azure OpenAI/Vertex AI/Anthropic等)のモデル廃止に対応するため、TypeScriptで型安全なモデルカタログを構築した事例を紹介している。
Continue in AI NEW LAB
このニュースを、実務の判断につなげる
AI NEW LABで、試したことや先に確認したい条件を共有できます。まずはログインなしで読めます。
AI NEW LABで論点を見るSource Article
元記事を日本語で読む
本文に関係しない購読案内、埋め込み通知、サイト内プロモーションは除いています。
TypeScript tech はじめに
こんにちは、株式会社AI Shift の yoshi です。 私たちはエンタープライズ向けの AI エージェントプラットフォーム AI Worker Platform を開発しています。
2026年に入ってから、LLM のモデル廃止対応を行いました。DALL-E 3、Gemini 1.0 / 1.5 など...。
最初の DALL-E 3 の廃止対応では、「モデルの参照を差し替えるだけだし、すぐ終わるだろう」と思っていました。ところがいざ着手してみると、影響範囲が想定以上に広かったので、「次の廃止でも同じことをやるのか?」と考えると、これは設計で解決すべき問題でした。そこでモデルカタログ基盤を構築したので、その設計と運用について共有します。
この記事で話すこと / 話さないこと
LLM プロバイダー(Azure OpenAI / Vertex AI / Anthropic)のモデルを一元管理する内部パッケージの設計と実装
マルチテナント環境でテナントごとに異なる Azure デプロイ名を型安全に扱う方法
LLM の選定基準やモデルの性能比較
Vercel AI SDK / LangChain 自体の使い方チュートリアル
Azure OpenAI / Vertex AI のセットアップ手順やインフラ構築
設計方針 — 何を解決するパッケージにするか
モデルカタログを設計するにあたって、3つの問いを立てました。
Q1. モデルが廃止されたとき、1箇所の変更で全サービスに反映できるか?
これが最も重要な要件でした。DALL-E 3 の廃止対応では、バックエンドのロジック部分から、フロントエンドの UI コンポーネント、locale ファイルなど、あらゆる場所に変更が必要でした。「retired とマークしたら、あとは自動で fallback する」という運用が自分の理想でした。
Q2. テナントごとに異なる Azure デプロイ名をどう抽象化するか?
Azure OpenAI では、同じ gpt-4o
Q3. Vercel AI SDK との結合度をどう保つか?
私たちは Vercel AI SDK を使って LLM を呼び出していますが、モデルカタログ自体は AI SDK に依存すべきではないと考えました。理由はフロントエンドのモデル選択 UI でも同じカタログを参照したいためとAI SDK のバージョンアップにカタログが引きずられるのを避けたいためです。
この3つの問いを行ったり来たりしながら、最終的に4つの方針に落ち着きました。
as const satisfies
Fallback は replacementModelId
テナント設定は型パラメータで分離する
パッケージ設計 — model-catalog の全体像

パッケージの構造は以下のとおりです。
packages/model-catalog/ ├── src/ │ ├── types/ # Model, DeploymentConfig, FallbackResult 等の型定義 │ ├── catalog/ # プロバイダー別モデル定義 (azure.ts, vertex.ts, anthropic.ts) │ │ └── fallback.ts # Fallback ロジック │ ├── tenants/ # テナント別デプロイ設定 │ ├── utils/ # isReasoningModel, supportsParallelToolCalls 等 │ └── index.ts # Public API
モデルは ChatModel | ImageModel | EmbeddingModel
type ModelType = 'chat' | 'image' | 'embedding' interface BaseModel { id: string type: ModelType provider: ProviderType status: 'active' | 'deprecated' | 'retired' replacementModelId?: string } interface ChatModel extends BaseModel { type: 'chat' series: 'gpt' | 'claude' | 'gemini' | 'o' capabilities: { temperatureSupport: boolean parallelToolCalls: boolean vision: boolean } } type Model = ChatModel | ImageModel | EmbeddingModel
非推奨。新規選択は不可だが、既存の利用は継続できる。
廃止済み。replacementModelId
as const satisfies
モデル定義には TypeScript 4.9 で導入された satisfies
export const AZURE_CHAT_MODELS = [ { id: 'gpt-4o', type: 'chat', provider: 'azure', status: 'active', series: 'gpt', capabilities: { temperatureSupport: true, parallelToolCalls: true, vision: true, }, }, { id: 'gpt-35-turbo', type: 'chat', provider: 'azure', status: 'retired', replacementModelId: 'gpt-4o-mini', series: 'gpt', capabilities: { temperatureSupport: true, parallelToolCalls: false, vision: false, }, }, // ... ] as const satisfies readonly ChatModel[]
このパターンのメリットは2つあります。
'gpt-4o' | 'gpt-35-turbo'
satisfies ChatModel[]
つまり、リテラル型の精度と構造の型安全性を両立できます。as ChatModel[]
Fallback システム — 廃止モデルを自動で置き換える
やりたいことはシンプルです。 廃止されたモデル ID を渡しても、後継モデルが自動的に返ってくる。呼び出し側のコードを変える必要はありません。

gpt-35-turbo (retired) → gpt-4o-mini (active) ✅ 自動解決 gemini-1.5-flash-001 (retired) → gemini-2.0-flash-001 (active) ✅ 自動解決 o1-mini (retired) → o4-mini (active) ✅ 自動解決
呼び出し側の変更は最小限で、getChatModel()
getChatModelWithFallback()
// Before const model = getChatModel(modelId) if (!model) { throw new Error(Unknown model: ${modelId}) } // After const result = getChatModelWithFallback(modelId) if (!result.ok) { throw new Error(Model resolution failed: ${result.error.reason}) } if (result.fallbackApplied) { logger.warn(Model ${result.originalModelId} is retired, falling back to ${result.value.id}) } const model = result.value
「fallback 先のモデルは性能やコストが違うのでは?」という疑問はもっともです。 例えば gpt-35-turbo
status: 'retired'
replacementModelId
FallbackResult
type FallbackResult = | { ok: true; value: T; fallbackApplied: false } | { ok: true; value: T; fallbackApplied: true; originalModelId: string } | { ok: false; error: FallbackError }
// チェインを辿る関数例 function getChatModelWithFallback(modelId: string): FallbackResult { const model = getChatModel(modelId) if (!model) return { ok: false, error: new FallbackError('MODEL_NOT_FOUND') } if (model.status !== 'retired') return { ok: true, value: model, fallbackApplied: false } // Fallback チェインを辿る const visited = new Set() let current = model while (current.status === 'retired') { if (visited.has(current.id)) { return { ok: false, error: new FallbackError('CIRCULAR_REFERENCE') } } visited.add(current.id) if (visited.size > 10) { return { ok: false, error: new FallbackError('MAX_DEPTH_EXCEEDED') } } if (!current.replacementModelId) { return { ok: false, error: new FallbackError('NO_REPLACEMENT') } } const next = getChatModel(current.replacementModelId) if (!next) { return { ok: false, error: new FallbackError('REPLACEMENT_NOT_FOUND') } } current = next } return { ok: true, value: current, fallbackApplied: true, originalModelId: modelId } }
Fallback チェインの解決には、設定ミスを早期に検出するための安全装置を4つ設けています。
retired だが後継が未指定
REPLACEMENT_NOT_FOUND
後継の modelId がカタログに存在しない
CIRCULAR_REFERENCE
MAX_DEPTH_EXCEEDED
これらはランタイムのチェックですが、テストで担保しています。
describe('getChatModelWithFallback', () => { it('retired モデルを渡すと active な後継モデルが返る', () => { const result = getChatModelWithFallback('gpt-35-turbo') expect(result.ok).toBe(true) expect(result.value.id).toBe('gpt-4o-mini') expect(result.value.status).toBe('active') expect(result.fallbackApplied).toBeTruthy() expect(result.originalModelId).toBe('gpt-35-turbo') }) it('循環参照を検出してエラーを返す', () => { const result = getChatModelWithFallback('circular-model-a') expect(result.ok).toBeFalsy() expect(result.error.reason).toBe('CIRCULAR_REFERENCE') }) })
マルチテナント対応 — テナント別デプロイ名の型安全な管理
Azure OpenAI では、モデルをデプロイする際に任意の名前をつけられる可能性があります。そのため、同じ gpt-4o
Before の実装では、これが巨大な switch/case として各サービスにコピーされていました。
// Before: 各サービスに散在していた switch/case function getDeploymentName(tenantId: string, modelId: string): string { switch (tenantId) { case TENANT_A_ID: switch (modelId) { case 'gpt-4o': return 'acme-chat4o_s' case 'gpt-4o-mini': return 'acme-chat4omini_s' // ... 10行以上続く } case TENANT_B_ID: switch (modelId) { case 'gpt-4o': return 'corp-4o' // ... さらに続く } // ... テナントが増えるたびに膨張 } }
解決策: テナント別デプロイ設定
model-catalog では、テナントごとにデプロイ設定ファイルを用意し、型安全な lookup 関数を提供しています。
interface DeploymentConfig { modelId: string deploymentId: string }
// tenants/acme.ts export const ACME_CHAT_DEPLOYMENTS = [ { modelId: 'gpt-4o', deploymentId: 'acme-chat4o_s' }, { modelId: 'gpt-4o-mini', deploymentId: 'acme-chat4omini_s' }, { modelId: 'gpt-4', deploymentId: 'acme-chat4_s' }, ] as const satisfies readonly DeploymentConfig[] export function getAcmeDeployment(modelId: string) { return ACME_CHAT_DEPLOYMENTS.find(d => d.modelId === modelId) }
TypedAzureProvider による型制約
Vercel AI SDK の Azure Provider の chat()
type AcmeChatDeploymentId = typeof ACME_CHAT_DEPLOYMENTS[number]['deploymentId'] // => 'acme-chat4o_s' | 'acme-chat4omini_s' | 'acme-chat4_s' // LanguageModelV1, EmbeddingModelV1 は Vercel AI SDK が提供するモデルのインターフェース interface TypedAzureProvider { chat(deploymentId: ChatId): LanguageModelV1 embeddingModel(deploymentId: EmbeddingId): EmbeddingModelV1 }
存在しないデプロイ名を渡すとコンパイル時にエラーになります。
// OK provider.chat('acme-chat4o_s') // コンパイルエラー: 'acme-chat4o_typo' は型に含まれない provider.chat('acme-chat4o_typo')
// Before: マジックナンバーとswitch文の塊 if (tenantId === TENANT_A_ID) { switch (modelName) { case 'gpt-4o': return azure.chat('acme-chat4o_s') case 'gpt-4o-mini': return azure.chat('acme-chat4omini_s') // ... } } // After: カタログ lookup の1行 const deployment = getAcmeDeployment(modelId) if (deployment) return azure.chat(deployment.deploymentId)
運用して分かったこと — 学びと失敗

廃止対応の工数: 約30ファイル → 2ファイル
DALL-E 3 の廃止対応は約30ファイル・丸1日以上かかりました。カタログ導入後の o1-mini
replacementModelId: 'o4-mini'
Frontend / Backend のモデル定義のズレ解消
モデル選択 UI のフィルタリング(deprecated モデルを非表示にする等)にも同じカタログを使えるので、「Backend では廃止済みなのに Frontend のドロップダウンにまだ表示されている」というズレがなくなりました。(マイグレーションしろ、という意見はごもっともです...)
provider.chat('gpt-4o-mni')
catalog/azure.ts
現在はテナントごとに TypeScript ファイルを用意していますが、テナント数が増えるとコード量が膨張します。将来的にはテナント設定を DB または環境変数から読み込む外部化が必要になるでしょう。ただ、現時点ではテナント数が限定的なため、型安全性のメリットのほうが大きいと判断しています。
LLM プロバイダーのモデル廃止は、AI プラットフォームを運用する上で避けて通れません。2026年だけでも4回の廃止対応を経験し、手作業では追いつかなくなっていました。
@pkg/model-catalog
モデルの定義、ステータス、fallback 設定を1箇所に集約
replacementModelId
as const satisfies
TypedAzureProvider
正直に言えば、この model-catalog が完璧だとは思っていません。テナント設定の外部化も積み残しています。次の廃止ラッシュが来たとき「余裕だった」と言えるかはわかりません。
ただ、モデルが変わってもコードは変えなくていいですし、その状態に一歩近づけたことは確かで、そのぶん本来やるべきプロダクト開発に集中できるようになりました。
同じ苦労をしている方の参考になれば幸いです。
AI Shiftではエンジニアの採用に力を入れています。少しでも興味を持っていただけましたら、カジュアル面談でお話しませんか(オンライン・19時以降の面談も可能です)。
【面談フォームはこちら】 https://hrmos.co/pages/cyberagent-group/jobs/1826557091831955459
AI Shift Tech BlogPublication株式会社AI Shift(サイバーエージェント100%子会社)開発チームのTech Blogです。

原文を表示
TypeScript tech はじめに
こんにちは、株式会社AI Shift の yoshi です。 私たちはエンタープライズ向けの AI エージェントプラットフォーム AI Worker Platform を開発しています。
2026年に入ってから、LLM のモデル廃止対応を行いました。DALL-E 3、Gemini 1.0 / 1.5 など...。
最初の DALL-E 3 の廃止対応では、「モデルの参照を差し替えるだけだし、すぐ終わるだろう」と思っていました。ところがいざ着手してみると、影響範囲が想定以上に広かったので、「次の廃止でも同じことをやるのか?」と考えると、これは設計で解決すべき問題でした。そこでモデルカタログ基盤を構築したので、その設計と運用について共有します。
この記事で話すこと / 話さないこと
LLM プロバイダー(Azure OpenAI / Vertex AI / Anthropic)のモデルを一元管理する内部パッケージの設計と実装
マルチテナント環境でテナントごとに異なる Azure デプロイ名を型安全に扱う方法
LLM の選定基準やモデルの性能比較
Vercel AI SDK / LangChain 自体の使い方チュートリアル
Azure OpenAI / Vertex AI のセットアップ手順やインフラ構築
設計方針 — 何を解決するパッケージにするか
モデルカタログを設計するにあたって、3つの問いを立てました。
Q1. モデルが廃止されたとき、1箇所の変更で全サービスに反映できるか?
これが最も重要な要件でした。DALL-E 3 の廃止対応では、バックエンドのロジック部分から、フロントエンドの UI コンポーネント、locale ファイルなど、あらゆる場所に変更が必要でした。「retired とマークしたら、あとは自動で fallback する」という運用が自分の理想でした。
Q2. テナントごとに異なる Azure デプロイ名をどう抽象化するか?
Azure OpenAI では、同じ gpt-4o
Q3. Vercel AI SDK との結合度をどう保つか?
私たちは Vercel AI SDK を使って LLM を呼び出していますが、モデルカタログ自体は AI SDK に依存すべきではないと考えました。理由はフロントエンドのモデル選択 UI でも同じカタログを参照したいためとAI SDK のバージョンアップにカタログが引きずられるのを避けたいためです。
この3つの問いを行ったり来たりしながら、最終的に4つの方針に落ち着きました。
as const satisfies
Fallback は replacementModelId
テナント設定は型パラメータで分離する
パッケージ設計 — model-catalog の全体像

パッケージの構造は以下のとおりです。
packages/model-catalog/ ├── src/ │ ├── types/ # Model, DeploymentConfig, FallbackResult 等の型定義 │ ├── catalog/ # プロバイダー別モデル定義 (azure.ts, vertex.ts, anthropic.ts) │ │ └── fallback.ts # Fallback ロジック │ ├── tenants/ # テナント別デプロイ設定 │ ├── utils/ # isReasoningModel, supportsParallelToolCalls 等 │ └── index.ts # Public API
モデルは ChatModel | ImageModel | EmbeddingModel
type ModelType = 'chat' | 'image' | 'embedding' interface BaseModel { id: string type: ModelType provider: ProviderType status: 'active' | 'deprecated' | 'retired' replacementModelId?: string } interface ChatModel extends BaseModel { type: 'chat' series: 'gpt' | 'claude' | 'gemini' | 'o' capabilities: { temperatureSupport: boolean parallelToolCalls: boolean vision: boolean } } type Model = ChatModel | ImageModel | EmbeddingModel
非推奨。新規選択は不可だが、既存の利用は継続できる。
廃止済み。replacementModelId
as const satisfies
モデル定義には TypeScript 4.9 で導入された satisfies
export const AZURE_CHAT_MODELS = [ { id: 'gpt-4o', type: 'chat', provider: 'azure', status: 'active', series: 'gpt', capabilities: { temperatureSupport: true, parallelToolCalls: true, vision: true, }, }, { id: 'gpt-35-turbo', type: 'chat', provider: 'azure', status: 'retired', replacementModelId: 'gpt-4o-mini', series: 'gpt', capabilities: { temperatureSupport: true, parallelToolCalls: false, vision: false, }, }, // ... ] as const satisfies readonly ChatModel[]
このパターンのメリットは2つあります。
'gpt-4o' | 'gpt-35-turbo'
satisfies ChatModel[]
つまり、リテラル型の精度と構造の型安全性を両立できます。as ChatModel[]
Fallback システム — 廃止モデルを自動で置き換える
やりたいことはシンプルです。 廃止されたモデル ID を渡しても、後継モデルが自動的に返ってくる。呼び出し側のコードを変える必要はありません。

gpt-35-turbo (retired) → gpt-4o-mini (active) ✅ 自動解決 gemini-1.5-flash-001 (retired) → gemini-2.0-flash-001 (active) ✅ 自動解決 o1-mini (retired) → o4-mini (active) ✅ 自動解決
呼び出し側の変更は最小限で、getChatModel()
getChatModelWithFallback()
// Before const model = getChatModel(modelId) if (!model) { throw new Error(Unknown model: ${modelId}) } // After const result = getChatModelWithFallback(modelId) if (!result.ok) { throw new Error(Model resolution failed: ${result.error.reason}) } if (result.fallbackApplied) { logger.warn(Model ${result.originalModelId} is retired, falling back to ${result.value.id}) } const model = result.value
「fallback 先のモデルは性能やコストが違うのでは?」という疑問はもっともです。 例えば gpt-35-turbo
status: 'retired'
replacementModelId
FallbackResult<T>
type FallbackResult<T> = | { ok: true; value: T; fallbackApplied: false } | { ok: true; value: T; fallbackApplied: true; originalModelId: string } | { ok: false; error: FallbackError }
// チェインを辿る関数例 function getChatModelWithFallback(modelId: string): FallbackResult<ChatModel> { const model = getChatModel(modelId) if (!model) return { ok: false, error: new FallbackError('MODEL_NOT_FOUND') } if (model.status !== 'retired') return { ok: true, value: model, fallbackApplied: false } // Fallback チェインを辿る const visited = new Set<string>() let current = model while (current.status === 'retired') { if (visited.has(current.id)) { return { ok: false, error: new FallbackError('CIRCULAR_REFERENCE') } } visited.add(current.id) if (visited.size > 10) { return { ok: false, error: new FallbackError('MAX_DEPTH_EXCEEDED') } } if (!current.replacementModelId) { return { ok: false, error: new FallbackError('NO_REPLACEMENT') } } const next = getChatModel(current.replacementModelId) if (!next) { return { ok: false, error: new FallbackError('REPLACEMENT_NOT_FOUND') } } current = next } return { ok: true, value: current, fallbackApplied: true, originalModelId: modelId } }
Fallback チェインの解決には、設定ミスを早期に検出するための安全装置を4つ設けています。
retired だが後継が未指定
REPLACEMENT_NOT_FOUND
後継の modelId がカタログに存在しない
CIRCULAR_REFERENCE
MAX_DEPTH_EXCEEDED
これらはランタイムのチェックですが、テストで担保しています。
describe('getChatModelWithFallback', () => { it('retired モデルを渡すと active な後継モデルが返る', () => { const result = getChatModelWithFallback('gpt-35-turbo') expect(result.ok).toBe(true) expect(result.value.id).toBe('gpt-4o-mini') expect(result.value.status).toBe('active') expect(result.fallbackApplied).toBeTruthy() expect(result.originalModelId).toBe('gpt-35-turbo') }) it('循環参照を検出してエラーを返す', () => { const result = getChatModelWithFallback('circular-model-a') expect(result.ok).toBeFalsy() expect(result.error.reason).toBe('CIRCULAR_REFERENCE') }) })
マルチテナント対応 — テナント別デプロイ名の型安全な管理
Azure OpenAI では、モデルをデプロイする際に任意の名前をつけられる可能性があります。そのため、同じ gpt-4o
Before の実装では、これが巨大な switch/case として各サービスにコピーされていました。
// Before: 各サービスに散在していた switch/case function getDeploymentName(tenantId: string, modelId: string): string { switch (tenantId) { case TENANT_A_ID: switch (modelId) { case 'gpt-4o': return 'acme-chat4o_s' case 'gpt-4o-mini': return 'acme-chat4omini_s' // ... 10行以上続く } case TENANT_B_ID: switch (modelId) { case 'gpt-4o': return 'corp-4o' // ... さらに続く } // ... テナントが増えるたびに膨張 } }
解決策: テナント別デプロイ設定
model-catalog では、テナントごとにデプロイ設定ファイルを用意し、型安全な lookup 関数を提供しています。
interface DeploymentConfig { modelId: string deploymentId: string }
// tenants/acme.ts export const ACME_CHAT_DEPLOYMENTS = [ { modelId: 'gpt-4o', deploymentId: 'acme-chat4o_s' }, { modelId: 'gpt-4o-mini', deploymentId: 'acme-chat4omini_s' }, { modelId: 'gpt-4', deploymentId: 'acme-chat4_s' }, ] as const satisfies readonly DeploymentConfig[] export function getAcmeDeployment(modelId: string) { return ACME_CHAT_DEPLOYMENTS.find(d => d.modelId === modelId) }
TypedAzureProvider による型制約
Vercel AI SDK の Azure Provider の chat()
type AcmeChatDeploymentId = typeof ACME_CHAT_DEPLOYMENTS[number]['deploymentId'] // => 'acme-chat4o_s' | 'acme-chat4omini_s' | 'acme-chat4_s' // LanguageModelV1, EmbeddingModelV1 は Vercel AI SDK が提供するモデルのインターフェース interface TypedAzureProvider<ChatId extends string, EmbeddingId extends string> { chat(deploymentId: ChatId): LanguageModelV1 embeddingModel(deploymentId: EmbeddingId): EmbeddingModelV1<string> }
存在しないデプロイ名を渡すとコンパイル時にエラーになります。
// OK provider.chat('acme-chat4o_s') // コンパイルエラー: 'acme-chat4o_typo' は型に含まれない provider.chat('acme-chat4o_typo')
// Before: マジックナンバーとswitch文の塊 if (tenantId === TENANT_A_ID) { switch (modelName) { case 'gpt-4o': return azure.chat('acme-chat4o_s') case 'gpt-4o-mini': return azure.chat('acme-chat4omini_s') // ... } } // After: カタログ lookup の1行 const deployment = getAcmeDeployment(modelId) if (deployment) return azure.chat(deployment.deploymentId)
運用して分かったこと — 学びと失敗

廃止対応の工数: 約30ファイル → 2ファイル
DALL-E 3 の廃止対応は約30ファイル・丸1日以上かかりました。カタログ導入後の o1-mini
replacementModelId: 'o4-mini'
Frontend / Backend のモデル定義のズレ解消
モデル選択 UI のフィルタリング(deprecated モデルを非表示にする等)にも同じカタログを使えるので、「Backend では廃止済みなのに Frontend のドロップダウンにまだ表示されている」というズレがなくなりました。(マイグレーションしろ、という意見はごもっともです...)
provider.chat('gpt-4o-mni')
catalog/azure.ts
現在はテナントごとに TypeScript ファイルを用意していますが、テナント数が増えるとコード量が膨張します。将来的にはテナント設定を DB または環境変数から読み込む外部化が必要になるでしょう。ただ、現時点ではテナント数が限定的なため、型安全性のメリットのほうが大きいと判断しています。
LLM プロバイダーのモデル廃止は、AI プラットフォームを運用する上で避けて通れません。2026年だけでも4回の廃止対応を経験し、手作業では追いつかなくなっていました。
@pkg/model-catalog
モデルの定義、ステータス、fallback 設定を1箇所に集約
replacementModelId
as const satisfies
TypedAzureProvider
正直に言えば、この model-catalog が完璧だとは思っていません。テナント設定の外部化も積み残しています。次の廃止ラッシュが来たとき「余裕だった」と言えるかはわかりません。
ただ、モデルが変わってもコードは変えなくていいですし、その状態に一歩近づけたことは確かで、そのぶん本来やるべきプロダクト開発に集中できるようになりました。
同じ苦労をしている方の参考になれば幸いです。
AI Shiftではエンジニアの採用に力を入れています。少しでも興味を持っていただけましたら、カジュアル面談でお話しませんか(オンライン・19時以降の面談も可能です)。
【面談フォームはこちら】 https://hrmos.co/pages/cyberagent-group/jobs/1826557091831955459
AI Shift Tech BlogPublication株式会社AI Shift(サイバーエージェント100%子会社)開発チームのTech Blogです。

同じ出来事を6媒体で確認
同じ出来事を扱う別媒体の記事です。見出しと公開時刻を比較できます。
今日のまとめ
AIデイリーブリーフで今日の重要ニュースをまとめ読み