For three years, every AI tutorial assumed you were using Python. That assumption is now wrong. Laravel’s first-party AI SDK does everything LangChain does — agents, tool calling, RAG, memory, vector search, streaming — in the language you already know, in the framework you already love.
For the last three years, PHP developers building AI-powered applications faced a choice that shouldn’t have been a choice: learn Python, adopt LangChain, wire up a Flask or FastAPI microservice, and maintain two codebases — or accept that your application’s AI features would be second-class citizens bolted on from the outside.
That era is over.
Laravel 13 shipped a first-party AI SDK that covers the full AI application stack: text generation, streaming, embeddings, vector search, structured output, tool calling, agentic loops, and native pgvector integration. Not a community wrapper that might be abandoned next year — a Laravel-team package with the same long-term commitment as Horizon, Telescope, and Sanctum.
This post is the comparison every PHP developer who’s been waiting deserves: what Python + LangChain does, what Laravel + the AI SDK does, and where each still has advantages. With real code for both.
The Python Assumption and Why It Existed
Python’s AI dominance was legitimate. The foundational AI libraries — PyTorch, TensorFlow, NumPy, scikit-learn — are Python. The research community publishes in Python. The early LLM SDKs shipped Python-first.
But building an AI-powered web application is not the same as AI research. When you’re building a product, you need:
- Authentication and user management
- Database access and ORM
- Queue jobs for async processing
- Caching and rate limiting
- HTTP API endpoints
- File handling and storage
- Email, notifications, scheduling
LangChain + Python gives you chains, agents, and retrievers. It does not give you user authentication, Eloquent models, queue workers, or a mature routing system. To build a real product, you wire LangChain to Django or FastAPI — and now you’re managing two frameworks, two dependency systems, two deployment targets.
Laravel gives you all of it in one place. The AI SDK plugs into a framework that already has everything a web application needs.
Side-by-Side: Text Generation
Python + LangChain
from langchain.chat_models import ChatOpenAI
from langchain.schema import HumanMessage, SystemMessage
llm = ChatOpenAI(model="gpt-4o", openai_api_key="sk-...")
response = llm.invoke([
SystemMessage(content="You are a helpful assistant."),
HumanMessage(content="Explain Laravel queues in simple terms."),
])
print(response.content)
Laravel AI SDK
use Illuminate\Support\Facades\AI;
$response = AI::text(
prompt: 'Explain Laravel queues in simple terms.',
system: 'You are a helpful assistant.',
model: 'gpt-4o',
);
echo $response->text;
Advantage: equivalent. Both are clean, readable APIs. Laravel’s is slightly shorter. Python’s has a more explicit message structure.
With Prism PHP (Provider Switching)
use Prism\Prism\Facades\Prism;
use Prism\Prism\Enums\Provider;
$response = Prism::text()
->using(Provider::Anthropic, 'claude-3-5-sonnet-20241022')
->withSystemPrompt('You are a helpful assistant.')
->withPrompt('Explain Laravel queues in simple terms.')
->asText();
Advantage: PHP. Prism’s provider abstraction is cleaner than LangChain’s equivalent. Provider switching is one line.
Side-by-Side: Streaming
Python + LangChain
from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler
from langchain.chat_models import ChatOpenAI
llm = ChatOpenAI(
model="gpt-4o",
streaming=True,
callbacks=[StreamingStdOutCallbackHandler()],
)
# Or with async streaming in FastAPI
async def stream_response(prompt: str):
async for chunk in llm.astream(prompt):
yield f"data: {json.dumps({'token': chunk.content})}\n\n"
Laravel AI SDK
// In a Laravel controller — native SSE streaming
return response()->stream(function () use ($prompt) {
if (ob_get_level() > 0) ob_end_flush();
$stream = AI::text(prompt: $prompt, model: 'gpt-4o', stream: true);
foreach ($stream as $chunk) {
echo 'data: ' . json_encode(['token' => $chunk->text]) . "\n\n";
flush();
}
echo 'data: ' . json_encode(['done' => true]) . "\n\n";
flush();
}, 200, [
'Content-Type' => 'text/event-stream',
'Cache-Control' => 'no-cache',
'X-Accel-Buffering' => 'no',
]);
Advantage: PHP. Laravel’s response()->stream() integrates directly with the HTTP layer. In Python, streaming in a web context requires async FastAPI or Flask, an ASGI server, and manual SSE formatting — more moving parts.
Side-by-Side: Tool Calling / Agents
Python + LangChain
from langchain.agents import AgentExecutor, create_openai_tools_agent
from langchain.tools import tool
from langchain.chat_models import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
@tool
def get_product_info(product_id: int) -> str:
"""Get information about a product by its ID."""
# Your database call here
return f"Product {product_id}: Widget, Price: $29.99, Stock: 50"
@tool
def check_order_status(order_id: str) -> str:
"""Check the status of an order."""
return f"Order {order_id}: Shipped, Expected delivery: 2026-06-15"
llm = ChatOpenAI(model="gpt-4o")
tools = [get_product_info, check_order_status]
prompt = ChatPromptTemplate.from_messages([
("system", "You are a helpful customer support agent."),
("human", "{input}"),
("placeholder", "{agent_scratchpad}"),
])
agent = create_openai_tools_agent(llm, tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools)
response = agent_executor.invoke({"input": "What's the status of order ORD-12345?"})
print(response["output"])
Laravel AI SDK
use Illuminate\Support\Facades\AI;
use Laravel\Ai\Tools\Tool;
// Define tools
$getProductInfo = Tool::define(
name: 'get_product_info',
description: 'Get information about a product by its ID.',
parameters: ['product_id' => 'integer'],
handler: fn(int $productId) =>
Product::find($productId)?->toJson()
?? "Product {$productId} not found."
);
$checkOrderStatus = Tool::define(
name: 'check_order_status',
description: 'Check the status of an order.',
parameters: ['order_id' => 'string'],
handler: fn(string $orderId) =>
Order::where('reference', $orderId)->first()?->toJson()
?? "Order {$orderId} not found."
);
// Build and run the agent
$agent = AI::agent()
->model('gpt-4o')
->system('You are a helpful customer support agent.')
->tools([$getProductInfo, $checkOrderStatus]);
$response = $agent->ask("What's the status of order ORD-12345?");
echo $response->text;
Advantage: PHP, significantly. The Laravel AI SDK’s agent API is cleaner and shorter. The LangChain equivalent requires AgentExecutor, prompt templates, scratchpad management, and a specific agent factory — four concepts for what Laravel does in one fluent chain.
More importantly: the Laravel tool handlers have direct access to Eloquent models, the database, cache, queue — everything in the Laravel container. LangChain tool handlers need separate database connection management.
Side-by-Side: RAG and Vector Search
Python + LangChain
from langchain.vectorstores import Chroma
from langchain.embeddings import OpenAIEmbeddings
from langchain.chains import RetrievalQA
from langchain.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.chat_models import ChatOpenAI
# Load and chunk documents
loader = PyPDFLoader("document.pdf")
docs = loader.load()
splitter = RecursiveCharacterTextSplitter(chunk_size=1000, overlap=200)
chunks = splitter.split_documents(docs)
# Store in vector database
embeddings = OpenAIEmbeddings()
vectorstore = Chroma.from_documents(chunks, embeddings)
# Create RAG chain
qa_chain = RetrievalQA.from_chain_type(
llm=ChatOpenAI(model="gpt-4o"),
retriever=vectorstore.as_retriever(search_kwargs={"k": 5}),
)
response = qa_chain.invoke({"query": "What is the refund policy?"})
print(response["result"])
LangChain requires: Chroma (vector store), OpenAIEmbeddings, RetrievalQA chain, PyPDFLoader, RecursiveCharacterTextSplitter — five separate concepts and five imports for a basic RAG pipeline.
Laravel AI SDK + pgvector
// Migration (run once)
// $table->vector('embedding', dimensions: 1536)->index();
// Ingest: embed and store
$text = Parsel::file('document.pdf')->text();
$chunks = app(DocumentChunker::class)->chunk($text);
foreach (array_chunk($chunks, 100) as $batch) {
$embeddings = AI::embed(array_column($batch, 'content'))->embeddings;
DocumentChunk::insert(
collect($batch)->map(fn($chunk, $i) => [
'content' => $chunk['content'],
'embedding' => json_encode($embeddings[$i]->embedding),
])->all()
);
}
// Query: semantic search + generation
$queryEmbedding = AI::embed($question)->embeddings[0]->embedding;
$chunks = DocumentChunk::whereVectorSimilarTo('embedding', $queryEmbedding, limit: 5)->get();
$context = $chunks->map(fn($c) => $c->content)->join("\n\n---\n\n");
$answer = AI::text(
prompt: $question,
system: "Answer based only on this context:\n\n{$context}",
model: 'gpt-4o',
);
echo $answer->text;
Advantage: PHP, substantially. The Laravel implementation uses:
- Your existing PostgreSQL database (no separate vector store service)
- Native pgvector in the query builder (
whereVectorSimilarTo) - Parsel for PDF extraction (covered earlier in this series)
- Eloquent models you already know
LangChain’s RAG requires Chroma (or Pinecone, Weaviate, etc.) as a separate infrastructure dependency. The Laravel implementation stores vectors in the same database as the rest of your application — one database, one deployment, one backup strategy.
Side-by-Side: Structured Output
Python + LangChain
from langchain.output_parsers import PydanticOutputParser
from langchain.pydantic_v1 import BaseModel, Field
from langchain.prompts import PromptTemplate
from langchain.chat_models import ChatOpenAI
class ProductAnalysis(BaseModel):
sentiment: str = Field(description="Overall sentiment: positive, neutral, or negative")
category: str = Field(description="Product category")
keywords: list[str] = Field(description="Key product keywords")
summary: str = Field(description="One-sentence summary")
parser = PydanticOutputParser(model=ProductAnalysis)
prompt = PromptTemplate(
template="Analyse this product:\n{product}\n\n{format_instructions}",
input_variables=["product"],
partial_variables={"format_instructions": parser.get_format_instructions()},
)
llm = ChatOpenAI(model="gpt-4o")
chain = prompt | llm | parser
result = chain.invoke({"product": product_description})
print(result.sentiment)
print(result.category)
Laravel AI SDK
use Laravel\Ai\Schemas\ObjectSchema;
use Laravel\Ai\Schemas\StringSchema;
use Laravel\Ai\Schemas\ArraySchema;
$response = AI::structured(
prompt: "Analyse this product: {$productDescription}",
schema: new ObjectSchema(
name: 'product_analysis',
description: 'Analysis of a product description',
properties: [
new StringSchema('sentiment', 'Overall sentiment: positive, neutral, or negative'),
new StringSchema('category', 'Product category'),
new ArraySchema('keywords', 'Key product keywords', new StringSchema('keyword', '')),
new StringSchema('summary', 'One-sentence summary'),
],
requiredFields: ['sentiment', 'category', 'keywords', 'summary'],
),
);
$data = $response->structured;
echo $data['sentiment'];
echo $data['category'];
Advantage: comparable. Both achieve structured output cleanly. Python’s Pydantic integration is slightly more type-safe. PHP’s schema approach is more explicit.
Where Python Still Has Real Advantages
An honest comparison acknowledges where Python leads:
1. ML Model Training and Fine-Tuning
If you need to train, fine-tune, or quantise a model, Python is required. PyTorch and TensorFlow are Python-native. No PHP equivalent exists, and none is coming.
When this matters: Building custom models, fine-tuning base models on proprietary data, running local inference with custom weights.
When this doesn’t matter: Most production AI applications use hosted APIs (OpenAI, Anthropic, Google) and never touch model training. For these applications, the language of the inference layer is irrelevant.
2. Computer Vision and Audio
The mature CV libraries — OpenCV, Pillow, scikit-image, Whisper for audio — are Python. PHP has limited equivalents.
Caveat: Many CV and audio tasks can now be offloaded to hosted APIs. OpenAI Whisper for transcription, GPT-4o Vision for image analysis — these are API calls available from any language. Only if you need local, offline processing does Python’s CV advantage matter.
3. Data Science Workflows
Pandas, NumPy, Jupyter notebooks — the data exploration and analysis ecosystem is Python. For preprocessing training data or doing exploratory analysis, Python is the right tool.
When this matters: Pre-production research and data pipeline work.
When this doesn’t matter: Production inference against existing models.
4. Community Packages and Research Code
New research papers publish code in Python. New model architectures ship Python implementations first. If you need to run code from a recent paper, it’ll be Python.
Where Laravel Has Real Advantages
1. The Full Application Stack in One Place
Python AI app:
├── FastAPI (HTTP layer) → separate framework
├── SQLAlchemy (ORM) → separate ORM
├── Celery (background tasks) → separate system
├── Redis (caching) → configured separately
├── Alembic (migrations) → separate migration tool
├── LangChain (AI) → the thing you wanted
└── Chroma/Pinecone (vectors) → separate infrastructure
Laravel AI app:
├── Routing + middleware → built in
├── Eloquent ORM → built in
├── Queue + Horizon → built in
├── Cache → built in
├── Migrations → built in
├── Laravel AI SDK → the thing you wanted
└── pgvector in query builder → built in
This isn’t a minor convenience. The integration depth — tools that can query Eloquent models, jobs that dispatch from AI responses, middleware that guards AI endpoints, policies that authorise AI access — makes Laravel AI development qualitatively different from piecing together Python components.
2. Type Safety with PHP 8.4
// PHP 8.4 strict types throughout
function processAiResponse(
AI\Response\TextResponse $response,
User $user,
#[Description('Maximum tokens to use')]
int $maxTokens = 1000,
): ConversationEntry {
return ConversationEntry::create([
'user_id' => $user->id,
'content' => $response->text,
'tokens' => $response->usage->totalTokens,
]);
}
Python’s type hints are annotations, not enforced. PHP 8.4 enforces types at runtime and modern tooling like PHPStan gives you compile-time checking.
3. Deployment Simplicity
# Python AI app deployment dependencies:
pip install fastapi uvicorn langchain openai chromadb
# Configure Celery workers
# Configure Redis
# Configure Chroma or Pinecone connection
# ASGI server setup
# Process management
# Laravel AI app deployment:
composer install
php artisan migrate
php artisan queue:work # if needed
# Done
4. Artisan Commands as AI Tools
The agent experiments earlier in this series showed something Python can’t easily match: Laravel tools have access to the full Artisan command layer, all Eloquent models, all queued jobs, and all service bindings.
// AI agent tool that triggers a queue job
$generateReport = Tool::define(
name: 'generate_report',
handler: fn(string $type, string $period) => GenerateReportJob::dispatch($type, $period)->id,
);
// AI agent tool that queries your specific application's models
$findOrders = Tool::define(
name: 'find_orders',
handler: fn(string $customerId) => Order::forCustomer($customerId)
->pending()
->with('items')
->get()
->toJson(),
);
These tools are native — they use your models, your scopes, your business logic, your queue system. The Python equivalent requires maintaining database connection management, serialisation, and compatibility layers.
The Migration Decision: When to Rewrite Python Microservices
Some teams built Python microservices for AI features when Laravel didn’t have first-party AI support. With the AI SDK now shipped, the question becomes: should you rewrite?
Rewrite to Laravel when:
✓ The microservice is primarily API glue (calling OpenAI, processing results)
✓ The microservice needs Eloquent model access
✓ The team doesn't have strong Python expertise
✓ The two-codebase maintenance cost is significant
✓ Deployment complexity of two stacks is painful
Keep the Python microservice when:
✓ It does genuine ML work (training, fine-tuning, local inference)
✓ It uses Python-native CV or audio processing libraries
✓ The team has strong Python expertise and low Laravel expertise
✓ The service is stable and not actively developed
✓ The integration overhead of rewriting would outweigh the benefit
A Note on the Timing
This isn’t a “PHP finally caught up with Python” argument in the general sense. PHP and Python are different tools for different contexts, and Python’s ML and data science ecosystem isn’t going anywhere.
The specific argument is narrower: for PHP developers building AI-powered web applications, the case for a Python microservice no longer exists. The capability gap that justified the architectural complexity is closed.
If you’re a Python developer building AI products, Python remains an excellent choice. If you’re a Laravel developer who added Python to your stack because the AI tooling wasn’t there yet — it’s there now.
What the Laravel AI Stack Covers in 2026
Text generation: AI::text() ✅
Streaming: AI::text(stream: true) ✅
Structured output: AI::structured() ✅
Embeddings: AI::embed() ✅
Vector search: whereVectorSimilarTo() ✅ (pgvector native)
Tool calling: Tool::define() ✅
Agents: AI::agent() ✅
RAG: SimilaritySearch agent tool ✅
Multi-turn chat: Message history in AI::text() ✅
Multi-modal: Image + Document support ✅
Provider switching: Prism PHP ✅ (9+ providers)
Local inference: Ollama via Prism ✅
Background AI jobs: Queue jobs with AI calls ✅
Feature flags on AI: Laravel Pennant ✅
What requires Python:
Model training: Fine-tuning, RLHF ❌
Local model hosting: Custom model weights ❌ (use API providers)
Computer vision: PyTorch CV pipelines ❌ (or use hosted APIs)
Research code: Paper implementations ❌
Final Thoughts
The PHP developer’s position in AI-powered web development has fundamentally changed. Not because PHP suddenly got ML capabilities it will never have. Because building AI-powered web applications in 2026 doesn’t require ML capabilities — it requires a good HTTP client, an embedding API, a vector search layer, a streaming response handler, and a framework to tie everything together.
Laravel has all of those. The AI SDK makes them first-class. The combination of Prism PHP for provider flexibility, the AI SDK for native framework integration, pgvector for vector storage, and Parsel for document processing gives Laravel developers a complete AI application stack — one that competes with Python not on ML research, but on what actually matters for building products: speed, maintainability, integration depth, and the ability to ship.
The Python assumption is wrong. The era of the two-codebase AI application is over.
