AI Engineer World's Fair 2025
Why the Best AI Agents Are Built Without Frameworks (Primitives over Frameworks)
Read the talk
Why the Best AI Agents Are Built Without Frameworks (Primitives over Frameworks)
A PDF chatbot, a task router and a set of worker agents show how managed AI primitives and ordinary TypeScript can carry an application from input to answer.
From a talk by Ahmad Awais
Before you start: Basic familiarity with LLM calls, retrieval-augmented generation and TypeScript promises will help you follow the examples.
Start with a PDF
You have documents, and you want a chatbot that can answer questions about them. Ahmad Awais starts with exactly that request: he opens Chai and enters “Chat with PDF.” Chai begins generating the agent while he continues the talk. The historical product name is Chai; chai.new now leads to Command. Awais estimates that generation takes about a minute.
The architectural choice is what happens underneath that prompt. Awais points to Perplexity, Cursor, v0, Lovable, Bolt and Chai as examples behind his position that successful production agents do not need AI frameworks. He criticizes frameworks for bloat, slow evolution and abstractions that make the application harder to understand. His assertion about those products is an argument for composable primitives, not an implementation audit of each product.
That preference comes after substantial framework and developer-tool work. Awais describes contributions to WordPress, Next.js, Node.js and React, along with Node.js automation CLIs and the Shades of Purple theme. He reports roughly 40–50 million annual downloads across his packages and theme. He also cites a contribution to NASA’s Ingenuity helicopter mission, VP roles in developer tools and engineering, and work on the Google Developers advisory board. The question is why someone with that background would now favor smaller building blocks.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Small interfaces, substantial infrastructure
Amazon S3 supplies the analogy: upload an object, download an object, and let the service handle the infrastructure required to scale those operations. The interface stays small even when the implementation behind it is substantial. A primitive is useful because many applications can compose it without adopting a prescribed application architecture.
Awais traces his LLM work to 2020, when Greg Brockman gave him access to GPT-3. He says he began building something resembling GitHub Copilot before Copilot launched in 2021. Yet years of experimentation have not made building, deploying and scaling agents painless. His framing is that agents are a new way of writing code—a broad change that may not fit comfortably inside one framework’s abstractions.
Conversation history illustrates a more durable boundary. Many agents need somewhere to store context, so a thread can be a reusable primitive across otherwise different applications. Awais expects frontend, full-stack, DevOps and ML engineers increasingly to build AI products; Langbase aims to make that transition a fast route to production agents.
The proposed alternative to a framework is not to implement every infrastructure component yourself. Awais contrasts debugging a framework and then separately solving deployment with using composable primitives that include managed cloud infrastructure. He describes memory as a vector-store-backed primitive that automatically scales to terabytes of uploaded data. That is a service capability he claims, rather than a load test shown here. Parsing, chunking, threads and tools complete the supporting infrastructure for a serverless agent.
Returning to Chai, the generated PDF application is ready to inspect. Awais announces a tour of eight architectures and says he intends to deploy the application before moving on. Its generated workflow already exposes the central composition: a memory holds the PDF content, retrieval finds relevant passages, and an LLM uses those passages to generate an answer. The code uses langbase.memories to address the PDF-document memory with the user’s question.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
From uploaded PDFs to cross-document answers
Awais uploads saved PDFs of his About page, his talks page and instructions for obtaining a Langbase API key. Those documents provide separate sources for identity, speaking history and product instructions. Ingestion then moves through a concrete sequence:
- A parser converts each PDF into text.
- A chunker divides the text into smaller pieces of context for similarity search.
- After processing, refreshing the memory view shows the files as ready.
- A question retrieves relevant passages, which become the context for the generate-answer step.
Chai also supplies an agent app, while the API allows callers to use their preferred programming language.
The first test asks who the founder is and what his last three talks were. Awais notes that the required information lives in two different files, automatically parsed, chunked and embedded by the primitives. The app returns his name and a list of three talks. This makes the retrieval boundary concrete: the answer needs context from more than one uploaded document.
He then asks how to obtain an API key, drawing on the remaining instructions document. The app returns an answer, but Awais also notices an unspecified app bug. He proposes fixing it through Chai’s app mode; the demonstration does not show that repair being completed.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
The augmented LLM
The app can also be made public. Underneath that interface, Awais identifies the same small inventory: memory as an autonomous retrieval-augmented generation engine, a workflow engine for multistep execution, threads for conversation and context, a parser for extraction, and a chunker for splitting content. He points to stateofaiagents.com for his team’s research into agent patterns, primitives and model choices across industries.
The first architecture is an augmented LLM: a model receives input and produces output, with access to tools, threads and memory. Tool calls can reach MCP servers or other APIs. Threads retain context even when work happens asynchronously. Awais compares a thread to a flight-booking scratchpad: where you will land and what you plan to do next are details worth keeping while the task progresses. Long-term memory serves a different purpose, searching a larger body of stored information.
| Primitive | Responsibility |
|---|---|
| Tools | Invoke external capabilities |
| Threads | Retain conversation and working context |
| Memory | Retrieve relevant long-term information |
Langbase’s pipes, also described as agents, provide this augmented-LLM building block. The distinction between a thread and memory matters: keeping the current booking context is a different operation from searching a large collection for relevant facts.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Chain agents with explicit gates
With the augmented LLM as a building block, prompt chaining passes one agent’s output into the next step. A conditional gate can stop the chain: classify an incoming email as spam, then draft a reply only if it is not spam. The gate is ordinary application logic around an agent result.
A second example composes a summary agent, a features agent and a marketing-copy agent. Each stage does a narrower job, and the application controls how their outputs flow forward. Awais emphasizes that this composition is plain JavaScript or TypeScript. The email gate can be expressed with the same kind of code:
typescript
type EmailAgents = {
classify: (email: string) => Promise<{ spam: boolean }>;
draftReply: (email: string) => Promise<string>;
};
async function prepareReply(
email: string,
agents: EmailAgents,
): Promise<string | null> {
const classification = await agents.classify(email);
if (classification.spam) return null;
return agents.draftReply(email);
}
The function returns a draft; sending it would be a separate operation. The useful abstraction here is the function boundary, with the branching rule visible in the code.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Let a router select the specialist
A router changes the question from “what happens next in this fixed chain?” to “which specialist should handle this task?” Awais creates three specialists: summarization, reasoning and coding. Summarization uses Gemini, coding uses Claude Sonnet, and he describes the reasoning model as DeepSeek, Llama 70B. The matching Langbase architecture example identifies the reasoning model as groq:deepseek-r1-distill-llama-70b; that resolves the companion example’s identifier without treating it as a verified historical code snapshot.
The router receives descriptions of the available agents and instructions to return valid JSON selecting one. Application code reads that decision and invokes the selected specialist. The model chooses the destination; the program performs the dispatch. A small TypeScript boundary can make the permitted destinations explicit:
typescript
type Specialist = "summary" | "reasoning" | "coding";
type Agent = (input: string) => Promise<string>;
function readRoute(json: string): Specialist {
const decision: unknown = JSON.parse(json);
if (
typeof decision !== "object" ||
decision === null ||
!("agent" in decision)
) {
throw new Error("Router response must contain an agent");
}
const name = decision.agent;
if (name !== "summary" && name !== "reasoning" && name !== "coding") {
throw new Error("Router selected an unknown agent");
}
return name;
}
async function routeTask(
input: string,
router: Agent,
specialists: Record<Specialist, Agent>,
): Promise<string> {
const selected = readRoute(await router(input));
return specialists[selected](input);
}
This keeps the original task intact while restricting the router to known destinations.
The live input asks why days are shorter in winter. The router selects reasoning rather than summarization or coding. Awais then passes the same question to the reasoning agent, which produces the explanation. The demonstrated behavior is a two-stage operation: choose a specialist, then ask that specialist to solve the original task.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
From parallel calls to dynamic workers
When multiple agents can work independently, the composition is simpler still. Awais shows sentiment, summary and decision-maker agents running in parallel through JavaScript’s Promise.all. The application starts the calls together and waits for their results; it does not need an agent-specific concurrency abstraction.
The orchestrator–worker architecture adds a planning step before that parallel execution. An orchestrator determines the subtasks, worker agents complete them, and a synthesizer combines their outputs. Awais presents this as a useful architecture for deep research: the number and content of the tasks can depend on the request instead of being fixed in advance.
The demonstration asks for a blog post about the benefits of remote work, specifically productivity, work-life balance and environmental impact. The orchestrator produces five subtasks: an introduction, a section for each of those three topics, and a conclusion. Five workers then produce the parts, which are passed into synthesis.
Awais estimates the demonstrated orchestrator–worker example at about 90 lines of code. Its core data flow is a plan, a parallel map over tasks, and a final synthesis call:
typescript
type WritingAgents = {
plan: (request: string) => Promise<string[]>;
write: (subtask: string) => Promise<string>;
synthesize: (request: string, sections: string[]) => Promise<string>;
};
async function writeRemoteWorkPost(agents: WritingAgents): Promise<string> {
const request =
"Write a blog post on benefits of remote work. " +
"I care about productivity, work-life balance, and environmental impact.";
const subtasks = await agents.plan(request);
const sections = await Promise.all(
subtasks.map((subtask) => agents.write(subtask)),
);
return agents.synthesize(request, sections);
}
The worker count follows the plan; it is not hard-coded to five. The demonstrated run happens to produce five sections.
This is where the framework critique becomes a maintenance argument. If models improve at planning and coordinating work, the best orchestration strategy may change. Awais expects ordinary code over primitives to be easier to adapt than an application tied to a framework’s fixed representation of an agent workflow. The intended stable layer is the capability—run an agent, retain context, retrieve information—while the application remains free to change how those capabilities compose.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Revise with evaluator feedback
An evaluator–optimizer adds a feedback loop. A generator produces an output; a separate LLM judge either accepts it or rejects it with feedback. The evaluator’s prompt defines what counts as an acceptable result. In the live example, the generator writes a description of an eco-friendly water bottle for eco-conscious millennials.
The evaluator rejects the first attempt because it misses the intended audience, then gives specific instructions for improvement. Awais recommends choosing the strongest evaluator model for the domain rather than treating evaluation as a generic afterthought. For a health-related task, for example, the evaluator might retrieve relevant information from memory to support its judgment. The next generated version incorporates the feedback, and Awais describes it as substantially better. The demonstration establishes a revision after critique, without specifying a general acceptance rate or an automatic stopping policy.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Tools and memory complete the architecture set
Tool calling is another supported pattern, though Awais skips a separate walkthrough. He returns instead to memory: create the memory, upload data manually or through an API, retrieve relevant content, and use an agent to answer questions about it. This is the same lifecycle exercised by the opening PDF application.
The memory diagram expands those few public operations into a longer internal workflow. It shows Create, Upload Docs and Retrieve beneath a Memory Agent, with semantic parsing, chunking, embedding, indexing, rewriting, retrieval, reranking, generation and evaluation below. A small primitive interface can therefore hide considerable processing without dictating the rest of the application’s control flow.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Compose a research agent
Awais estimates that these patterns cover roughly 80% of even complicated agent applications. He presents that as a practical judgment, without a measured application set. His next example is a Chai-generated researcher requested in the style of Perplexity. It decomposes the job into query analysis, web search, result consolidation and response generation, using Exa for search.
He submits a question about the latest developments at OpenAI, starting the research workflow. The talk moves on before describing a completed research answer. What the example adds is the composition of an external search capability with analysis and synthesis, beyond the earlier retrieval over uploaded PDFs.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Add a missing primitive from another provider
A receipt checker makes the composability claim more concrete. Chai and Langbase did not have the OCR primitive Awais wanted, so he supplied a usage example for Mistral OCR and asked Chai to incorporate it. The resulting application uses GPT-4.1 alongside Mistral OCR, described in the demonstration as the latest OCR model. The original OCR model discussed here is now deprecated, so that historical “latest” designation should not be read as a current model recommendation.
Awais calls the combination a “double fallback” intended to improve extraction, but does not explain the exact fallback order or trigger conditions. The visible test extracts City of Palo Alto and a $15 total paid. He tentatively identifies the source document as a parking ticket. This is one successful extraction example; the important architectural move is that a missing capability can be supplied by another provider without changing the overall composition model.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Ask a question about an image
The next application takes an image URL and a question about the person’s expression. Its answer describes a raised eyebrow and a skeptical, curious look, which Awais jokes resembles his usual expression. The code uses GPT-4o, a vision-capable model, and passes it the image URL together with the user’s input. No elaborate orchestration is necessary for this task: the model already exposes the capability the application needs.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Keep the application free to change
The closing activity view is labeled May 30. Awais reports seeing a new agent appear almost every other minute in that view. Examples include a dental cost advisor, a business-oriented app and a task refiner. Their variety supports the product ambition: use a common set of primitives to build applications with quite different purposes.
His closing recommendation is to choose building blocks that leave room for models and agent workflows to change. Developers can build those primitives themselves, use Langbase’s hosted versions, or combine capabilities from other providers, as the OCR example did. Chai’s role is to compose the primitives it knows so that a generated agent can be deployed and used promptly.
The target is an application that can be operated, not merely generated. Managed primitives are meant to carry the infrastructure burden while ordinary code retains control of the workflow. The recording demonstrates working application paths, though it does not walk through a deployment command or infrastructure configuration. The practical design choice is where to place each responsibility: infrastructure inside reusable services, and task-specific decisions in code that remains understandable and changeable.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Resources
From the talk
Documentation for Langbase's managed agent primitives, TypeScript SDK and HTTP API.
The original document-understanding API announcement, covering image and PDF extraction. The original model is now marked deprecated.
Current documentation for web search and content retrieval with Python and JavaScript examples.
Further reading
TypeScript examples for augmented LLMs, chaining, routing, parallelization, orchestration, evaluation, tools and memory.
- Building effective agentsArticle
Anthropic's explanation of composable agent patterns and when to use workflows, autonomous agents or frameworks. Tooling examples have since been updated.
Updates since the talk
The current destination of chai.new, offering prompt-based agent creation and community examples.
Read the transcript and explore slides
Loading transcript and slides…