Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README-ja.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Learn Claude Code -- 0 から 1 へ構築する nano Claude Code-like agent

[English](./README.md) | [中文](./README-zh.md) | [日本語](./README-ja.md)
[English](./README.md) | [中文](./README-zh.md) | [日本語](./README-ja.md) | [Русский](./README-ru.md)

```
THE AGENT PATTERN
Expand Down Expand Up @@ -84,7 +84,7 @@ def agent_loop(messages):
このリポジトリは、nano Claude Code-like agent を 0->1 で構築・学習するための教材プロジェクトです。
学習を優先するため、以下の本番メカニズムは意図的に簡略化または省略しています。

- 完全なイベント / Hook バス (例: PreToolUse, SessionStart/End, ConfigChange)。
- 完全なイベント / Hook バス (例: PreToolUse, SessionStart/End, ConfigChange)。
s12 では教材用に最小の追記型ライフサイクルイベントのみ実装している。
- ルールベースの権限ガバナンスと信頼フロー
- セッションライフサイクル制御 (resume/fork) と高度な worktree ライフサイクル制御
Expand Down Expand Up @@ -153,7 +153,7 @@ s08 バックグラウンドタスク [6] s10 チームプロトコル
learn-claude-code/
|
|-- agents/ # Python リファレンス実装 (s01-s12 + s_full 総括)
|-- docs/{en,zh,ja}/ # メンタルモデル優先のドキュメント (3言語)
|-- docs/{en,zh,ru,ja}/ # メンタルモデル優先のドキュメント (3言語)
|-- web/ # インタラクティブ学習プラットフォーム (Next.js)
|-- skills/ # s05 の Skill ファイル
+-- .github/workflows/ci.yml # CI: 型チェック + ビルド
Expand Down
238 changes: 238 additions & 0 deletions README-ru.md

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions README-zh.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Learn Claude Code -- 从 0 到 1 构建 nano Claude Code-like agent

[English](./README.md) | [中文](./README-zh.md) | [日本語](./README-ja.md)
[English](./README.md) | [中文](./README-zh.md) | [日本語](./README-ja.md) | [Русский](./README-ru.md)

```
THE AGENT PATTERN
Expand Down Expand Up @@ -84,7 +84,7 @@ def agent_loop(messages):
本仓库是一个 0->1 的学习型项目,用于从零构建 nano Claude Code-like agent。
为保证学习路径清晰,仓库有意简化或省略了部分生产机制:

- 完整事件 / Hook 总线 (例如 PreToolUse、SessionStart/End、ConfigChange)。
- 完整事件 / Hook 总线 (例如 PreToolUse、SessionStart/End、ConfigChange)。
s12 仅提供教学用途的最小 append-only 生命周期事件流。
- 基于规则的权限治理与信任流程
- 会话生命周期控制 (resume/fork) 与更完整的 worktree 生命周期控制
Expand Down Expand Up @@ -153,7 +153,7 @@ s08 后台任务 [6] s10 团队协议 [12]
learn-claude-code/
|
|-- agents/ # Python 参考实现 (s01-s12 + s_full 总纲)
|-- docs/{en,zh,ja}/ # 心智模型优先的文档 (3 种语言)
|-- docs/{en,zh,ru,ja}/ # 心智模型优先的文档 (3 种语言)
|-- web/ # 交互式学习平台 (Next.js)
|-- skills/ # s05 的 Skill 文件
+-- .github/workflows/ci.yml # CI: 类型检查 + 构建
Expand Down
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
[English](./README.md) | [中文](./README-zh.md) | [日本語](./README-ja.md)
[English](./README.md) | [中文](./README-zh.md) | [日本語](./README-ja.md) | [Русский](./README-ru.md)

# Learn Claude Code -- A nano Claude Code-like agent, built from 0 to 1

```
Expand Down Expand Up @@ -83,7 +84,7 @@ Every session layers one mechanism on top of this loop -- without changing the l
This repository is a 0->1 learning project for building a nano Claude Code-like agent.
It intentionally simplifies or omits several production mechanisms:

- Full event/hook buses (for example PreToolUse, SessionStart/End, ConfigChange).
- Full event/hook buses (for example PreToolUse, SessionStart/End, ConfigChange).
s12 includes only a minimal append-only lifecycle event stream for teaching.
- Rule-based permission governance and trust workflows
- Session lifecycle controls (resume/fork) and advanced worktree lifecycle controls
Expand Down Expand Up @@ -152,7 +153,7 @@ s08 Background Tasks [6] s10 Team Protocols [12]
learn-claude-code/
|
|-- agents/ # Python reference implementations (s01-s12 + s_full capstone)
|-- docs/{en,zh,ja}/ # Mental-model-first documentation (3 languages)
|-- docs/{en,zh,ru,ja}/ # Mental-model-first documentation (3 languages)
|-- web/ # Interactive learning platform (Next.js)
|-- skills/ # Skill files for s05
+-- .github/workflows/ci.yml # CI: typecheck + build
Expand Down Expand Up @@ -225,8 +226,8 @@ learn-claude-code claw0
## About
<img width="260" src="https://github.com/user-attachments/assets/fe8b852b-97da-4061-a467-9694906b5edf" /><br>

Scan with Wechat to fellow us,
or fellow on X: [shareAI-Lab](https://x.com/baicai003)
Scan with Wechat to fellow us,
or fellow on X: [shareAI-Lab](https://x.com/baicai003)

## License

Expand Down
114 changes: 114 additions & 0 deletions docs/ru/s01-the-agent-loop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# s01: Цикл агента

`[ s01 ] s02 > s03 > s04 > s05 > s06 | s07 > s08 > s09 > s10 > s11 > s12`

> *«Одного цикла и Bash достаточно»* — один инструмент + один цикл = агент.

## Проблема

Языковая модель умеет рассуждать о коде, но не может *взаимодействовать* с реальным миром — читать файлы, запускать тесты, проверять ошибки. Без цикла каждый вызов инструмента требует, чтобы вы вручную копировали результаты обратно. Вы сами становитесь циклом.

## Решение

```
+----------+ +-------+ +----------+
| Пользов.| ---> | LLM | ---> | Инструм. |
| запрос | | | | выполнить|
+----------+ +---+---+ +----+-----+
^ |
| tool_result |
+----------------+
(цикл до stop_reason != "tool_use")
```

Одно условие выхода управляет всем потоком. Цикл работает, пока модель продолжает вызывать инструменты.

## Как это работает

1. Запрос пользователя становится первым сообщением.

```python
messages.append({"role": "user", "content": query})
```

2. Отправляем сообщения и определения инструментов в LLM.

```python
response = client.messages.create(
model=MODEL, system=SYSTEM, messages=messages,
tools=TOOLS, max_tokens=8000,
)
```

3. Добавляем ответ ассистента. Проверяем `stop_reason` — если модель не вызвала инструмент, цикл завершён.

```python
messages.append({"role": "assistant", "content": response.content})
if response.stop_reason != "tool_use":
return
```

4. Выполняем каждый вызов инструмента, собираем результаты, добавляем как сообщение пользователя. Возвращаемся к шагу 2.

```python
results = []
for block in response.content:
if block.type == "tool_use":
output = run_bash(block.input["command"])
results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": output,
})
messages.append({"role": "user", "content": results})
```

Собрано в одну функцию:

```python
def agent_loop(query):
messages = [{"role": "user", "content": query}]
while True:
response = client.messages.create(
model=MODEL, system=SYSTEM, messages=messages,
tools=TOOLS, max_tokens=8000,
)
messages.append({"role": "assistant", "content": response.content})

if response.stop_reason != "tool_use":
return

results = []
for block in response.content:
if block.type == "tool_use":
output = run_bash(block.input["command"])
results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": output,
})
messages.append({"role": "user", "content": results})
```

Это весь агент менее чем в 30 строках. Всё остальное в этом курсе надстраивается сверху — не меняя сам цикл.

## Что изменилось

| Компонент | До | После |
|---------------|------------|--------------------------------|
| Цикл агента | (нет) | `while True` + stop_reason |
| Инструменты | (нет) | `bash` (один инструмент) |
| Сообщения | (нет) | Накапливаемый список |
| Управление | (нет) | `stop_reason != "tool_use"` |

## Попробуйте

```sh
cd learn-claude-code
python agents/s01_agent_loop.py
```

1. `Create a file called hello.py that prints "Hello, World!"`
2. `List all Python files in this directory`
3. `What is the current git branch?`
4. `Create a directory called test_output and write 3 files in it`
97 changes: 97 additions & 0 deletions docs/ru/s02-tool-use.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# s02: Использование инструментов

`s01 > [ s02 ] s03 > s04 > s05 > s06 | s07 > s08 > s09 > s10 > s11 > s12`

> *«Добавить инструмент — значит добавить один обработчик»* — цикл не меняется; новые инструменты регистрируются в dispatch map.

## Проблема

Имея только `bash`, агент использует командную оболочку для всего. `cat` обрезает результат непредсказуемо, `sed` ломается на спецсимволах, и каждый вызов bash — это неограниченная поверхность угроз безопасности. Специализированные инструменты вроде `read_file` и `write_file` позволяют применять ограничения путей на уровне инструмента.

Ключевое понимание: добавление инструментов не требует изменения цикла.

## Решение

```
+----------+ +-------+ +--------------------+
| Пользов.| ---> | LLM | ---> | Dispatch инструм. |
| запрос | | | | { |
+----------+ +---+---+ | bash: run_bash |
^ | read: run_read |
| | write: run_wr |
+-----------+ edit: run_edit |
tool_result | } |
+--------------------+

Dispatch map — словарь: {имя_инструмента: функция_обработчик}.
Один lookup заменяет любую цепочку if/elif.
```

## Как это работает

1. Каждый инструмент получает функцию-обработчик. Ограничение путей предотвращает выход за пределы рабочей директории.

```python
def safe_path(p: str) -> Path:
path = (WORKDIR / p).resolve()
if not path.is_relative_to(WORKDIR):
raise ValueError(f"Path escapes workspace: {p}")
return path

def run_read(path: str, limit: int = None) -> str:
text = safe_path(path).read_text()
lines = text.splitlines()
if limit and limit < len(lines):
lines = lines[:limit]
return "\n".join(lines)[:50000]
```

2. Dispatch map связывает имена инструментов с обработчиками.

```python
TOOL_HANDLERS = {
"bash": lambda **kw: run_bash(kw["command"]),
"read_file": lambda **kw: run_read(kw["path"], kw.get("limit")),
"write_file": lambda **kw: run_write(kw["path"], kw["content"]),
"edit_file": lambda **kw: run_edit(kw["path"], kw["old_text"],
kw["new_text"]),
}
```

3. В цикле находим обработчик по имени. Тело цикла само по себе не изменилось с s01.

```python
for block in response.content:
if block.type == "tool_use":
handler = TOOL_HANDLERS.get(block.name)
output = handler(**block.input) if handler \
else f"Unknown tool: {block.name}"
results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": output,
})
```

Добавить инструмент = добавить обработчик + добавить запись в схему. Цикл никогда не меняется.

## Что изменилось по сравнению с s01

| Компонент | До (s01) | После (s02) |
|----------------|-----------------------|----------------------------|
| Инструменты | 1 (только bash) | 4 (bash, read, write, edit)|
| Dispatch | Жёсткий вызов bash | Словарь `TOOL_HANDLERS` |
| Безопасность | Нет | Sandbox `safe_path()` |
| Цикл агента | Без изменений | Без изменений |

## Попробуйте

```sh
cd learn-claude-code
python agents/s02_tool_use.py
```

1. `Read the file requirements.txt`
2. `Create a file called greet.py with a greet(name) function`
3. `Edit greet.py to add a docstring to the function`
4. `Read greet.py to verify the edit worked`
94 changes: 94 additions & 0 deletions docs/ru/s03-todo-write.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# s03: TodoWrite

`s01 > s02 > [ s03 ] s04 > s05 > s06 | s07 > s08 > s09 > s10 > s11 > s12`

> *«Агент без плана дрейфует»* — сначала составь список шагов, потом выполняй.

## Проблема

На многошаговых задачах модель теряет нить. Она повторяет работу, пропускает шаги или отклоняется от курса. Длинные разговоры усугубляют проблему — системный промпт тускнеет, пока результаты инструментов заполняют контекст. При 10-шаговом рефакторинге модель может выполнить шаги 1-3, а потом начать импровизировать, потому что забыла шаги 4-10.

## Решение

```
+----------+ +-------+ +----------+
| Пользов.| ---> | LLM | ---> | Инструм. |
| запрос | | | | + todo |
+----------+ +---+---+ +----+-----+
^ |
| tool_result |
+----------------+
|
+-----------+-----------+
| Состояние TodoManager |
| [ ] задача А |
| [>] задача Б <- сейчас|
| [x] задача В |
+-----------------------+
|
если rounds_since_todo >= 3:
добавить <reminder> в tool_result
```

## Как это работает

1. TodoManager хранит элементы со статусами. Только один элемент может быть `in_progress` одновременно.

```python
class TodoManager:
def update(self, items: list) -> str:
validated, in_progress_count = [], 0
for item in items:
status = item.get("status", "pending")
if status == "in_progress":
in_progress_count += 1
validated.append({"id": item["id"], "text": item["text"],
"status": status})
if in_progress_count > 1:
raise ValueError("Only one task can be in_progress")
self.items = validated
return self.render()
```

2. Инструмент `todo` добавляется в dispatch map как любой другой.

```python
TOOL_HANDLERS = {
# ...базовые инструменты...
"todo": lambda **kw: TODO.update(kw["items"]),
}
```

3. Напоминание-«напоминалка» вставляет подсказку, если модель 3+ раундов не вызывает `todo`.

```python
if rounds_since_todo >= 3 and messages:
last = messages[-1]
if last["role"] == "user" and isinstance(last.get("content"), list):
last["content"].insert(0, {
"type": "text",
"text": "<reminder>Update your todos.</reminder>",
})
```

Ограничение «только один in_progress одновременно» обеспечивает последовательный фокус. Напоминание создаёт подотчётность.

## Что изменилось по сравнению с s02

| Компонент | До (s02) | После (s03) |
|----------------|------------------|----------------------------|
| Инструменты | 4 | 5 (+todo) |
| Планирование | Нет | TodoManager со статусами |
| Напоминание | Нет | `<reminder>` после 3 раундов|
| Цикл агента | Простой dispatch | + счётчик rounds_since_todo|

## Попробуйте

```sh
cd learn-claude-code
python agents/s03_todo_write.py
```

1. `Refactor the file hello.py: add type hints, docstrings, and a main guard`
2. `Create a Python package with __init__.py, utils.py, and tests/test_utils.py`
3. `Review all Python files and fix any style issues`
Loading