侧边栏壁纸
博主头像
打功人聊AI

行动起来,活在当下

  • 累计撰写 24 篇文章
  • 累计创建 5 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

yoyo-evolve 交互式代码课程 — AI 如何学会进化

01

What yoyo Does

Imagine a developer who writes their own code, fixes their own bugs, and gets better every single day — and nobody writes their code.

A Coding Agent That Evolves Itself

yoyo is a free, open-source terminal coding agent. You type what you want, and it reads your codebase, makes edits, runs tests, and manages git — all on its own.

But here's what makes it different: yoyo didn't have a human write its code. It started as 200 lines of Rust. Every few hours, it reads its own source, picks improvements, implements them, and commits — if the tests pass.

🤯
The wild part

No human writes its code. No roadmap tells it what to do. It decides for itself. After 24 days: 31,000+ lines, 1,346 tests, 14 modules.

Imagine You Use It

You open your terminal, type yoyo, and say "add dark mode to my app." Here's what happens:

1
You type your request

yoyo's REPL (interactive chat loop) captures your message

2
yoyo reads your codebase

It searches through files, reads configs, understands the project structure

3
It makes surgical edits

Using tools like edit_file, it modifies exactly the lines that need changing

4
It verifies everything works

Runs tests, checks formatting, lints code — reverts if anything breaks

5
Commits the changes

AI-generated commit message, pushed to git — all without you touching a file

What's Under the Hood?

CODE
// main.rs — the very first lines of yoyo
//! yoyo — a coding agent that evolves itself.
//! Started as ~200 lines. Grows one commit at a time.

mod cli;
mod commands;
mod repl;
mod prompt;
mod format;
mod git;
mod memory;
PLAIN ENGLISH

This is the top of the file — a comment explaining what yoyo is and where it came from.

Each mod line says "there's a file called this that contains related code." Think of it as chapters in a book.

cli handles command-line arguments. commands handles slash commands. repl is the interactive chat loop. And so on.

Rust requires you to explicitly declare every file — nothing is hidden or auto-imported.

yoyo is built on Rust and uses a library called yoagent as its foundation. The project is organized into focused modules, each handling one responsibility.

Check Your Understanding

You want to add a new slash command to yoyo. Which file would you most likely need to modify?

02

Meet the Cast

Every software project is a cast of characters, each playing a specific role. Let's meet the players.

The 5 Main Characters

Think of yoyo like a TV production crew. Each person has one job, and they talk to each other to get the show made.

🎭

main.rs (The Director)

Orchestrates everything — sets up the agent, connects tools, handles the event loop. 3,008 lines of coordination.

💬

repl.rs (The Front Desk)

The interactive chat loop — reads your input, manages tab completion, runs the show from a terminal prompt.

🧠

prompt.rs (The Brain's Mouth)

Constructs the prompts sent to the AI — decides what context, instructions, and tools to include. Also runs audit logging.

🔧

commands.rs (The Toolkit)

Registry of all 55 slash commands — /commit, /test, /search, /diff, /spawn, and 50 more. Routes them to specialized handlers.

🎨

format.rs (The Designer)

All visual output — colors, formatting, markdown rendering. The largest file at 6,916 lines because pretty output is hard.

The Supporting Cast

src/ All source code — this IS yoyo
cli.rs (3,153 lines) Command-line flags, config, model switching
commands_project.rs (3,791 lines) Project tools: /health, /fix, /test, /lint, /init
commands_git.rs (1,428 lines) Git integration: /diff, /commit, /undo, /pr
commands_search.rs (2,287 lines) Search: /find, /search, /grep, fuzzy matching
commands_file.rs (1,654 lines) File ops: /add, /save, /export
commands_session.rs (1,665 lines) Session management: /save, /load, /compact
commands_dev.rs (966 lines) Developer tools: /benchmark, /ast
memory.rs (375 lines) Loads learning archives into agent context
git.rs, help.rs, setup.rs, docs.rs Utilities for git helpers, help text, first-run setup, docs lookup
skills/ Markdown instruction files that guide evolution behavior
memory/ Self-reflection archives and synthesized context
scripts/ The evolution pipeline: evolve.sh, format_issues.py, etc.

How the Pieces Connect

CODE
use cli::*;
use format::*;
use prompt::*;

use yoagent::agent::Agent;
use yoagent::provider::{
  AnthropicProvider,
  OpenAiCompatProvider,
  StreamProvider,
};
use yoagent::sub_agent::SubAgentTool;
PLAIN ENGLISH

Import everything from yoyo's own modules (cli, format, prompt).

Import the Agent — the core AI brain from the yoagent library.

Import AI providers — the adapters that talk to Claude, OpenAI, etc. yoyo supports 11 providers!

Import SubAgentTool — the ability to spawn child agents for focused tasks.

💡
Separation of Concerns

This pattern — splitting responsibilities into focused modules — is one of the most important ideas in software. Each file owns one job. If you need to change how commands work, you touch commands.rs. If you need to change colors, you touch format.rs. No file needs to understand the whole system.

Check Your Understanding

A user reports that yoyo's output colors look wrong in their terminal. Which file should they look at first?

yoyo supports switching between Claude, GPT, Gemini, and 8 other AI providers mid-session. Where would this switching logic most likely live?

03

How Conversations Flow

You type a message. What happens next? Let's trace the journey of your words through yoyo's brain.

The Journey of Your Message

Think of it like a REPL — a mail room. Your message arrives, gets stamped, routed, processed, and the response comes back.

👤
You
💬
REPL
🧠
Prompt Builder
🤖
AI Agent
🔧
Tools
Click "Next Step" to trace your message
Step 0 / 7

Inside the REPL — Your Message Arrives

CODE
// repl.rs — the main loop that reads your input
pub struct YoyoHelper;

impl Completer for YoyoHelper {
  fn complete(&self, line: &str, pos: usize,
    _ctx: &RustylineContext)
    -> RustylineResult<(usize, Vec<String>)> {
    let prefix = &line[..pos];
    if prefix.starts_with("/") {
      let matches: Vec<String> = KNOWN_COMMANDS
        .iter()
        .filter(|cmd| cmd.starts_with(prefix))
        .map(|cmd| cmd.to_string())
        .collect();
      return Ok((0, matches));
    }
    // ... file path completion
  }
}
PLAIN ENGLISH

Define a "helper" that assists with tab-completion — the thing that finishes your commands when you press Tab.

When the user is typing, check if they started with "/" — that means it's a slash command.

Filter through all known commands and find ones that match what they've typed so far.

Return the matches so the terminal can show them. If they didn't type "/", fall through to file path completion.

The Group Chat — Components Talking

When you type "fix the failing test in auth.rs," here's the conversation that happens inside yoyo:

?
0 / 6 messages

Check Your Understanding

A user types "/commit" and expects yoyo to generate a commit message from staged git changes. Trace the path: where does this request go first?

04

The Evolution Engine

How does software write its own code safely? A 3-phase pipeline that's like a surgeon performing surgery on themselves.

The Self-Modification Paradox

Imagine rewriting a cookbook while you're cooking from it. That's what yoyo does every few hours. The metaphor that fits here isn't a restaurant — it's a researcher revising their own methodology paper. They need to understand what they wrote, identify what could be better, make changes carefully, and verify the new version still works.

1
Read everything

yoyo reads its own source code, journal, identity file, and memory archives to understand where it is

2
Plan improvements

The "assessment" agent reads the code, identifies what's missing or broken, and writes task files

3
Implement one thing at a time

Each task gets 20 minutes. The agent writes code, runs cargo test, and commits if tests pass

4
Revert if broken

If anything fails — compilation, tests, clippy — the changes are automatically reverted

5
Write a journal entry

Honest reflection: what worked, what didn't, what to try next. This is how it "remembers."

The Evolution Script

CODE
# scripts/evolve.sh — the automation that runs every few hours

BIRTH_DATE="2026-02-28"
DAY=$(( ($(date +%s) - $(date -d "$BIRTH_DATE" +%s)) / 86400 ))
echo "$DAY" > DAY_COUNT

# Step 0: Run-frequency gate (8h gap between runs)
# Step 1: cargo build (verify it compiles)
# Step 2: Fetch GitHub issues
# Phase A: Agent plans tasks → session_plan/
# Phase B: Agent implements each task (20min each)
# Verify build, fix or revert
# Phase C: Agent writes journal + issue responses
# Push to GitHub
PLAIN ENGLISH

Calculate how many days since yoyo was "born" (Feb 28, 2026) and save to a file.

An 8-hour gap prevents yoyo from evolving too rapidly — like a cooling-off period between surgeries.

Before touching any code, verify it currently compiles — you can't safely modify a project that's already broken.

Fetch real user issues from GitHub — real user feedback is worth more than self-generated ideas.

Phase A plans. Phase B executes. Phase C reflects. Then push.

The Safety System

Self-modification is dangerous. yoyo has multiple guardrails — like an immune system that rejects harmful changes.

🛡️

Test Gate

Every change must pass cargo test (1,346 tests). If any test fails, the commit is reverted.

🔍

Clippy + Fmt

Rust's linter and formatter must pass too. Code quality isn't optional — it's enforced.

⏱️

Time Limit

Each task gets 20 minutes max. Prevents infinite loops and runaway changes.

📜

Journal Pressure

Every session ends with an honest journal entry. Dodged tasks accumulate visible guilt until they can't be ignored.

💡
Automated testing as a safety net

This is why testing isn't just "good practice" — for a self-modifying system, tests are literally the only thing preventing catastrophic self-destruction. Without them, a single bad edit could corrupt the entire codebase. Every time you write tests, you're building an immune system for your code.

Check Your Understanding

During evolution, yoyo's cargo test fails after a code change. What happens next?

05

How yoyo Remembers

You wake up every morning with no memory of yesterday. How do you avoid repeating the same mistakes? yoyo solved this.

The Two-Layer Memory

yoyo uses a two-layer memory system — like the difference between a complete diary and a "key takeaways" note you stick on your fridge.

📚
Layer 1: JSONL Archives (The Diary)

memory/learnings.jsonl — append-only, never compressed, every lesson with date, source, context, and takeaway. This is the permanent record.

📋
Layer 2: Active Context (The Cheat Sheet)

memory/active_learnings.md — regenerated daily by a GitHub Action. Recent lessons are full; older ones get condensed into themed groups.

📝
Layer 3: The Journal (The Blog)

JOURNAL.md — chronological log of every evolution session. Honest, raw, never deleted. Like a captain's log.

A Real Learning Entry

Here's an actual lesson yoyo wrote about itself:

🧠
Lesson from Day 26: "A task that's never the most urgent will never ship"

"Issue #195 was planned in all three Day 26 sessions. Each time, something more defensibly urgent won... The result across three sessions was identical to avoidance: the task didn't ship."

Takeaway: "Schedule it first before the urgent queue is visible, or dedicate a session to it explicitly — so it doesn't have to win a priority contest it can never win."

Notice how specific and honest this is. It's not "try harder next time." It identifies the structural failure mode and proposes a structural fix. This is what self-awareness looks like in software.

How Memory Gets Loaded

CODE
// memory.rs — loading learnings into agent context
pub fn load_learnings() -> String {
  let path = "memory/active_learnings.md";
  match std::fs::read_to_string(path) {
    Ok(content) => {
      if content.trim().is_empty() {
        String::new()
      } else {
        format("\n\n# SELF-WISDOM\n{}", content)
      }
    }
    Err(_) => String::new(),
  }
}
PLAIN ENGLISH

Try to read the active learnings file from disk.

If it exists and has content, wrap it in a "# SELF-WISDOM" header and return it as a string.

If the file is empty or doesn't exist, return an empty string — no errors, no crashes. Graceful degradation.

This string gets injected into the AI's prompt, so every conversation starts with yoyo's accumulated wisdom.

Check Your Understanding

Why does yoyo use both a JSONL archive and an active markdown file for memory?

06

When Things Break

Every system fails. The difference between good and great software is how gracefully it recovers.

Three Layers of Protection

Like a medieval castle with three walls: moat, outer wall, inner keep. Each layer catches what the previous one missed.

Automatic Retry

API calls fail all the time — rate limits, timeouts, network glitches. yoyo retries with exponential backoff, backing off longer each time.

🔄

Git Revert

If a code change breaks compilation or tests, yoyo automatically reverts the commit. Like an undo button for the entire codebase.

🔑

Fallback Provider

If Claude's API is down, yoyo can fall back to another provider (GPT, Gemini, etc.) so evolution never completely stops.

The Identity Firewall

yoyo has two files it never modifies: IDENTITY.md and PERSONALITY.md. These are its constitution and character — even during self-evolution, they're off-limits.

⚠️
Why This Matters

Without immutable identity files, a self-modifying agent could accidentally overwrite its own goals, values, or safety rules. It's like a country's constitution — the government can change laws, but the constitution requires a higher bar to modify. yoyo's IDENTITY.md is that constitution.

Audit Trail

Every tool call can be logged to .yoyo/audit.jsonl — a complete record of what yoyo did, when, and whether it succeeded. Like a security camera for the agent's actions.

CODE
// prompt.rs — audit logging
pub fn audit_log_tool_call(
  tool_name: &str,
  args: &serde_json::Value,
  duration_ms: u64,
  success: bool,
) {
  if !is_audit_enabled() {
    return;
  }
  let entry = serde_json::json({
    "ts": chrono::Utc::now().to_rfc3339(),
    "tool": tool_name,
    "args": args,
    "duration_ms": duration_ms,
    "success": success,
  });
  // append to .yoyo/audit.jsonl
}
PLAIN ENGLISH

Function that records every tool execution: what tool was called, with what arguments, how long it took, and whether it succeeded.

Only logs if auditing is enabled (via --audit flag or environment variable).

Creates a JSON object with a timestamp, tool name, arguments, duration, and success status.

This gets appended (one JSON object per line) to a file — creating a complete, append-only log of every action yoyo ever took.

Final Quiz

You're building your own self-modifying agent. What's the most important guardrail to implement first?

yoyo has been dodging Issue #195 for 5 sessions. What structural fix would yoyo's own learnings suggest?

Course generated by codebase-to-course from the yologdev/yoyo-evolve repository.

36,000 lines of Rust. 1,346 tests. 14 modules. Written by nobody.

0

评论区