Batch-Verarbeitung und asynchrone Ausführung
OpenAI Batch API, asynchrones Python und nebenläufige Prompt-Ausführung.
Batch-Verarbeitung und asynchrone Ausführung ist eine kostenlose AI Prompt Engineering-Lektion auf CoddyKit. Dies ist Lektion 2 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des AI Prompt Engineering-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der AI Prompt Engineering-Kurs umfasst insgesamt 4 Lektionen.
Warum Batch-Verarbeitung und Async?
Tausende LLM-Anfragen nacheinander zu verarbeiten, ist langsam und teuer. Batch-Verarbeitung gruppiert Anfragen und reduziert die Kosten um 50 %. Asynchrone Ausführung parallelisiert Anfragen, um den Durchsatz innerhalb der Rate-Limits zu maximieren. Zusammen reduzieren beide Ansätze sowohl die Kosten als auch die Gesamtlaufzeit erheblich.
OpenAI Batch API: 50 % Kostenersparnis
Die OpenAI Batch API verarbeitet Anfragen asynchron im Hintergrund (bis zu 24 Stunden) zum halben Preis der normalen API. Sie eignet sich ideal für Evaluierungsläufe, die Verarbeitung von Datensätzen und nicht echtzeitkritische Workloads.
import openai
import json
client = openai.OpenAI(api_key='YOUR_API_KEY')
# Step 1: Create batch input file (JSONL format)
batch_requests = [
{
'custom_id': f'request-{i}',
'method': 'POST',
'url': '/v1/chat/completions',
'body': {
'model': 'gpt-4o-mini',
'messages': [
{'role': 'user', 'content': f'Summarize this document: {doc}'}
],
'max_tokens': 200
}
}
for i, doc in enumerate(['Doc A text...', 'Doc B text...', 'Doc C text...'])
]
# Write to JSONL file
with open('batch_input.jsonl', 'w') as f:
for req in batch_requests:
f.write(json.dumps(req) + '\n')
# Step 2: Upload the file
batch_file = client.files.create(
file=open('batch_input.jsonl', 'rb'),
purpose='batch'
)
print(f'Batch file uploaded: {batch_file.id}')Batch-Auftrag übermitteln und abfragen
Laden Sie nach dem Hochladen der Eingabedatei den Batch-Auftrag an und fragen Sie seinen Status bis zum Abschluss regelmäßig ab. Die Batch API verarbeitet Anfragen innerhalb von 24 Stunden (kleine Batches sind normalerweise deutlich schneller).
import time
# Step 3: Create batch job
batch = client.batches.create(
input_file_id=batch_file.id,
endpoint='/v1/chat/completions',
completion_window='24h'
)
print(f'Batch created: {batch.id} | Status: {batch.status}')
# Step 4: Poll for completion
def wait_for_batch(batch_id, poll_interval=30, timeout=3600):
start = time.time()
while time.time() - start < timeout:
batch = client.batches.retrieve(batch_id)
print(f'Status: {batch.status} | '
f'Completed: {batch.request_counts.completed}/ '
f'{batch.request_counts.total}')
if batch.status == 'completed':
return batch
if batch.status in ('failed', 'expired', 'cancelling', 'cancelled'):
raise RuntimeError(f'Batch {batch_id} ended with status: {batch.status}')
time.sleep(poll_interval)
raise TimeoutError('Batch polling timed out')
# batch = wait_for_batch(batch.id)Batch-Ergebnisse abrufen
Sobald der Batch abgeschlossen ist, laden Sie die Ausgabedatei herunter und wandeln die JSONL-Ergebnisse wieder in ein nutzbares Format um.
def retrieve_batch_results(batch):
if not batch.output_file_id:
raise ValueError('No output file — batch may have failed')
# Download output file
content = client.files.content(batch.output_file_id).text
# Parse JSONL: one result per line
results = {}
for line in content.strip().split('\n'):
if not line:
continue
result = json.loads(line)
custom_id = result['custom_id']
if result.get('error'):
results[custom_id] = {'error': result['error']}
else:
response_body = result['response']['body']
text = response_body['choices'][0]['message']['content']
results[custom_id] = {'text': text}
# Report error rate
errors = sum(1 for r in results.values() if 'error' in r)
print(f'Retrieved {len(results)} results, {errors} errors')
return results
# results = retrieve_batch_results(batch)
# for req_id, result in results.items():
# print(req_id, result.get('text', result.get('error', ''))[:50])Asynchrones Python mit asyncio.gather()
Für Parallelität in Echtzeit (außerhalb von Batches) führt asyncio von Python mit asyncio.gather() mehrere API-Aufrufe gleichzeitig aus und wartet, bis alle abgeschlossen sind. Dadurch wird die Gesamtlaufzeit bei Workloads mit mehreren Anfragen erheblich verkürzt.
import asyncio
import openai
async_client = openai.AsyncOpenAI(api_key='YOUR_API_KEY')
async def async_completion(messages, model='gpt-4o-mini', max_tokens=200):
response = await async_client.chat.completions.create(
model=model,
messages=messages,
max_tokens=max_tokens
)
return response.choices[0].message.content
async def process_parallel(prompts):
tasks = [
async_completion([{'role': 'user', 'content': p}])
for p in prompts
]
results = await asyncio.gather(*tasks, return_exceptions=True)
return results
# Usage
async def main():
prompts = ['Explain photosynthesis.', 'Explain gravity.', 'Explain evolution.']
results = await process_parallel(prompts)
for prompt, result in zip(prompts, results):
if isinstance(result, Exception):
print(f'ERROR: {result}')
else:
print(f'{prompt[:30]}... -> {result[:60]}...')
# asyncio.run(main())
print('asyncio.gather: all 3 requests fire simultaneously')Rate-Limit-bewusste parallele Aufrufe
Zu viele parallele Anfragen führen zu Rate-Limit-Fehlern. Ein Semaphor begrenzt die Parallelität, damit die Rate-Limits eingehalten und gleichzeitig möglichst hohe Durchsätze erzielt werden.
import asyncio
# Rate limits (example for gpt-4o-mini):
# RPM (requests per minute): 500
# TPM (tokens per minute): 200,000
MAX_CONCURRENT = 20 # stay well below rate limit
async def process_with_rate_limit(prompts, max_concurrent=MAX_CONCURRENT):
semaphore = asyncio.Semaphore(max_concurrent)
results = [None] * len(prompts)
async def bounded_completion(i, prompt):
async with semaphore:
try:
result = await async_completion(
[{'role': 'user', 'content': prompt}]
)
results[i] = result
except openai.RateLimitError as e:
print(f'Rate limited on prompt {i}: {e}')
await asyncio.sleep(60) # back off and retry
result = await async_completion(
[{'role': 'user', 'content': prompt}]
)
results[i] = result
await asyncio.gather(*[
bounded_completion(i, p) for i, p in enumerate(prompts)
])
return results
print('Semaphore limits to', MAX_CONCURRENT, 'concurrent requests')Anthropic Batch API
Anthropic bietet ebenfalls eine Message Batches API mit einer ähnlichen Kostenstruktur wie die Batch API von OpenAI an. Batches werden asynchron verarbeitet, und ihre Ergebnisse werden abgefragt oder gestreamt.
import anthropic
client = anthropic.Anthropic(api_key='YOUR_API_KEY')
# Create a batch of messages
batch = client.messages.batches.create(
requests=[
{
'custom_id': f'doc-{i}',
'params': {
'model': 'claude-haiku-4-5',
'max_tokens': 200,
'messages': [{
'role': 'user',
'content': f'Classify the sentiment of: {text}'
}]
}
}
for i, text in enumerate([
'Amazing product, exceeded expectations!',
'Terrible quality, broke after one use.',
'It works as described.'
])
]
)
print(f'Batch created: {batch.id} | Status: {batch.processing_status}')
# Poll for completion
# while (batch := client.messages.batches.retrieve(batch.id)).processing_status != 'ended':
# time.sleep(30)
# Retrieve results
# for result in client.messages.batches.results(batch.id):
# print(result.custom_id, result.result.message.content[0].text[:50])Durchsatzoptimierung: Batch-Strategien
Maximieren Sie den Durchsatz, indem Sie abhängig von Ihren Latenzanforderungen und den Eigenschaften Ihres Workloads die passende Batch-Strategie auswählen.
throughput_strategies = {
'API Batch (OpenAI/Anthropic)': {
'cost': '50% of normal price',
'latency': 'Minutes to hours (background processing)',
'best_for': 'Offline workloads: eval runs, dataset labeling, report generation',
'max_batch_size': '50,000 requests per batch'
},
'asyncio.gather()': {
'cost': 'Normal price',
'latency': 'Same as slowest individual request',
'best_for': 'Real-time parallel enrichment, multi-step pipelines',
'max_concurrent': '10-50 depending on rate limits'
},
'Streaming + Concurrent': {
'cost': 'Normal price',
'latency': 'First token arrives faster, total similar',
'best_for': 'User-facing applications needing perceived speed',
'pattern': 'asyncio with stream=True per request'
},
'Worker Queue (Celery, RQ)': {
'cost': 'Normal price',
'latency': 'Variable (depends on queue depth)',
'best_for': 'High-volume production with auto-scaling workers',
'backends': 'Redis, RabbitMQ'
}
}
for strategy, details in throughput_strategies.items():
print(f'{strategy}: {details["best_for"][:60]}')Fehlerbehandlung und Wiederholungslogik für Batch/Async
Parallele und Batch-Workloads benötigen eine robuste Fehlerbehandlung. Fehler einzelner Anfragen dürfen nicht den gesamten Batch zum Absturz bringen — protokollieren Sie sie, wiederholen Sie die Anfragen mit Backoff und melden Sie aggregierte Erfolgsquoten.
import asyncio
import random
async def resilient_completion(prompt, max_retries=3, base_delay=1.0):
for attempt in range(max_retries):
try:
return await async_completion(
[{'role': 'user', 'content': prompt}]
)
except openai.RateLimitError:
wait = base_delay * (2 ** attempt) + random.uniform(0, 1)
print(f'Rate limited. Waiting {wait:.1f}s (attempt {attempt+1})')
await asyncio.sleep(wait)
except openai.APITimeoutError:
print(f'Timeout on attempt {attempt+1}')
await asyncio.sleep(base_delay)
except openai.APIError as e:
if e.status_code >= 500:
await asyncio.sleep(base_delay * (attempt + 1))
else:
raise # Don't retry 4xx errors
raise RuntimeError(f'Failed after {max_retries} attempts')
async def batch_with_error_reporting(prompts):
tasks = [resilient_completion(p) for p in prompts]
results = await asyncio.gather(*tasks, return_exceptions=True)
successes = sum(1 for r in results if not isinstance(r, Exception))
print(f'Batch complete: {successes}/{len(prompts)} succeeded')
return resultsGroße Eingaben für die Batch-Verarbeitung aufteilen
Dokumente, die größer als das Kontextfenster des Modells sind, müssen vor der Batch-Verarbeitung in Abschnitte aufgeteilt werden. Jeder Abschnitt wird zu einer separaten Batch-Anfrage; die Ergebnisse werden später zusammengeführt oder zusammengefasst.
def chunk_document(text, max_tokens=3000, overlap_tokens=200):
'''
Split a long document into overlapping chunks for batch processing.
Approximate: 1 token ~ 4 characters
'''
max_chars = max_tokens * 4
overlap_chars = overlap_tokens * 4
chunks = []
start = 0
while start < len(text):
end = min(start + max_chars, len(text))
# Try to break at a sentence boundary
if end < len(text):
last_period = text.rfind('.', start, end)
if last_period > start + max_chars // 2:
end = last_period + 1
chunks.append({'text': text[start:end], 'start': start, 'end': end})
start = end - overlap_chars # overlap for context continuity
return chunks
def batch_summarize_long_document(document_text, summary_prompt):
chunks = chunk_document(document_text)
print(f'Document split into {len(chunks)} chunks')
# Create one batch request per chunk
batch_inputs = [
{'custom_id': f'chunk-{i}',
'content': summary_prompt + '\n\n' + chunk['text']}
for i, chunk in enumerate(chunks)
]
# Submit all chunks as one batch job
return batch_inputs
long_doc = 'Lorem ipsum ' * 5000 # ~20K character document
chunks = chunk_document(long_doc)
print(f'Chunks: {len(chunks)}, first chunk length: {len(chunks[0]["text"])} chars')Fortschrittsverfolgung für große Batches
Zeigen Sie bei großen Batch-Aufträgen (mit Tausenden von Anfragen) den Fortschritt in Echtzeit an, damit Betreiber den Durchsatz überwachen und die Abschlusszeit einschätzen können.
import asyncio
import time
async def batch_with_progress(prompts, max_concurrent=20):
semaphore = asyncio.Semaphore(max_concurrent)
completed = 0
total = len(prompts)
start_time = time.time()
results = [None] * total
async def process_one(i, prompt):
nonlocal completed
async with semaphore:
results[i] = await resilient_completion(prompt)
completed += 1
elapsed = time.time() - start_time
rate = completed / elapsed if elapsed > 0 else 0
eta = (total - completed) / rate if rate > 0 else float('inf')
if completed % 10 == 0 or completed == total:
print(f'Progress: {completed}/{total} '
f'({completed/total:.0%}) | '
f'{rate:.1f} req/s | '
f'ETA: {eta:.0f}s')
await asyncio.gather(*[
process_one(i, p) for i, p in enumerate(prompts)
])
return results
print('Progress tracking: reports every 10 completions with ETA.')Kurzer Check
Sie müssen über Nacht 10.000 Dokumente für eine Sentimentanalyse klassifizieren. Sie möchten die Kosten minimieren und benötigen keine Ergebnisse in Echtzeit. Welcher Ansatz ist am besten geeignet?
Zusammenfassung von Batch und Async
Batch-Verarbeitung und asynchrone Ausführung sind für Prompt Engineering im großen Maßstab unverzichtbar:
- OpenAI/Anthropic Batch API: 50 % Kostenersparnis, Verarbeitung im Hintergrund, bis zu 50.000 Anfragen pro Batch
- asyncio.gather(): parallele Echtzeitanfragen, die alle gleichzeitig gestartet werden und auf sämtliche Ergebnisse warten
- Semaphor: Rate-Limit-bewusste Steuerung der Parallelität (typischerweise 10–50 parallele Anfragen)
- Exponential Backoff: Wiederholung mit sich verdoppelnder Verzögerung bei Rate-Limit- oder Timeout-Fehlern
- Resilient Gather: return_exceptions=True verhindert, dass ein einzelner Fehler den gesamten Batch zum Absturz bringt
- Fortschrittsverfolgung: Bei großen Aufträgen abgeschlossene Vorgänge zusammen mit Rate und voraussichtlicher Restzeit melden
Häufig gestellte Fragen
Ist die Lektion „Batch-Verarbeitung und asynchrone Ausführung“ kostenlos?
Ja — der vollständige Text von „Batch-Verarbeitung und asynchrone Ausführung“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des AI Prompt Engineering-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der AI Prompt Engineering-Kurs umfasst insgesamt 4 Lektionen.
Was lerne ich in „Batch-Verarbeitung und asynchrone Ausführung“?
OpenAI Batch API, asynchrones Python und nebenläufige Prompt-Ausführung. Du übst AI Prompt Engineering mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.
Brauche ich Erfahrung, um AI Prompt Engineering zu starten?
Keine Vorkenntnisse erforderlich. AI Prompt Engineering auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 2 von 4.
Wie lange dauert die Lektion „Batch-Verarbeitung und asynchrone Ausführung“?
Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.
Kann ich in dieser AI Prompt Engineering-Lektion Code schreiben und ausführen?
Ja. Jede AI Prompt Engineering-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.
Alle Lektionen in diesem Kurs
- Caching-Strategien für Prompts
- Batch-Verarbeitung und asynchrone Ausführung
- Lastverteilung über Modelle hinweg
- Überwachung und Alerting für Prompt-Pipelines