26.5.2. API wrapper implementációk

2025.10.06.
AI Biztonság Blog

Amikor egy Red Teaming művelet során több különböző nyelvi modellt (LLM) kell tesztelned, gyorsan szembesülsz a szolgáltatók közötti API-különbségekkel. Az egyik másképp kezeli a rendszerszintű üzeneteket, a másiknak eltérőek a paraméternevei, a harmadik pedig teljesen más authentikációs sémát használ. Az API wrapper (vagy becsomagoló) pont ezt a káoszt hivatott megszüntetni: egy egységes, általad definiált interfészt hoz létre, amely mögé elrejtheted a különböző implementációs részleteket.

Kapcsolati űrlap

AI Biztonság kérdésed van? Itt elérsz minket:

Ez azonban messze nem csak kényelmi funkció. A wrapper egy stratégiai kontrollpont, amely lehetővé teszi a központosított naplózást, költségkövetést, biztonsági szűrést és a tesztek konzisztens végrehajtását, függetlenül az éppen használt modelltől.

Az egységes interfész koncepciója

A cél egy olyan absztrakt réteg létrehozása, amely a Red Teaming eszközeid számára egységesen jeleníti meg a különböző LLM-eket. Ahelyett, hogy a kódod tele lenne `if provider == ‘openai’: … elif provider == ‘anthropic’: …` típusú elágazásokkal, egyszerűen meghívsz egy közös metódust, például `generate_response()`. A wrapper feladata, hogy ezt a hívást lefordítsa az adott szolgáltató specifikus API-jára.

Red Teaming Eszköz generate_response() API Wrapper Réteg OpenAIWrapper AnthropicWrapper LocalLLMWrapper OpenAI API Anthropic API Lokális Modell
1. ábra: Az API wrapper réteg elvonatkoztatja a Red Teaming eszközt a konkrét LLM implementációktól.

Egy alapvető wrapper osztály (interfész) definiálása Pythonban így nézhet ki. Ez csak egy váz, ami kikényszeríti a konzisztenciát a konkrét implementációkban.


from abc import ABC, abstractmethod

class BaseLLMWrapper(ABC):
 """
 Absztrakt alaposztály minden LLM wrapper számára.
 Meghatározza a kötelezően implementálandó metódusokat.
 """
 def __init__(self, api_key: str, model_name: str):
 self.api_key = api_key
 self.model_name = model_name

 @abstractmethod
 def generate_response(self, prompt: str, **kwargs) -> dict:
 """
 Egy prompt alapján generál választ a modelltől.
 A kwargs további paramétereket (pl. temperature) tartalmazhat.
 Visszatér egy szótárral, ami a választ és a metaadatokat tartalmazza.
 """
 pass
 

A wrapper kiterjesztése Red Teaming funkciókkal

Az igazi ereje a wrappernek akkor mutatkozik meg, amikor Red Teaming-specifikus logikával bővítjük. Minden API hívás keresztülmegy ezen a rétegen, így tökéletes hely a beavatkozásra.

Költségkövetés és Rate Limiting

Az automatizált tesztek könnyen elszabadíthatják a költségeket. A wrapperbe épített számlálók segítenek ezt kordában tartani. A hívás előtt és után naplózhatod a felhasznált tokenek számát, és a szolgáltató árazása alapján azonnal kalkulálhatsz egy becsült költséget.


# Egy konkrét wrapper implementáció (pl. OpenAI) metódusának részlete
def generate_response(self, prompt: str, **kwargs) -> dict:
 # ... kliens inicializálása ...
 
 # Metaadatok gyűjtésének kezdete
 start_time = time.time()
 
 # API hívás
 response = self.client.chat.completions.create(
 model=self.model_name,
 messages=[{"role": "user", "content": prompt}],
 **kwargs
 )
 
 latency = time.time() - start_time
 
 # Költség- és token-információk kinyerése
 usage = response.usage
 input_tokens = usage.prompt_tokens
 output_tokens = usage.completion_tokens
 
 # A naplózáshoz vagy további feldolgozáshoz visszaadott adatok
 return {
 "response_text": response.choices[0].message.content,
 "metadata": {
 "input_tokens": input_tokens,
 "output_tokens": output_tokens,
 "latency_ms": int(latency * 1000)
 # ... itt lehetne a költségkalkuláció is ...
 }
 }
 

Gyakorlati tipp

Implementálj egy egyszerű rate limitert (például egy „token bucket” algoritmust) a wrapperen belül, hogy elkerüld a szolgáltatói limitek túllépését, ami a tesztjeid megbízhatóságát rontaná.

Adatgyűjtés és központi naplózás

A wrapper a legmegfelelőbb hely minden interakció naplózására. Ments el minden egyes promptot, a kapott választ, a paramétereket (hőmérséklet, top_p stb.), a késleltetést és a token-használatot. Ezek az adatok felbecsülhetetlen értékűek lesznek a későbbi elemzések és a monitoring dashboardok számára. A strukturált (pl. JSON) naplózás megkönnyíti az automatizált feldolgozást.

Be- és kimeneti szűrés

A wrapper lehetővé teszi, hogy automatikusan módosítsd vagy ellenőrizd a modellnek küldött és onnan érkező adatokat.

  • Bemeneti szűrés (pre-processing): Automatikusan eltávolíthatsz személyes adatokat (PII), hozzáadhatsz egy standard rendszerüzenetet (pl. „Viselkedj segítőkész asszisztensként!”), vagy átalakíthatod a promptot egy specifikus formátumra.
  • Kimeneti szűrés (post-processing): Ellenőrizheted a választ káros tartalmakra, detektálhatsz bizonyos kulcsszavakat (pl. „sajnálom, de nem segíthetek”), vagy megpróbálhatsz strukturált adatot (pl. JSON) kinyerni a nyers szövegből.

Több szolgáltató kezelése Factory mintával

Ha már több wrapper implementációd is van (egy az OpenAI-hoz, egy az Anthropic-hoz, stb.), egy Factory (gyártó) tervezési minta segítségével teheted a kódodat tisztábbá és könnyebben bővíthetővé. A factory egy egyszerű függvény vagy osztály, ami a kért szolgáltató neve alapján visszaadja a megfelelő wrapper objektumot.


# Külön fájlokban definiált wrapper osztályok
from .openai_wrapper import OpenAIWrapper
from .anthropic_wrapper import AnthropicWrapper

def get_llm_client(provider: str, api_key: str, model: str) -> BaseLLMWrapper:
 """
 LLM Wrapper Factory.
 Visszaadja a megfelelő wrapper példányt a szolgáltató neve alapján.
 """
 if provider == "openai":
 return OpenAIWrapper(api_key=api_key, model_name=model)
 elif provider == "anthropic":
 return AnthropicWrapper(api_key=api_key, model_name=model)
 # ... további szolgáltatók ...
 else:
 raise ValueError(f"Ismeretlen szolgáltató: {provider}")

# Használat a Red Teaming szkriptben:
# Ahelyett, hogy közvetlenül példányosítanánk, a factory-t hívjuk.
gpt_client = get_llm_client("openai", OPENAI_API_KEY, "gpt-4-turbo")
claude_client = get_llm_client("anthropic", ANTHROPIC_API_KEY, "claude-3-opus-20240229")

# Innentől mindkét kliens ugyanazt az interfészt használja
response_gpt = gpt_client.generate_response("Mesélj egy viccet!")
response_claude = claude_client.generate_response("Mesélj egy viccet!")
 

Ezzel a megközelítéssel a tesztlogikád teljesen függetlenné válik a konkrét LLM-szolgáltatótól. Új modell hozzáadása mindössze egy új wrapper osztály és egy sor hozzáadását jelenti a factory függvényben, anélkül, hogy a meglévő teszteseteket módosítani kellene.