Modele i platformy AI
WywoÅania Asynchroniczne API LLM w Pythonie: Kompleksowy Przewodnik
Jako deweloperzy i naukowcy danych, czÄsto musimy wchodziÄ w interakcje z tymi potÄÅžnymi modelami za pomocÄ API. Jednak gdy nasze aplikacje rosnÄ w zÅoÅžonoÅci i skali, potrzeba wydajnych i efektywnych interakcji API staje siÄ kluczowa. To wÅaÅnie tutaj programowanie asynchroniczne bÅyszczy, pozwalajÄ c nam maksymalizowaÄ przepÅywnoÅÄ i minimalizowaÄ opÃģÅšnienia podczas pracy z API LLM.
W tym kompleksowym przewodniku, bÄdziemy eksplorowaÄ Åwiat asynchronicznych wywoÅaÅ API LLM w Pythonie. Pokryjemy wszystko, od podstaw programowania asynchronicznego po zaawansowane techniki obsÅugi zÅoÅžonych przepÅywÃģw pracy. Po przeczytaniu tego artykuÅu, bÄdziesz miaÅ solidne zrozumienie, jak wykorzystaÄ programowanie asynchroniczne, aby zwiÄkszyÄ wydajnoÅÄ swoich aplikacji zasilanych przez LLM.
Przed tym, jak zagÅÄbimy siÄ w szczegÃģÅy asynchronicznych wywoÅaÅ API LLM, ustanÃģwmy solidnÄ podstawÄ w pojÄciach programowania asynchronicznego.
Programowanie asynchroniczne pozwala na wykonywanie wielu operacji jednoczeÅnie bez blokowania gÅÃģwnego wÄ tku wykonywania. W Pythonie, jest to gÅÃģwnie osiÄ gane za pomocÄ moduÅu asyncio, ktÃģry zapewnia ramy dla pisania kodu wspÃģÅbieÅžnego za pomocÄ koreutin, pÄtli zdarzeÅ i przyszÅoÅci.
Kluczowe pojÄcia:
- Koreutyny: Funkcje zdefiniowane z async def, ktÃģre mogÄ byÄ wstrzymane i wznowione.
- PÄtla zdarzeÅ: Centralny mechanizm wykonywania, ktÃģry zarzÄ dza i uruchamia zadania asynchroniczne.
- Obiekty oczekujÄ ce: Obiekty, ktÃģre mogÄ byÄ uÅžywane z kluczem await (koreutyny, zadania, przyszÅoÅci).
Oto prosty przykÅad, ktÃģry ilustruje te pojÄcia:
import asyncio
<p>async def greet(name):</p>
<p> await asyncio.sleep(1) # Symulacja operacji wejÅcia/wyjÅcia</p>
<p> print(f"CzeÅÄ, {name}!")</p>
<p>async def main():</p>
<p> await asyncio.gather(</p>
<p> greet("Alice"),</p>
<p> greet("Bob"),</p>
<p> greet("Charlie")</p>
<p> )</p>
asyncio.run(main())
W tym przykÅadzie, definiujemy funkcjÄ asynchronicznÄ
greet, ktÃģra symuluje operacjÄ wejÅcia/wyjÅcia za pomocÄ
asyncio.sleep(). Funkcja main uÅžywa asyncio.gather(), aby uruchomiÄ wiele powitaÅ jednoczeÅnie. Pomimo opÃģÅšnienia spowodowanego przez asyncio.sleep(), wszystkie trzy powitania zostanÄ
wydrukowane po okoÅo 1 sekundzie, demonstrujÄ
c moc wykonywania asynchronicznego.
Potrzeba AsynchronicznoÅci w WywoÅaniach API LLM
Podczas pracy z API LLM, czÄsto spotykamy siÄ z sytuacjami, w ktÃģrych musimy wykonaÄ wiele wywoÅaÅ API, albo sekwencyjnie, albo rÃģwnolegle. Tradycyjny kod synchroniczny moÅže prowadziÄ do znacznych wÄ skich gardeÅ wydajnoÅci, szczegÃģlnie podczas radzenia sobie z operacjami o wysokiej latencji, takimi jak ÅžÄ dania sieciowe do usÅug LLM.
RozwaÅžmy sytuacjÄ, w ktÃģrej musimy wygenerowaÄ podsumowania dla 100 rÃģÅžnych artykuÅÃģw za pomocÄ API LLM. Z podejÅciem synchronicznym, kaÅžde wywoÅanie API zablokuje siÄ, aÅž do otrzymania odpowiedzi, potencjalnie zajmujÄ c kilka minut, aby ukoÅczyÄ wszystkie ÅžÄ dania. PodejÅcie asynchroniczne pozwala nam natomiast zainicjowaÄ wiele wywoÅaÅ API jednoczeÅnie, znacznie redukujÄ c caÅkowity czas wykonywania.
Konfigurowanie Årodowiska
Aby rozpoczÄ Ä pracÄ z asynchronicznymi wywoÅaniami API LLM, musisz skonfigurowaÄ swoje Årodowisko Pythona z niezbÄdnymi bibliotekami. Oto, co bÄdziesz potrzebowaÄ:
- Python 3.7 lub nowszy (dla natywnego wsparcia asyncio)
- aiohttp: Asynchroniczna biblioteka klienta HTTP
- openai: Oficjalna biblioteka klienta OpenAI dla Pythona (jeÅli uÅžywasz modeli GPT OpenAI)
- langchain: Ramy dla budowania aplikacji z LLM (opcjonalnie, ale zalecane dla zÅoÅžonych przepÅywÃģw pracy)
MoÅžesz zainstalowaÄ te zaleÅžnoÅci za pomocÄ pip:
<p>pip install aiohttp openai langchain <div class="relative flex flex-col rounded-lg">
Podstawowe Asynchroniczne WywoÅania API LLM z asyncio i aiohttp
Zacznijmy od wykonania prostego asynchronicznego wywoÅania do API LLM za pomocÄ aiohttp. BÄdziemy uÅžywaÄ API GPT-3.5 OpenAI jako przykÅadu, ale pojÄcia te majÄ zastosowanie do innych API LLM.
import asyncio
import aiohttp
from openai import AsyncOpenAI
<p>async def generate_text(prompt, client):</p>
<p> response = await client.chat.completions.create(</p>
<p> model="gpt-3.5-turbo",</p>
<p> messages=[{"role": "user", "content": prompt}]</p>
<p> )</p>
<p> return response.choices[0].message.content</p>
<p>async def main():</p>
<p> prompts = [</p>
<p> "WyjaÅnij kwantowÄ
komputacjÄ w prostych sÅowach.",</p>
<p> "Napisz haiku o sztucznej inteligencji.",</p>
<p> "Opisz proces fotosyntezy."</p>
<p> ]</p>
<p>async with AsyncOpenAI() as client:</p>
<p> tasks = [generate_text(prompt, client) for prompt in prompts]</p>
<p> results = await asyncio.gather(*tasks)</p>
<p>for prompt, result in zip(prompts, results):</p>
<p> print(f"Prompt: {prompt}\nOdpowiedÅš: {result}\n")</p>
asyncio.run(main())
W tym przykÅadzie, definiujemy funkcjÄ asynchronicznÄ
generate_text, ktÃģra wykonuje wywoÅanie do API OpenAI za pomocÄ
klienta AsyncOpenAI. Funkcja main tworzy wiele zadaÅ dla rÃģÅžnych prompty i uÅžywa asyncio.gather(), aby uruchomiÄ je jednoczeÅnie.
To podejÅcie pozwala nam wysyÅaÄ wiele ÅžÄ daÅ do API LLM jednoczeÅnie, znacznie redukujÄ c caÅkowity czas przetwarzania.
Zaawansowane Techniki: Partionowanie i Kontrola WspÃģÅbieÅžnoÅci
Podczas gdy poprzedni przykÅad demonstruje podstawy asynchronicznych wywoÅaÅ API LLM, aplikacje rzeczywiste czÄsto wymagajÄ bardziej zaawansowanych podejÅÄ. RozwaÅžmy dwie waÅžne techniki: partionowanie ÅžÄ daÅ i kontrola wspÃģÅbieÅžnoÅci.
Partionowanie ÅŧÄ daÅ: Podczas radzenia sobie z duÅžÄ liczbÄ prompty, czÄsto jest bardziej efektywne partionowanie ich w grupy, zamiast wysyÅania poszczegÃģlnych ÅžÄ daÅ dla kaÅždego promptu. To redukuje nakÅad pracy zwiÄ zany z wieloma wywoÅaniami API i moÅže prowadziÄ do lepszej wydajnoÅci.
import asyncio
from openai import AsyncOpenAI
<p>async def process_batch(batch, client):</p>
<p> responses = await asyncio.gather(*[</p>
<p> client.chat.completions.create(</p>
<p> model="gpt-3.5-turbo",</p>
<p> messages=[{"role": "user", "content": prompt}]</p>
<p> ) for prompt in batch</p>
<p> ])</p>
<p> return [response.choices[0].message.content for response in responses]</p>
<p>async def main():</p>
<p> prompts = [f"Powiedz mi fakt o liczbie {i}" for i in range(100)]</p>
<p> batch_size = 10</p>
<p>async with AsyncOpenAI() as client:</p>
<p> results = []</p>
<p> for i in range(0, len(prompts), batch_size):</p>
<p> batch = prompts[i:i+batch_size]</p>
<p> batch_results = await process_batch(batch, client)</p>
<p> results.extend(batch_results)</p>
<p>for prompt, result in zip(prompts, results):</p>
<p> print(f"Prompt: {prompt}\nOdpowiedÅš: {result}\n")</p>
asyncio.run(main())
Kontrola WspÃģÅbieÅžnoÅci: Podczas gdy programowanie asynchroniczne pozwala na wykonywanie wspÃģÅbieÅžne, waÅžne jest kontrolowanie poziomu wspÃģÅbieÅžnoÅci, aby uniknÄ Ä przeciÄ Åženia serwera API lub przekroczenia limitÃģw stawek. MoÅžemy uÅžyÄ asyncio.Semaphore do tego celu.
import asyncio
from openai import AsyncOpenAI
<p>async def generate_text(prompt, client, semaphore):</p>
<p> async with semaphore:</p>
<p> response = await client.chat.completions.create(</p>
<p> model="gpt-3.5-turbo",</p>
<p> messages=[{"role": "user", "content": prompt}]</p>
<p> )</p>
<p> return response.choices[0].message.content</p>
<p>async def main():</p>
<p> prompts = [f"Powiedz mi fakt o liczbie {i}" for i in range(100)]</p>
<p> max_concurrent_requests = 5</p>
<p> semaphore = asyncio.Semaphore(max_concurrent_requests)</p>
<p>async with AsyncOpenAI() as client:</p>
<p> tasks = [generate_text(prompt, client, semaphore) for prompt in prompts]</p>
<p> results = await asyncio.gather(*tasks)</p>
<p>for prompt, result in zip(prompts, results):</p>
<p> print(f"Prompt: {prompt}\nOdpowiedÅš: {result}\n")</p>
asyncio.run(main())
W tym przykÅadzie, uÅžywamy semafora, aby ograniczyÄ liczbÄ wspÃģÅbieÅžnych ÅžÄ daÅ do 5, zapewniajÄ c, Åže nie przeciÄ Åžymy serwera API.
ObsÅuga BÅÄdÃģw i Ponowienia w Asynchronicznych WywoÅaniach LLM
Podczas pracy z zewnÄtrznymi API, waÅžne jest wdroÅženie solidnej obsÅugi bÅÄdÃģw i mechanizmÃģw ponowienia. RozwaÅžmy, jak moÅžemy udoskonaliÄ nasz kod, aby obsÅuÅžyÄ typowe bÅÄdy i wdroÅžyÄ wykÅadnicze ponowienie.
import asyncio
import random
from openai import AsyncOpenAI
from tenacity import retry, stop_after_attempt, wait_exponential
class APIError(Exception):
pass
<p>@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=4, max=10))</p>
<p>async def generate_text_with_retry(prompt, client):</p>
<p> try:</p>
<p> response = await client.chat.completions.create(</p>
<p> model="gpt-3.5-turbo",</p>
<p> messages=[{"role": "user", "content": prompt}]</p>
<p> )</p>
<p> return response.choices[0].message.content</p>
<p> except Exception as e:</p>
<p> print(f"WystÄ
piÅ bÅÄ
d: {e}")</p>
<p> raise APIError("Nie udaÅo siÄ wygenerowaÄ tekstu")</p>
<p>async def process_prompt(prompt, client, semaphore):</p>
<p> async with semaphore:</p>
<p> try:</p>
<p> result = await generate_text_with_retry(prompt, client)</p>
<p> return prompt, result</p>
<p> except APIError:</p>
<p> return prompt, "Nie udaÅo siÄ wygenerowaÄ odpowiedzi po wielu prÃģbach."</p>
<p>async def main():</p>
<p> prompts = [f"Powiedz mi fakt o liczbie {i}" for i in range(20)]</p>
<p> max_concurrent_requests = 5</p>
<p> semaphore = asyncio.Semaphore(max_concurrent_requests)</p>
<p>async with AsyncOpenAI() as client:</p>
<p> tasks = [process_prompt(prompt, client, semaphore) for prompt in prompts]</p>
<p> results = await asyncio.gather(*tasks)</p>
<p>for prompt, result in results:</p>
<p> print(f"Prompt: {prompt}\nOdpowiedÅš: {result}\n")</p>
asyncio.run(main())
Ta ulepszona wersja zawiera:
- Niestandardowy wyjÄ
tek
APIErrordla bÅÄdÃģw zwiÄ zanych z API. - FunkcjÄ
generate_text_with_retryozdobionÄ dekoratorem@retryz biblioteki tenacity, wdraÅžajÄ c wykÅadnicze ponowienie. - ObsÅugÄ bÅÄdÃģw w funkcji
process_prompt, aby przechwytywaÄ i zgÅaszaÄ niepowodzenia.
Optymalizacja WydajnoÅci: Transmisja Strumieniowa
Dla generacji dÅugich treÅci, transmisja strumieniowa moÅže znacznie poprawiÄ postrzeganÄ wydajnoÅÄ Twojej aplikacji. Zamiast czekaÄ na caÅÄ odpowiedÅš, moÅžesz przetwarzaÄ i wyÅwietlaÄ fragmenty tekstu, gdy stajÄ siÄ one dostÄpne.
import asyncio
from openai import AsyncOpenAI
<p>async def stream_text(prompt, client):</p>
<p> stream = await client.chat.completions.create(</p>
<p> model="gpt-3.5-turbo",</p>
<p> messages=[{"role": "user", "content": prompt}],</p>
<p> stream=True</p>
<p> )</p>
<p>full_response = ""</p>
<p> async for chunk in stream:</p>
<p> if chunk.choices[0].delta.content is not None:</p>
<p> content = chunk.choices[0].delta.content</p>
<p> full_response += content</p>
<p> print(content, end="", flush=True)</p>
<p>print("\n")</p>
<p> return full_response</p>
<p>async def main():</p>
<p> prompt = "Napisz krÃģtkÄ
historiÄ o podrÃģÅžujÄ
cym w czasie naukowcu."</p>
<p>async with AsyncOpenAI() as client:</p>
<p> result = await stream_text(prompt, client)</p>
<p>print(f"PeÅna odpowiedÅš:\n{result}")</p>
asyncio.run(main())
Ten przykÅad demonstruje, jak transmitowaÄ strumieniowo odpowiedÅš z API, drukujÄ c kaÅždy fragment, gdy tylko jest dostÄpny. To podejÅcie jest szczegÃģlnie przydatne w aplikacjach czatu lub w kaÅždej sytuacji, w ktÃģrej chcesz zapewniÄ uÅžytkownikowi natychmiastowÄ informacjÄ zwrotnÄ .
Budowanie Asynchronicznych PrzepÅywÃģw Pracy z LangChain
Dla bardziej zÅoÅžonych aplikacji zasilanych przez LLM, ramy LangChain zapewniajÄ wysoki poziom abstrakcji, ktÃģry upraszcza proces ÅÄ czenia wielu wywoÅaÅ LLM i integracji z innymi narzÄdziami. RozwaÅžmy przykÅad uÅžycia LangChain z moÅžliwoÅciami asynchronicznymi:
Ten przykÅad pokazuje, jak LangChain moÅže byÄ uÅžyty do tworzenia bardziej zÅoÅžonych przepÅywÃģw pracy z transmisjÄ
strumieniowÄ
i wykonywaniem asynchronicznym. AsyncCallbackManager i StreamingStdOutCallbackHandler umoÅžliwiajÄ
transmisjÄ strumieniowÄ
wygenerowanego tekstu w czasie rzeczywistym.
import asyncio
from langchain.llms import OpenAI
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain
from langchain.callbacks.manager import AsyncCallbackManager
from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler
<p>async def generate_story(topic):</p>
<p> llm = OpenAI(temperature=0.7, streaming=True, callback_manager=AsyncCallbackManager([StreamingStdOutCallbackHandler()]))</p>
<p> prompt = PromptTemplate(</p>
<p> input_variables=["topic"],</p>
<p> template="Napisz krÃģtkÄ
historiÄ o {topic}."</p>
<p> )</p>
<p> chain = LLMChain(llm=llm, prompt=prompt)</p>
<p> return await chain.arun(topic=topic)</p>
<p>async def main():</p>
<p> topics = ["magiczny las", "miasto przyszÅoÅci", "podwodna cywilizacja"]</p>
<p> tasks = [generate_story(topic) for topic in topics]</p>
<p> stories = await asyncio.gather(*tasks)</p>
<p>for topic, story in zip(topics, stories):</p>
<p> print(f"\nTemat: {topic}\nHistoria: {story}\n{'='*50}\n")</p>
asyncio.run(main())
Uruchamianie Asynchronicznych Aplikacji LLM z FastAPI
Aby udostÄpniÄ swojÄ asynchronicznÄ aplikacjÄ LLM jako usÅugÄ internetowÄ , FastAPI jest doskonaÅym wyborem ze wzglÄdu na jego natywne wsparcie dla operacji asynchronicznych. Oto przykÅad, jak utworzyÄ prosty punkt koÅcowy API dla generacji tekstu:
from fastapi import FastAPI, BackgroundTasks
from pydantic import BaseModel
from openai import AsyncOpenAI
app = FastAPI()
client = AsyncOpenAI()
<p>class GenerationRequest(BaseModel):</p>
<p> prompt: str</p>
<p>class GenerationResponse(BaseModel):</p>
<p> generated_text: str</p>
<p>@app.post("/generate", response_model=GenerationResponse)</p>
<p>async def generate_text(request: GenerationRequest, background_tasks: BackgroundTasks):</p>
<p> response = await client.chat.completions.create(</p>
<p> model="gpt-3.5-turbo",</p>
<p> messages=[{"role": "user", "content": request.prompt}]</p>
<p> )</p>
<p> generated_text = response.choices[0].message.content</p>
<p># Symulacja pewnych post-procesÃģw w tle</p>
<p> background_tasks.add_task(log_generation, request.prompt, generated_text)</p>
<p> return GenerationResponse(generated_text=generated_text)</p>
<p>async def log_generation(prompt: str, generated_text: str):</p>
<p> # Symulacja logowania lub dodatkowej obrÃģbki</p>
<p> await asyncio.sleep(2)</p>
<p> print(f"Zalogowano: Prompt '{prompt}' wygenerowaÅ tekst o dÅugoÅci {len(generated_text)}")</p>
<p>if __name__ == "__main__":</p>
<p> import uvicorn</p>
<p> uvicorn.run(app, host="0.0.0.0", port=8000)</p>
Ta aplikacja FastAPI tworzy punkt koÅcowy /generate, ktÃģry akceptuje prompt i zwraca wygenerowany tekst. Pokazuje rÃģwnieÅž, jak uÅžywaÄ zadaÅ w tle do dodatkowej obrÃģbki bez blokowania odpowiedzi.
Najlepsze Praktyki i Powszechne PuÅapki
Podczas pracy z asynchronicznymi wywoÅaniami API LLM, pamiÄtaj o tych najlepszych praktykach:
- UÅžywaj puli poÅÄ czeÅ: Podczas wykonywania wielu ÅžÄ daÅ, ponownie uÅžywaj poÅÄ czeÅ, aby zmniejszyÄ nakÅad pracy.
- WdraÅžaj odpowiedniÄ obsÅugÄ bÅÄdÃģw: Zawsze uwzglÄdniaj problemy z sieciÄ , bÅÄdy API i nieoczekiwane odpowiedzi.
- Szanhuj limity stawek: UÅžywaj semaforÃģw lub innych mechanizmÃģw kontroli wspÃģÅbieÅžnoÅci, aby uniknÄ Ä przeciÄ Åženia serwera API.
- Monitoruj i rejestruj: WdraÅžaj kompleksywne logowanie, aby ÅledziÄ wydajnoÅÄ i identyfikowaÄ problemy.
- UÅžywaj transmisji strumieniowej dla dÅugich treÅci: Poprawia to doÅwiadczenie uÅžytkownika i pozwala na wczesnÄ obrÃģbkÄ czÄÅciowych wynikÃģw.












