Skip to content

Latest commit

 

History

History
419 lines (306 loc) · 11.9 KB

File metadata and controls

419 lines (306 loc) · 11.9 KB

behest

Rust AI Agent 运行时基础库,覆盖类型化工具、provider-neutral LLM、流式传输、存储和可观测性

behest — Rust 原生 Agent 运行时

CI License: MIT OR Apache-2.0

English · 简体中文


项目简介

behest 是用于构建生产级 AI Agent 运行时的 Rust 原生工具库。它提供强类型契约,覆盖 LLM provider、流式对话、工具调用、嵌入、运行时执行、存储、队列、RAG 和可观测性。

当你需要显式控制模型 provider、工具执行、运行时状态、持久化和运维边界,而不是把 agent loop 藏在不透明框架里时,behest 更适合。

状态:早期基础 crate。公共 API 刻意保持紧凑、强类型、有文档。当前 crate 版本:0.5.9

为什么使用 behest

  • Rust 原生运行时核心:edition 2024、严格 lint、类型化 API、显式错误、无隐藏运行时假设。
  • Provider-neutral LLM 层:OpenAI、Anthropic、本地模型、代理或内部 provider 均可实现相同契约。
  • 类型化工具边界:工具通过 JSON Schema 声明,通过显式注册表执行。
  • 流式优先 agent loop:模型事件、工具调用、持久化和运行时事件都是一等路径。
  • 生产级运维表面:会话门控、快照、压缩、重试策略、队列、存储后端、健康检查、tracing 和可选 OpenTelemetry。

快速开始

[dependencies]
behest = "0.5.9"

创建一个 provider-neutral 的对话请求:

use behest::prelude::*;

let request = ChatRequest::new(ModelName::new("example-model"))
    .with_message(Message::system_text("You are concise."))
    .with_user_text("Summarize this project in one sentence.");

在注册表中注册 provider 并路由请求:

use behest::prelude::*;

let registry = ProviderRegistry::new();
let provider_id = ProviderId::new("my-provider");

// 先注册一个 ChatProvider 实现。
// registry.register_chat(my_provider);

// 然后通过中性注册表路由。
// let response = registry.complete(&provider_id, request).await?;

更多示例见 examples/

为什么叫 behest

behest /bɪˈhest/ — 名词 一个人的命令或指令。

At the behest of the user, the agent acts.

Agent 运行时的核心不是「自主意识」,而是受控的委托执行:用户下达意图,系统在明确边界内组合上下文、调用模型、执行工具、持久化状态、发布事件——可审计、可恢复、可限制、可替换。

behest 这个名字刻意避开 "brain / cognition / intelligence" 这类膨胀隐喻。它只陈述一个工程事实:tool-calling、streaming、memory、queue、RAG、snapshot 的存在,都是因为有人下达了命令。

功能概览

领域 能力
Provider 契约 ChatProviderEmbeddingProvider、请求/响应模型、流事件、provider 能力
Provider 注册表 对话和嵌入 provider 的内存路由
对话模型类型 消息、内容部件、工具调用、响应格式、token 用量、结束原因
工具运行时 ToolFunctionToolExternalToolToolRegistry、schema 生成、执行分发
Agent 运行时 上下文构建、模型调用、工具循环、会话持久化、事件发射
Managed 运行时 ManagedRuntime 统一容器、协调生命周期、类型化组件访问、热重载
热重载 排空感知的组件替换,支持 pre/post 钩子
Drain 辅助 DrainGuard<T> 引用计数守卫,跟踪未释放的 Arc 引用
健康聚合 HealthStatus::aggregatehealthz_response、就绪门控
运行时调用 RuntimeInvocationEmitRequestEventKindControl,传输中立的 emit/on 门面
运行时流 RuntimeEventStoreRuntimeStreamAdapterRuntimeSubscriptionHub,重放 + 实时广播
运行时安全 会话门控、运行时策略、输入准入、死循环检测、工具输出截断
存储 内存存储、Redis、SQLx、MongoDB、对象存储、Qdrant 嵌入
上下文与 RAG 上下文适配器、静态/函数适配器、可选 RAG 适配器
队列 通过 NATS 或 Redis Streams 的可选事件发布
配置 构建器、基于文件的配置、环境变量加载、secret 间接引用
可观测性 tracing 和可选 OpenTelemetry 集成

实现自定义 Provider

behest 不强制将某个厂商 SDK 置于核心。为任何模型后端、网关、本地推理服务或内部 provider 实现 ChatProvider

use async_trait::async_trait;
use behest::prelude::*;

struct EchoProvider {
    id: ProviderId,
}

#[async_trait]
impl ChatProvider for EchoProvider {
    fn id(&self) -> ProviderId {
        self.id.clone()
    }

    fn capabilities(&self) -> ProviderCapabilities {
        ProviderCapabilities::chat()
    }

    async fn complete(&self, request: ChatRequest) -> ProviderResult<ChatResponse> {
        Ok(ChatResponse {
            provider: self.id.clone(),
            model: request.model,
            message: Message::assistant_text("echo"),
            finish_reason: FinishReason::Stop,
            usage: None,
            raw: None,
        })
    }
}

流式 provider 可覆写 stream

定义和执行工具

工具是显式的运行时对象。每个工具暴露稳定的名称、人类可读的描述和 JSON Schema 参数契约。

use behest::prelude::*;
use serde_json::{json, Value};

let tool = FunctionTool::new(
    "echo",
    "Echoes the input message.",
    json!({
        "type": "object",
        "properties": {
            "message": { "type": "string" }
        },
        "required": ["message"]
    }),
    |args: Value| async move {
        Ok(args.get("message").cloned().unwrap_or_else(|| Value::Null))
    },
)
.read_only()
.concurrency_safe();

let registry = ToolRegistry::new();
registry.register(tool);

Provider 返回的工具调用可通过注册表执行:

use behest::prelude::*;
use serde_json::json;

let call = ToolCall::new("call_1", "echo", json!({ "message": "hello" }));
let output = registry.execute(&call).await?;

运行时模型

在运行时层,AgentRuntime 编排完整的 agent 循环,而 ManagedRuntime 提供生产部署的统一容器:

use behest::prelude::*;

let config = AgentConfig::builder()
    .with_file("behest.toml")?
    .with_env("BEHEST")?
    .build()?;

// 一键构建完整配置的 ManagedRuntime。
let managed = config.build_managed().await?;

// 生命周期:init → start → serve → stop
managed.init_all().await?;
managed.start_all().await?;
managed.serve().await?; // 阻塞直到关闭信号
managed.stop_all().await?;

运行时循环:

RunRequest
  -> 加载或创建会话
  -> 准入输入
  -> 构建上下文
  -> 调用模型 provider
  -> 流式/持久化助手输出
  -> 执行工具调用
  -> 追加工具结果
  -> 重复直到完成、限制或错误
  -> 发射 AgentEvent

运行时整合:

  • ProviderRegistry
  • ContextPipeline
  • ToolRuntime
  • RuntimeStore
  • RuntimePolicy
  • CompactionService
  • SessionGate
  • 可选事件发布器
  • 可选快照存储
  • 可选后台任务池

配置

AgentConfig 支持分层配置:

  1. 默认值
  2. 文件源
  3. 环境变量
  4. 手动构建器设置
use behest::prelude::*;

let config = AgentConfig::builder()
    .with_file("behest.toml")?
    .with_env("BEHEST")?
    .build()?;

let runtime = config.into_runtime().await?;

Secret 可通过 env:VAR_NAME 间接加载:

[providers.openai]
api_key = "env:OPENAI_API_KEY"

完整配置结构见 behest.toml 示例

Provider 适配器

具体 provider 适配器通过 feature gate 启用。

Feature 适配器 Chat Stream Embeddings Tools
openai OpenAiChatAdapterOpenAiEmbeddingAdapter
anthropic AnthropicChatAdapter

启用适配器:

[dependencies]
behest = { version = "0.5.9", features = ["openai", "anthropic"] }

Feature Flags

点击展开完整 feature 列表

默认:

Feature 说明
tls-rustls 使用 rustls 的默认 TLS 栈

Provider 适配器:

Feature 说明
openai OpenAI 兼容的对话和嵌入适配器
anthropic Anthropic 兼容的对话适配器

TLS:

Feature 说明
tls-rustls 为 HTTP/已启用后端启用 rustls TLS 集成
tls-native 为 HTTP/已启用后端启用 native TLS 集成

存储:

Feature 说明
redis Redis 存储支持和 Redis Streams 原语
redis-cluster Redis Cluster 支持;隐含 redis
sqlx-postgres SQLx PostgreSQL 存储支持
sqlx-mysql SQLx MySQL 存储支持
sqlx-sqlite SQLx SQLite 存储支持
mongodb MongoDB 会话存储支持
object_store 对象存储支持,包括 AWS S3
storage-all Redis、PostgreSQL、MySQL、SQLite 和 MongoDB 存储 feature

RAG:

Feature 说明
rag 核心 RAG 上下文适配器
qdrant Qdrant 嵌入存储后端
tantivy Tantivy 后端支持
rag-all 启用 ragqdranttantivy

队列:

Feature 说明
queue 核心事件发布器 trait
nats NATS 事件发布器
queue-all 启用 queuenatsredis

可观测性:

Feature 说明
otel OpenTelemetry tracing 集成

便捷 profile:

Feature 说明
full 开箱即用的完整运行时 profile:OpenAI、Anthropic、Redis、Redis Cluster、NATS、PostgreSQL、MongoDB、OpenTelemetry、所有 RAG 后端、所有队列后端和对象存储。刻意不启用 sqlx-mysqlsqlx-sqlite

使用选定 feature 的示例:

[dependencies]
behest = {
    version = "0.5.9",
    default-features = false,
    features = ["tls-rustls", "openai", "anthropic", "redis", "queue", "nats"]
}

错误模型

behest 暴露类型化错误类别,而非字符串化的框架失败:

  • ProviderError
  • ToolError
  • StorageError
  • ContextError
  • RuntimeError
  • 顶层 Error
  • crate 级 Result<T>

Provider 错误区分不支持的能力、可重试失败、传输失败、无效响应和适配器特定错误。

工具错误区分缺失工具、无效参数、执行失败、超时和未实现的外部工具。

Lint 策略

crate 刻意严格:

  • unsafe_code = "forbid"
  • missing_docs = "deny"
  • unreachable_pub = "deny"
  • clippy::all = "deny"
  • dbg_macro = "deny"
  • expect_used = "deny"
  • todo = "deny"
  • unimplemented = "deny"
  • unwrap_used = "deny"

本项目将公共 API 清晰度和失败路径卫生视为运行时契约的一部分。

开发

# 格式化
cargo fmt --all --check

# 检查所有目标和 feature
cargo check --all-targets --all-features --locked

# Lint
cargo clippy --all-targets --all-features --locked -- -D warnings

# 测试
cargo test --all-features --locked

# 构建文档
RUSTDOCFLAGS="-D warnings" cargo doc --all-features --no-deps --locked

运行完整本地验证集:

cargo fmt --all --check && \
cargo check --all-targets --all-features --locked && \
cargo clippy --all-targets --all-features --locked -- -D warnings && \
cargo test --all-features --locked && \
RUSTDOCFLAGS="-D warnings" cargo doc --all-features --no-deps --locked

许可证

以下任一许可:

由您选择。