Představuji knihovnu AI Access: jednotné rozhraní pro různé LLM v PHP

David Grudl
Nette Core | 8268
+
+15
-

Ahoj,

poslední dobou hodně pracuju s různými AI modely (OpenAI, Claude, Gemini, Grok…), takže mě frustrovalo, že má každý z nich jiný API styl, jiné SDK, některé naopak žádného klienta v PHP nemají, některé knihovny jsou větší než celé Nette, mají různé pojmenování parametrů, odlišné výstupy atd.

Tak jsem napsal vlastní knihovnu. Malou, jednoduchou, čistě v PHP, s jednotným rozhraním pro všechny hlavní poskytovatele. Výměna modelu nebo výměna poskytovatele je tak otázkou pár znaků.

📦 Knihovna ai-access

Vydal jsem první verzi 0.1 – co umí?

  • 📤 Chatování – jednotné metody createChat(), sendMessage() atd., stejné pro OpenAI, Claude, Gemini, DeepSeek i Grok (xAI)
  • 🧠 Embeddings – podpora pro OpenAI a Gemini, snadno použitelné v RAG projektech
  • 📚 Batch API – chatování ale za poloviční cenu
  • ⚙️ Nastavení parametrů (temperature, topP, tools, …)
  • 📜 Historie konverzace, systemInstruction, funkce, tokenové limity…

Podporuje 90% toho, co s LLM dělám, ale postupně chci přidávat další funkcionalitu.

Instalace (funguje s PHP 8.1+)

composer require ai-access/ai-access

Základní použití:

use AIAccess\Provider\OpenAI\Client;

$client = new Client(apiKey: 'sk-...'); // nahraď svým klíčem
$chat = $client->createChat('gpt-4o-mini');

$response = $chat->sendMessage('Napiš krátké haiku o PHP.');
echo $response->getText(); // např. "Elegantní kód / proudí mezi tagy / server tiše zpívá"

Metoda $response->getFinishReason() vrací enum AIAccess\Chat\FinishReason, který říká, proč model ukončil odpověď (např. TokenLimit, ContentFiltered, Complete…).

To se hodí pro ošetření případů, kdy třeba model odpověď nestihl dokončit a tak:

use AIAccess\Chat\FinishReason;

$reason = $response->getFinishReason();

if ($reason === FinishReason::Complete) {
	echo "✅ Model odpověď dokončil.\n";
} elseif ($reason === FinishReason::TokenLimit) {
	echo "⚠️ Odpověď byla přerušena tokenovým limitem. Dopsání…\n";
	$response = $chat->sendMessage(); // pokračuje tam, kde skončil
	echo $response->getText();
}

Knihovna má vlastní výjimkovou hierarchii:

AIAccess\ServiceException ← základ pro všechny chyby služby
├── ApiException ← API odpovědělo validně, ale s chybou (např. 401, 429, bad input…)
├── CommunicationException ← Nelze se spojit / odpověď je nečitelná (timeout, DNS, invalid JSON…)
└── UnexpectedResponseException ← API odpovědělo zvláštně (změna struktury, chybějící pole…)

Můžeš tedy zpracovat všechny chyby jednotně:

use AIAccess\ServiceException;

try {
	$response = $chat->sendMessage('Napiš článek o PHP.');
	echo $response->getText();

} catch (ServiceException $e) {
	echo "Chyba při komunikaci s AI: " . $e->getMessage();
}

A nebo rozlišit konkrétní typy podle potřeby (např. při rate-limitu zkusit znovu atd.).

💸 Batch API = za poloviční cenu

U OpenAI a Claude lze posílat dávky dotazů (batch), které se zpracují na pozadí.
Výhoda? Mnohem nižší cena, např. OpenAI GPT-4o batch = 50 % běžné ceny.

Konverzací lze do dávky poslat víc, každou si pojmenuješ (customId), abys později poznal, co je co:

use AIAccess\Provider\Claude\Client;
use AIAccess\Chat\Role;

$client = new Client($apiKey);
$batch = $client->createBatch();

$chat1 = $batch->addChat('claude-3-haiku-20240307', customId: 'pozdrav');
$chat1->addMessage('Ahoj!', Role::User);

$chat2 = $batch->addChat('claude-3-haiku-20240307', customId: 'tip-na-clanek');
$chat2->addMessage('O čem psát v PHP?', Role::User);

$response = $batch->submit();
echo "Batch ID: " . $response->getId(); // uložit pro kontrolu později

Zpracování je asynchronní (trvá minuty až hodiny), výsledek zjistíš třeba v CRONu:

$batch = $client->retrieveBatch($batchId);

if ($batch->getStatus()->isCompleted()) {
	foreach ($batch->getMessages() as $customId => $msg) {
		echo "[$customId] " . $msg->getText() . "\n";
	}
}

🧬 Embeddings – najdi podobné články

Embeddings jsou vektory, které reprezentují význam textu. Využívám je třeba na phpFashion.com pro doporučování podobných článků.

$texts = [
	'Nette a komponentový přístup',
	'Novinky v PHP 8.3',
	'Jak psát bezpečné šablony',
];

$embeddings = $client->calculateEmbeddings(
	model: 'text-embedding-3-small',
	input: $texts
);

foreach ($embeddings as $index => $vector) {
	echo "Text: " . $texts[$index] . "\n";
	echo "Rozměr vektoru: " . count($vector->getVector()) . "\n";
}

Knihovna obsahuje počítání vzdálenosti vektorů

Budu rád za zpětnou vazbu, testování, bugreporty nebo nápady na vylepšení.

🧙 Jeden klient vládne všem…