Skip to content
Closed
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
377 changes: 377 additions & 0 deletions README-ru.md

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[English](./README.md) | [中文](./README-zh.md) | [日本語](./README-ja.md)
[English](./README.md) | [中文](./README-zh.md) | [日本語](./README-ja.md) | [Русский](./README-ru.md)
# Learn Claude Code -- Harness Engineering for Real Agents

## The Model IS the Agent
Expand Down Expand Up @@ -299,7 +299,7 @@ learn-claude-code/
## Documentation

Mental-model-first: problem, solution, ASCII diagram, minimal code.
Available in [English](./docs/en/) | [中文](./docs/zh/) | [日本語](./docs/ja/).
Available in [English](./docs/en/) | [中文](./docs/zh/) | [日本語](./docs/ja/) | [Русский](./docs/ru/).

| Session | Topic | Motto |
|---------|-------|-------|
Expand Down
116 changes: 116 additions & 0 deletions docs/ru/s01-the-agent-loop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# s01: The Agent Loop

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

> *"Один loop и Bash — это всё, что нужно"* -- один tool + один loop = agent.
>
> **Harness layer**: The loop -- первое соединение model с реальным миром.

## Проблема

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

## Решение

```
+--------+ +-------+ +---------+
| User | ---> | LLM | ---> | Tool |
| prompt | | | | execute |
+--------+ +---+---+ +----+----+
^ |
| tool_result |
+----------------+
(loop until stop_reason != "tool_use")
```

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

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

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

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

2. Отправляем messages и определения tools в LLM.

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

3. Добавляем ответ ассистента. Проверяем `stop_reason` — если model не вызвала tool, работа завершена.

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

4. Выполняем каждый вызов tool, собираем результаты, добавляем как сообщение пользователя. Возвращаемся к шагу 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})
```

Это весь agent — менее 30 строк. Всё остальное в этом курсе надстраивается сверху, не изменяя loop.

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

| Компонент | До | После |
|---------------|------------|--------------------------------|
| Agent loop | (нет) | `while True` + stop_reason |
| Tools | (нет) | `bash` (один tool) |
| Messages | (нет) | Накапливаемый список |
| Control flow | (нет) | `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`
99 changes: 99 additions & 0 deletions docs/ru/s02-tool-use.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# s02: Tool Use

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

> *"Добавить tool — значит добавить один handler"* -- loop остаётся прежним; новые tools регистрируются в dispatch map.
>
> **Harness layer**: Tool dispatch -- расширяем возможности model.

## Проблема

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

Ключевая идея: добавление tools не требует изменения loop.

## Решение

```
+--------+ +-------+ +------------------+
| User | ---> | LLM | ---> | Tool Dispatch |
| prompt | | | | { |
+--------+ +---+---+ | bash: run_bash |
^ | read: run_read |
| | write: run_wr |
+-----------+ edit: run_edit |
tool_result | } |
+------------------+

The dispatch map — это словарь: {tool_name: handler_function}.
Один поиск заменяет любую цепочку if/elif.
```

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

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

```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 связывает имена tools с handlers.

```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. В loop ищем handler по имени. Тело loop осталось неизменным по сравнению с 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,
})
```

Добавить tool = добавить handler + добавить запись в схему. Loop никогда не меняется.

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

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

## Попробуй сам

```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`
96 changes: 96 additions & 0 deletions docs/ru/s03-todo-write.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# s03: TodoWrite

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

> *"Agent без плана блуждает"* -- сначала перечисли шаги, затем выполняй.
>
> **Harness layer**: Планирование -- удерживаем model на курсе, не прописывая маршрут.
## Проблема

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

## Решение

```
+--------+ +-------+ +---------+
| User | ---> | LLM | ---> | Tools |
| prompt | | | | + todo |
+--------+ +---+---+ +----+----+
^ |
| tool_result |
+----------------+
|
+-----------+-----------+
| TodoManager state |
| [ ] task A |
| [>] task B <- doing |
| [x] task C |
+-----------------------+
|
if rounds_since_todo >= 3:
inject <reminder> into 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. Tool `todo` добавляется в dispatch map как любой другой tool.

```python
TOOL_HANDLERS = {
# ...базовые tools...
"todo": lambda **kw: TODO.update(kw["items"]),
}
```

3. Напоминание встраивает подсказку, если model не вызывала `todo` 3 и более раундов подряд.

```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) |
|----------------|------------------|----------------------------|
| Tools | 4 | 5 (+todo) |
| Планирование | Нет | TodoManager со статусами |
| Напоминание | Нет | `<reminder>` после 3 раундов|
| Agent loop | Простой 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