Claude Opus 5 is now live on CometAPI โ†’

Cara membina aplikasi AI yang tidak terikat kepada satu penyedia

CometAPI
AnnaJun 7, 2026
Cara membina aplikasi AI yang tidak terikat kepada satu penyedia

Penguncian vendor dalam aplikasi AI biasanya tidak berlaku serta-merta. Ia meresap sedikit demi sedikit โ€” satu import openai terus di sini, satu nama model dikeraskan (hardcoded) di sana, satu medan respons yang anda huraikan tanpa menyemak sama ada pembekal lain memulangkan perkara yang sama. Enam bulan kemudian, menukar pembekal bermaksud menulis semula separuh backend anda.

Empat cara penguncian berlaku

Kebanyakan pembangun berfikir penguncian bermaksud โ€œSaya menggunakan OpenAI SDK.โ€ Itu jenis yang paling kurang berisiko. Perangkap sebenar lebih halus:

Jenis penguncianBagaimana ia berlakuAkibat
SDK lock-infrom openai import OpenAI di seluruh kodMenukar SDK bermaksud menyentuh setiap fail
Model name lock-inmodel="gpt-4o" dikeraskan dalam logik perniagaanSetiap pertukaran model memerlukan perubahan kod
Parameter lock-inMenggunakan logprobs, n>1, atau reasoning_effortIni tidak wujud pada Claude atau Gemini
Response format lock-inMenghurai medan respons khusus pembekalPembekal berbeza memulangkan bentuk yang berbeza

Matlamatnya bukan untuk menghapuskan semuanya โ€” sesetengahnya ialah pertukaran yang boleh diterima. Matlamatnya ialah mengetahui yang mana satu anda sanggupi ambil.

Guna endpoint serasi OpenAI sebagai lapisan abstraksi anda

Cara paling bersih untuk mengelakkan penguncian SDK ialah menggunakan satu endpoint serasi OpenAI yang merutekan ke berbilang pembekal. Anda kekalkan OpenAI SDK, tetapi backend boleh daripada mana-mana pembekal.

CometAPI melakukannya โ€” satu endpoint, satu kunci, 500+ model merentasi OpenAI, Anthropic, Google, DeepSeek, xAI dan lain-lain:

import osfrom openai import OpenAIfrom dotenv import load_dotenvโ€‹load_dotenv()โ€‹api_key = os.environ.get("AI_API_KEY")if not api_key: ย  ย raise ValueError("AI_API_KEY environment variable is not set")โ€‹client = OpenAI( ย  ย base_url=os.environ.get("AI_BASE_URL", "https://api.cometapi.com/v1"), ย  ย api_key=api_key,)

Menukar daripada GPT ke Claude ke Gemini hanyalah perubahan satu baris:

# Beforeresponse = client.chat.completions.create(model="gpt-5.4", messages=[...])โ€‹# After โ€” same code, different modelresponse = client.chat.completions.create(model="claude-sonnet-4-6", messages=[...])

Nota: Nama model seperti gpt-5.4 dan claude-sonnet-4-6 ialah pengecam platform CometAPI โ€” ia berfungsi melalui https://api.cometapi.com/v1 sahaja, bukan terus melalui API OpenAI atau Anthropic. Lihat senarai model penuh untuk katalog dan harga lengkap.

Jauhkan nama model daripada logik perniagaan anda

Nama model yang bertaburan dalam kod anda ialah bentuk penguncian yang paling biasa. Penyelesaiannya ialah konfigurasi pusat yang membaca daripada pemboleh ubah persekitaran:

# config.py โ€” one place to change model assignmentsimport osโ€‹MODEL_CONFIG = { ย  ย "summarize": os.environ.get("MODEL_SUMMARIZE", "claude-opus-4-7"), ย  ย "code": ย  ย  ย os.environ.get("MODEL_CODE", ย  ย  ย "gpt-5.4"), ย  ย "classify": ย os.environ.get("MODEL_CLASSIFY", ย "claude-haiku-4-5"), ย  ย "chat": ย  ย  ย os.environ.get("MODEL_CHAT", ย  ย  ย  "gpt-5.4-mini"),}โ€‹# Validate at startup โ€” fail fast rather than getting mysterious API errorsfor task, model in MODEL_CONFIG.items(): ย  ย if not model: ย  ย  ย  ย raise ValueError(f"Model config for '{task}' is not set")

Logik perniagaan anda tidak pernah merujuk nama model secara langsung:

from config import MODEL_CONFIGโ€‹def summarize(text: str) -> str: ย  ย response = client.chat.completions.create( ย  ย  ย  ย model=MODEL_CONFIG["summarize"], ย  ย  ย  ย messages=[{"role": "user", "content": f"Summarize: {text}"}], ย  ย  ย  ย max_tokens=300 ย # move to config in production ย   ) ย  ย return response.choices[0].message.content

Untuk menukar model pemeringkasan di seluruh aplikasi anda, ubah satu pemboleh ubah persekitaran. Tiada grep, tiada cari-dan-ganti.

Bungkus respons supaya kod anda tidak bergantung pada medan khusus pembekal

Pembekal berbeza memulangkan bentuk respons yang sedikit berbeza. Jika anda menghurai respons API mentah di seluruh pangkalan kod anda, anda terkunci kepada format pembekal tersebut.

Bungkuskannya ke dalam dataclass yang dinormalkan:

from dataclasses import dataclassfrom typing import Optionalfrom openai import OpenAI, APIStatusError, APIConnectionError, APITimeoutErrorfrom openai.types.chat import ChatCompletionimport loggingโ€‹@dataclassclass AIResponse: ย  ย content: str ย  ย model: str ย  ย input_tokens: int ย  ย output_tokens: intโ€‹def call_model(task: str, messages: list, **kwargs) -> AIResponse: ย  ย """ ย   Single entry point for all LLM calls. ย   Returns a normalized AIResponse regardless of which model handled it. ย   Raises on 4xx (client errors). Logs and re-raises on 5xx/network errors. ย   """ ย  ย model = MODEL_CONFIG.get(task, "gpt-5.4-mini") ย  ย if not model: ย  ย  ย  ย raise ValueError(f"No model configured for task '{task}'")โ€‹ ย  ย try: ย  ย  ย  ย response: ChatCompletion = client.chat.completions.create( ย  ย  ย  ย  ย  ย model=model, ย  ย  ย  ย  ย  ย messages=messages, ย  ย  ย  ย  ย  ย **kwargs ย  ย  ย   ) ย  ย except APIStatusError as e: ย  ย  ย  ย logging.error(f"API error for task={task} model={model}: {e.status_code} {e.message}") ย  ย  ย  ย raise ย  ย except (APIConnectionError, APITimeoutError) as e: ย  ย  ย  ย logging.error(f"Network error for task={task} model={model}: {e}") ย  ย  ย  ย raiseโ€‹ ย  ย # content is None when the model triggers a tool call instead of returning text ย  ย content = response.choices[0].message.content or ""โ€‹ ย  ย # usage is None in streaming mode โ€” default to 0 if not available ย  ย usage = response.usage ย  ย input_tokens = usage.prompt_tokens if usage else 0 ย  ย output_tokens = usage.completion_tokens if usage else 0โ€‹ ย  ย logging.info( ย  ย  ย  ย f"task={task} model={model} " ย  ย  ย  ย f"input_tokens={input_tokens} output_tokens={output_tokens}" ย   )โ€‹ ย  ย return AIResponse( ย  ย  ย  ย content=content, ย  ย  ย  ย model=response.model, ย  ย  ย  ย input_tokens=input_tokens, ย  ย  ย  ย output_tokens=output_tokens, ย   )

Kini logik perniagaan anda berfungsi dengan objek AIResponse, bukan respons API mentah. Jika pembekal mengubah format respons mereka, anda membetulkannya di satu tempat.

Tambah sokongan penstriman pada pembungkus

Untuk antara muka sembang, anda akan mahukan penstriman. Pembungkus menanganinya sebagai laluan berasingan:

from typing import Iteratorโ€‹def stream_model(task: str, messages: list, **kwargs) -> Iterator[str]: ย  ย """ ย   Stream tokens from the routed model. ย   Note: streaming doesn't return usage data. ย   Fallback is not supported in streaming mode โ€” you've already ย   started yielding tokens before you know if the full request succeeds. ย   """ ย  ย model = MODEL_CONFIG.get(task, "gpt-5.4-mini") ย  ย if not model: ย  ย  ย  ย raise ValueError(f"No model configured for task '{task}'")โ€‹ ย  ย stream = client.chat.completions.create( ย  ย  ย  ย model=model, ย  ย  ย  ย messages=messages, ย  ย  ย  ย stream=True, ย  ย  ย  ย **kwargs ย   )โ€‹ ย  ย for chunk in stream: ย  ย  ย  ย delta = chunk.choices[0].delta.content ย  ย  ย  ย if delta: ย  ย  ย  ย  ย  ย yield deltaโ€‹# Usagefor token in stream_model("chat", [{"role": "user", "content": "Hello"}]): ย  ย print(token, end="", flush=True)

Ketahui parameter mana yang mewujudkan penguncian

Sesetengah parameter hanya wujud pada pembekal tertentu. Menggunakannya tidak mengapa โ€” cuma sedari bahawa anda membuat pilihan yang disengajakan:

ParameterBerfungsi padaRisiko penguncian
logprobsHanya GPTTinggi โ€” tiada setara pada Claude atau Gemini
n > 1GPT, Gemini (bukan Claude)Sederhana โ€” Claude memerlukan gelungan
reasoning_effortHanya GPT siri oTinggi โ€” tiada setara di tempat lain
temperature > 1.0GPT, Gemini (bukan Claude)Rendah โ€” Claude mengehadkan pada 1.0
toolsSemua pembekal utamaTiada โ€” selamat digunakan
response_formatSemua pembekal utamaRendah โ€” perbezaan skema kecil

Jika anda menggunakan logprobs untuk pemarkahan keyakinan, anda terkunci kepada GPT untuk ciri tersebut. Itu pertukaran yang munasabah โ€” cuma dokumentasikan supaya pembangun seterusnya tahu sebabnya.

Jadikan endpoint pembekal boleh dikonfigur

Mengkeras kod base_url="https://api.cometapi.com/v1" masih merupakan satu bentuk penguncian. Jadikannya pemboleh ubah persekitaran:

# .env โ€” using CometAPIAI_BASE_URL=https://api.cometapi.com/v1AI_API_KEY=your_cometapi_keyโ€‹# To switch to OpenAI directly, change two lines:# AI_BASE_URL=https://api.openai.com/v1# AI_API_KEY=your_openai_key

Inisialisasi klien daripada Langkah 1 sudah membaca daripada pemboleh ubah ini. Bertukar antara CometAPI dan sambungan pembekal terus kini hanya perubahan konfigurasi, bukan perubahan kod.

Versi Node.js

import OpenAI from 'openai';โ€‹const apiKey = process.env.AI_API_KEY;if (!apiKey) throw new Error('AI_API_KEY is not set');โ€‹const client = new OpenAI({ ย baseURL: process.env.AI_BASE_URL ?? 'https://api.cometapi.com/v1', ย apiKey,});โ€‹// Model IDs are CometAPI platform identifiers โ€” see cometapi.com/modelsconst MODEL_CONFIG = { ย summarize: process.env.MODEL_SUMMARIZE ?? 'claude-opus-4-7', ย code: ย  ย  ย process.env.MODEL_CODE ย  ย  ย ?? 'gpt-5.4', ย classify: ย process.env.MODEL_CLASSIFY ย ?? 'claude-haiku-4-5', ย chat: ย  ย  ย process.env.MODEL_CHAT ย  ย  ย ?? 'gpt-5.4-mini',};โ€‹// Validate at startupfor (const [task, model] of Object.entries(MODEL_CONFIG)) { ย if (!model) throw new Error(`Model config for '${task}' is not set`);}โ€‹/** * Single entry point for all LLM calls. * Returns normalized response. Raises on 4xx, logs and re-raises on 5xx/network. */async function callModel(task, messages, options = {}) { ย const model = MODEL_CONFIG[task] ?? 'gpt-5.4-mini';โ€‹ ย let response; ย try { ย  ย response = await client.chat.completions.create({ ย  ย  ย model, ย  ย  ย messages, ย  ย  ย ...options, ย   });  } catch (err) { ย  ย // Don't swallow errors โ€” log and re-raise ย  ย console.error(`API error task=${task} model=${model}:`, err.message); ย  ย throw err;  }โ€‹ ย // content is null when model triggers a tool call ย const content = response.choices[0].message.content ?? '';โ€‹ ย // usage may be absent in some configurations ย const inputTokens ย = response.usage?.prompt_tokens ย  ย  ?? 0; ย const outputTokens = response.usage?.completion_tokens ?? 0;โ€‹ ย console.log(`task=${task} model=${model} input=${inputTokens} output=${outputTokens}`);โ€‹ ย return { content, model: response.model, inputTokens, outputTokens };}โ€‹/** * Stream tokens from the routed model. * Usage data is not available in streaming mode. */async function* streamModel(task, messages, options = {}) { ย const model = MODEL_CONFIG[task] ?? 'gpt-5.4-mini';โ€‹ ย const stream = await client.chat.completions.create({ ย  ย model, ย  ย messages, ย  ย stream: true, ย  ย ...options,  });โ€‹ ย for await (const chunk of stream) { ย  ย const delta = chunk.choices[0]?.delta?.content; ย  ย if (delta) yield delta;  }}โ€‹// Usage โ€” blockingconst result = await callModel('classify', [  { role: 'user', content: 'Positive or negative? "Loved it!"' }]);console.log(result.content);โ€‹// Usage โ€” streamingfor await (const token of streamModel('chat', [  { role: 'user', content: 'Hello' }])) { ย process.stdout.write(token);}

Penguncian yang boleh diterima

Tidak semua penguncian wajar dilawan. Sesetengah pertukaran masuk akal:

  • Menggunakan OpenAI SDK โ€” Ia standard de facto. Kebanyakan pembekal menyokongnya. Penguncian berisiko rendah.
  • Ciri khusus pembekal yang anda benar-benar perlukan โ€” Jika anda perlukan logprobs, gunakannya. Asingkan kod itu supaya mudah dicari dan diganti kemudian.
  • Model ditala halus (fine-tuned) โ€” Model ditala halus secara asasnya terikat pada satu pembekal. Itu dijangka.

Penguncian yang patut dielakkan ialah jenis tidak sengaja โ€” nama model dalam logik perniagaan, penghuraian respons mentah merata fail, kunci API dikeraskan dalam sumber.

Langkah seterusnya

Kini anda mempunyai lapisan abstraksi yang mengekalkan butiran pembekal di luar logik perniagaan anda. Artikel terakhir dalam siri ini membincangkan apa yang berlaku apabila sesuatu tidak kena: cara menyahpepijat penjanaan yang gagal, mentafsir kod ralat, dan membina pengendalian ralat yang benar-benar memberitahu anda apa yang rosak.

Seterusnya: Cara Menyahpepijat Penjanaan API AI yang Gagal

Soalan Lazim

Q: Apakah perbezaan antara penguncian SDK dan penguncian model?

Penguncian SDK bermaksud kod anda mengimport pustaka tertentu dan perlu diubah jika anda menukar SDK. Penguncian model bermaksud nama model bertaburan dalam logik perniagaan anda. Penguncian SDK kurang berbahaya kerana kebanyakan pembekal kini menyokong format OpenAI SDK. Penguncian model lebih licik kerana sukar dikesan dan diperbaiki.

Q: Jika saya menggunakan CometAPI, adakah saya hanya menukar penguncian OpenAI kepada penguncian CometAPI?

Sebahagiannya. Anda menukar penguncian pembekal terus kepada lapisan proksi. Kelebihannya: satu kunci, satu endpoint, pertukaran model yang mudah. Risikonya: jika CometAPI terhenti, semua pembekal anda turut terjejas. Mitigasinya sudah ada dalam kod di atas โ€” AI_BASE_URL ialah pemboleh ubah persekitaran. Jika anda perlu memintas CometAPI dan menghubungi pembekal secara langsung, itu perubahan konfigurasi, bukan perubahan kod.

Q: Bolehkah saya menggunakan extended thinking Claude atau OpenAI's reasoning_effort melalui corak ini?

Ya, hantarkan sebagai **kwargs kepada call_model. Sedari bahawa jika anda merutekan tugas itu ke model lain, parameter tersebut akan diabaikan atau menyebabkan ralat. Dokumentasikan tugas mana yang menggunakan ciri khusus pembekal supaya pembangun seterusnya tahu sebabnya.

Q: Bagaimana saya mengendalikan had temperature Claude pada 1.0 apabila merutek antara Claude dan GPT**?****

Kekalkan temperature pada atau di bawah 1.0 untuk kekal dalam julat selamat bagi kedua-duanya. Jika anda memerlukan temperature lebih tinggi untuk tugas kreatif pada GPT secara khusus, rute tugas tersebut secara jelas ke GPT dalam MODEL_CONFIG dan jangan biarkan ia melalui penghala umum.

Q: Patutkah saya mengabstrakkan API penjanaan imej dan video dengan cara yang sama?

Prinsip yang sama terpakai โ€” konfigurasi pusat, pembungkus respons dinormalkan, tiada medan khusus pembekal dalam logik perniagaan. API imej dan video mempunyai lebih banyak perbezaan struktur (asinkron vs segerak, set parameter berbeza) jadi lapisan abstraksi memerlukan lebih banyak kerja. Mulakan dengan teks, kemudian kembangkan corak apabila struktur telah terbukti.

Q: Bagaimana dengan perbezaan context window antara model?

Ini risiko sebenar apabila merutekan. GPT-5.5 mempunyai context window 1M token, model Claude menyokong sehingga 200K, dan Gemini 3.5 Flash menyokong sehingga 1M. Jika anda merutekan tugas dokumen panjang ke model dengan context window yang lebih pendek, input akan dipotong secara senyap. Tambahkan semakan panjang konteks sebelum merutekan jika tugas anda melibatkan input panjang โ€” atau sentiasa rute tugas berkonteks panjang ke model khusus dalam MODEL_CONFIG dan jangan biarkan ia jatuh ke lalai.

Bersedia untuk mengurangkan kos pembangunan AI sebanyak 20%?

Mulakan secara percuma dalam beberapa minit. Kredit percubaan percuma disertakan. Tiada kad kredit diperlukan.

Baca Lagi