yoagent

yologdevMIT活跃维护

Rust 写的 Agent 循环库,统一接入 7 种 LLM 协议,流式输出、调用工具、循环执行直到任务完成,从笔记本到 Cloudflare Worker 都能跑。

The agent loop for Rust — stream from 7 LLM protocols, run tools, loop until done. From a laptop to a Cloudflare Worker.

v0.27.7发布于 2026-11-09 22:12 UTC370star +1 / 7 天53fork同步于 2026-11-10 12:01 UTC

怎么上手

由 README 提炼,执行前请确认
git clone https://github.com/yologdev/yoagent && cd yoagent
ollama serve &
ollama pull llama3.1:8b
cargo run --example cli -- --provider ollama
硬件要求按 README 说明

README 未说明显存或 GPU 要求;可编译为 wasm32 运行在 Cloudflare Worker,本地示例可用 Ollama 跑 llama3.1:8b

下载:源码 zip·GitHub Releases(安装包)本站不镜像安装包

项目说明

由 README 自动翻译提炼

Rust 的 agent 循环库,从 7 种 LLM 协议流式接收、执行工具并循环到完成

Rust 写的 Agent 循环库,统一接入 7 种 LLM 协议,流式输出、调用工具、循环执行直到任务完成,从笔记本到 Cloudflare Worker 都能跑。

核心能力

  • 提供无状态 agent_loop() 自由函数,可自行驱动循环
  • 原生实现 7 种协议,保留 thinking 预算与缓存断点
  • 用 Extension 统一钩子拦截、修改或拒绝工具调用
  • Session 历史为树结构,支持分叉、检查点与回放
  • 内置 MockProvider,可离线测试多轮工具调用

容易踩的坑

原生目标必须保留默认 native feature,否则 reqwest 无 TLS,HTTPS 调用全部失败;编译 wasm32 需设 default-features = false;示例依赖本地 Ollama 并需先拉取模型。

内容由模型根据 README 生成,可能不完整或不准确,以官方文档为准。看原文 README

最近发版

5 个版本v0.27.7
  1. v0.27.7最新正式版2026-11-09 · 2026-11-09 22:12 UTC

    One preset, no breaking changes.

    • A preset for Gemini 2.5 Flash-Lite. ModelConfig::gemini_2_5_flash_lite() and ModelConfig::preset("google", "gemini-2.5-flash-lite") give the model Google's limits (1,048,576 context, 65,536 output) instead of the generic constructor's guess.
    • Price: $0.10 / $0.40, cached $0.01 for text, image and video. Audio input bills three times that, so it is under-counted.
    • Why this one: Google does not deprecate this model ("remains available through the API until further notice"). 2.5 Flash and 2.5 Pro shut down on 2026-12-01 and get no preset.

    yoagent-rutis (0.13.0) and yoagent-workers (0.4.0) are unchanged. Full notes: CHANGELOG.md.

  2. v0.27.62026-11-09 · 2026-11-09 11:17 UTC
    查看更新说明

    Presets only, no breaking changes.

    • Presets for every current Gemini 3+ text model. ModelConfig::gemini_3_5_flash_lite(), gemini_3_1_flash_lite() and gemini_3_flash_preview() join the existing Gemini presets. ModelConfig::preset("google", id) now returns all seven current models, each with Google's limits: a 1,048,576-token context and 65,536 output.
    • Prices, from Google's pricing page:
      • 3.5 Flash-Lite: $0.30 / $2.50, cached $0.03.
      • 3.1 Flash-Lite: $0.25 / $1.50, cached $0.025.
      • 3 Flash Preview: $0.50 / $3, cached $0.05.
    • Audio input: 3.1 Flash-Lite and 3 Flash Preview bill audio input at twice the text rate. Their presets carry the text rate, so audio input is under-counted.

    yoagent-rutis (0.13.0) and yoagent-workers (0.4.0) are unchanged. Full notes: CHANGELOG.md.

  3. v0.27.52026-11-08 · 2026-11-08 23:59 UTC
    查看更新说明

    Fix release, with no breaking changes.

    • Built-in tools:
      • write_file / edit_file replace a file atomically. They write a temp file, sync it and rename it over the original. Read-only files are refused, symlinks are followed, and the mode and owner are kept.
      • bash keeps the end of long output, not only the start.
      • read_file pages through files over its size limit with offset/limit.
      • edit_file no longer suggests write_file for a file it can't read.
    • LlmCompaction: past its transcript cap it keeps the newest messages, and pins the previous briefing and the latest request.
    • MCP:
      • tools/list follows every page.
      • A cancelled call sends notifications/cancelled.
      • A 404 clears only the session it was sent with.
      • A server that crashed before close() reads as exited.
    • Providers:
      • Anthropic: the history cache survives large parallel tool batches. A null usage count no longer zeroes usage. Tool arguments that aren't an object aren't run.
      • Gemini: a blocked or failed response doesn't run its calls, and a final event without its blank line is kept.
      • OpenAI-compatible: no empty call from a skipped tool-call index.
      • All providers: an in-stream rate limit waits as long as it names.
    • Loop:
      • A loop-detection abort reports its results as events.
      • on_before_turn returning false now ends the run as Stopped, with a marker message.
      • The outcome reads only markers the loop itself wrote.
    • Agent: changes made while a run is out apply when it is joined.
    • GASP: one run per recording.
    • FileBackend: scoped stash files are evicted again.
    • search: max_results: 0 no longer hides matches.

    yoagent-rutis 0.13.0 and yoagent-workers 0.4.0 are unchanged.

    Full notes: CHANGELOG.md

  4. v0.27.42026-11-07 · 2026-11-07 16:04 UTC
    查看更新说明

    A fix release, with no breaking changes.

    • No temperature for GPT-6. OpenAI's latest-model guide says: "When reasoning effort is not none, remove temperature, top_p, and top_logprobs."
      • No GPT-6 model defaults to none. Astra and 6.1 Sol do not support it at all.
      • yoagent never sends none.
      • So with_temperature on GPT-6 failed every request.
      • ModelConfig::openai and openai_responses now set accepts_sampling_params from the id, and a gpt-6… id turns it off. That covers the presets, generic("openai", ..), OpenCode and OpenRouter. The agent loop then leaves the temperature out.
      • OpenAI's other reasoning models keep the flag on. If yours rejects a temperature, call with_accepts_sampling_params(false).

    yoagent-rutis 0.13.0 and yoagent-workers 0.4.0 are unchanged.

    Full notes: CHANGELOG.md

  5. v0.27.32026-11-07 · 2026-11-07 12:24 UTC
    查看更新说明

    This is a fix release, and nothing in it breaks existing code.

    • No temperature for a model that rejects it (asked for by yoyo-evolve, its #1146).
      • The bug: Claude models from 4.7 on (Opus 4.7, 4.8, 5 and 5.5; Sonnet 5 and 5.5; Haiku 5.5; Fable) reject temperature, top_p and top_k with a 400. So with_temperature made every request to them fail.
      • The new field: ModelConfig::accepts_sampling_params defaults to true. ModelConfig::anthropic, the OpenCode gateways and the presets built on them turn it off for those ids. That includes the Bedrock and OpenRouter configs preset(..) builds.
      • What the loop does: with the flag off, it leaves the temperature out and logs a warn! once per model.
      • Setting it yourself: call with_accepts_sampling_params on a gateway or custom config.
      • Saved configs: a config persisted before this release loads with the flag on, so rebuild it.

    yoagent-rutis 0.13.0 and yoagent-workers 0.4.0 are unchanged.

    Full notes: CHANGELOG.md

用户点评

登录用户的真实使用记录,可按版本和硬件筛选
共 0 条

    还没有人点评。第一个说出真实体验的人最有价值。

    README 原文

    未翻译

    Getting started · Design philosophy · Docs · API · Changelog · 中文

    The agent loop for Rust. Stream from any LLM protocol, run tools and extensions, loop until done.

    A loop library, not an agent: it powers yoyo, a coding agent evolving its own source since March 2026. Your agent ships as one native binary, or builds for wasm32 to run in a Cloudflare Worker.


    Try it in one command — no API key

    git clone https://github.com/yologdev/yoagent && cd yoagent
    ollama serve &
    ollama pull llama3.1:8b                           # or pass --model <any pulled model>
    cargo run --example cli -- --provider ollama
    

    That's a working coding agent in your terminal — file read/write/edit, shell, ripgrep search, streaming output, skills. No signup, no key, nothing to configure.

      yoagent cli — mini coding agent
      Type /quit to exit, /clear to reset
    
      model: llama3.1:8b
      cwd:   /home/user/my-project
    
    > find all TODO comments in src/
    
      ▶ search 'TODO' ✓
    
    Found 3 TODOs:
      src/main.rs:42: // TODO: handle edge case
      src/lib.rs:15:  // TODO: add tests
      src/utils.rs:8: // TODO: optimize this
    
      tokens: 1250 in / 89 out
    

    Point it at a hosted model instead by swapping the flag:

    ANTHROPIC_API_KEY=sk-... cargo run --example cli
    GROQ_API_KEY=...        cargo run --example cli -- --provider groq --model openai/gpt-oss-120b
    cargo run --example cli -- --api-url http://localhost:1234/v1 --model my-model   # LM Studio, llama.cpp, vLLM
    

    Install

    [dependencies]
    yoagent = "0.27"
    tokio = { version = "1", features = ["full"] }
    

    Building for wasm32? Use default-features = false (see WebAssembly & Cloudflare Workers). On a native target keep the default native feature: without it reqwest has no TLS, and every HTTPS provider call fails at runtime.

    Quick start

    An agent that actually uses a tool — the thing the crate exists for:

    use yoagent::provider::ModelConfig;
    use yoagent::{tools, Agent, AgentEvent, StreamDelta};
    
    #[tokio::main]
    async fn main() {
        // The provider is selected from the config's protocol and the key is read
        // from ANTHROPIC_API_KEY. Call `.with_api_key(k)` to pass one explicitly.
        let mut agent = Agent::from_config(ModelConfig::claude_sonnet_5())
            .with_system_prompt("You are a coding assistant.")
            .with_tools(tools::default_tools());
    
        let mut events = agent.prompt("Find every TODO in src/ and summarise them").await;
    
        while let Some(event) = events.recv().await {
            match event {
                AgentEvent::MessageUpdate { delta: StreamDelta::Text { delta }, .. } => print!("{delta}"),
                AgentEvent::ToolExecutionStart { tool_name, .. } => println!("\n▶ {tool_name}"),
                AgentEvent::AgentEnd { .. } => break,
                _ => {}
            }
        }
        agent.finish().await;
    }
    

    Swap the model by swapping the config — the provider follows, and the key is read from that provider's conventional env var:

    Agent::from_config(ModelConfig::groq("openai/gpt-oss-120b", "GPT-OSS 120B"));    // GROQ_API_KEY
    Agent::from_config(ModelConfig::gemini_3_8_flash());                              // GEMINI_API_KEY
    Agent::from_config(ModelConfig::ollama("http://localhost:11434/v1", "llama3.1:8b")); // no key
    

    Why yoagent

    Every agent runs the same loop, and almost every hand-written one gets the same things wrong: streams that end badly, tools that panic or hang, context that runs out, runs that never stop, spend nobody can account for. yoagent writes that loop once, with each guarantee pinned by a test, and keeps one rule for what goes in: does every agent need it, the same way? Anything else is an extension, a feature or a companion crate (design philosophy).

    So it ships no vector stores, embedding pipelines, or task-graph layer — if your problem is retrieval or orchestration, one of these is the better fit:

    If you needLook at
    RAG pipelines, vector stores, embeddings, transcription and image generationrig — "Build modular and scalable LLM Applications in Rust"
    Typed task graphs and streaming RAG indexing alongside agentsswiftide — "Composable LLM agents and harness, typed task graphs, and streaming RAG pipelines in Rust"
    A tool-calling loop you host, gate, steer, branch, and recordyoagent

    What that focus bought:

    • The loop is a free function. agent_loop() is stateless and takes everything it needs as arguments. Agent is an optional wrapper that adds history and queues. You can drive the loop yourself without adopting our state model.
    • 7 native wire protocols, not one OpenAI-compat shim with adapters bolted on. Anthropic Messages, OpenAI Completions, OpenAI Responses, Azure, Gemini, Vertex, and Bedrock each have a real implementation, so provider-specific features (thinking budgets, prompt-cache breakpoints, reasoning deltas) survive instead of being flattened away.
    • One plug-in contract for the whole run. An Extension can add tools, check input, allow, modify or deny each tool call, redact results, and check the final answer, with state that starts fresh each run. Install it as host policy and it governs every sub-agent too. Hooks that guard a call fail closed: a policy that cannot run denies, an input check rejects, a redaction that fails withholds.
    • Steer a run that's already going. Inject guidance mid-flight; it's picked up between tool batches without restarting the turn.
    • History is a tree, not a list. Session forks, checkpoints, and seeks. Edit an earlier turn and re-run it without destroying the original branch.
    • Runs are recordable. With features = ["gasp"], a run becomes an append-only semantic event log in a git repo — restore is clone + replay. Conformance-checked in CI.
    • The whole loop is testable offline. MockProvider scripts multi-turn tool-calling conversations and honours cancellation, so abort and steering paths are testable with no network or key.

    Extend it

    Everything that changes what the loop does goes through one contract, Extension: its run's RunHooks see on_input, before_model, before_tool, after_tool, on_stop, every event, and finish. A tool policy is a few lines:

    use yoagent::extension::{ClonedHooks, RunHooks};
    use yoagent::{ToolCallRequest, ToolDecision};
    
    #[derive(Clone)]
    struct NoForcePush;
    
    #[async_trait::async_trait] // the `async-trait` crate
    impl RunHooks for NoForcePush {
        async fn before_tool(&self, call: &ToolCallRequest<'_>) -> ToolDecision {
            let command = call.args["command"].as_str().unwrap_or_default();
            if command.contains("push --force") {
                ToolDecision::Deny("force-push is not allowed here".into())
            } else {
                ToolDecision::Allow
            }
        }
    }
    
    let agent = agent.with_extension(ClonedHooks::new("no-force-push", NoForcePush));
    

    yoagent's own budget, tool gate and input guard are built the same way. Six runnable extension_* examples cover policies, redaction, verifiers, budgets, sub-agent policy and audit logs (guide).

    Plugins and other ecosystems. yoagent-rutis turns rutis plugins — Rust, TypeScript (Node or Bun), Python or Go, loaded, reloaded and unloaded at runtime — into one Extension. Through small adapters it also runs many pi extensions and DeepSeek harness (DSH) tool plugins unchanged, with no change to yoagent's core: tools, tool policies, input checks, prompt additions and images cross over; commands and UI don't. An extension that hooks something the adapter can't enforce is refused, not half-run. How the 20 most-downloaded packages of each ecosystem fare (by monthly downloads, among npm's first 1000 search results per keyword), measured: plugin compatibility. Agents built this way — a coding agent with pi and DSH plugins in your browser, a handoff agent that asks without waiting and reports back — live in yobuild.

    Runs where agents run

    The same loop builds natively and for wasm32-unknown-unknown. In yoyo's Cloudflare Worker we measured about 4 ms to start and a median of ~45 ms of CPU per agent run — an agent spends most of a run waiting on the model, which Workers do not bill as CPU. A minimal Worker (one provider via Agent::from_provider, one tool, no decision model) is about 330 KiB gzipped. HTTP MCP works over the host's fetch; yoagent-workers turns the Workers AI binding into a decision model. See WebAssembly & Cloudflare Workers.


    Built with yoagent

    yoyo-evolve — a coding agent that evolves its own source in public. It began as 200 lines of Rust; every commit since has been agent-written and gated on tests. It runs on this loop with the openapi feature enabled.

    Also built on yoagent:

    ProjectWhat it is
    rabA lightweight, extensible Rust coding agent
    greatsage"Rimuru's Unique Skill, you know the one"
    yoclawOpenClaw reborn in Rust — a single-binary agent that remembers you

    Built something on yoagent? Open a PR and add it here — we'd like to see it.


    What's in the box

    The loopFull event stream; parallel, sequential or batched tools; steering and follow-ups; execution limits; retry with backoff and jitter; unanswered tool calls closedAgent loop · Events · Retry
    ExtensionsOne plug-in contract for the run; on_stop sees cut-off answers too; Budget; the older hooks (ToolMiddleware, input filters, TurnHook, callbacks) still workExtensions · Callbacks
    Providers7 native protocols reaching 20+ providers, or your own StreamProvider; thinking controls; prompt-cache hints; one context-overflow classifier; images sent the way each protocol takes them, or a text note with image_input offProviders · Prompt caching
    Toolsbash, file read/write/edit, list_files, search; custom tools via one trait; MCP over stdio or HTTP; OpenAPI specs; ToolSources (refreshed mid-run, MCP list_changed included); per-file locking, images checked before sending, no overwrite after a partial read (ReadLog, on in default_tools())Tools · MCP · OpenAPI
    Sub-agentsChild loops with their own model and tools; large artifacts passed by reference; spend rolled upSub-agents
    ContextUsage-calibrated tracking, tiered compaction, optional LlmCompaction, loop detectionContext management
    Sessions, skills, structured outputBranching session trees with JSONL; AgentSkills SKILL.md; typed prompt_structured::<T>()Sessions · Skills · Structured outputs
    Decision models (decision)Typed yes/no, one-of-N and score judgments in a few hundred ms (Jev, Clef, OpenAI Decisions, any logprobs server); a tool gate and an input guardDecision models
    Cost and telemetryOpt-in pricing (nothing priced by default); model limits from the same table, applied even with pricing off; SessionStats per run incl. sub-agents; tracing spans with tokens and costPricing · Telemetry
    Recording (gasp)serde on every core type; runs recorded into a GASP repo, plugin logs too (opt-in)Persistence · GASP
    WebAssembly--no-default-features for wasm32-unknown-unknown, e.g. Cloudflare WorkersWebAssembly & Workers

    Examples

    Seventeen of the 22 runnable examples in examples/ are below; ten need no API key at all (eleven counting cli with a local model). The rest are live-provider harnesses and offline evaluation sweeps.

    ExampleWhat it showsKey needed
    cliA ~400-line coding agent — all tools, skills, streaming, colored output. Like a baby Claude Codeoptional¹
    rlmAn LLM that explores a codebase on its own by spawning sub-agentsyes
    code_reviewThree sub-agents reviewing a diff in parallel, results mergedyes
    shared_statePassing a large artifact between sub-agents by referenceyes
    sub_agentDelegation basics with a per-sub-agent modelyes
    basicThe smallest possible agentyes
    callbacksLifecycle hooks and a custom toolno
    persistenceSave and restore a sessionno
    telemetrytracing spans with token and cost fieldsno
    gasp_emitRecording a run into a GASP repono
    decisionDecision-model questions in one line, and attaching a model to an agent (feature decision)yes
    extension_policy, _redact, _verifier, _budget, _tree, _auditExtensions: a tool policy, redaction, a verifier, budgets, policy over sub-agents, an audit log (guide)no²

    ¹ --provider ollama or --api-url needs no key; hosted providers read their conventional env var.

    ² Scripted offline by default; -- --live uses DEEPSEEK_API_KEY or ANTHROPIC_API_KEY.

    The companion crates have their own: language_plugins, pi_extensions and dsh_tools (TypeScript, Python, pi and DSH plugins) and a Clef tool-gate Worker.


    Testing

    MockProvider scripts a whole multi-turn tool-calling conversation with no network, and honours cancellation, so abort and steering paths are testable too. See Testing Your Agent; how the crate itself is tested and what CI runs is in CONTRIBUTING.


    Documentation

    MSRV is 1.86, enforced in CI. Raising it is a minor-version change.

    Contributing

    Bug reports, ideas and PRs are welcome — open an issue or pick one labelled help wanted. CONTRIBUTING covers building, the checks CI runs, and the PR checklist. Report security issues privately as described in SECURITY.

    Acknowledgements

    rutis, the plugin runtime behind yoagent-rutis, and the contributors who send fixes and ideas — including the ones who suggest them on X.

    License

    MIT — see LICENSE.