设计哲学#
OpenProgram 是 Agentic Programming 这一编程范式的产品化实现。 这篇文档讲的是范式本身:它解决什么问题、为什么要反转控制权、核心原语是什么。 范式的正式表述见论文 LLM-as-Code: Agentic Programming for Agent Harness(arXiv:2606.15874),已被 KDD 2026 AgenticSE workshop 接收。
问题#
当前所有 LLM Agent 框架都把控制权交给模型:
- 做什么 由 LLM 决定(planner 先规划、agent 再执行)
- 何时做 由 LLM 决定(while loop 直到 agent 说"我做完了")
- 怎么做 由 LLM 决定(工具调用、参数、顺序)
代价:
- 执行不可预测 —— 同样的输入,每次轨迹不同
- 上下文爆炸 —— 每一步都把历史塞给模型
- 输出无保证 —— 没人能说"这个任务一定会跑完"
- 调试地狱 —— 出错时,你分不清是 prompt 问题、工具问题、还是模型幻觉
根本原因:用一个黑箱概率系统,去做一件本来就能用确定性代码完成的工作。
反转:Python 控流,LLM 推理#
Agentic Programming 把控制权还给程序员:
| 维度 | 传统 Agent | Agentic Programming |
|---|---|---|
| 流程 | LLM 规划 | Python 代码 |
| 决策 | LLM 每步判断 | Python 决定调不调 LLM |
| 状态 | 塞在上下文里 | 函数变量、返回值 |
| 可测 | prompt 回归 | 单元测试 |
把一个复杂任务拆解成函数调用图。图上的每个节点,你决定:
- 不需要推理的 —— 用普通 Python 函数
- 需要理解 / 生成 / 判断的 —— 用
Agentmethod 装饰,函数体里调llm(...)
LLM 变成一个工具,被你调用、被你约束、被你组合。
三个原语#
整个范式只有三样东西:
1. Agent method#
一个装饰器。被它装饰的函数,docstring 作为描述性上下文随调用传递,函数体里的 llm(...) 发起一次模型调用。装饰器提供环境 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
外部调用者感觉不到差别 —— summarize(article) 看起来和任何 Python 函数一样。
2. Runtime#
LLM 调用的运行时抽象。负责:
- 把当前对话历史打包
- 调用底层 provider(Anthropic / OpenAI / Claude Code / ...)
- 把结果写回上下文
用户编排代码使用 llm() 发起单次模型请求。Runtime.exec() 保留为其下层的嵌入与基础设施 API。
3. Context#
每个 Agent 执行分别管理自己的 DAG。聊天只记录用户消息、模型请求和直接工具调用;普通辅助函数不产生持久化节点。调用另一个 Agent 时创建独立 DAG,父图只保存调用结果和 child_session_id 引用。权限、取消与文件修改备份仍由原执行负责,不依赖图所有权。
这张 DAG 不只是执行轨迹——它同时是每次 LLM 调用的历史来源:llm() 通过环境 runtime 从 DAG 渲染消息历史。装饰器上的两个旋钮按函数塑造这条数据流:
expose—— 一次调用完成后向父级暴露什么(默认"io":函数名 + 输入 + 输出,内部细节隐藏)。render_range—— 函数自身的exec拉取多少历史。render_range={"callers": 0}得到一个隔离的草稿上下文,看不到任何先前对话。
于是上下文管理不再是 prompt 拼接的体力活,而变成写在函数声明上的属性。同一张 DAG 也兼作你的调试视图:可视化、token 记账、回放失败路径。
衍生概念#
LLM 也写代码#
LLM 不只是运行时的推理引擎,它也可以写代码——生成、修改、修复符合 API 文档的 Agent method。这件事不需要专门的 create() / fix() 框架函数;agent 直接用普通文件编辑工具完成。后台 watcher 会重扫 programs/workflow/ 并热加载新模块:import 时 Agent method 装饰器触发、自行注册,刚写完的函数无需重启即可调用。
代码是数据,LLM 是编译器,函数是产品 —— 循环闭合。
双模式#
Agentic Programming 同时是:
- 一个库 —— 你写
Agentmethod,手动搭 pipeline - 一个跑着的产品 —— 在 CLI 或 WebUI 里聊天,让 agent 帮你把函数写出来;生成的文件落到
programs/workflow/并热加载
初学者从提需求开始,拿到手的就是完整可读的 Python 文件。想深挖的人再 import 手写。这是一个可以被逐步理解的工具。
和传统 Agent 框架的对照#
| 场景 | LangChain / AutoGPT | Agentic Programming |
|---|---|---|
| "抓 10 个页面,每个生成摘要" | Agent 自己决定顺序和并行 | Python 写 for url in urls: summarize(fetch(url)) |
| "连续 3 次对话里记住上下文" | 把对话塞进 memory store,每次查询 | 就是 Python 函数的局部变量 |
| "让 LLM 决定调哪个工具" | function calling + agent loop | agent(prompt, tools=[...]) 或 decision.make(prompt, options) |
| "错了要重试" | Agent 自己决定 | try / except + 代码门控:无效的选择被校验拦下,让模型重新决策 |
不是说 Agent 框架错了,它们适合一类任务(完全开放、目标模糊)。但大多数你想做的事,其实都能用 Agentic Programming 更可靠地完成。
OpenProgram = 范式的产品化#
agentic_programming/ 子包是范式的引擎代码。context/ 实现扁平 DAG 上下文模型。providers/ 适配各家 LLM。programs/workflow/ 是这个范式下已经写好的函数和应用。webui/ 让初学者不写代码也能跑。
范式先行,产品为用。
延伸阅读: