# 设计哲学

> OpenProgram 是 Agentic Programming 这一编程范式的产品化实现。
> 这篇文档讲的是范式本身：它解决什么问题、为什么要反转控制权、核心原语是什么。
> 范式的正式表述见论文 *LLM-as-Code: Agentic Programming for Agent Harness*（[arXiv:2606.15874](https://arxiv.org/abs/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 函数
- **需要理解 / 生成 / 判断的** —— 用 `Agent` method 装饰，函数体里调 `llm(...)`

LLM 变成一个工具，被你调用、被你约束、被你组合。

## 三个原语

整个范式只有三样东西：

### 1. `Agent` method

一个装饰器。被它装饰的函数，docstring 作为描述性上下文随调用传递，函数体里的 `llm(...)` 发起一次模型调用。装饰器提供环境 runtime。

```python
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 同时是：
- **一个库** —— 你写 `Agent` method，手动搭 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/` 让初学者不写代码也能跑。

范式先行，产品为用。

---

延伸阅读：
- [快速开始](../../start/GETTING_STARTED.zh.md)
- [API 参考](../../reference/api/)
- [设计细节](../../reference/design/)
