Przejdź do treści
AI & ML

ChatGPT API - Integracja AI w aplikacjach webowych

Opublikowano:
·5 min czytania·Autor: MDS Software Solutions Group

ChatGPT API Integracja

ai-ml

ChatGPT API - Integracja AI w aplikacjach webowych

OpenAI API to jedno z najpotezniejszych narzedzi dostepnych dla deweloperow, ktorzy chca wzbogacic swoje aplikacje o mozliwosci sztucznej inteligencji. Od generowania tekstu, przez analize dokumentow, po budowe zaawansowanych chatbotow - ChatGPT API otwiera drzwi do nowej generacji aplikacji webowych. W tym przewodniku omowimy wszystkie kluczowe aspekty integracji, od podstaw po zaawansowane techniki optymalizacji.

Przeglad OpenAI API#

OpenAI udostepnia RESTful API, ktore pozwala na interakcje z modelami jezykowymi z poziomu dowolnego jezyka programowania. Kluczowe elementy ekosystemu to:

  • Chat Completions API - glowny endpoint do konwersacji i generowania tekstu
  • Embeddings API - generowanie wektorowych reprezentacji tekstu
  • Moderation API - filtrowanie tresci pod katem bezpieczenstwa
  • Assistants API - zaawansowane asystenty z pamiecia i narzedziami
  • Images API (DALL-E) - generowanie i edycja obrazow
  • Audio API (Whisper, TTS) - transkrypcja mowy i synteza glosu

Instalacja i konfiguracja#

Zacznijmy od instalacji oficjalnego SDK dla Node.js:

npm install openai

Konfiguracja klienta:

import OpenAI from "openai";

const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY,
});

Klucz API uzyskasz w panelu platform.openai.com. Nigdy nie umieszczaj klucza bezposrednio w kodzie - zawsze korzystaj ze zmiennych srodowiskowych.

Modele GPT-4 i GPT-3.5#

OpenAI oferuje kilka modeli rozniących sie mozliwosciami, szybkoscia i kosztem:

GPT-4 Turbo (gpt-4-turbo)#

  • Najnowsza wersja GPT-4 z oknem kontekstowym 128K tokenow
  • Wiedza do kwietnia 2024
  • Doskonaly do zlozonych zadan: analiza kodu, rozumowanie wieloetapowe, generowanie dlugich tekstow
  • Koszt: ~$10/1M tokenow wejsciowych, ~$30/1M tokenow wyjsciowych

GPT-4o (gpt-4o)#

  • Zoptymalizowana wersja GPT-4 - szybsza i tansza
  • Multimodalna - obsluguje tekst, obrazy i audio
  • Okno kontekstowe 128K tokenow
  • Koszt: ~$2.50/1M tokenow wejsciowych, ~$10/1M tokenow wyjsciowych

GPT-3.5 Turbo (gpt-3.5-turbo)#

  • Szybki i ekonomiczny model do prostszych zadan
  • Okno kontekstowe 16K tokenow
  • Idealny do: klasyfikacji, podsumowań, prostych chatbotow
  • Koszt: ~$0.50/1M tokenow wejsciowych, ~$1.50/1M tokenow wyjsciowych
// Wybor modelu w zaleznosci od zadania
const model = taskComplexity === "high"
  ? "gpt-4-turbo"
  : taskComplexity === "medium"
    ? "gpt-4o"
    : "gpt-3.5-turbo";

Chat Completions API - podstawy#

Chat Completions API to glowny endpoint do interakcji z modelami GPT. Komunikacja odbywa sie za pomoca tablicy wiadomosci z rolami:

import OpenAI from "openai";

const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY,
});

async function chatCompletion() {
  const response = await openai.chat.completions.create({
    model: "gpt-4-turbo",
    messages: [
      {
        role: "system",
        content:
          "Jestes ekspertem ds. programowania webowego. " +
          "Odpowiadasz zwiezle i podajesz przyklady kodu.",
      },
      {
        role: "user",
        content: "Jak zaimplementowac middleware w Next.js?",
      },
    ],
    temperature: 0.7,
    max_tokens: 1000,
  });

  return response.choices[0].message.content;
}

Role w konwersacji#

  • system - instrukcje definiujace zachowanie modelu (persona, styl, ograniczenia)
  • user - wiadomosci od uzytkownika
  • assistant - odpowiedzi modelu (wykorzystywane do kontynuacji konwersacji)

Kluczowe parametry#

| Parametr | Opis | Zakres | |----------|------|--------| | temperature | Kreatywnosc odpowiedzi | 0.0 - 2.0 | | max_tokens | Maksymalna dlugosc odpowiedzi | 1 - limit modelu | | top_p | Nucleus sampling | 0.0 - 1.0 | | frequency_penalty | Kara za powtarzanie slow | -2.0 - 2.0 | | presence_penalty | Kara za powtarzanie tematow | -2.0 - 2.0 | | stop | Sekwencje zatrzymania | string[] |

Streaming - odpowiedzi w czasie rzeczywistym#

Streaming pozwala na wyswietlanie odpowiedzi token po tokenie, co znacznie poprawia UX:

async function streamChat(userMessage: string) {
  const stream = await openai.chat.completions.create({
    model: "gpt-4-turbo",
    messages: [
      { role: "system", content: "Jestes pomocnym asystentem." },
      { role: "user", content: userMessage },
    ],
    stream: true,
  });

  let fullResponse = "";

  for await (const chunk of stream) {
    const content = chunk.choices[0]?.delta?.content || "";
    fullResponse += content;
    process.stdout.write(content); // Wyswietlanie w czasie rzeczywistym
  }

  return fullResponse;
}

Streaming w Next.js z Server-Sent Events#

// app/api/chat/route.ts
import { NextRequest } from "next/server";
import OpenAI from "openai";

const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY,
});

export async function POST(req: NextRequest) {
  const { messages } = await req.json();

  const stream = await openai.chat.completions.create({
    model: "gpt-4-turbo",
    messages,
    stream: true,
  });

  const encoder = new TextEncoder();
  const readable = new ReadableStream({
    async start(controller) {
      for await (const chunk of stream) {
        const content = chunk.choices[0]?.delta?.content || "";
        if (content) {
          controller.enqueue(
            encoder.encode(`data: ${JSON.stringify({ content })}\n\n`)
          );
        }
      }
      controller.enqueue(encoder.encode("data: [DONE]\n\n"));
      controller.close();
    },
  });

  return new Response(readable, {
    headers: {
      "Content-Type": "text/event-stream",
      "Cache-Control": "no-cache",
      Connection: "keep-alive",
    },
  });
}

Function Calling i Tool Use#

Function calling pozwala modelowi na wywolywanie zdefiniowanych przez dewelopera funkcji, co umozliwia integracje z zewnetrznymi API i bazami danych:

import OpenAI from "openai";

const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY,
});

// Definicja narzedzi
const tools: OpenAI.Chat.Completions.ChatCompletionTool[] = [
  {
    type: "function",
    function: {
      name: "get_weather",
      description: "Pobiera aktualną pogode dla podanego miasta",
      parameters: {
        type: "object",
        properties: {
          city: {
            type: "string",
            description: "Nazwa miasta, np. Warszawa",
          },
          unit: {
            type: "string",
            enum: ["celsius", "fahrenheit"],
            description: "Jednostka temperatury",
          },
        },
        required: ["city"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "search_products",
      description:
        "Wyszukuje produkty w bazie danych na podstawie zapytania",
      parameters: {
        type: "object",
        properties: {
          query: { type: "string", description: "Fraza wyszukiwania" },
          category: { type: "string", description: "Kategoria produktu" },
          maxPrice: { type: "number", description: "Maksymalna cena" },
        },
        required: ["query"],
      },
    },
  },
];

// Implementacje funkcji
async function getWeather(city: string, unit = "celsius") {
  // Wywolanie zewnetrznego API pogodowego
  const response = await fetch(
    `https://api.weather.example/current?city=${city}&unit=${unit}`
  );
  return response.json();
}

async function searchProducts(
  query: string,
  category?: string,
  maxPrice?: number
) {
  // Zapytanie do bazy danych
  return db.products.findMany({
    where: {
      name: { contains: query },
      ...(category && { category }),
      ...(maxPrice && { price: { lte: maxPrice } }),
    },
  });
}

// Obsluga function calling
async function handleToolCalls(userMessage: string) {
  const response = await openai.chat.completions.create({
    model: "gpt-4-turbo",
    messages: [{ role: "user", content: userMessage }],
    tools,
    tool_choice: "auto",
  });

  const message = response.choices[0].message;

  if (message.tool_calls) {
    const toolResults = await Promise.all(
      message.tool_calls.map(async (toolCall) => {
        const args = JSON.parse(toolCall.function.arguments);

        let result;
        switch (toolCall.function.name) {
          case "get_weather":
            result = await getWeather(args.city, args.unit);
            break;
          case "search_products":
            result = await searchProducts(
              args.query,
              args.category,
              args.maxPrice
            );
            break;
          default:
            result = { error: "Nieznana funkcja" };
        }

        return {
          role: "tool" as const,
          tool_call_id: toolCall.id,
          content: JSON.stringify(result),
        };
      })
    );

    // Drugie wywolanie z wynikami narzedzi
    const finalResponse = await openai.chat.completions.create({
      model: "gpt-4-turbo",
      messages: [
        { role: "user", content: userMessage },
        message,
        ...toolResults,
      ],
    });

    return finalResponse.choices[0].message.content;
  }

  return message.content;
}

Embeddingi - wektorowe reprezentacje tekstu#

Embeddingi to numeryczne reprezentacje tekstu w przestrzeni wektorowej. Pozwalaja na wyszukiwanie semantyczne, klasteryzacje i porownywanie tresci:

// Generowanie embeddingu
async function generateEmbedding(text: string): Promise<number[]> {
  const response = await openai.embeddings.create({
    model: "text-embedding-3-small",
    input: text,
    dimensions: 1536,
  });

  return response.data[0].embedding;
}

// Obliczanie podobienstwa kosinusowego
function cosineSimilarity(a: number[], b: number[]): number {
  const dotProduct = a.reduce((sum, ai, i) => sum + ai * b[i], 0);
  const magnitudeA = Math.sqrt(a.reduce((sum, ai) => sum + ai * ai, 0));
  const magnitudeB = Math.sqrt(b.reduce((sum, bi) => sum + bi * bi, 0));
  return dotProduct / (magnitudeA * magnitudeB);
}

// Wyszukiwanie semantyczne
async function semanticSearch(
  query: string,
  documents: { id: string; text: string; embedding: number[] }[]
) {
  const queryEmbedding = await generateEmbedding(query);

  const results = documents
    .map((doc) => ({
      ...doc,
      similarity: cosineSimilarity(queryEmbedding, doc.embedding),
    }))
    .sort((a, b) => b.similarity - a.similarity)
    .slice(0, 5);

  return results;
}

Modele embeddingowe#

| Model | Wymiary | Koszt/1M tokenow | |-------|---------|-------------------| | text-embedding-3-small | 512-1536 | ~$0.02 | | text-embedding-3-large | 256-3072 | ~$0.13 | | text-embedding-ada-002 | 1536 | ~$0.10 |

Zarzadzanie tokenami#

Tokeny to podstawowa jednostka rozliczeniowa w OpenAI API. Efektywne zarzadzanie tokenami jest kluczowe dla kontroli kosztow:

import { encoding_for_model } from "tiktoken";

// Liczenie tokenow
function countTokens(text: string, model = "gpt-4-turbo"): number {
  const enc = encoding_for_model(model as any);
  const tokens = enc.encode(text);
  enc.free();
  return tokens.length;
}

// Przycinanie kontekstu do limitu tokenow
function trimMessages(
  messages: OpenAI.Chat.Completions.ChatCompletionMessageParam[],
  maxTokens: number
): OpenAI.Chat.Completions.ChatCompletionMessageParam[] {
  const systemMessage = messages.find((m) => m.role === "system");
  const conversationMessages = messages.filter((m) => m.role !== "system");

  let totalTokens = systemMessage
    ? countTokens(systemMessage.content as string)
    : 0;
  const trimmed: OpenAI.Chat.Completions.ChatCompletionMessageParam[] = [];

  // Iteracja od najnowszych wiadomosci
  for (let i = conversationMessages.length - 1; i >= 0; i--) {
    const msgTokens = countTokens(
      conversationMessages[i].content as string
    );
    if (totalTokens + msgTokens > maxTokens) break;
    totalTokens += msgTokens;
    trimmed.unshift(conversationMessages[i]);
  }

  if (systemMessage) trimmed.unshift(systemMessage);
  return trimmed;
}

Rate Limiting i obsluga bledow#

OpenAI stosuje limity zapytan (RPM - requests per minute) i tokenow (TPM - tokens per minute). Oto strategia obslugi:

import OpenAI from "openai";

// Klasa z retry i exponential backoff
class OpenAIClient {
  private client: OpenAI;
  private maxRetries: number;

  constructor(apiKey: string, maxRetries = 3) {
    this.client = new OpenAI({ apiKey });
    this.maxRetries = maxRetries;
  }

  async chatCompletion(
    params: OpenAI.Chat.Completions.ChatCompletionCreateParamsNonStreaming
  ) {
    let lastError: Error | null = null;

    for (let attempt = 0; attempt < this.maxRetries; attempt++) {
      try {
        return await this.client.chat.completions.create(params);
      } catch (error) {
        lastError = error as Error;

        if (error instanceof OpenAI.RateLimitError) {
          const waitTime = Math.pow(2, attempt) * 1000;
          console.warn(
            `Rate limit - czekam ${waitTime}ms (proba ${attempt + 1})`
          );
          await new Promise((resolve) => setTimeout(resolve, waitTime));
          continue;
        }

        if (error instanceof OpenAI.APIError) {
          if (error.status && error.status >= 500) {
            const waitTime = Math.pow(2, attempt) * 1000;
            await new Promise((resolve) => setTimeout(resolve, waitTime));
            continue;
          }
        }

        throw error;
      }
    }

    throw lastError;
  }
}

Optymalizacja kosztow#

Koszty API moga szybko rosnac. Oto sprawdzone strategie optymalizacji:

1. Wybor odpowiedniego modelu#

function selectModel(task: string): string {
  const simpleTaskPatterns = [
    /klasyfikacja|kategoria/i,
    /podsumowanie|streszczenie/i,
    /tlumaczenie|przetlumacz/i,
    /formatowanie|format/i,
  ];

  const isSimple = simpleTaskPatterns.some((p) => p.test(task));
  return isSimple ? "gpt-3.5-turbo" : "gpt-4o";
}

2. Cache odpowiedzi#

import { Redis } from "ioredis";

const redis = new Redis(process.env.REDIS_URL!);

async function cachedCompletion(
  messages: OpenAI.Chat.Completions.ChatCompletionMessageParam[],
  model: string
) {
  const cacheKey = `openai:${model}:${JSON.stringify(messages)}`;
  const cached = await redis.get(cacheKey);

  if (cached) {
    return JSON.parse(cached);
  }

  const response = await openai.chat.completions.create({
    model,
    messages,
  });

  await redis.setex(cacheKey, 3600, JSON.stringify(response));
  return response;
}

3. Kompresja promptow#

// Zamiast dlugich instrukcji, uzyj zwiezlych promptow
// ZLE:
const verbosePrompt =
  "Chcialabym, zebys przeanalizowal ponizszy tekst i wygenerowal " +
  "krotkie podsumowanie, ktore bedzie zawierac najwazniejsze informacje...";

// DOBRZE:
const concisePrompt =
  "Podsumuj tekst w 2-3 zdaniach, zachowujac kluczowe fakty:";

4. Batching zapytan#

async function batchProcess(items: string[], batchSize = 5) {
  const results: string[] = [];

  for (let i = 0; i < items.length; i += batchSize) {
    const batch = items.slice(i, i + batchSize);
    const promises = batch.map((item) =>
      openai.chat.completions.create({
        model: "gpt-3.5-turbo",
        messages: [{ role: "user", content: `Przetworz: ${item}` }],
      })
    );

    const batchResults = await Promise.all(promises);
    results.push(
      ...batchResults.map(
        (r) => r.choices[0].message.content || ""
      )
    );
  }

  return results;
}

Prompt Engineering - najlepsze praktyki#

Jakość odpowiedzi modelu zalezy w duzej mierze od jakosci promptu. Oto kluczowe techniki:

Few-shot prompting#

const messages: OpenAI.Chat.Completions.ChatCompletionMessageParam[] = [
  {
    role: "system",
    content: "Klasyfikujesz opinie klientow jako: pozytywna, negatywna, neutralna.",
  },
  {
    role: "user",
    content: "Swietny produkt, polecam kazdemu!",
  },
  {
    role: "assistant",
    content: "pozytywna",
  },
  {
    role: "user",
    content: "Produkt okej, nic specjalnego.",
  },
  {
    role: "assistant",
    content: "neutralna",
  },
  {
    role: "user",
    content: "Fatalna jakosc, nigdy wiecej.",
  },
];

Chain-of-Thought#

const systemPrompt = `Jestes ekspertem od analizy danych.
Gdy odpowiadasz:
1. Zidentyfikuj kluczowe dane wejsciowe
2. Opisz swoj tok rozumowania krok po kroku
3. Podaj wnioski z uzasadnieniem
4. Zakoncz konkretna rekomendacja

Mysli poczatkowe oznacz tagiem <thinking>, a finalna odpowiedz tagiem <answer>.`;

Structured Output#

const response = await openai.chat.completions.create({
  model: "gpt-4-turbo",
  messages: [
    {
      role: "system",
      content:
        "Zwracaj odpowiedzi wylacznie w formacie JSON. " +
        "Schemat: { summary: string, keywords: string[], sentiment: string, score: number }",
    },
    {
      role: "user",
      content: `Przeanalizuj opinie: "${reviewText}"`,
    },
  ],
  response_format: { type: "json_object" },
});

Budowanie chatbota konwersacyjnego#

Kompletna implementacja chatbota z pamiecia konwersacji:

import OpenAI from "openai";

interface ConversationMessage {
  role: "system" | "user" | "assistant";
  content: string;
}

class Chatbot {
  private openai: OpenAI;
  private conversations: Map<string, ConversationMessage[]>;
  private systemPrompt: string;
  private maxMessages: number;

  constructor(systemPrompt: string, maxMessages = 20) {
    this.openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
    this.conversations = new Map();
    this.systemPrompt = systemPrompt;
    this.maxMessages = maxMessages;
  }

  private getConversation(sessionId: string): ConversationMessage[] {
    if (!this.conversations.has(sessionId)) {
      this.conversations.set(sessionId, [
        { role: "system", content: this.systemPrompt },
      ]);
    }
    return this.conversations.get(sessionId)!;
  }

  async sendMessage(
    sessionId: string,
    userMessage: string
  ): Promise<string> {
    const conversation = this.getConversation(sessionId);
    conversation.push({ role: "user", content: userMessage });

    // Przycinanie do limitu wiadomosci
    if (conversation.length > this.maxMessages + 1) {
      const system = conversation[0];
      const recent = conversation.slice(-this.maxMessages);
      conversation.length = 0;
      conversation.push(system, ...recent);
    }

    const response = await this.openai.chat.completions.create({
      model: "gpt-4o",
      messages: conversation,
      temperature: 0.7,
      max_tokens: 800,
    });

    const assistantMessage =
      response.choices[0].message.content || "";
    conversation.push({ role: "assistant", content: assistantMessage });

    return assistantMessage;
  }

  clearConversation(sessionId: string): void {
    this.conversations.delete(sessionId);
  }
}

// Uzycie
const supportBot = new Chatbot(
  "Jestes asystentem obslugi klienta firmy TechShop. " +
    "Pomagasz w sprawach zamowien, zwrotow i informacji o produktach. " +
    "Odpowiadasz uprzejmie i zwiezle po polsku."
);

Integracja RAG (Retrieval-Augmented Generation)#

RAG pozwala modelowi odpowiadac na pytania na podstawie wlasnych danych, eliminujac halucynacje:

import OpenAI from "openai";
import { Pool } from "pg";

const pool = new Pool({ connectionString: process.env.DATABASE_URL });

// Wyszukiwanie relevantnych dokumentow
async function findRelevantDocs(
  query: string,
  limit = 5
): Promise<{ content: string; source: string; similarity: number }[]> {
  const queryEmbedding = await generateEmbedding(query);

  const result = await pool.query(
    `SELECT content, source,
            1 - (embedding <=> $1::vector) as similarity
     FROM documents
     WHERE 1 - (embedding <=> $1::vector) > 0.7
     ORDER BY embedding <=> $1::vector
     LIMIT $2`,
    [JSON.stringify(queryEmbedding), limit]
  );

  return result.rows;
}

// RAG pipeline
async function ragQuery(userQuestion: string): Promise<string> {
  // 1. Wyszukaj relevantne dokumenty
  const docs = await findRelevantDocs(userQuestion);

  // 2. Zbuduj kontekst
  const context = docs
    .map((d) => `[Zrodlo: ${d.source}]\n${d.content}`)
    .join("\n\n---\n\n");

  // 3. Wygeneruj odpowiedz
  const response = await openai.chat.completions.create({
    model: "gpt-4-turbo",
    messages: [
      {
        role: "system",
        content:
          "Odpowiadaj na pytania wylacznie na podstawie podanego kontekstu. " +
          "Jesli kontekst nie zawiera odpowiedzi, powiedz o tym wprost. " +
          "Cytuj zrodla w odpowiedzi.",
      },
      {
        role: "user",
        content: `Kontekst:\n${context}\n\nPytanie: ${userQuestion}`,
      },
    ],
    temperature: 0.3,
  });

  return response.choices[0].message.content || "";
}

Bezpieczenstwo i moderacja tresci#

OpenAI udostepnia Moderation API do filtrowania niebezpiecznych tresci:

// Moderacja tresci
async function moderateContent(
  text: string
): Promise<{ flagged: boolean; categories: string[] }> {
  const response = await openai.moderations.create({ input: text });
  const result = response.results[0];

  const flaggedCategories = Object.entries(result.categories)
    .filter(([, flagged]) => flagged)
    .map(([category]) => category);

  return {
    flagged: result.flagged,
    categories: flaggedCategories,
  };
}

// Middleware moderacji dla chatbota
async function safeChatMiddleware(
  userMessage: string,
  handler: (msg: string) => Promise<string>
): Promise<string> {
  // Sprawdz wiadomosc uzytkownika
  const inputModeration = await moderateContent(userMessage);
  if (inputModeration.flagged) {
    console.warn("Zablokowano wiadomosc:", inputModeration.categories);
    return "Przepraszam, ale nie moge przetworzyc tej wiadomosci.";
  }

  // Wygeneruj odpowiedz
  const response = await handler(userMessage);

  // Sprawdz odpowiedz modelu
  const outputModeration = await moderateContent(response);
  if (outputModeration.flagged) {
    console.warn("Zablokowano odpowiedz:", outputModeration.categories);
    return "Przepraszam, nie jestem w stanie udzielić odpowiedzi na to pytanie.";
  }

  return response;
}

Najlepsze praktyki bezpieczenstwa#

  1. Walidacja wejscia - sprawdzaj dlugosc i zawartosc wiadomosci uzytkownikow
  2. Rate limiting - ogranicz liczbe zapytan na uzytkownika
  3. Prompt injection protection - oddzielaj instrukcje systemowe od danych uzytkownika
  4. Logowanie - monitoruj uzycie API i podejrzane wzorce
  5. Koszty - ustaw limity budzetowe w panelu OpenAI

Integracja z Next.js - kompletny przyklad API Route#

Pelna implementacja endpointu chatowego w Next.js App Router:

// app/api/chat/route.ts
import { NextRequest, NextResponse } from "next/server";
import OpenAI from "openai";
import { Redis } from "ioredis";

const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
const redis = new Redis(process.env.REDIS_URL!);

// Rate limiting
async function checkRateLimit(ip: string): Promise<boolean> {
  const key = `ratelimit:chat:${ip}`;
  const requests = await redis.incr(key);
  if (requests === 1) await redis.expire(key, 60);
  return requests <= 10; // max 10 req/min
}

// Walidacja wejscia
function validateInput(body: unknown): {
  valid: boolean;
  messages?: OpenAI.Chat.Completions.ChatCompletionMessageParam[];
  error?: string;
} {
  if (!body || typeof body !== "object") {
    return { valid: false, error: "Nieprawidlowe dane wejsciowe" };
  }

  const { messages } = body as { messages: unknown };
  if (!Array.isArray(messages) || messages.length === 0) {
    return { valid: false, error: "Brak wiadomosci" };
  }

  if (messages.length > 50) {
    return { valid: false, error: "Za wiele wiadomosci" };
  }

  return { valid: true, messages };
}

export async function POST(req: NextRequest) {
  try {
    // Rate limiting
    const ip = req.headers.get("x-forwarded-for") || "unknown";
    const allowed = await checkRateLimit(ip);
    if (!allowed) {
      return NextResponse.json(
        { error: "Zbyt wiele zapytan. Sprobuj ponownie za minute." },
        { status: 429 }
      );
    }

    // Walidacja
    const body = await req.json();
    const validation = validateInput(body);
    if (!validation.valid) {
      return NextResponse.json(
        { error: validation.error },
        { status: 400 }
      );
    }

    // Moderacja ostatniej wiadomosci
    const lastMessage = validation.messages![
      validation.messages!.length - 1
    ];
    if (lastMessage.role === "user") {
      const moderation = await openai.moderations.create({
        input: lastMessage.content as string,
      });
      if (moderation.results[0].flagged) {
        return NextResponse.json(
          { error: "Wiadomosc narusza zasady uzytkowania." },
          { status: 400 }
        );
      }
    }

    // Streaming response
    const stream = await openai.chat.completions.create({
      model: "gpt-4o",
      messages: [
        {
          role: "system",
          content:
            "Jestes pomocnym asystentem. " +
            "Odpowiadasz po polsku, zwiezle i merytorycznie.",
        },
        ...validation.messages!,
      ],
      stream: true,
      temperature: 0.7,
      max_tokens: 1000,
    });

    const encoder = new TextEncoder();
    const readable = new ReadableStream({
      async start(controller) {
        try {
          for await (const chunk of stream) {
            const content = chunk.choices[0]?.delta?.content;
            if (content) {
              controller.enqueue(
                encoder.encode(`data: ${JSON.stringify({ content })}\n\n`)
              );
            }
          }
          controller.enqueue(encoder.encode("data: [DONE]\n\n"));
        } catch (err) {
          controller.error(err);
        } finally {
          controller.close();
        }
      },
    });

    return new Response(readable, {
      headers: {
        "Content-Type": "text/event-stream",
        "Cache-Control": "no-cache",
        Connection: "keep-alive",
      },
    });
  } catch (error) {
    console.error("Chat API error:", error);
    return NextResponse.json(
      { error: "Wewnetrzny blad serwera" },
      { status: 500 }
    );
  }
}

Porownanie z Claude API i Gemini API#

Wybierajac dostawce AI API, warto porownac kluczowe roznice:

| Cecha | OpenAI (GPT-4) | Anthropic (Claude 3) | Google (Gemini Pro) | |-------|-----------------|---------------------|---------------------| | Okno kontekstowe | 128K | 200K | 1M+ | | Function calling | Tak | Tak (tool use) | Tak | | Streaming | Tak | Tak | Tak | | Multimodal | Tak (vision, audio) | Tak (vision) | Tak (vision, audio, video) | | Structured output | JSON mode | JSON z XML | JSON mode | | Cena (input/1M) | od $2.50 | od $3 | od $1.25 | | Cena (output/1M) | od $10 | od $15 | od $5 |

Kiedy wybrac OpenAI?#

  • Najszerszy ekosystem - Assistants API, fine-tuning, DALL-E, Whisper
  • Function calling - najbardziej dojrzala implementacja
  • Spolecznosc - najwieksza baza wiedzy i przykladow
  • Fine-tuning - mozliwosc dostosowania modeli do wlasnych danych

Kiedy rozwazyc alternatywy?#

  • Claude - dlugi kontekst (200K), lepsza zgodnosc z instrukcjami, bezpieczenstwo
  • Gemini - milionowe okno kontekstowe, integracja z Google Cloud, nizsza cena
  • Lokalne modele (Ollama/Llama) - pelna prywatnosc danych, brak kosztow API

Ujednolicona warstwa abstrakcji#

// Wspolny interfejs dla roznych dostawcow AI
interface AIProvider {
  chat(messages: Message[], options?: ChatOptions): Promise<string>;
  stream(
    messages: Message[],
    options?: ChatOptions
  ): AsyncIterable<string>;
  embed(text: string): Promise<number[]>;
}

class OpenAIProvider implements AIProvider {
  async chat(messages: Message[], options?: ChatOptions) {
    const response = await openai.chat.completions.create({
      model: options?.model || "gpt-4o",
      messages: messages as any,
    });
    return response.choices[0].message.content || "";
  }

  async *stream(messages: Message[], options?: ChatOptions) {
    const stream = await openai.chat.completions.create({
      model: options?.model || "gpt-4o",
      messages: messages as any,
      stream: true,
    });
    for await (const chunk of stream) {
      yield chunk.choices[0]?.delta?.content || "";
    }
  }

  async embed(text: string) {
    const response = await openai.embeddings.create({
      model: "text-embedding-3-small",
      input: text,
    });
    return response.data[0].embedding;
  }
}

Podsumowanie#

ChatGPT API to potezne narzedzie, ktore otwiera ogromne mozliwosci dla deweloperow aplikacji webowych. Kluczowe wnioski:

  1. Wybieraj model swiadomie - GPT-4 do zlozonych zadan, GPT-3.5 do prostych operacji
  2. Implementuj streaming - znacznie poprawia doswiadczenie uzytkownika
  3. Korzystaj z function calling - integruje AI z logika biznesowa aplikacji
  4. Stosuj RAG - eliminuje halucynacje i pozwala korzystac z wlasnych danych
  5. Optymalizuj koszty - cache, odpowiedni model, kompresja promptow
  6. Dbaj o bezpieczenstwo - moderacja, rate limiting, walidacja wejscia

Potrzebujesz integracji AI w swojej aplikacji?#

MDS Software Solutions Group specjalizuje sie w integracji rozwiazan AI z aplikacjami webowymi. Budujemy inteligentne chatboty, systemy RAG, wyszukiwarki semantyczne i narzedzia automatyzacji oparte na OpenAI API, Claude i innych modelach LLM.

Skontaktuj sie z nami, aby omowic, jak sztuczna inteligencja moze usprawnić Twoj biznes i dac Ci przewage konkurencyjna. Oferujemy konsultacje, projektowanie architektury AI oraz pelna implementacje - od prototypu po produkcje.

Autor
MDS Software Solutions Group

Zespół ekspertów programistycznych specjalizujących się w nowoczesnych technologiach webowych.

ChatGPT API - Integracja AI w aplikacjach webowych | MDS Software Solutions Group | MDS Software Solutions Group