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
Agentmethod, and callllm(...)inside the function body
The LLM becomes a tool that you call, constrain, and compose.
The Three Primitives#
The entire paradigm is just three things:
1. Agent method#
A decorator. For a function it decorates, the docstring travels with the call as descriptive context, and llm(...) inside the body triggers one model call. The decorator supplies the ambient runtime.
from openprogram import Agent
from openprogram.agentic_programming import llm
class ExampleAgent(Agent):
method_options = {
'summarize': {'tool': True},
}
def summarize(self, text: str) -> str:
"""Summarize a text in one sentence, preserving the core point."""
return llm([{"type": "text", "text": (
f"Summarize in one sentence, preserving the core point:\n\n{text}"
)}])
_example_agent = ExampleAgent()
summarize = _example_agent.summarize
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
User orchestration calls llm() for a single model request. Runtime.exec() remains the lower-level embedding and infrastructure API used underneath it.
3. Context#
Each Agent execution owns its DAG. Chat records user messages, model requests and directly dispatched tools; ordinary helpers create no durable nodes. Calling another Agent creates an independent DAG; the parent stores the invocation result and a child_session_id reference. Permission, cancellation and file-checkpoint ownership remain with the originating execution, independent of graph ownership.
The DAG is not just a trace — it is also where each LLM call's history comes from: llm() renders its message history from the DAG through the ambient runtime. 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 Agent methods that conform to the documented API. This needs no dedicated create() / fix() framework functions; the agent authors new functions with ordinary file edits. A background watcher rescans programs/workflow/ and hot-loads new modules: their Agent method executions 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
Agentmethods 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
programs/workflow/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 | agent(prompt, 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. programs/workflow/ 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: