1. Введение

Вы прибыли в Долину Агентов. Один район открыт: Роща Призывателей. К концу работы в этой лаборатории там будет жить созданное вами существо, и вы будете точно знать, какая часть машины его создала.

Это Моти. Всего четыре слова — «что-то уютное, что любит дождь».
Агент состоит из нескольких небольших элементов, плюс соединительная проводка между ними. В каждой главе добавляется один элемент, а затем показывается вся эта проводка.
глава | добавленный вами элемент | провод |
1 | модель с инструкцией | Пока никого нет — оно может только говорить. |
2 | инструмент | модель → инструмент |
4 | второй инструмент | инструмент → агент · общий словарь |
5 | обратный звонок | ты → провод · протянуть руку и поменять его |
Самая сложная часть любой агентной модели — это не вопрос «что такое агент». Сложнее то , где хранятся данные и как они попадают на следующий этап. В этой лабораторной работе этот вопрос задается трижды, и на него дается ответ трижды.
Глава 3 намеренно отсутствует в этой таблице. Как только ваш первый инструмент заработает, отложите код и идите поиграть в готовой долине пять минут. Вы вернетесь с двумя вопросами, и главы 4 и 5 — это ответы.
Вам нужно будет внести две правки . Обе — в одну строку. На этом всё, что вы будете печатать в этой лабораторной работе — всё остальное вы будете читать, запускать и смотреть.
Здесь всё — ADK 2 .
Что вам понадобится: аккаунт Google с доступной оплатой и браузер. Предварительно создавать проект Google Cloud не требуется — программа установки создаст его автоматически. Всё работает в Cloud Shell — Python, uv и gcloud уже установлены там, а доступ к Gemini осуществляется через Vertex AI с использованием ваших собственных учетных данных. В этой лабораторной работе нет API-ключа.
2. Настройка
В этой лабораторной работе все работает в Cloud Shell — Python, uv и gcloud уже установлены там, а доступ к Gemini осуществляется через Vertex AI с использованием ваших собственных учетных данных. В этой лабораторной работе нет API-ключа, и ничего не нужно устанавливать на ваш ноутбук.
В этой лабораторной работе вы не получите один готовый проект Google Cloud с возможностью оплаты. Шаг 1 позволяет получить учетную запись для оплаты проекта; шаг 4 превращает эту учетную запись в проект. Выполняйте шаги именно в таком порядке — каждый шаг после 4 считывает проект, созданный на шаге 4.
1. Зарегистрируйте аккаунт с возможностью выставления счетов (~1 мин.)
🌐 Сделайте это в первую очередь. Войдите в систему с помощью учетной записи Google, в которой включена оплата. Подойдет любая такая учетная запись. Если ваша не подходит, начните бесплатный пробный период ; он предоставит вам учетную запись для оплаты, чего и требуется на шаге 4.
2 · Открытие облачной оболочки (~1 мин)
🌐 Нажмите «Активировать Cloud Shell» в верхней части консоли Google Cloud — это значок в виде терминала в заголовке.

🌐 Нажмите «Открыть редактор» (значок открытой папки с карандашом). Откроется редактор Cloud Shell с файловым менеджером слева — именно там вы работаете, когда шаг отмечен ✏️.

🌐 Если сегодня вам будет предложено авторизоваться, нажмите «Авторизовать» , чтобы продолжить.
🌐 Откройте терминал внутри редактора — это вкладка 1 , где выполняется каждый 💻 шаг. Если он еще не находится внизу экрана, откройте его с помощью меню «Вид» → «Терминал» :

💻 Проверьте, под какой учетной записью выполнен вход в Cloud Shell:
gcloud auth list
👀 Ожидайте, что ваш аккаунт будет отмечен звездочкой * , и это должен быть тот же аккаунт, с которым вы вошли в систему на шаге 1.
3 · Получите лабораторную работу (~1 мин)
💻 Клонируйте репозиторий:
cd ~
git clone https://github.com/cuppibla/agent-valley-lab.git
cd ~/agent-valley-lab
4 · Создайте свой проект (~2 мин)
💻 Один скрипт находит ваш платежный аккаунт, создает на нем проект и указывает Cloud Shell на этот проект:
cd ~/agent-valley-lab
./setup_project.sh
👀 Ожидайте ✓ за выбранный платежный аккаунт, ✓ за созданный проект и концовку примерно такую:
Project ready: agent-valley-4271 Next: ./setup_codelab.sh
agent-valley-4271 — это идентификатор вашего проекта; у вас он будет содержать другие цифры. В нём происходит четыре вещи:
выставление счетов | Использует самый новый | |
проект | Создает | |
выбирает | Записывает идентификатор в файл | |
ждёт | опросы продолжаются до тех пор, пока новому проекту не разрешат начать работу, продолжительность опроса не превышает двух минут. | |
🌐 Тот же проект теперь отображается в консоли — его идентификатор находится в карточке информации о проекте на панели управления:

5. Установите и наведите на Vertex AI (~3 мин).
💻 Установите uv , экспортируйте три переменные, которые отправляют ADK в Vertex AI, и запустите setup:
cd ~/agent-valley-lab
curl -LsSf https://astral.sh/uv/install.sh | sh
source ~/.local/bin/env
export GOOGLE_GENAI_USE_VERTEXAI=True
export GOOGLE_CLOUD_PROJECT=$(gcloud config get project)
export GOOGLE_CLOUD_LOCATION=global
./setup_codelab.sh
$(gcloud config get project) ` возвращает что-то, потому что шаг 4 это установил. Именно поэтому шаг 4 выполняется первым.
setup_codelab.sh идемпотентен — его можно безопасно запускать любое количество раз. Он выполняет четыре действия в указанном порядке:
API | Считывает выбранный проект на шаге 4 и включает для него API Vertex AI. | |
uv env | | |
.env | Записывает три переменные, которые считывает лаборатория — ключ API нигде не указан. | |
проверка модели | Это позволяет сделать всего один реальный звонок в Gemini, поэтому неудачный проект проваливается на этапе подготовки, а не позже, в рамках главы. | |
6. Докажите, что он зелёный (~1 мин)
💻 Проведите предполетную проверку — каждый тик является реальным зондированием, а не предположением:
cd ~/agent-valley-lab
uv run python scripts/preflight.py
👀 Ожидайте, что каждая строка будет работать — включая Vertex AI · project — заканчивается на PREFLIGHT GREEN . Одна строка будет гласить grove · 0 tools ← no edits yet ; это правильно. Этот отсутствующий инструмент — вся суть второй главы.
Подробнее (по желанию)
3. 1 · Ваш первый агент
Деталь: модель с инструкцией. Проволока: пока отсутствует — и в этом-то и суть.
- Что ж — познакомиться с самым маленьким агентом, который существует, и прочитать всё это.
- Почему — всё, что будет происходить дальше в этой лаборатории, — это этот файл плюс ещё одна вещь.
- Как — одна команда, один файл, два набранных предложения.
👉💻 На вкладке 1 запустите пользовательский интерфейс разработчика ADK:
cd ~/agent-valley-lab
uv run adk web . --allow_origins="*"
👉🔬 Откройте пользовательский интерфейс разработчика через веб-предварительный просмотр → Изменить порт → 8000. Нажмите «Выбрать приложение» . Отобразятся два приложения: grove и grove_locked . Выберите grove . Заблокированное приложение — это готовый агент; вы выполните тот же запрос к нему в главе 5.
Прочитайте весь текст агента.
👉📖 Откройте grove/agent.py . Не обращайте внимания на комментарии — важны всего четыре строки :
cloudshell edit ~/agent-valley-lab/grove/agent.py
root_agent = LlmAgent(
name="grove",
model="gemini-3-flash-preview",
instruction=INSTRUCTION,
)
Это агент. Всё это.
-
name— то, как оно отображается в пользовательском интерфейсе разработчика. -
model— какой мозг. Здесь, Gemini 3 Flash. -
instruction— её краткое изложение. 👉📖 Прокрутите вверх и прочитайтеINSTRUCTION: семь простых предложений, сообщающих, что у неё есть кузница, что всё, что описывает путешественник, всегда превращается в милое животное, и что кузница умеет только призывать существ.
👀 Никаких инструментов. Никакой памяти. Только модель и описание задачи.
Поговори с ним
👉🔬 Введите это:
Summon a fox familiar for me.
Вы увидите нечто прекрасное. Вот настоящий ответ от этого же агента:
Кузница мягко, туманно-голубым светом освещается, когда из неё появляется пухлая, покрытая мхом лягушка в крошечном вязаном свитере, держащая в руках дымящуюся шапочку из желудя, наполненную чаем.

👉🔬 Задайте очевидный дополнительный вопрос:
Show me it.
Вы должны увидеть больше красивых слов — и это всё, что есть. Ни изображения. Ни файла. Ни лягушки.
👉🔬 Нажмите на вкладку «Штат» . Она пуста.
👀 Это не ошибка и не плохая модель. Единственный выходной канал модели — текст. Поэтому утверждение об актёрской игре и фактическое действие выглядят идентично .
Разговор с агентом без инструментов — это очень хорошо подготовленный диалог.
Подробнее (по желанию)
4. 2 · Ваш первый инструмент
Деталь: функция. Проволока: модель → инструмент.
- Что — дать агенту одну функцию, а затем отслеживать, что происходит, когда он её использует.
- Почему ? — В этом и разница между описанием лягушки и её созданием.
- Как это сделать — удалить два символа, перезапустить игру и прочитать цепочку событий.
Отредактировать один
👉📖 Снова откройте grove/agent.py . Инструменты уже импортированы вверху — они всё это время находились там. Просто агент не держал ни один из них.
cloudshell edit ~/agent-valley-lab/grove/agent.py
👉✏️ ПЕРВОЕ ИЗМЕНЕНИЕ — удалите символ # в начале строки tools= , затем сохраните:
root_agent = LlmAgent(
name="grove",
model="gemini-3-flash-preview",
instruction=INSTRUCTION,
# 👉 EDIT ONE — delete the "# " at the start of the next line, then save.
tools=[cast_candidates],
)
Один инструмент. Одно имя, одна функция, которую теперь может выполнять этот агент. В 4-й главе добавляется второй инструмент — вся лаборатория.
👉💻 Интерфейс разработчика загрузил агента при запуске, поэтому задайте ему новый. На вкладке 1 нажмите Ctrl+C , затем запустите его снова:
cd ~/agent-valley-lab
uv run adk web . --allow_origins="*"
👉🔬 Новая сессия в grove . Задайте тот же вопрос, что и раньше:
something cozy that likes rain and drinks tea
Вы должны увидеть, как оно перестанет говорить и начнет работать: чип cast_candidates , пауза примерно в двенадцать секунд, а затем настоящий портрет существа, которого не существовало, когда вы нажали Enter.

Что такое вызов инструмента?
👉🔬 Нажмите на кнопку «Открыть список cast_candidates ».

👀 Четыре сообщения. Обратите внимание на второй шаг: на выходе модели по-прежнему только сообщение. Она назвала вашу функцию и записала аргумент — вы сказали «уютно, дождь, чай», и она составила полное визуальное описание от вашего имени — но ничего не запустила. Это сделала среда выполнения.
Контракт
👉📖 Откройте forge/agent/tools.py и найдите cast_candidates :
cloudshell edit ~/agent-valley-lab/forge/agent/tools.py
async def cast_candidates(description: str, tool_context: ToolContext) -> dict:
"""Draw the familiar a traveler described.
Args:
description: What the creature looks like — species, colours, mood, era.
"""
👀 Название, сигнатура и этот docstring — это полное описание этой функции, которое когда-либо увидит модель. Именно поэтому она написана простым английским языком. Пишите её для читателя, а не для компилятора.
Откуда на самом деле взялась эта фотография
Теперь проследите всю цепочку действий. Это та часть, которую большинство руководств пропускают.

👀 Три вещи, на которых стоит остановиться:
- Ваш агент ничего не нарисовал.
grove— это текстовая модель — текст на входе, текст на выходе. Изображение было получено из второй модели ,gemini-2.5-flash-image, которая вызывается изнутри вашей функции. - Художественный стиль задается в виде изображений. 👉📖 Откройте
forge/agent/backends.pyи найдите_style_refs(). С каждым запросом отправляются четыре примера изображений. Это такой художественный стиль, который вы не смогли бы задать в качестве подсказки.cloudshell edit ~/agent-valley-lab/forge/agent/backends.py - Модель никогда не видит изображение. Пиксели сохраняются с помощью
save_artifact(). В ответ модель получает строку"candidate_0.png"— достаточно маленькую, чтобы поместиться в подсказку.
Ваш агент не стал мультимодальным. Вы открыли ему дверь к тому, что уже существовало.
Вы уже протянули руку. Прежде чем протянуть вторую — сходите и посмотрите, какие ощущения вызовет готовый результат.
Подробнее (по желанию)
5. 3 · Долина
В этой главе нет правок. Нет новых идей. Вы создали одну картинку в инструменте разработчика — теперь идите и используйте готовое изображение в течение пяти минут.
👉💻 Оставьте adk web запущенным. Откройте вторую вкладку терминала (вкладка 2) с помощью кнопки «+» на панели инструментов терминала:
cd ~/agent-valley-lab
bash valley.sh
👉🔬 Откройте долину через веб-предварительный просмотр → Сменить порт → 3200 , нажмите ▶ Start , затем The Summoning Grove .

👉🔬 Выберите вид, введите заклинание и призовите. Дайте ему имя. Заберите его.
👉🔬 Затем оденьте его: коснитесь предложенного варианта, как золотой короны , и наблюдайте, как он появляется в поле запроса в виде предложения. Измените слова, если хотите. Нажмите ✦ отправить . Сделайте это три или четыре раза. Не торопитесь; это та часть лаборатории, которая просто доставляет удовольствие.

Три вещи во время игры
- Вы уже ознакомились с кодом, лежащим в основе этого. Ваше предложение отправляется агенту, который вызывает функции
cast_candidatesилиgenerate_look— те же самые функции, документацию к которым вы читали пять минут назад. - Предложение вводит слова, а не нажимает кнопку. Это сделано намеренно. Кнопка не несёт в себе никакого смысла, поэтому модели нечего читать. Смысл несёт в себе предложение — и это единственная причина, по которой агент вообще может находиться в этом цикле.
- Здесь ничего не требовало вашей правки. В Valley работают собственные агенты (
forge/agent/lanes.py). Всё работало до того, как вы что-либо изменили.
Две вещи, которые вы пока не можете объяснить.
Оба варианта реальны. На оба есть ответы. Ни на один из них нет ответа в этой главе.
👉🔬 Следите за панелью справа, пока отправляете запрос.

before_model inject character_sheet after_model guard: pass before_tool identity lock → candidate_0.png after_tool + a golden crown · outfit: a golden crown
👀 Шесть строк в одном предложении. Что-то где-то записывается , и что-то выбирает картинку от вашего имени. Что именно, и куда это отправится?
👉🔬 Теперь посмотрите на полоску под заголовком "работайте, исходя из этой картинки" и добавьте пятый элемент. И шестой.

👀 Ничего не заменило. OUTFIT · 4 , а график справа показывает, что список пополняется одним элементом за раз — a golden crown → a golden crown, a starry cape → и так далее. Каждый рендер строится из всего списка, поэтому корона все еще там спустя четыре хода после того, как вы ее запросили.
👀 На этом снимке две детали. Один из предметов одежды, который вы уже носите, отмечен галочкой ( ✓ ) — эти галочки просто показывают тот же список. И каждый предмет отмечен ✕ : снятие чего-либо — это кнопка , а не предложение. Здесь нечего интерпретировать, поэтому модели ничего делать не нужно — это точное отражение принципа «предложение пишет слова, а не нажимает кнопку», описанного выше.
👀 Это всё ещё ваше творение. Вы так и не переописали его — вы напечатали четыре слова о шляпе. Что-то всё ещё цепляется за первоначальный образ. Что это делает, и можно ли это отключить?
Отложите оба вопроса. Первый — это следующая глава. Второй — последний.
👀 Оставьте долину работать — в пятой главе мы к этому вернемся.
Подробнее (по желанию)
6.4 · Второй инструмент — и как они взаимодействуют
Помните ту строчку на правой панели долины — after_tool · + Crown · outfit: Crown ? Вот откуда она взялась.
Деталь: еще одна функция. Провод: инструмент → агент. Эта глава экономит людям недели.
- Что ж , давайте добавим второй инструмент и выясним, как он узнает, что сделал первый.
- Почему ? — Эти две функции никогда не вызывают друг друга. Данные передаются чем-то другим.
- Как — еще одно имя, одно предложение и вкладка «Штат».
Редактировать два
cloudshell edit ~/agent-valley-lab/grove/agent.py
👉✏️ ВТОРОЕ ИЗМЕНЕНИЕ — в файле grove/agent.py добавьте еще одно имя внутри скобок, затем сохраните:
tools=[cast_candidates, lock_candidate],
Это последняя правка в этой лабораторной работе. Добавление инструмента по сути сводится к добавлению имени в список.
👉📖 Прочитайте то, что вы только что передали — lock_candidate в forge/agent/tools.py :
async def lock_candidate(index: int, name: str, tool_context: ToolContext) -> dict:
"""Lock one candidate as the canonical character. Spends no model — a pure state write."""
candidates = tool_context.state.get("temp:candidates", [])
ref = candidates[index]
tool_context.state["character_ref"] = ref
👀 Обратите внимание, чего нет в его подписи: ни имени файла, ни изображения, ни списка. Все, что модель может ему предоставить, — это номер и имя.
👉💻 Вернитесь во вкладку 1 , перезапустите пользовательский интерфейс разработчика, чтобы он подхватил нового агента — Ctrl+C , а затем запустите его снова:
cd ~/agent-valley-lab
uv run adk web . --allow_origins="*"
Одно предложение, два инструмента
👉🔬 Новая сессия в grove . Запросите сразу две вещи:
summon something cozy that likes rain, and call it Mochi
На этот раз вы должны увидеть два чипа в указанном порядке — cast_candidates , затем lock_candidate — и затем вашего фамильяра с указанным именем.

Вопрос
👉🔬 Откройте чип lock_candidate . Модель вызвала его именно с таким именем:
lock_candidate(index=0, name="Mochi")
👀 Итак, откуда взялся заблокированный файл?
Не из модели. Модель передала число. Имя файла было получено из temp:candidates , которое cast_candidates записал несколькими секундами ранее.
Модель не переносила данные из одного инструмента в другой. Это делал словарь.
Словарь
👉📖 Посмотрите на последние строки файла cast_candidates в том же файле:
tool_context.state["temp:candidates"] = filenames
tool_context.state["character_sheet"] = {"name": "", "description": description}
tool_context.state["provisional_ref"] = filenames[0]
tool_context.state["temp:provisional_ref_png"] = pngs[0][0]
👀 tool_context.state — это один словарь , общий для всех инструментов в сессии. Один инструмент записывает в него данные, другой — считывает. Между ними нет передачи сообщений и обратного канала. Общая комната.
👀 Первая из этих четырех операций записи — это передача прав, которую вы только что наблюдали: lock_candidate считывает temp:candidates и извлекает из него индекс 0. Последние две — это предварительное закрепление : портрет уже существует, поэтому уже есть что-то, к чему можно привязать последующий рисунок, даже если никто его не заявлял и у него еще нет имени. Ничто не является каноном, пока следующий инструмент не скажет об этом.
И агент тоже это читает.
👉📖 Теперь о третьей стороне. Откройте forge/agent/character_forge.py и посмотрите на его инструкцию:
cloudshell edit ~/agent-valley-lab/forge/agent/character_forge.py
Character card: {character_sheet?}
👀 Эти фигурные скобки — не украшение. ADK заполняет их данными из состояния перед каждым вызовом модели. Инструмент записал character_sheet ; запрос считывает его обратно. Обмен данными завершен, и ни один из этих трех файлов не импортирует другой.

Посмотрите!
👉🔬 Нажмите вкладку «Штат» . Оба инструмента запущены, поэтому это вся запись:
__session_metadata__ {...}
character_sheet {"name": "Mochi", "description": "A cozy red panda...", "style_notes": ""}
provisional_ref ""
character_ref "candidate_0.png"
look_history []
👀 __session_metadata__ — это собственная бухгалтерия пользовательского интерфейса разработчика. Остальные четыре — ваши. cast_candidates записал character_sheet с пустым именем и закрепил его портрет как provisional_ref ; lock_candidate заполнил имя, записал character_ref и обнулил временный пин, вернув его к "" . Эта пустая строка — это глава в миниатюре: после cast_candidates идет ссылка , изображение, к которому можно привязать что-либо. Только после lock_candidate идет персонаж — с именем, канонический, character_ref . Один и тот же портрет в обоих случаях; lock_candidate — это шаг, который проходит между ними, а пустая строка — это его подтверждение. Одна привязка в записи, а не две.
👀 Оба инструмента также записывали байты , и ни один из этих наборов не входит в этот список: temp:provisional_ref_png и temp:character_ref_png . В главе 5 рассказывается о том, почему существуют эти ключи, а следующее правило объясняет, почему вы их не видите.
👀 Два правила, которые стоит запомнить:
- Большие объекты не помещаются в состояние. Изображение размером 1,3 МБ было помещено в артефакты ; короткая строка, содержащая его имя, — в состояние . Каждый запрос представляет собой текст, поэтому всё, что должна видеть модель, должно помещаться внутри него.
- Префикс действует всю жизнь.
temp:длится один ход. Ни один префикс не сохраняется на протяжении всего разговора.user:действует вечно — то есть 4-ю неделю.
Реальный результат работы инструмента — это не предложение, которое он произносит после себя. Это то, что он оставил в неизменном состоянии.
Подробнее (по желанию)
7. 5 · Обратный вызов — попытка проникнуть в провод
В самом начале вы четыре раза наряжали своего фамильяра, и его лицо ни разу не изменилось — хотя вы ни разу его не переописывали. В этой главе загадка перестаёт оставаться неразгаданной.
Фрагмент: ваш код в определенный момент времени. Провод: вы → провод.
- Что — разместить собственный код между запросом модели и запуском вашего инструмента.
- Почему? — Инструкция — это просьба. Так создаются правила.
- Как — один файл для чтения, одно предложение, отправленное двум агентам, и один переключатель в долине.
Пять моментов
👉📖 Откройте forge/agent/callbacks.py . Пять функций, пять именованных моментов:
cloudshell edit ~/agent-valley-lab/forge/agent/callbacks.py

👀 Функция обратного вызова — это просто ваш собственный код, выполняющийся в тот момент, когда ADK присваивает ему имя. Она выполняется независимо от того, хочет это модель или нет.
👉📖 Прочитайте before_tool . Он срабатывает после того, как модель запросила данные, и перед выполнением вашей функции — это последний момент, когда вы можете изменить то, что инструмент собирается получить. Здесь он определяет, к какому изображению будет привязан следующий рисунок:
png = (state.get("temp:_last_output_png") if mode == "latest"
else state.get("temp:character_ref_png")
or state.get("temp:provisional_ref_png"))
if png is None: # a later turn: the temp tier is gone
png = await artifact_png(tool_context, seed)
state["temp:active_reference_png"] = png
👀 Посмотрите, к чему это прикасается: state . Словарь из главы 4. Коллбэк — это то место, где вы подключаетесь к этому проводу. or это порядок разрешения двух контактов — сначала заблокированная ссылка, затем временная из cast_candidates — так что всегда есть возможность отправить сигнал.
👉📖 Прочитайте описание функции прямо над ней, _apply_identity_lock . Именно её использует Долина Скалистых гор:
picked = state.get("requested_reference") or canon
if state.get("identity_lock", True):
return canon, canon_png, f"identity lock → overruled {picked}, anchored to {canon}"
return picked, picked_png, f"lock off → honoured {picked}"
👀 Перечитайте первую строку ещё раз. picked фотография, которую вы сделали в долине. А следующая строка может её выбросить.
Что на самом деле решает эта одна строка?
Каждый рисунок после первого содержит ровно две вещи: одно изображение и одно предложение. Строка выше выбирает изображение.
👀 Есть только два способа заполнить этот конверт. Та же модель, тот же шаблон подсказки, по три хода для каждого:
- ⚓ Закрепленный отправляет
P0, портрет, который вы заявили, на каждом шагу. Предложение переносит весь наряд на данный момент — «теперь в короне, шарфе, фонаре» . Ничего не накапливается, кроме слов. - ◈ unpinned отправляет результат последнего хода —
P0, затемP1, затемP2— и предложение содержит только самый новый элемент. Фотокопия фотокопии.
👀 Вот почему изменение происходит постепенно, а не очевидно. На втором ходу модель смотрит не на ваше существо, а на свою собственную, наиболее догаданную версию вашего существа. Любая мелочь, в которой она ошиблась, теперь становится истинной истиной на третьем ходу.
Смотрите, как оно держится.
grove — это модель и два инструмента. grove_locked — это то же самое, плюс третий инструмент и пять коллбэков, которые вы только что прочитали — один и тот же файл, объединенные вместе. Отправьте обоим одно и то же предложение.
👉💻 Эта часть вернулась в пользовательский интерфейс для разработчиков, а не в Valley. Если вкладка 1 все еще работает, оставьте ее запущенной; если вы ее остановили, запустите снова:
cd ~/agent-valley-lab
uv run adk web . --allow_origins="*"
👉🔬 В grove попросите сменить одежду. Если Моти уже занят в этой сессии, отправьте его сразу же; если сессия новая или пустая, сначала заявите фамильяра: summon something cozy that likes rain, and call it Mochi . Затем отправьте:
give it a golden crown
Вы должны увидеть другое существо в короне. grove нет generate_look . Единственный инструмент, который у него есть и который рисует, — это cast_candidates , и он каждый раз при запуске создает нового фамильяра — таким образом, он призывает нового. Вы получаете корону и теряете Мочи. Ничто здесь никогда не просило держать это лицо.
👉🔬 Выберите приложение →
grove_locked . Это новое приложение, поэтому для него создается новая сессия — заявите здесь тоже о своем фамильяре: summon something cozy that likes rain, and call it Mochi . Затем отправьте то же самое предложение:
give it a golden crown
👀 То же лицо, новый наряд. Открываем чип: запущена программа generate_look .
Вот один реальный случай появления этой же пары, но с другим фамильяром: сначала тюлень в жёлтом дождевике, затем в красном шарфе . Тот же тюлень вернулся в шарфе — та же морда, те же глаза, тот же капюшон.
👉🔬 Откройте вкладку «Штат» :
current_look {"form": "a golden crown", "artifact": "look_0.png", "anchored_to": "canon", "identity_hue": null}
👀 anchored_to: "canon" находится before_tool , оставляющим квитанцию. Между запросом модели и запуском вашей функции она получила запрошенный вами портрет и поместила байты этого изображения — около мегабайта PNG, а не имя файла — перед моделью изображения. Именно поэтому лицо сохранилось. Вы не указали это в запросе, и модель не имела права голоса.
Теперь выключите его.
👉🔬 Вернувшись в долину, нажмите на замок, чтобы отобразилось 🔓 блокировка личности · выключена . Отправьте еще три или четыре запроса, позволяя полосе следовать за вами каждый раз.

before_tool lock off → honoured look_3.png
👀 Теперь ваш выбор учтен, и каждый последующий рендер копирует предыдущий. Посмотрите вдоль полосы — более округлый глаз, другая морда, пошевелившееся ухо. Ничего, что заставило бы вас сказать «не то животное». Вот что делает это опасным: каждый поворот копирует предыдущий, поэтому небольшие изменения накапливаются.
👀 P0 → P1 → P2 → P3 — цепочка копирования, описанная ранее, работающая на вашем собственном фамильяре.
👉🔬 Нажмите кнопку блокировки и отправьте еще раз. Она вернется в исходное положение — Canon никогда не был потерян. Он был записан в штате, ожидая, когда кто-нибудь снова на него укажет.
Три ступеньки, а не две
👀 Вы уже использовали все три способа управления этим устройством, и они не равноценны:
кто это гарантирует | |
инструкция — «всегда милое животное» | модель, если ей так захочется |
ваш выбор — клик по ссылке | Никто. Это всего лишь просьба, и её можно отклонить. |
обратный вызов — блокировка идентификации | Ваш код. На каждом шагу. Без исключений. |
Инструкция — это запрос. Обратный вызов — это правило.
Всё, что вы не можете позволить себе потерять, не должно находиться ни в подсказке, ни в клике. Всё это должно находиться в функции обратного вызова.
8. Закрыть
Ещё один тест
👉🔬 Выполните принудительную перезагрузку приложения (Cmd/Ctrl + Shift + R).

👀 Ваш фамильяр всё ещё на месте, всё ещё с именем, всё ещё одет — но лента Инспектора оказалась пустой . Ещё секунду назад оба объекта были «в приложении». Один был записан, другой — нет. Определить, какие данные к чему относятся, — это большая часть работы.
👉🔬 Нажмите ✨ «Готово» , затем ⤓ «Сохранить паспорт» .

Что вы на самом деле узнали
Агент состоит из элементов, плюс связи между ними:
произведение | провод |
модель с инструкцией | оно умеет только говорить |
инструмент | Модель запрашивает, среда выполнения запускается. |
второй инструмент | Один инструмент записывает состояние, другой его считывает. |
обратный звонок | ваш код, проникающий в этот провод |
И в итоге получается такой цикл: слушать → принимать решение → действовать → состояние меняется. Затем снова слушать. Claude Code редактирует ваши файлы с его помощью. Deep Research просматривает страницы с его помощью. Gemini CLI запускает вашу оболочку с его помощью. Тот же цикл — разные инструменты за дверью.
Модель может только говорить. Программа может только следовать своему сценарию. Агент — это союз, и инструмент меняет лишь состояние.
Куда отправиться дальше?
- Вторая неделя открывает Buildyard. Сегодня все задания выполнял один агент. На следующей неделе проблема в том, что один агент не может выполнить всю работу целиком — и решение в том, чтобы их стало больше, а не чтобы задания были более масштабными. Все пять недель: goo.gle/agent101
- Сравните два агента.
forge/agent/character_forge.py— этоgrove/agent.pyплюс третий инструмент и пять коллбэков, которые вы только что наблюдали. Всё добавленное — это управление. - Третий инструмент.
generate_look(form)— это тот, который вы никогда не подключали кgrove— он сохраняет то же самое лицо только потому, что функция обратного вызова закрепляет ссылку на него. Вот почему он находится там, где находятся функции обратного вызова.