LangChain exists. LlamaIndex exists. CrewAI, AutoGen, Semantic Kernel — all exist. They're mature, well-documented, and used by thousands of teams. We use none of them.
The agent runtime in baseKRIZAN is ~2,500 lines of PHP across 16 core files + 18 tools. It handles everything: agent registration, execution loops, tool calling, delegation chains, token budgets, context windowing, approval workflows, event-driven triggers, and per-agent model routing.
Sitting underneath it is a parallel layer we built for the same reason: the multi-provider AI plumbing. Three adapters (Anthropic, OpenAI-compatible, Gemini), an AIProviderRegistry that holds one lazy AIProvider instance per configured backend, a TaskRouter that maps task types (orchestration, embeddings, classification, vision, transcription, TTS, local-private, …) to a concrete (provider, model) pair with per-task failover chains, plus dedicated EmbeddingProvider, TranscriptionProvider, and TtsProvider for the non-chat endpoints. Every agent and every watcher ends up calling into this layer, so it deserves to be in the same no-framework bucket as the agent runtime.
Here's why we built it from scratch, and when you shouldn't.
What We Gave Up
Ecosystem integrations — LangChain has connectors for vector databases, document loaders, embedding models, retrieval chains. We'd need to build these if we needed them.
Community tools — Thousands of pre-built tools and chains. Our 18 tools are custom — each one written for our specific codebase (classic read/grep/list/write/bash/delegate plus newer additions like batch_read, architecture_query, consult_agent, and parallel_delegate_agent).
Python ecosystem — Most AI tooling is Python-first. Our agents run in PHP, the same runtime as the application they manage. No separate Python service, no inter-process communication, no deployment complexity.
Abstractions — LangChain's AgentExecutor, ToolNode, StateGraph are battle-tested abstractions. Ours are simpler but less general.
What We Gained
No Abstraction Tax
LangChain's abstractions have a cost: you need to understand their model to debug your code. When a LangChain agent fails, you debug through LangChain's internals. When our agent fails, we debug through our own code — code we wrote, understand, and can modify.
// Our AgentExecutor loop — the entire core logic is visible
while ($iteration < $maxIterations) {
$result = $this->aiProvider->chat($messages, $options);
if ($result['stop_reason'] === 'end_turn') break;
foreach ($result['tool_calls'] as $toolCall) {
$toolResult = $this->toolRegistry->execute($toolCall['name'], $toolCall['input']);
$messages[] = ['role' => 'user', 'content' => [['type' => 'tool_result', ...]]];
}
}
No framework magic. No middleware chain you didn't configure. No lifecycle hooks you didn't register.
Full Control Over Message Format
We use Claude's message format as the internal contract and translate at the adapter boundary — OpenAIAdapter handles OpenAI/DeepSeek/Groq/Ollama, GeminiAdapter handles Google's native API. LangChain normalizes to its own format, then translates — adding a layer of indirection. When Anthropic adds a new feature (extended thinking, prompt caching), we support it by modifying one adapter. When we want embeddings on OpenAI while chat stays on Claude, we ask the TaskRouter and move on. No waiting for LangChain to release a compatible version.
No Version Drift
LangChain releases frequently. Breaking changes happen. When you depend on LangChain + LangSmith + LangGraph + community tools, each with its own release cycle, version management becomes a project in itself.
Our agent runtime changes when we change it. No upstream surprises.
Debuggable With var_dump
The PHP developer's best friend. When something goes wrong:
var_dump($messages); // What did we send to the API?
var_dump($result); // What came back?
var_dump($toolResult); // What did the tool return?
No framework abstraction layers between you and the data. The entire agent execution fits in your head.
When This Approach Makes Sense
✅ Your application language is PHP — Agents run in the same runtime. No service boundaries, no serialization, no network calls between the app and the agent system.
✅ You need deep integration — Agents read your database, your files, your git history. They don't consume a generic API — they understand your specific codebase.
✅ You have few tools — 15 custom tools is manageable. 150 would be painful to build from scratch.
✅ You value simplicity — 2,500 lines of code you understand vs 25,000 lines of framework you'll need to learn.
When It Doesn't
❌ You need vector search / RAG — Build or buy. We have the embedding plumbing (EmbeddingProvider with OpenAI / Gemini / Ollama backends), and LIMAlife uses it for semantic personal memory, but there's no hosted RAG framework behind it. A full RAG pipeline (chunking strategies, reranking, hybrid retrieval) would still be a project of its own.
❌ You need rapid prototyping — LangChain gets you from zero to demo faster. Our approach is slower to start but faster to maintain.
❌ Your team doesn't own the AI infrastructure — If agents are built by a separate team, a framework provides shared vocabulary. Our approach works because the same developer writes both the application and the agents.
Up Next
Next up: Security, Token Economics, and Observability in an Agentic System — the full security model, cost tracking, and logging infrastructure that makes 19 agents manageable in production.
Comments (0)
No comments yet. Be the first to share your thoughts!
Leave a Comment