Metadata-Version: 2.5
Name: saddler-harness
Version: 0.1.3
Summary: Whitebox agent harness runtime
Requires-Python: >=3.10
Requires-Dist: anthropic[vertex]<1,>=0.120
Requires-Dist: httpx>=0.28
Requires-Dist: jsonschema>=4.23
Requires-Dist: packaging>=24
Requires-Dist: pydantic>=2.10
Requires-Dist: pyyaml>=6.0.2
Requires-Dist: rich>=13
Requires-Dist: typer>=0.15
Provides-Extra: langfuse
Requires-Dist: langfuse<5,>=4.14.4; extra == 'langfuse'
Provides-Extra: mcp
Requires-Dist: mcp<2,>=1.28; extra == 'mcp'
Provides-Extra: service
Requires-Dist: fastapi>=0.115; extra == 'service'
Requires-Dist: prometheus-client>=0.21; extra == 'service'
Requires-Dist: uvicorn>=0.34; extra == 'service'
Description-Content-Type: text/markdown

# Saddler

Saddler 是一个白盒、事件溯源的 Agent Harness 运行时。它用声明式配置组合 Prompt、Tool、Middleware、Skill、模型参数和执行预算，并在 Local、Worker 或 HTTP/SSE Service 中运行同一个 Agent。

## Quick Start

Saddler 要求 Python 3.10+、[uv](https://docs.astral.sh/uv/) 和一个 OpenAI-compatible 模型服务。

Saddler 以 `saddler-harness` 为发行包名发布到 PyPI；Python import package 和命令行入口仍为 `saddler`。

从 PyPI 安装正式发布版本和可选的 Langfuse 集成。`uv tool install` 会把
Saddler 安装到用户级的独立环境，并将 `saddler` 命令持久暴露到 `PATH`：

```bash
uv tool install "saddler-harness[langfuse]"
saddler --help
```

如果希望 Saddler 只属于当前项目，并通过 `pyproject.toml` 和 `uv.lock` 固定版本，把它
加入开发依赖并通过项目环境运行：

```bash
uv add --dev "saddler-harness[langfuse]"
uv run saddler --help
```

如果 Saddler 是应用的正式运行时依赖，省略 `--dev`。只需临时运行、不修改当前项目或
持久安装时，使用隔离的临时工具环境：

```bash
uvx --isolated --from "saddler-harness[langfuse]" \
  saddler --help
```

下文以用户级持久安装后的 `saddler` 命令为例。使用项目级安装时，在命令前加 `uv run`；
临时运行时，在上述 `uvx ... saddler` 后传入相同参数。

在运行目录创建 `.env`。该文件包含凭证，不应提交到版本控制：

```dotenv
OPENAI_API_KEY=...
OPENAI_BASE_URL=https://your-openai-compatible-server/v1
OPENAI_MODEL=your-model

LANGFUSE_PUBLIC_KEY=pk-lf-...
LANGFUSE_SECRET_KEY=sk-lf-...
LANGFUSE_BASE_URL=https://cloud.langfuse.com
```

后三个变量只在启用 Langfuse 时需要。检查并运行随 wheel 发布的 Coding Agent，同时把 Trace 上报到 Langfuse：

```bash
saddler inventory builtin --item coding_agent
saddler run builtin/coding_agent \
  "Inspect the current directory and summarize the project." \
  --langfuse
```

启用 `--langfuse` 后，CLI 会在 Attempt 接纳时打印该次 Trace 的可点击 Langfuse URL，便于直接查看运行中的 Agent。

`run` 在执行过程中实时显示已提交的 Step、Model、Tool、上下文变化和终态进度，并按实际上下文顺序显示 System、User、Reasoning、Assistant、Tool Call 和 Tool message；结束后再展示 effective System Prompt、完整消息、Tool 请求与响应、最终结果和时间线。同一个 Attempt 可以同时在 Langfuse 中查看。不需要 Langfuse 时，省略三个环境变量、安装地址中的 `[langfuse]` 和 `--langfuse`。`saddler show` 会列出最近 Run 的 input 和结果摘要；运行结束后可以重新查看最近一次完整结果：

```bash
saddler show latest
```

单文件 HTML 报告使用明确的 Run ID：

```bash
saddler show RUN_ID --html run.html
```

源码仓库中的命令需要以 `uv run` 开头。开发环境通过 `uv sync --all-extras --all-groups --locked` 安装。

## Scope

Saddler 负责解析 Harness、驱动单 Agent tool-calling loop、执行 Middleware、提交运行事实并物化审计视图。它不替代模型，不定义训练算法，也不提供不可信代码的 sandbox。

```mermaid
flowchart TD
    H["Harness"] --> R["Resolve and admit"]
    R --> A["Agent loop"]
    A --> M["Model"]
    A --> T["Tools"]
    W["Middleware"] --> A
    A --> E["Canonical events"]
    E --> V["Trajectory, playback and evaluation"]
```

Local、Worker 和 Service 共享 Harness 解析器、Agent loop 和持久化语义。相同 Harness 在不同宿主中具有相同的组件组成和执行规则。

## Composable Harness

Inventory 提供积木，Harness 负责选择、配置和排列这些积木。一个 Harness 可以通过 `<inventory>/<item-id>` 同时组合 builtin、团队或项目 Inventory 中的组件：

```mermaid
flowchart LR
    subgraph I["Inventories：可复用组件库"]
        P["Instructions / Prompt<br/>告诉模型做什么"]
        T["Tools<br/>给模型执行能力"]
        M["Middleware<br/>在调用前后扩展行为"]
        S["Skills<br/>提供按需知识和流程"]
    end
    P --> H["Harness<br/>选择、配置和排序"]
    T --> H
    M --> H
    S --> H
    B["Sampling / Budget<br/>生成参数和执行上限"] --> H
    H --> R["Resolved Harness<br/>校验后的不可变执行配置"]
    R --> X["Local / Worker / Service"]
```

| 组件 | 作用 |
|---|---|
| Instructions / Prompt | 就是 Agent 的基础 System Prompt：直接告诉模型“你是谁、要完成什么、如何工作、哪些规则必须遵守”。可以内联文本，也可以引用 Inventory 中的 Prompt。 |
| Tool | 告诉模型“你能做什么”，声明可调用能力和参数 schema，并绑定 Python、MCP 或宿主提供的实际操作。 |
| Middleware | 在 Model 或 Tool 调用前后插入行为，例如读取项目指令、改写上下文、安全重试、限制输出、压缩历史或清理资源。 |
| Skill | 提供模型可按需加载的领域知识、操作规范和工作流。 |
| Sampling / Budget | 控制模型如何生成，以及最多可使用的 Step、Model、Tool、Token 和时间。 |

Middleware 是 Harness 的主要行为扩展点。`before_*` Hook 正序执行，`after_*` Hook 逆序执行，`wrap_*` Hook 以洋葱结构包围实际操作；它可以改变一次 Model 或 Tool 调用的有效输入和结果，但不能绕过 Runtime 的预算、持久化和完整性规则。Python Tool 和自定义 Middleware 都是受信任的进程内代码。完整组装规则见 [Agent assembly](docs/assembly.md)，内建实现见 [Middleware reference](docs/reference/middlewares/README.md)。

## Capabilities

| 能力 | 行为 |
|---|---|
| Inventory | 从 builtin、本地目录、zip、HTTP 或 OSS 加载 Agent、Prompt、Tool、Middleware 和 Skill。 |
| Harness | 在执行前解析组件引用、配置、Tool schema、模型参数和预算。 |
| Runtime | 执行单 Agent tool-calling loop，并处理预算、超时、取消和上下文变化。 |
| Tool | 支持 Python Tool、MCP stdio 和 MCP Streamable HTTP。 |
| Hosting | 通过 Python API、长生命周期 Worker、CLI 或 HTTP/SSE Service 运行 Agent。 |
| Audit | 将 Canonical Event 作为运行事实来源，并按需物化 Trajectory 和 Playback。 |
| Session | 通过线性 revision 续接跨 Run 对话，不改变独立 Run 的语义。 |
| Observability | 提供 SSE、结构化日志、Prometheus Metrics 和可选 Langfuse Trace。 |

## Langfuse

`saddler run --langfuse` 直接从当前目录 `.env` 或进程环境创建 Langfuse client，不需要 Service Config，也不会启动 HTTP Service。源码开发环境先安装 extra：

```bash
uv sync --extra langfuse --all-groups --locked
```

CLI 必须能够读取 `LANGFUSE_PUBLIC_KEY`、`LANGFUSE_SECRET_KEY` 和 `LANGFUSE_BASE_URL`；还可以设置：

```dotenv
LANGFUSE_ENVIRONMENT=development
LANGFUSE_RELEASE=saddler-local
```

Saddler 会将每个 Attempt 的 Harness、Model、Tool、有效 Middleware 变化和终态异步投影到 Langfuse，并在 CLI 正常退出时 flush。Python 集成也可以通过 `langfuse_client` 参数直接注入 `SaddlerWorker`。Langfuse 会接收完整 Prompt、消息、reasoning、Tool 参数和结果，应将对应项目按敏感数据系统管理。完整 Worker 示例见 [Langfuse tracing](examples/langfuse_tracing/README.md)。

运行中的根 `saddler.agent` 尚未结束，因此不会出现在 Langfuse 默认的 Root Observations 视图。要查看正在执行的 Attempt，在 Observations 页面移除 `Is Root Observation = true`，筛选 `Name = harness.resolved`、`Tags contains saddler` 和 `Tags contains trace_schema:v3`，再按 Start Time 倒序排列并保存为视图。点击最新一行的 Trace ID，可以查看启动快照和已经完成的 Step、Model 与 Tool observation；根 Agent 会在 Attempt 结束后补齐。

## Documentation

文档索引按从整体说明到具体实现的方向组织。

| 主题 | 文档 |
|---|---|
| 系统定位、核心概念和运行流 | [System overview](docs/overview.md) |
| Agent、Tool、Middleware 和 Skill 的组装规则 | [Agent assembly](docs/assembly.md) |
| Agentic RL 中的集成与使用 | [Whitebox Harness usage](docs/whitebox-harness-usage.md) |
| 配置、接口、运行时和公开组件 | [Reference](docs/reference/README.md) |
| 内部架构和模块设计 | [Design](docs/design/README.md) |
| 可执行组合样本 | [Examples](examples/README.md) |
| 组件源码布局和开发规范 | [Components](components/README.md) |
| 分支、提交和合并规则 | [Contributing](CONTRIBUTING.md) |

## Development

安装开发依赖并执行检查：

```bash
uv sync --all-extras --all-groups --locked
uv run ruff check .
uv run pytest
uv build
```

需要扩展 Tool 时，可以 [Fork builtin Inventory](docs/whitebox-harness-usage.md#66-fork-官方-inventory)，也可以使用 `saddler init <name>` [自建 Inventory](docs/whitebox-harness-usage.md#62-创建自己的-inventory)。

贡献代码前必须阅读 [CONTRIBUTING.md](CONTRIBUTING.md)。发布 Saddler 时，从干净的工作树运行发布脚本；脚本更新版本、执行检查、创建 release commit 和 tag，并推送到远端：

```bash
./scripts/release.sh VERSION
```

在 release commit 的干净工作树中构建 wheel 和 source distribution，使用 PyPI API token 上传：

```bash
rm -rf dist
uv build --no-sources
uv run python scripts/check_wheel.py dist/*.whl
uv publish dist/*
```
