> For the complete documentation index, see [llms.txt](https://tinyhumans.gitbook.io/openhuman/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://tinyhumans.gitbook.io/openhuman/zh/kai-fa/quickstart.md).

# Rust 快速入门

从一个空的 Cargo 项目开始，在你自己的 Rust 进程中于一个 OpenHuman 运行时上运行多个代理。

本页将带你从一个空的 Cargo 项目一路走到单个进程中的一组代理。每一步都建立在上一步之上，并以你可以运行的代码收尾。 [Embedding OpenHuman](https://github.com/tinyhumansai/openhuman/tree/main/docs/gitbooks/en/developing/embedding.md) 是此处所用每种类型的完整参考。

## 你将获得

`openhuman-embed` 将 OpenHuman 核心运行在你的二进制文件内。无需启动守护进程，也没有 RPC 往返，因此一次 turn 就是一次函数调用。你先构建一个 `Runtime` （运行时）用于每个进程，然后按需创建任意多个 `Agent`。每个代理都有自己的模型、访问级别、工作目录、MCP 服务器和技能。

共享同一个进程正是高密度的来源。启动成本只支付一次，而每增加一个代理只带来很小的边际成本。使用一个模拟推理提供方时，单个进程中的 500 个代理平均每个只占约 1.8 MiB 的边际内存。每台机器承载数千个代理是目标，而不是实测结果。相关数字以及复现方法见 [性能](/openhuman/zh/kai-fa/performance.md) 页面。

## 前置条件

你需要 Rust 1.99.1 或更新版本，也就是在 [`rust-toolchain.toml`](https://github.com/tinyhumansai/openhuman/blob/main/rust-toolchain.toml)中固定的版本。你还需要任意 OpenAI 兼容端点的 API 密钥，或者 TinyHumans API 密钥（见第 6 步）。

如果你像下面这样通过 Cargo 依赖 OpenHuman，Cargo 会自动为你拉取供应商化的子模块。如果你是从克隆仓库构建，请先初始化它们：

```bash
git submodule update --init --recursive vendor/
```

## 第 1 步：添加依赖

```toml
[dependencies]
openhuman-embed = { git = "https://github.com/tinyhumansai/openhuman", package = "openhuman-embed" }
# 第 2 步中的 tokio worker 设置所需。
openhuman_core = { git = "https://github.com/tinyhumansai/openhuman", package = "openhuman" }
tokio = { version = "1", features = ["rt-multi-thread", "macros", "sync", "time"] }
anyhow = "1"
```

默认特性集是贡献者构建。若要减少随程序发布的代码，请关闭默认特性并选择你需要的特性。下面这组会保留模型提供层和 MCP：

```toml
openhuman-embed = { git = "https://github.com/tinyhumansai/openhuman", package = "openhuman-embed", default-features = false, features = ["inference", "mcp"] }
```

Cargo 只会从顶层 workspace 应用 `[patch]` 表，因此 OpenHuman 的 [根目录 `Cargo.toml`](https://github.com/tinyhumansai/openhuman/blob/main/Cargo.toml) 不会传递到你的 crate。它们会让每个 crate 共享同一份 `tinytools` 和 `tinyinference`。如果这些 crate 的依赖解析失败，或者你看到两个不兼容的 `tinytools` 类型，请把同样的 `[patch]` 部分复制到你自己的 workspace 中。

每个特性都会转发到核心中同名的特性。 `mcp` 和 `skills` 还会控制 `AgentSpec::mcp` 和 `AgentSpec::skills_dir`，因此如果你的代理需要这些功能，请启用它们。

## 第 2 步：自己构建 tokio runtime

不要使用 `#[tokio::main]`。一次 turn 是一个大型 async 状态机，而嵌套在 turn 中的子代理会使 tokio 默认的 2 MiB worker 栈溢出并使进程中止。请使用核心导出的栈大小和阻塞线程上限来构建 runtime：

```rust
use openhuman_core::core::runtime::{AGENT_WORKER_STACK_BYTES, MAX_BLOCKING_THREADS};

fn main() -> anyhow::Result<()> {
    let rt = tokio::runtime::Builder::new_multi_thread()
        .enable_all()
        .thread_stack_size(AGENT_WORKER_STACK_BYTES)
        .max_blocking_threads(MAX_BLOCKING_THREADS)
        .build()?;
    rt.block_on(run())
}

async fn run() -> anyhow::Result<()> {
    Ok(())
}
```

其余步骤将补全 `run`.

## 第 3 步：你的第一个代理

`Harness` 是一个 runtime 加上恰好一个代理。它是获得回复的最短路径。此版本指向一个你提供密钥的 OpenAI 兼容端点，使用一个临时 workspace，并且是只读的：

```rust
use openhuman_embed::{Access, Harness, Provider, Workspace};

async fn run() -> anyhow::Result<()> {
    let harness = Harness::builder()
        .provider(
            Provider::openai_compatible(
                "https://api.openai.com/v1",
                std::env::var("OPENAI_API_KEY")?,
            )
            .model("gpt-5"),
        )
        .workspace(Workspace::Ephemeral)
        .access(Access::readonly())
        .action_dir(std::env::current_dir()?)
        .build()
        .await?;

    let outcome = harness.run("在这个目录里你能看到什么？").await?;
    println!("{}", outcome.reply);
    Ok(())
}
```

将 API 根地址作为 URL 传入。 `/chat/completions` 会自动为你追加。 `Workspace::Ephemeral` 会在 harness 被销毁时移除，因此不会影响现有安装。 `Access::readonly()` 不允许写入，也不允许 shell，因此可以安全地指向任意目录。

一个完整、可运行的版本，并带有进度流式输出，见 [`run_turn` 示例](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/run_turn.rs):

```bash
OPENHUMAN_EXAMPLE_BASE_URL=https://api.openai.com/v1 \
OPENHUMAN_EXAMPLE_API_KEY=sk-... \
OPENHUMAN_EXAMPLE_MODEL=gpt-5 \
  cargo run -p openhuman-embed --example run_turn -- "在这个目录里你能看到什么？"
```

## 第 4 步：在一个 runtime 上运行多个代理

当你需要多个代理时，请直接构建 `Runtime` 并从 `AgentSpec`创建代理。 `Harness` 内部已经替你完成了这些，因此之后迁移只需要很小的改动。

```rust
use openhuman_embed::{Access, AgentSpec, Provider, Runtime, Workspace};

async fn run() -> anyhow::Result<()> {
    let runtime = Runtime::builder()
        .workspace(Workspace::dir("/var/lib/my-product/openhuman"))
        .build()
        .await?;

    let provider = Provider::openai_compatible(
        "https://api.openai.com/v1",
        std::env::var("OPENAI_API_KEY")?,
    )
    .model("gpt-5");

    let analyst = runtime.agent(
        AgentSpec::new("analyst")
            .system_prompt("你负责总结文档，且绝不编辑文件。")
            .provider(provider.clone())
            .access(Access::readonly())
            .action_dir("/srv/documents"),
    )?;

    let writer = runtime.agent(
        AgentSpec::new("writer")
            .system_prompt("你在工作目录中撰写说明。")
            .provider(provider)
            .access(Access::full())
            .action_dir("/srv/documents"),
    )?;

    let analysis = analyst.run("总结这份文档。").await?;
    let draft = writer
        .turn(format!("解释这些要点：\n{}", analysis.reply))
        .send()
        .await?;
    println!("{}", draft.reply);
    Ok(())
}
```

Agent id 必须匹配 `^[a-z0-9][a-z0-9_-]{0,63}$`。避免使用内置 id，例如 `orchestrator`.

访问级别是主要的安全控制：

* `Access::readonly()` 仅观察。
* `Access::supervised()` 可以执行操作，但有风险的操作会等待人工决定。它会为该代理开启自主策略，因此无人值守的进程会在第一次审批处停住，直到 10 分钟超时后被拒绝。
* `Access::full()` 在 `action_dir`下拥有真实的 shell 和文件访问权限，并可自主执行操作。请将其指向你愿意让其修改的目录。诸如 `~/.ssh` 这样的凭据存储无论如何都会保持阻止状态。

`action_dir` 是代理的读写根目录。 `.trust(path, access)` 会授予其外部某个目录的权限。

MCP 服务器和技能是按代理分别配置的。它们需要 `mcp` 和 `skills` 特性，而 runtime 必须启用这些域，因为代理只能缩小 runtime 的集合：

```rust
use openhuman_embed::McpServer;

let spec = AgentSpec::new("triage")
    .mcp(McpServer::stdio("github", "gh-mcp", ["stdio"]).deny_tools(["delete_repo"]))
    .skills_dir("./skills/triage");
```

在一个代理上声明的服务器对其他代理不可见。技能包会复制到 `<workspace>/agents/<id>/skills/`，而操作员的 `~/.openhuman/skills` 会保持隐藏，除非你调用 `.include_user_skills(true)`。通过 `.allow_tools` 或 `.deny_tools` 来缩小 MCP 服务器公开的工具范围，对于大型服务器尤其重要，因为每个暴露的工具都会消耗提示词预算。

该 [`two_agents` 示例](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/two_agents.rs) 端到端运行一个 analyst 和一个 writer。

## 第 5 步：多轮会话与流式输出

`agent.turn(message)` 会返回一个 builder。每个结果都带有一个 `session_id`；把它通过 `.session(..)` 传回即可继续对话。没有它就会开启新会话。

```rust
let first = writer.run("运行测试。").await?;
let again = writer
    .turn("现在修复这些失败项。")
    .session(&first.session_id)
    .send()
    .await?;
```

对话记录按代理 id 进行键控，一次 turn 只会恢复它自己的线程。使用 `Workspace::Ephemeral` 时，会话不会跨进程保留；要做到这一点，请使用 `Workspace::dir(..)` 。

若要在 turn 运行时观察它，请通过 `.on_progress(tx)`:

```rust
let (tx, mut rx) = tokio::sync::mpsc::channel(256);
let printer = tokio::spawn(async move {
    while let Some(progress) = rx.recv().await {
        eprintln!("[progress] {progress:?}");
    }
});
let outcome = writer.turn("go").on_progress(tx).send().await?;
let _ = tokio::time::timeout(std::time::Duration::from_secs(30), printer).await;
```

核心会等待每一次 send，因此如果接收端停止读取，turn 就会停滞。超时处理覆盖的是已分离的子代理：在 `send()` 返回后，它们仍可能持有发送端的一个克隆。其他按 turn 设置的选项还有 `.model(id)`, `.temperature(t)`, `.cwd(dir)` 和 `.origin(..)`.

## 第 6 步：用一个 API 密钥进行托管推理

与其为每个代理提供一个提供方密钥，不如只给 runtime 一个 TinyHumans API 密钥。那些没有指定 `Provider` 的代理会运行在托管推理上，而诸如集成和搜索等后端功能会使用同一个密钥。详见 [一个 TinyHumans API 密钥](/openhuman/zh/kai-fa/tinyhumans-api-key.md) ，了解它能解锁什么。

`openhuman-embed` 单独使用不会安装任何后端传输，因此托管界面会返回 `BACKEND_UNAVAILABLE:`。若要以连接方式启动，请使用 `RuntimeBuilder` 来自 `openhuman-tinyhumans`，它与 embed builder 保持一致，并在 `build()`:

```toml
openhuman-tinyhumans = { git = "https://github.com/tinyhumansai/openhuman", package = "openhuman-tinyhumans" }
```

```rust
use openhuman_tinyhumans::{embed::{Access, AgentSpec, Workspace}, RuntimeBuilder};

let runtime = RuntimeBuilder::new()
    .workspace(Workspace::Ephemeral)
    .api_key(std::env::var("TINYHUMANS_API_KEY")?)
    .build()
    .await?;

let agent = runtime.agent(AgentSpec::new("helper").access(Access::readonly()))?;
let reply = agent.run("你好。").await?;
println!("{}", reply.reply);
```

对于启动核心自身的无头进程，请改为在启动前设置 `OPENHUMAN_BACKEND_API_KEY` 。命名了自己 `Provider` 的代理不会使用该密钥。

## 第 7 步：迈向规模化

几条规则可以让大规模代理群保持小而可预测：

* 每个进程只运行一个 `Runtime` 。第二个 `build()` 会返回 `RuntimeError::AlreadyRunning`，因为密钥环、事件总线和域订阅者都是进程级的。增加代理，而不是增加 runtime。
* 构建要精简。一个无头构建使用 `--no-default-features --features "skills,flows"` 进行 strip 后大约是 60 MiB。打开所有门控后，不进行 strip 时为 115.9 MiB。配方见 [`docs/library-minimal-recipe.md`](https://github.com/tinyhumansai/openhuman/blob/main/docs/library-minimal-recipe.md).
* 让每个代理都保持精简。 `.tool_groups(..)`, `.domains(..)`, `ToolScopeSpec::Named(..)` 和 `.disallow_tools(..)` 可以减少提示词大小以及代理能够触及的范围。
* 通过 `AgentDefinitionSpec`: `为每个代理选择一个沙箱模式`, `ReadOnly` （会移除写入和执行工具）或 `Sandboxed` （命令在平台沙箱或 Docker 中运行）。
* 对于从单个进程为许多用户提供服务的云主机，请使用 `Workspace::stateless()` 配合 `SessionStoreProvider` ，这样对话就会保存在你自己的数据库中。embed crate 的 README 在“Conversations in a host store”中覆盖了这一点。

要衡量你自己的数据，脚本和方法位于 [openhuman-benchmarks](https://github.com/tinyhumansai/openhuman-benchmarks) ([`profile/docs/library-benchmarking.md`](https://github.com/tinyhumansai/openhuman-benchmarks/blob/main/profile/docs/library-benchmarking.md)），同时还包括公开的基准结果和测试装置。

## 其他接入方式

你不必嵌入库也可以使用核心。

JSON-RPC 服务器通过 HTTP 运行同一个核心。用以下命令启动它： `openhuman-core serve`. `GET /schema` 列出所有方法， `GET /health` 报告存活状态，而 `GET /events` 流式传输事件。服务器位于 [`crates/openhuman-rpc`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-rpc/README.md).

CLI 是 `openhuman-core` 二进制程序。它以子命令形式暴露相同的控制器，并在 [`crates/openhuman-cli`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-cli/README.md).

终端客户端是 `openhuman-tui`，这是一个 ratatui 前端，它会在自己的进程中启动核心，并驱动与桌面应用相同的聊天界面。见 [`crates/openhuman-tui`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-tui/README.md).

## 下一步

* [Embedding OpenHuman](https://github.com/tinyhumansai/openhuman/tree/main/docs/gitbooks/en/developing/embedding.md) 是以下内容的完整参考： `AgentSpec`, `Access`、workspaces、MCP、skills 以及示例。
* [架构](/openhuman/zh/kai-fa/architecture/architecture.md) 以及 [代理 harness](/openhuman/zh/kai-fa/architecture/agent-harness.md) 说明一次 turn 是如何运行的。
* [可加载模块](/openhuman/zh/kai-fa/loadable-modules.md) 涵盖核心所组合的模块契约。
* [可插拔引擎](/openhuman/zh/kai-fa/engines.md) 涵盖代理通过配置选择的推理、记忆和搜索后端。
* [性能和占用](/openhuman/zh/kai-fa/performance.md) 包含实测密度、冷启动和二进制大小的数据。
