Stripe, WorkOS ve Slack istemci SDK'larını kullanmayı bıraktık ve kalanları da taşıma sürecindeyiz. Bunun yerine, HttpBaseClient adını verdiğimiz küçük bir sarmalayıcı sınıf aracılığıyla doğrudan REST API'lerini çağırıyoruz.
Kulağa ters geliyor. SDK'ların size zaman kazandırması gerekir. Ama AI'nın daha fazla kodu yerel olarak çalışacak şekilde taşıdığını duyuyorsunuz. Aynı fikir SDK'lar için de geçerli. Neden mi?
- SDK'lar, hata ayıklama yapan veya ayrıntıları üst ekibe ileten ajanlar için kritik olan HTTP yanıt başlıkları ve ham yanıt gövdeleri gibi ham ayrıntıları gizler.
- Uygun yanıtlar beklerler, ancak gerçekte güvenlik duvarlarından, yük dengeleyicilerden ve ağ geçitlerinden hatalı biçimlendirilmiş yanıtlar gelir. Bu da ihtiyacımız olan hata ayıklama ayrıntılarını maskeler.
- SDK'lar, yeniden denemeler, hata işleme ve gözlemlenebilirlik için merkezi bir giriş noktasının zorlayıcı işlevini atlayarak ajanların kendi kalıplarını yeniden yazmasına olanak tanır.
- Genellikle OpenAPI'den otomatik oluşturulan ve yalnızca küçük bir uç nokta alt kümesini kullandığımızda bile ağır bir şişkinlik taşırlar.
SDK'ların neden başladığını bir düşünün. Şirketlerin benimsemeyi kolay hissettirmek istemesiyle doğdular: paylaşılan bir kütüphane gönderin, müşterilere zaman kazandırın ve tekrarlayan entegrasyon işini gizleyin. Ancak AI bu maliyet eğrisini değiştirdi. Bir SDK ile entegre olmak artık genellikle HTTP API'sini doğrudan çağırmak kadar iş gerektiriyor ve tam olarak kullandığımız uç noktalar için özel bir REST istemcisi yazmak, daha iyi birleşik gözlemlenebilirlik sağlarken ucuz.
Sonsuz bir kovalamaca

Ne kadar güzel "JSON" yanıtları
Sentry hata gösterge tablomuz eskiden böyle görünürdü. İsteklerin küçük bir yüzdesinin bazı kod yollarından başarısız olduğunu bulur, ardından kullanıcı adına SDK aksaklığını gidermek için yalnızca o yolu yamalardık. Her gün başka bir kod yolu beklenmedik bir hataya takılır; onu da yamalar ve bu oyun hiç bitmezdi.
SDK'lar yaygın bir üretim başarısızlık modunda kırılgandır: sunucu bir şeylerin ters gittiğini söyler, ancak yanıt SDK'nın beklediği JSON şeklinde değildir.
Aşırı yüklenmiş bir Nginx ağ geçidi beklenmeyen HTML döndürür. Cloudflare bir isteği engeller. Bir güvenlik duvarı hız sınırı sayfası gösterir. Satıcı API'si genellikle JSON'dur, ancak önündeki şey her zaman satıcı API'si değildir.
Bu olduğunda, birçok SDK yanıtı ayrıştırmaya çalışır, genel bir ayrıştırma hatasıyla başarısız olur ve kullanışlı kısımları atar. Ham gövde kaybolur. Durum metni gömülür. HTTP başlıkları genellikle kullanılabilir bir şekilde döndürülmez. Satıcı desteği bir HTTP başlığından bir istek kimliği isterse, SDK bunu döndürmediği için şansımız kalmaz.
Ajanlar SDK'ların doğrudanlığını kötüye kullanır
İstemci SDK çağrılarımız tam bir başıboşluktu. Stripe'ın bir kalıbı vardı. WorkOS'un başka bir kalıbı. Slack'in kendine özgü tuhaflıkları. Diğer entegrasyonlarda ham fetch, SDK çağrıları, tek seferlik yeniden deneme mantığı veya hiç yeniden deneme mantığı yoktu.
SDK'lar yanlış şeyi kolay gösterdi. Bir ajan her zaman doğrudan satıcı istemcisine gidip herhangi bir routetan stripe.customers.create(...) çağırabilirdi. Bu üretken hissettirir, ancak kimlik doğrulama, yeniden denemeler, metrikler, günlükler ve hata çevirisinin yaşaması gereken paylaşılan yeri atlar. Kod tabanımızda buna benzer kapanış sarmalayıcıları vardı:
1const response = await catchRateLimitError(() =>2 stripe.customers.retrieve(stripeCustomerId)3);
Bir yeri kaçırıp SDK çağrısını sarmalamayı unutursanız, kötü bir gün geçirirdiniz. Bir Stripe hız sınırı ve bir WorkOS hız sınırı, ürünümüz için aynı anlama gelir: üst kaynak bizden yavaşlamamızı istiyor. Ancak tür düzeyinde tamamen farklı nesnelerdi. Bazı kodlar SDK'ya özel istisnaları yakaladı. Bazıları genel Error'u yakaladı. Bazıları hiçbir şey yakalamadı. Bu da Sentry kovalamacasına dönüştü: bir çağrı noktasında 429'u düzelt, aynı başarısızlık sınıfının başka bir yerde olmasını bekle.
SDK'lar şişkindir
NPM paketleri openai ve anthropic-ai/sdk, Stainless tarafından OpenAPI şartnamelerinden otomatik oluşturulur. Stripe da Stripe'ın OpenAPI şartnamesinden oluşturulur. Büyük bir API için onlarca programlama dilinde genel bir SDK'nın bakımını ölçeklendirmenin yolu budur.
Ancak harika bir genel SDK herkese hizmet etmek zorundadır. Bizim backend'imizin herkesin SDK'sına ihtiyacı yok. Bizim ihtiyacımız olan, sekiz Stripe uç noktamız, WorkOS kullanıcı ve kuruluş uç noktalarımız ve gerçekten çağırdığımız Slack metodları. Stripe 6.5 MB, workos-inc/node 6.9 MB, slack/web-api 7.7 MB ve linear/sdk 34 MB. En uç noktada googleapis 198 MB'a ulaşıyor.
Oluşturulan SDK'lar tüm platformu beraberinde getirir: yüzlerce metod, aşırı yüklemeler, sayfalama yardımcıları, yeniden deneme davranışı, ortam algılama, uyumluluk shim'leri ve bir yerlerde hâlâ birileri bağımlı olduğu için yok olamayan eski yüzeyler. Kendi backend'imizin içinde bu genellik şişkinliktir. Daha da kötüsü, bizimle kablo arasında durur.
HTTP API'lerinin API sözleşmeleri olduğunu unutuyoruz
İnsanlar bazen HTTP API'lerinden sanki daha düşük seviyeli uygulama ayrıntılarıymış ve SDK'lar da gerçek kararlı arayüzmüş gibi bahseder. Genel REST API bir sözleşmedir. Satıcılar onu gelişigüzel bozamaz. SDK yazarları da bunu bilir, çünkü her müşteriyi yükseltmeye zorlayamazlar. Birçok müşteri yıllar öncesinin SDK sürümlerini üretimde çalıştırmaya devam eder, bu da eski kablo sözleşmesinin yine de çalışmaya devam etmesi gerektiği anlamına gelir.
Kendi HttpBaseClient'ımız
HttpBaseClient, istemci SDK'larının yerine geçen çözümümüz. Sağlayıcı alt sınıfları, satıcıya özgü parçaları sağlar: temel URL, kimlik doğrulama başlıkları, içerik türü, hata eşlemesi ve gerçekten kullandığımız uç noktalar için dar metodlar. HttpBaseClient geri kalanına sahiptir: serileştirme, ayrıştırma, aktarım hataları, yapılandırılmış günlükler, metrikler, durum eşlemesi ve süre takibi. Bu, gözlemlenebilirliği birleştirir ve her satıcının tutarlı standartları izlemesini sağlar. Basitleştirilmiş hali şöyledir:
1abstract class HttpBaseClient<TEndpoint extends string> {2 protected abstract readonly baseUrl: string;34 protected constructor(private readonly dependency: string) {}56 protected abstract buildAuthHeaders(): Promise<Record<string, string>>;78 protected async request<TBody, TResponse>(config: {9 method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';10 path: string;11 endpoint: TEndpoint;12 body?: TBody;13 }): Promise<TResponse> {14 const url = `${this.baseUrl}${config.path}`;15 const headers = await this.buildAuthHeaders();16 const labels = {17 dependency: this.dependency,18 endpoint: config.endpoint,19 method: config.method,20 };21 const start = performance.now();2223 logInfo('upstream request starting', labels);2425 try {26 const response = await callWithMetrics(27 () =>28 fetch(url, {29 method: config.method,30 headers,31 body: config.body === undefined ? undefined : JSON.stringify(config.body),32 }),33 this.dependency,34 labels35 );3637 const body = await parseBody(response);38 if (!response.ok) throw this.mapHttpError(response, body);3940 logInfo('upstream request succeeded', {41 ...labels,42 statusCode: response.status,43 durationMs: performance.now() - start,44 });4546 return body as TResponse;47 } catch (cause) {48 logWarn('upstream request failed', {49 ...labels,50 durationMs: performance.now() - start,51 cause,52 });53 throw cause;54 }55 }56}5758// Then a Stripe wrapper becomes small and explicit:59enum StripeEndpoint {60 CustomersCreate = 'stripe/customers/create',61}6263class StripeHttpClient extends HttpBaseClient<StripeEndpoint> {64 protected readonly baseUrl = 'https://api.stripe.com';6566 constructor(private readonly apiKey: string) {67 super(ClientVendor.Stripe);68 }6970 createCustomer(body: { email: string; name: string }) {71 return this.request<typeof body, Stripe.Customer>({72 method: 'POST',73 path: '/v1/customers',74 endpoint: StripeEndpoint.CustomersCreate,75 body,76 });77 }7879 // More endpoints go here.80}
İşin püf noktası bu. Sınıf, Stripe'ın tamamını modellemeye çalışmaz. Her satıcı çağrısının sahip olmasını istediğimiz HTTP davranışını modeller. Stripe hâlâ form kodlamasını alır. WorkOS hâlâ bearer auth ve JSON gövdelerini alır. Slack hâlâ HTTP 200'deki tuhaf ok: false davranışını alır. Ancak backend'imizin geri kalanı tutarlı bir şekil görür.
HttpBaseClient'ımızın neye benzediğinin daha uzun bir versiyonunu burada görebilirsiniz, örnek kod olarak okunması kolay ve genel olacak şekilde değiştirilmiştir.
Hâlâ SDK kullandığımız yerler
Mevcut yaklaşımımız hibrit: çalışma zamanında kendi HTTP istemcimizi kullanmak, ancak türlerinin hâlâ zaman kazandırdığı yerlerde SDK'ları elde tutmak. StripeHttpClient, Stripe.Customer döndürebilir, SlackHttpClient, slack/web-api argüman türlerini ödünç alabilir ve WorkOS türleri hâlâ kablo yanıtını tanımlayabilir.
Zamanla bunu da aşamalı olarak kaldırmayı bekliyorum. AI, ihtiyacımız olan tam istek ve yanıt türlerini oluşturma ve sürdürme konusunda daha iyi hale geldikçe, yalnızca türler için bir SDK paketini tutma gerekçesi zayıflıyor. Ancak çalışma zamanı davranışı acı veren kısım, bu yüzden geçiş oradan başlıyor.
SDK'ları, yalnızca REST etrafında bir sarmalayıcı değil de SDK'nın kendisi ürün sınırı olduğunda hâlâ kullanıyoruz. Gözlemlenebilirlik en net örnek. Sentry için SDK, çalışma zamanı enstrümantasyonu, hata yakalama, kapsam yayılımı, sürüm meta verileri ve kendimiz yapmak istemeyeceğimiz entegrasyonları halleder. Bu, bir satıcı SDK'sını sıradan arka uç HTTP çağrıları için ince bir istemci olarak kullanmaktan farklıdır.
Her API, HTTP üzerinden REST değildir ve bu sorun değil. Veritabanı çağrıları iyi bir örnektir. Daha düşük seviyedeki soyutlamamız BaseClient'tır: her istemciye aynı metrikleri, günlük kaydını ve hata işleme sözleşmesini verirken, bir alt sınıfın taşıması için "fetch"in ne anlama geldiğini geçersiz kılmasına izin verir.
Nereye gidiyoruz
Geliştiriciler, API dokümanlarını gerçek entegrasyon rehberi ve SDK'ları referans uygulamalar olarak ele alacak: kimlik doğrulama, yükler, sayfalama, yeniden denemeler ve uç durumlar için şablonlar. "SDK gönder"in bir sonraki sürümü, ajanlara API'yi nasıl doğru çağıracaklarını, doğru kalıpları nasıl yeniden kullanacaklarını ve her çalışma zamanı çağrısını bir satıcı paketi aracılığıyla yapmaktan nasıl kaçınacaklarını öğreten "bir ajan becerisi göndermek" olabilir.


![Yusuke Narita'nın Dahi Yapay Zeka Kullanım Teknikleri [Arşivlik Sürüm]](/cdn-cgi/image/width=1920,quality=90,format=auto,metadata=none/https%3A%2F%2Fcms-assets.youmind.com%2Fmedia%2F1784137658627_u4bwry_HNMS89bbsAAUPJI.jpg)


