Zintegruj OpenAI i Anthropic API z aplikacją internetową
Integracja API modelu językowego jest jedną z najbardziej poszukiwanych umiejętności w nowoczesnym tworzeniu stron internetowych. Niezależnie od tego, czy budujesz chatbota, asystenta generowanie treści lub system analizy tekstu, umiejętność komunikacji skutecznie z interfejsami API OpenAI e Antropiczny to jest fundamentalne.
W czwartym artykule z tej serii Sztuczna inteligencja dla twórców stron internetowych, zbadamy szczegółowo jak zintegrować oba interfejsy API w kompletną aplikację internetową. Zaczniemy od konfiguracji początkowo przeanalizujemy struktury komunikatów, zaimplementujemy streaming odpowiedzi i zbudujemy bezpieczną architekturę ze wzorcami proxy w celu ochrony kluczy API.
Czego się nauczysz
- Konfiguruj i uwierzytelniaj za pomocą interfejsów API OpenAI i Anthropic
- Zrozumienie różnic strukturalnych pomiędzy dwoma systemami komunikatów
- Zaimplementuj przesyłanie strumieniowe odpowiedzi, aby uzyskać doświadczenie w czasie rzeczywistym
- Niezawodnie obsługuj błędy, ponawianie prób i ograniczanie szybkości
- Licz tokeny i optymalizuj koszty użytkowania
- Zbuduj bezpieczny serwer proxy zaplecza, aby chronić klucze API
- Utwórz kompletny interfejs czatu z obsługą przesyłania strumieniowego
Przegląd serii
| # | Przedmiot | Centrum |
|---|---|---|
| 1 | Wprowadzenie do RAG | Podstawowe pojęcia |
| 2 | RAG z TypeScript i LangChain | Praktyczne wdrożenie |
| 3 | Porównanie bazy danych wektorów | Chromadb, Pinecone, Weaviate |
| 4 | OpenAI i Anthropic API (tutaj jesteś) | Integracja API |
| 5 | LLM Local z Ollamą | AI bez chmury |
| 6 | Dostrajanie vs RAG | Kiedy czego używać |
| 7 | Agenci AI | Architektura oparta na agentach |
| 8 | Sztuczna inteligencja w CI/CD | Inteligentna automatyzacja |
1. Konfiguracja API i uwierzytelnianie
Zanim będziesz mógł przesłać swoje pierwsze żądanie, musisz uzyskać dane logowania i poprawnie skonfiguruj środowisko programistyczne. Obaj dostawcy wymagają Klucz API, którym należy bezpiecznie zarządzać.
Uzyskaj klucze API
Dla OpenAI, zarejestruj się platform.openai.com i wygeneruj klucz
w sekcji Klucze API. Dla Antropiczny, uzyskaj dostęp do konsoli pod adresem
console.anthropic.com i utwórz klucz API w panelu ustawień.
Bezpieczeństwo klucza API
Nigdy nie dołączaj kluczy API bezpośrednio do kodu źródłowego lub zatwierdzonych plików na Gicie. Zawsze używaj zmiennych środowiskowych lub usługi zarządzania sekretami. Ujawniony klucz może generować nieoczekiwane koszty i zagrażać bezpieczeństwu.
# File .env (mai committare su Git!)
OPENAI_API_KEY=sk-proj-xxxxxxxxxxxxxxxxxxxxxxxx
ANTHROPIC_API_KEY=sk-ant-xxxxxxxxxxxxxxxxxxxxxxxx
# Configurazione opzionale
OPENAI_ORG_ID=org-xxxxxxxxxxxxxxxx
OPENAI_MODEL=gpt-4o
ANTHROPIC_MODEL=claude-sonnet-4-20250514
Instalowanie zestawów SDK
Obaj dostawcy oferują oficjalne zestawy SDK dla TypeScript/JavaScript, które to ułatwiają integracja w znaczący sposób. SDK automatycznie obsługują serializację, podstawowa deserializacja i ponowna próba.
# SDK ufficiali
npm install openai @anthropic-ai/sdk
# Gestione variabili d'ambiente
npm install dotenv
# Per il server proxy Express
npm install express cors
npm install -D @types/express @types/cors
Inicjalizacja klienta
// src/lib/ai-clients.ts
import OpenAI from 'openai';
import Anthropic from '@anthropic-ai/sdk';
import dotenv from 'dotenv';
dotenv.config();
// Client OpenAI
export const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
organization: process.env.OPENAI_ORG_ID, // opzionale
});
// Client Anthropic
export const anthropic = new Anthropic({
apiKey: process.env.ANTHROPIC_API_KEY,
});
// Verifica configurazione
export function validateConfig(): boolean {
if (!process.env.OPENAI_API_KEY) {
console.error('OPENAI_API_KEY non configurata');
return false;
}
if (!process.env.ANTHROPIC_API_KEY) {
console.error('ANTHROPIC_API_KEY non configurata');
return false;
}
return true;
}
2. Porównanie struktury komunikatu
Chociaż oba interfejsy API współpracują z koncepcją wiadomości w konwersacji, konstrukcje znacznie się od siebie różnią. Zrozum te różnice jest to niezbędne do zbudowania jednolitej abstrakcji.
Porównanie konstrukcji
| Charakterystyczny | OpenAI | Antropiczny |
|---|---|---|
| Role wiadomości | system, użytkownik, asystent, narzędzie | użytkownik, asystent (oddzielny system) |
| Monit systemowy | W wiadomościach | Dedykowany parametr system |
| Model | model: "gpt-4o" | model: "claude-sonnet-4-20250514" |
| Maksymalna moc wyjściowa | max_tokens (fakultatywny) | max_tokens (obowiązkowy) |
| Temperatura | 0,0 - 2,0 (domyślnie 1,0) | 0,0 - 1,0 (domyślnie 1,0) |
| Transmisja strumieniowa | Zdarzenia wysyłane przez serwer | Zdarzenia wysyłane przez serwer |
| Punkty końcowe | /v1/chat/uzupełnienia | /v1/wiadomości |
Wymagane w OpenAI
// src/lib/openai-chat.ts
import { openai } from './ai-clients';
interface ChatMessage {
role: 'system' | 'user' | 'assistant';
content: string;
}
export async function chatWithOpenAI(
messages: ChatMessage[],
model: string = 'gpt-4o'
): Promise<string> {
const response = await openai.chat.completions.create({
model,
messages,
temperature: 0.7,
max_tokens: 2048,
});
return response.choices[0].message.content ?? '';
}
// Esempio di utilizzo
const risposta = await chatWithOpenAI([
{ role: 'system', content: 'Sei un assistente tecnico esperto.' },
{ role: 'user', content: 'Spiega il pattern Observer in TypeScript.' },
]);
console.log(risposta);
Zapytaj w Anthropic
// src/lib/anthropic-chat.ts
import { anthropic } from './ai-clients';
interface ChatMessage {
role: 'user' | 'assistant';
content: string;
}
export async function chatWithAnthropic(
messages: ChatMessage[],
systemPrompt: string = '',
model: string = 'claude-sonnet-4-20250514'
): Promise<string> {
const response = await anthropic.messages.create({
model,
max_tokens: 2048,
system: systemPrompt,
messages,
});
// Il contenuto è un array di blocchi
const textBlock = response.content.find(
(block) => block.type === 'text'
);
return textBlock?.text ?? '';
}
// Esempio di utilizzo
const risposta = await chatWithAnthropic(
[{ role: 'user', content: 'Spiega il pattern Observer in TypeScript.' }],
'Sei un assistente tecnico esperto.'
);
console.log(risposta);
3. Strumień odpowiedzi
Przesyłanie strumieniowe jest kluczem do zapewnienia bezproblemowej obsługi użytkownika. Zamiast czekać po wygenerowaniu całej wiadomości wysyłane są tokeny jako model produkuje je, drastycznie skracając postrzegany czas oczekiwania.
Przesyłanie strumieniowe z OpenAI
// src/lib/openai-stream.ts
import { openai } from './ai-clients';
export async function* streamOpenAI(
messages: Array<{ role: string; content: string }>,
model: string = 'gpt-4o'
): AsyncGenerator<string> {
const stream = await openai.chat.completions.create({
model,
messages: messages as any,
stream: true,
temperature: 0.7,
});
for await (const chunk of stream) {
const content = chunk.choices[0]?.delta?.content;
if (content) {
yield content;
}
}
}
// Utilizzo
async function main() {
const messages = [
{ role: 'user', content: 'Scrivi una funzione di ordinamento.' }
];
for await (const token of streamOpenAI(messages)) {
process.stdout.write(token);
}
}
Streamuj z Anthropic
// src/lib/anthropic-stream.ts
import { anthropic } from './ai-clients';
export async function* streamAnthropic(
messages: Array<{ role: string; content: string }>,
systemPrompt: string = '',
model: string = 'claude-sonnet-4-20250514'
): AsyncGenerator<string> {
const stream = anthropic.messages.stream({
model,
max_tokens: 2048,
system: systemPrompt,
messages: messages as any,
});
for await (const event of stream) {
if (
event.type === 'content_block_delta' &&
event.delta.type === 'text_delta'
) {
yield event.delta.text;
}
}
}
// Utilizzo con metriche
async function main() {
const start = Date.now();
let totalTokens = 0;
for await (const token of streamAnthropic(
[{ role: 'user', content: 'Scrivi una funzione di ordinamento.' }],
'Sei un programmatore esperto.'
)) {
process.stdout.write(token);
totalTokens++;
}
console.log(`\nTempo: ${Date.now() - start}ms, Token: ${totalTokens}`);
}
4. Zarządzanie błędami i ponawianie prób
Zewnętrzne interfejsy API mogą zawieść z wielu powodów: limitów szybkości, przekroczeń limitu czasu, błędy serwera lub problemy z siecią. Niezbędny jest niezawodny system ponawiania prób aby zapewnić niezawodność aplikacji.
Typowe kody błędów
| Kod | Oznaczający | Strategia |
|---|---|---|
| 400 | Źle sformułowane żądanie | Nie próbuj ponownie, popraw parametry |
| 401 | Uwierzytelnienie nie powiodło się | Sprawdź swój klucz API |
| 429 | Przekroczono limit stawki | Poczekaj i spróbuj ponownie z wycofywaniem |
| 500 | Błąd serwera | Spróbuj ponownie z wykładniczym wycofaniem |
| 503 | Usługa niedostępna | Spróbuj ponownie po kilku sekundach |
| 529 | Przeciążenie (antropiczne) | Spróbuj ponownie z długim wycofywaniem |
// src/lib/retry.ts
interface RetryConfig {
maxRetries: number;
baseDelay: number; // millisecondi
maxDelay: number; // millisecondi
backoffFactor: number;
}
const DEFAULT_CONFIG: RetryConfig = {
maxRetries: 3,
baseDelay: 1000,
maxDelay: 30000,
backoffFactor: 2,
};
export async function withRetry<T>(
fn: () => Promise<T>,
config: Partial<RetryConfig> = {}
): Promise<T> {
const cfg = { ...DEFAULT_CONFIG, ...config };
let lastError: Error | null = null;
for (let attempt = 0; attempt <= cfg.maxRetries; attempt++) {
try {
return await fn();
} catch (error: any) {
lastError = error;
// Non ritentare per errori client (4xx eccetto 429)
if (error.status && error.status >= 400 &&
error.status < 500 && error.status !== 429) {
throw error;
}
if (attempt === cfg.maxRetries) break;
// Calcola il delay con jitter
const delay = Math.min(
cfg.baseDelay * Math.pow(cfg.backoffFactor, attempt) +
Math.random() * 1000,
cfg.maxDelay
);
console.warn(
`Tentativo ${attempt + 1} fallito. ` +
`Retry tra ${Math.round(delay)}ms...`
);
// Usa Retry-After se disponibile
const retryAfter = error.headers?.get?.('retry-after');
const waitTime = retryAfter
? parseInt(retryAfter) * 1000
: delay;
await new Promise(r => setTimeout(r, waitTime));
}
}
throw lastError;
}
5. Ograniczanie szybkości i kontrola częstotliwości
Jeśli aplikacja obsługuje wielu jednoczesnych użytkowników, jej wdrożenie ma kluczowe znaczenie system ograniczania stawek po stronie klienta, pozwalający uniknąć przekroczenia narzuconych limitów przez dostawców i zapewnić sprawiedliwe wykorzystanie zasobów.
// src/lib/rate-limiter.ts
export class TokenBucketRateLimiter {
private tokens: number;
private lastRefill: number;
constructor(
private maxTokens: number,
private refillRate: number, // token al secondo
) {
this.tokens = maxTokens;
this.lastRefill = Date.now();
}
private refill(): void {
const now = Date.now();
const elapsed = (now - this.lastRefill) / 1000;
this.tokens = Math.min(
this.maxTokens,
this.tokens + elapsed * this.refillRate
);
this.lastRefill = now;
}
async acquire(): Promise<void> {
this.refill();
if (this.tokens < 1) {
const waitTime = (1 - this.tokens) / this.refillRate * 1000;
await new Promise(r => setTimeout(r, waitTime));
this.refill();
}
this.tokens -= 1;
}
}
// Limiti per provider
const openaiLimiter = new TokenBucketRateLimiter(60, 1); // 60 RPM
const anthropicLimiter = new TokenBucketRateLimiter(50, 0.83); // 50 RPM
6. Liczenie tokenów i optymalizacja kosztów
Koszty API są wprost proporcjonalne do ilości przetworzonych tokenów. Monitorowanie i optymalizacja zużycia tokenów ma kluczowe znaczenie dla utrzymania kosztów pod kontrolą, szczególnie w zastosowaniach o dużym natężeniu ruchu.
Orientacyjne koszty tokena 1M (luty 2026)
| Model | Wejście (token 1M) | Wyjście (token 1M) |
|---|---|---|
| GPT-4o | 2,50 dolara | 10,00 dolarów |
| GPT-4o mini | 0,15 USD | 0,60 USD |
| Klaudiusz Sonnet 4 | 3,00 dolarów | 15,00 dolarów |
| Klaudiusz Haiku 3.5 | 0,80 USD | 4,00 dolarów |
Notatka: Ceny mają charakter orientacyjny i mogą ulec zmianie. Zawsze sprawdzaj oficjalną dokumentację pod kątem aktualnych kosztów.
// src/lib/token-counter.ts
import { encoding_for_model } from 'tiktoken';
export class TokenCounter {
private encoder;
constructor(model: string = 'gpt-4o') {
this.encoder = encoding_for_model(model as any);
}
count(text: string): number {
return this.encoder.encode(text).length;
}
countMessages(
messages: Array<{ role: string; content: string }>
): number {
let total = 0;
for (const msg of messages) {
total += 4; // overhead per messaggio
total += this.count(msg.role);
total += this.count(msg.content);
}
total += 2; // overhead della richiesta
return total;
}
estimateCost(
inputTokens: number,
outputTokens: number,
inputCostPer1M: number,
outputCostPer1M: number
): number {
return (
(inputTokens / 1_000_000) * inputCostPer1M +
(outputTokens / 1_000_000) * outputCostPer1M
);
}
dispose(): void {
this.encoder.free();
}
}
7. Wzorzec proxy dla bezpieczeństwa klucza API
Klucze API nie muszą nigdy być wystawionym na frontend. Wzór Zalecane jest utworzenie serwera proxy, który będzie pełnił rolę pośrednika pomiędzy przeglądarka użytkownika i interfejsy API dostawcy AI. Frontend komunikuje się z Twoim serwer, a serwer bezpiecznie zarządza kluczami.
// src/server/proxy.ts
import express from 'express';
import cors from 'cors';
import { openai, anthropic } from '../lib/ai-clients';
import { withRetry } from '../lib/retry';
const app = express();
app.use(cors({ origin: process.env.ALLOWED_ORIGINS?.split(',') }));
app.use(express.json());
// Middleware di rate limiting per IP
const ipRequests = new Map<string, number[]>();
function rateLimitMiddleware(
req: express.Request,
res: express.Response,
next: express.NextFunction
) {
const ip = req.ip ?? 'unknown';
const now = Date.now();
const windowMs = 60_000; // 1 minuto
const maxRequests = 20;
const requests = (ipRequests.get(ip) ?? [])
.filter(t => now - t < windowMs);
if (requests.length >= maxRequests) {
return res.status(429).json({
error: 'Troppe richieste. Riprova tra un minuto.'
});
}
requests.push(now);
ipRequests.set(ip, requests);
next();
}
app.use('/api', rateLimitMiddleware);
// Endpoint chat unificato
app.post('/api/chat', async (req, res) => {
const { provider, messages, system, model } = req.body;
try {
let content: string;
if (provider === 'openai') {
const result = await withRetry(() =>
openai.chat.completions.create({
model: model ?? 'gpt-4o',
messages,
temperature: 0.7,
})
);
content = result.choices[0].message.content ?? '';
} else {
const result = await withRetry(() =>
anthropic.messages.create({
model: model ?? 'claude-sonnet-4-20250514',
max_tokens: 2048,
system: system ?? '',
messages,
})
);
const textBlock = result.content.find(b => b.type === 'text');
content = textBlock?.text ?? '';
}
res.json({ content });
} catch (error: any) {
res.status(error.status ?? 500).json({
error: error.message ?? 'Errore interno del server'
});
}
});
app.listen(3001, () =>
console.log('Proxy AI in ascolto su porta 3001')
);
8. Streaming odpowiedzi do frontendu
Aby zapewnić możliwość przesyłania strumieniowego do przeglądarki użytkownika, serwera proxy musi wspierać Zdarzenia wysyłane przez serwer (SSE). Ten protokół pozwala do serwera w celu stopniowego wysyłania danych do klienta za pośrednictwem pojedynczego łącza Połączenie HTTP.
// src/server/stream-endpoint.ts
app.post('/api/chat/stream', async (req, res) => {
const { provider, messages, system, model } = req.body;
// Configura headers SSE
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
try {
if (provider === 'openai') {
const stream = await openai.chat.completions.create({
model: model ?? 'gpt-4o',
messages,
stream: true,
});
for await (const chunk of stream) {
const content = chunk.choices[0]?.delta?.content;
if (content) {
res.write(`data: ${JSON.stringify({ content })}\n\n`);
}
}
} else {
const stream = anthropic.messages.stream({
model: model ?? 'claude-sonnet-4-20250514',
max_tokens: 2048,
system: system ?? '',
messages,
});
for await (const event of stream) {
if (
event.type === 'content_block_delta' &&
event.delta.type === 'text_delta'
) {
res.write(
`data: ${JSON.stringify({ content: event.delta.text })}\n\n`
);
}
}
}
res.write('data: [DONE]\n\n');
res.end();
} catch (error: any) {
res.write(
`data: ${JSON.stringify({ error: error.message })}\n\n`
);
res.end();
}
});
Klient frontendowy do przesyłania strumieniowego
// src/app/services/ai-chat.service.ts
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class AiChatService {
private apiUrl = '/api/chat';
streamChat(
provider: 'openai' | 'anthropic',
messages: Array<{ role: string; content: string }>,
system?: string
): Observable<string> {
return new Observable(subscriber => {
const controller = new AbortController();
fetch(`${this.apiUrl}/stream`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ provider, messages, system }),
signal: controller.signal,
})
.then(response => {
const reader = response.body!.getReader();
const decoder = new TextDecoder();
function read() {
reader.read().then(({ done, value }) => {
if (done) {
subscriber.complete();
return;
}
const text = decoder.decode(value);
const lines = text.split('\n');
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = line.slice(6);
if (data === '[DONE]') {
subscriber.complete();
return;
}
try {
const parsed = JSON.parse(data);
if (parsed.content) {
subscriber.next(parsed.content);
}
} catch {
// Ignora righe non parsabili
}
}
}
read();
});
}
read();
})
.catch(err => subscriber.error(err));
return () => controller.abort();
});
}
}
9. Zbuduj kompletny interfejs czatu
Łącząc wszystkie zbudowane do tej pory komponenty, możemy stworzyć interfejs Pełny czat obsługujący obu dostawców, zarządza historią wiadomości i pokazuje odpowiedzi przesyłane strumieniowo w czasie rzeczywistym.
// src/app/components/chat/chat.component.ts
import { Component, signal } from '@angular/core';
import { AiChatService } from '../../services/ai-chat.service';
interface Message {
role: 'user' | 'assistant';
content: string;
provider?: string;
timestamp: Date;
}
@Component({
selector: 'app-chat',
standalone: true,
template: `...`,
})
export class ChatComponent {
messages = signal<Message[]>([]);
inputText = signal('');
isLoading = signal(false);
provider = signal<'openai' | 'anthropic'>('anthropic');
currentStream = signal('');
constructor(private chatService: AiChatService) {}
sendMessage(): void {
const text = this.inputText().trim();
if (!text || this.isLoading()) return;
// Aggiungi messaggio utente
this.messages.update(msgs => [
...msgs,
{ role: 'user', content: text, timestamp: new Date() }
]);
this.inputText.set('');
this.isLoading.set(true);
this.currentStream.set('');
// Prepara messaggi per l'API
const apiMessages = this.messages().map(m => ({
role: m.role,
content: m.content,
}));
// Avvia streaming
this.chatService.streamChat(
this.provider(),
apiMessages,
'Sei un assistente esperto e amichevole.'
).subscribe({
next: (token) => {
this.currentStream.update(s => s + token);
},
complete: () => {
this.messages.update(msgs => [
...msgs,
{
role: 'assistant',
content: this.currentStream(),
provider: this.provider(),
timestamp: new Date(),
}
]);
this.currentStream.set('');
this.isLoading.set(false);
},
error: (err) => {
console.error('Errore streaming:', err);
this.isLoading.set(false);
},
});
}
}
10. Najlepsze praktyki i optymalizacja
Po wdrożeniu podstawowej integracji istnieje kilka zaawansowanych praktyk które znacząco poprawiają jakość, niezawodność i wydajność swojej aplikacji.
Ujednolicona abstrakcja dostawcy
Stworzenie wspólnego interfejsu umożliwia przełączanie się od jednego dostawcy do drugiego bez modyfikowania kodu aplikacji. Ułatwia to również wdrożenie strategii awaryjnych.
// src/lib/unified-ai.ts
interface AIProvider {
chat(messages: ChatMessage[], system?: string): Promise<string>;
stream(messages: ChatMessage[], system?: string): AsyncGenerator<string>;
}
export class UnifiedAI {
private providers: Map<string, AIProvider> = new Map();
private primaryProvider: string;
constructor(primary: string) {
this.primaryProvider = primary;
}
registerProvider(name: string, provider: AIProvider): void {
this.providers.set(name, provider);
}
async chatWithFallback(
messages: ChatMessage[],
system?: string
): Promise<{ content: string; provider: string }> {
const providerOrder = [
this.primaryProvider,
...Array.from(this.providers.keys())
.filter(p => p !== this.primaryProvider)
];
for (const name of providerOrder) {
const provider = this.providers.get(name);
if (!provider) continue;
try {
const content = await withRetry(
() => provider.chat(messages, system),
{ maxRetries: 2 }
);
return { content, provider: name };
} catch (error) {
console.warn(`Provider ${name} fallito, provo il successivo...`);
}
}
throw new Error('Tutti i provider AI sono non disponibili');
}
}
Typowe błędy, których należy unikać
- Ujawnij klucze API w interfejsie: zawsze używaj serwera proxy
- Ignorowanie limitów stawek: implementuje ogranicznik szybkości po stronie klienta
- Nie obsługuj przekroczeń limitu czasu: skonfiguruj odpowiednie limity czasu dla każdego żądania
- Wyślij całą swoją historię: obetnij stare wiadomości, aby zapisać tokeny
- Brak śledzenia kosztów: wdrożyć alerty dotyczące progów wydatków
- Kodowanie modelu na stałe: umożliwia konfigurowanie szablonu w celu łatwej aktualizacji
Lista kontrolna integracji
- Klucze API zapisane w zmiennych środowiskowych, a nigdy w kodzie
- Serwer proxy skonfigurowany z CORS i ograniczeniem szybkości
- Ponów próbę z zaimplementowanym wykładniczym wycofywaniem
- Przesyłanie strumieniowe działa dla obu dostawców
- Aktywne liczenie tokenów i monitorowanie kosztów
- Zarządzanie błędami za pomocą komunikatów informacyjnych dla użytkownika
- Testy integracyjne dla obu dostawców
- Scentralizowane rejestrowanie w celu debugowania
Wniosek
Integracja interfejsów API OpenAI i Anthropic wymaga dbałości o szczegóły ale po prawidłowym skonfigurowaniu oferuje solidną podstawę, na której można budować wydajne i niezawodne aplikacje AI. Wzór proxy chroni Twoje klucze, system ponawiania gwarantuje odporność, a przesyłanie strumieniowe zapewnia wspaniałe wrażenia użytkownik wysokiej jakości.
W następnym artykule zastanowimy się, jak to zrobić Lokalne LLM z Ollamą, eliminując zależność od chmury i otwierając ciekawe możliwości prywatność, koszty i rozwój offline.







