Design philosophy#
OpenProgram is the productized implementation of the Agentic Programming paradigm. This document is about the paradigm itself: what problem it solves, why it inverts control, and what its core primitives are. The paradigm is described in the paper LLM-as-Code: Agentic Programming for Agent Harness (arXiv:2606.15874), accepted at the KDD 2026 AgenticSE workshop.
The Problem#
Every current LLM Agent framework hands control to the model:
- What to do is decided by the LLM (a planner plans first, then the agent executes)
- When to do it is decided by the LLM (a while loop runs until the agent says "I'm done")
- How to do it is decided by the LLM (tool calls, arguments, ordering)
The cost:
- Unpredictable execution — the same input produces a different trajectory every time
- Context explosion — every step crams the history back into the model
- No output guarantees — no one can say "this task is guaranteed to run to completion"
- Debugging hell — when something breaks, you can't tell whether it's a prompt problem, a tool problem, or a model hallucination
The root cause: using a black-box probabilistic system to do work that could have been done with deterministic code in the first place.
The Inversion: Python Controls Flow, the LLM Reasons#
Agentic Programming gives control back to the programmer:
| Dimension | Traditional Agent | Agentic Programming |
|---|---|---|
| Flow | LLM plans | Python code |
| Decisions | LLM judges at every step | Python decides whether to call the LLM |
| State | Crammed into the context | Function variables, return values |
| Testability | Prompt regression | Unit tests |
Decompose a complex task into a function call graph. For each node on the graph, you decide:
- Doesn't need reasoning — use a plain Python function
- Needs understanding / generation / judgment — decorate it with
@agentic_function, and callruntime.exec(...)inside the function body to trigger the LLM
The LLM becomes a tool that you call, constrain, and compose.
The Three Primitives#
The entire paradigm is just three things:
1. @agentic_function#
A decorator. For a function it decorates, the docstring travels with the call as descriptive context, and runtime.exec(...) inside the body triggers the model call. The runtime parameter is auto-injected.
from openprogram import agentic_function
@agentic_function
def summarize(text: str, runtime=None) -> str:
"""Summarize a text in one sentence, preserving the core point."""
return runtime.exec(content=[{"type": "text", "text": (
f"Summarize in one sentence, preserving the core point:\n\n{text}"
)}])
External callers can't tell the difference — summarize(article) looks just like any Python function.
2. Runtime#
The runtime abstraction for LLM calls. It is responsible for:
- Packaging the current conversation history
- Calling the underlying provider (Anthropic / OpenAI / Claude Code / ...)
- Writing the result back into the context
Runtime.exec() is the only LLM entry point. Every model call goes through it.
3. Context#
The automatic record of execution. Every user turn, every LLM call, and every function call is one node on a single flat DAG; the edges are caller (which function invoked this node) and reads (which nodes an LLM call saw in its prompt). Each node records inputs, outputs, token usage, elapsed time, and failure reason.
The DAG is not just a trace — it is also where each LLM call's history comes from: runtime.exec() renders its message history from the DAG. Two decorator knobs shape that flow per function:
expose— what a completed call reveals to its parent ("io"by default: name + input + output, internals hidden).render_range— how much history the function's ownexecpulls in.render_range={"callers": 0}gives an isolated scratch context that sees none of the prior conversation.
So context management stops being prompt plumbing and becomes a property you declare on the function. The same DAG doubles as your debugging view: visualization, token accounting, replaying failure paths.
Derived Concepts#
The LLM Writes Code Too#
The LLM isn't just the runtime's reasoning engine; it can also write code — generating, modifying, and fixing @agentic_functions that conform to the spec. This needs no dedicated create() / fix() framework functions; the agent authors new functions with ordinary file edits, following the agentic-programming skill as the spec — where files go, decorator metadata, the division of labor between the docstring and content, and the validation checklist. A background watcher rescans functions/agentics/ and hot-loads new modules: their @agentic_function decorators fire on import and self-register, so a freshly written function is callable without a restart.
Code is data, the LLM is the compiler, and functions are the product — the loop closes.
Dual Mode#
Agentic Programming is at the same time:
- A library — you write
@agentic_functions and wire up the pipeline by hand - A running product — chat in the CLI or WebUI and ask the agent to write the function for you; the generated file lands in
functions/agentics/and hot-loads
Beginners start by asking, and what they get is a complete, readable Python file. Those who want to dig deeper can then import and hand-write. This is a tool that can be understood incrementally.
Comparison with Traditional Agent Frameworks#
| Scenario | LangChain / AutoGPT | Agentic Programming |
|---|---|---|
| "Fetch 10 pages and generate a summary for each" | The agent decides ordering and parallelism itself | Python writes for url in urls: summarize(fetch(url)) |
| "Remember context across 3 consecutive conversations" | Stuff the conversation into a memory store and query it each time | It's just a local variable in a Python function |
| "Let the LLM decide which tool to call" | function calling + agent loop | runtime.exec(tools=[...]) or decision.make(prompt, options) |
| "Retry on error" | The agent decides itself | try / except + code gates: an invalid pick is caught by validation and the model is asked to re-decide |
This isn't to say agent frameworks are wrong; they suit a class of tasks (fully open-ended, with fuzzy goals). But most of what you want to do can actually be done more reliably with Agentic Programming.
OpenProgram = the Productized Paradigm#
The agentic_programming/ subpackage is the paradigm's engine code. context/ implements the flat-DAG context model. providers/ adapts the various LLMs. functions/agentics/ holds the functions and applications already written under this paradigm. webui/ lets beginners run things without writing code.
The paradigm comes first; the product exists to use it.
Further reading: