Создайте и разверните агента ADK, который использует сервер MCP в Cloud Run.

1. Введение

Эта лабораторная работа посвящена реализации и развертыванию клиентского агентского сервиса. Вы будете использовать Agent Development Kit (ADK) для создания ИИ-агента, использующего удаленные инструменты, такие как сервер MCP, созданный в лабораторной работе 1. Ключевой архитектурный принцип, продемонстрированный в работе, — это разделение задач, при котором отдельный слой рассуждений (агент) взаимодействует с отдельным слоем инструментов (сервер MCP) через защищенный API.

В лабораторной работе 1 вы создали MCP-сервер, который предоставляет данные о животных в вымышленном зоопарке студентам магистратуры, например, при использовании интерфейса командной строки Gemini. В этой лабораторной работе мы создаем агента-экскурсовода для вымышленного зоопарка. Агент будет использовать тот же MCP-сервер, что и в лабораторной работе 1, для доступа к информации о животных зоопарка, а также использовать Википедию для создания наилучшего опыта экскурсии.

f8d4423edbfe993d.png

Наконец, мы развернем агента гида в Google Cloud Run, чтобы к нему могли получить доступ все посетители зоопарка, а не только локально.

Предварительные требования

  • Запущенный сервер MCP в Cloud Run или связанный с ним URL-адрес службы.
  • Проект Google Cloud с включенной функцией выставления счетов.

Что вы узнаете

  • Как структурировать проект на Python для развертывания в ADK.
  • Как реализовать агента, использующего инструменты, с помощью Google AdK.
  • Как подключить агента к удаленному серверу MCP для использования его набора инструментов.
  • Как развернуть приложение Python в виде бессерверного контейнера в Cloud Run.
  • Как настроить безопасную аутентификацию между сервисами с использованием ролей IAM.
  • Как удалить облачные ресурсы, чтобы избежать будущих затрат.

Что вам понадобится

  • Аккаунт Google Cloud и проект Google Cloud
  • Веб-браузер, например Chrome.

2. Почему стоит развертывать приложения в Cloud Run?

Cloud Run — отличный выбор для размещения агентов ADK, поскольку это бессерверная платформа, а это значит, что вы можете сосредоточиться на своем коде, а не на управлении базовой инфраструктурой. Мы берем на себя операционную работу.

Представьте это как временный магазин: он открывается и использует ресурсы только тогда, когда поступают заказы. Когда заказов нет, он полностью закрывается, и вы не платите за пустой магазин.

Основные характеристики

Запускает контейнеры где угодно:

  • Вы предоставляете контейнер (образ Docker), внутри которого находится ваше приложение.
  • Cloud Run запускает его на инфраструктуре Google.
  • Никаких проблем с обновлением ОС, настройкой виртуальных машин или масштабированием.

Автоматическое масштабирование:

  • Если приложением пользуются 0 человек, то запускается 0 экземпляров (масштабирование до нуля экземпляров экономически выгодно).
  • Если поступает 1000 запросов, то запускается столько копий, сколько необходимо.

По умолчанию не сохраняющее состояние:

  • Каждый запрос может быть направлен в отдельный экземпляр.
  • Если вам необходимо хранить состояние, используйте внешний сервис, такой как Cloud SQL, Firestore или Memorystore.

Поддерживает любой язык программирования или фреймворк:

  • Пока приложение работает в контейнере Linux, Cloud Run неважно, используется ли Python, Go, Node.js, Java или .Net.

Платите только за то, что используете:

3. Настройка и требования

Настройка среды для самостоятельного обучения

  1. Войдите в консоль Google Cloud и создайте новый проект или используйте существующий. Если у вас еще нет учетной записи Gmail или Google Workspace, вам необходимо ее создать .

fbef9caa1602edd0.png

a99b7ace416376c4.png

5e3ff691252acf41.png

  • Название проекта — это отображаемое имя участников данного проекта. Это строка символов, не используемая API Google. Вы всегда можете его изменить.
  • Идентификатор проекта уникален для всех проектов Google Cloud и является неизменяемым (его нельзя изменить после установки). Консоль Cloud автоматически генерирует уникальную строку; обычно вам неважно, какая она. В большинстве практических заданий вам потребуется указать идентификатор вашего проекта (обычно обозначается как PROJECT_ID ). Если сгенерированный идентификатор вас не устраивает, вы можете сгенерировать другой случайный идентификатор. В качестве альтернативы вы можете попробовать свой собственный и посмотреть, доступен ли он. После этого шага его нельзя изменить, и он сохраняется на протяжении всего проекта.
  • К вашему сведению, существует третье значение — номер проекта , которое используется некоторыми API. Подробнее обо всех трех значениях можно узнать в документации .
  1. Далее вам потребуется включить оплату в консоли Cloud для использования ресурсов/API Cloud. Выполнение этого практического задания не потребует больших затрат, если вообще потребует. Чтобы отключить ресурсы и избежать дополнительных расходов после завершения этого урока, вы можете удалить созданные ресурсы или удалить проект. Новые пользователи Google Cloud имеют право на бесплатную пробную версию стоимостью 300 долларов США .

Запустить Cloud Shell

Если терминал не отображается в нижней части экрана, откройте его:

  • Нажмите «Терминал»
  • Нажмите «Новый терминал»

d32c46fffa0a30a5.png

В терминале настройте свой проект с помощью этой команды. Если вы выполнили лабораторную работу 1, убедитесь, что используете тот же идентификатор проекта:

gcloud config set project [YOUR-PROJECT-ID]

Если вы не помните идентификатор своего проекта, вы можете перечислить все идентификаторы своих проектов с помощью следующей команды:

gcloud projects list | awk '/PROJECT_ID/{print $2}'

4. Если появится запрос на авторизацию, нажмите «Авторизовать» , чтобы продолжить.

6356559df3eccdda.png

5. Вы должны увидеть следующее сообщение:

Updated property [core/project].
If you see a `WARNING` and are asked `Do you want to continue (Y/n)?`,

then you have likely entered the project ID incorrectly. Press `n`,

press `Enter`, and try to run the `gcloud config set project` command again.

4. Прежде чем начать

Включите API и установите переменные среды.

Включите все необходимые службы:

gcloud services enable \
    run.googleapis.com \
    artifactregistry.googleapis.com \
    cloudbuild.googleapis.com \
    aiplatform.googleapis.com \
    compute.googleapis.com 

Ожидаемый результат

Operation "operations/acat.p2-[GUID]" finished successfully.

5. Создайте папку проекта.

Создайте каталог проекта.

Эта команда создает основную папку для лаборатории, содержащую исходный код агента.

cd && mkdir zoo_guide_agent && cd zoo_guide_agent

Создайте файл requirements.txt . В этом файле перечислены библиотеки Python, необходимые вашему агенту. Следующая команда создаст файл и заполнит его.

cloudshell edit requirements.txt
google-adk==1.14.0
langchain-community==0.3.27
wikipedia==1.4.0

Задайте переменные для текущего идентификатора проекта, номера проекта, а также создайте выделенную учетную запись службы для вашего проекта. Это более надежный способ выполнения этих команд.

export PROJECT_ID=$(gcloud config get-value project)
export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format="value(projectNumber)")
export SA_NAME=lab2-cr-service
export SERVICE_ACCOUNT="${SA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com"
gcloud iam service-accounts create ${SA_NAME} \
    --display-name="Service Account for lab 2 "

Создайте и откройте файл .env для аутентификации агента в каталоге zoo_guide_agent .

cloudshell edit .env

Команда `cloudshell edit` откроет файл ` .env в редакторе над терминалом. Введите следующее в файл .env и вернитесь в терминал.

MODEL="gemini-2.5-flash"

Добавление URL-адреса MCP-сервера . Если вы выполнили лабораторную работу 1, выполните следующие шаги, чтобы использовать MCP-сервер, созданный вами в лабораторной работе 1:

  1. Предоставьте службе Cloud Run разрешение на обращение к удаленному серверу MCP.
gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT" \
  --role="roles/run.invoker"
  1. Сохраните URL-адрес сервера MCP из лабораторной работы 1 в переменную среды.
echo -e "\nMCP_SERVER_URL=https://zoo-mcp-server-${PROJECT_NUMBER}.europe-west1.run.app/mcp" >> .env

Если вы используете общедоступную ссылку на сервер MCP, выполните следующую команду и замените PROJECT_NUMBER на предоставленное значение.

echo -e "\nMCP_SERVER_URL=https://zoo-mcp-server-${PROJECT_NUMBER}.europe-west1.run.app/mcp" >> .env

6. Создание рабочего процесса агента

Создайте файл init.py.

Создайте файл init.py. Этот файл сообщает Python, что каталог zoo_guide_agent является пакетом.

cloudshell edit __init__.py

Приведённая выше команда открывает редактор кода. Добавьте следующий код в файл __init__.py :

from . import agent

Создайте основной файл agent.py.

Создайте основной файл agent.py. Эта команда создаст файл Python и вставит в него полный код вашей многоагентной системы.

cloudshell edit agent.py

Шаг 1: Импорт и первоначальная настройка

Первый блок подключает все необходимые библиотеки из ADK и Google Cloud. Он также настраивает логирование и загружает переменные окружения из вашего файла .env , что крайне важно для доступа к вашей модели и URL-адресу сервера.

Добавьте следующий код в файл agent.py:

import os
import logging
import google.cloud.logging
from dotenv import load_dotenv

from google.adk import Agent
from google.adk.agents import SequentialAgent
from google.adk.tools.mcp_tool.mcp_toolset import MCPToolset, StreamableHTTPConnectionParams
from google.adk.tools.tool_context import ToolContext
from google.adk.tools.langchain_tool import LangchainTool

from langchain_community.tools import WikipediaQueryRun
from langchain_community.utilities import WikipediaAPIWrapper

import google.auth
import google.auth.transport.requests
import google.oauth2.id_token

# --- Setup Logging and Environment ---

cloud_logging_client = google.cloud.logging.Client()
cloud_logging_client.setup_logging()

load_dotenv()

model_name = os.getenv("MODEL")

Шаг 2: Определение инструментов (возможностей агента)

3eb9c6772576b906.jpeg

Эффективность агента напрямую зависит от используемых им инструментов. В этом разделе мы описываем все возможности нашего агента, включая пользовательскую функцию сохранения данных, инструмент MCP, подключающийся к нашему защищенному серверу MCP, а также инструмент для работы с Википедией.

Добавьте следующий код в конец файла agent.py:

# Greet user and save their prompt

def add_prompt_to_state(
    tool_context: ToolContext, prompt: str
) -> dict[str, str]:
    """Saves the user's initial prompt to the state."""
    tool_context.state["PROMPT"] = prompt
    logging.info(f"[State updated] Added to PROMPT: {prompt}")
    return {"status": "success"}


# Configuring the MCP Tool to connect to the Zoo MCP server

mcp_server_url = os.getenv("MCP_SERVER_URL")
if not mcp_server_url:
    raise ValueError("The environment variable MCP_SERVER_URL is not set.")

def get_id_token():
    """Get an ID token to authenticate with the MCP server."""
    target_url = os.getenv("MCP_SERVER_URL")
    audience = target_url.split('/mcp/')[0]
    request = google.auth.transport.requests.Request()
    id_token = google.oauth2.id_token.fetch_id_token(request, audience)
    return id_token

"""
# Use this code if you are using the public MCP Server and comment out the code below defining mcp_tools
mcp_tools = MCPToolset(
    connection_params=StreamableHTTPConnectionParams(
        url=mcp_server_url
    )
)
"""

mcp_tools = MCPToolset(
            connection_params=StreamableHTTPConnectionParams(
                url=mcp_server_url,
                headers={
                    "Authorization": f"Bearer {get_id_token()}",
                },
            ),
        )

# Configuring the Wikipedia Tool
wikipedia_tool = LangchainTool(
    tool=WikipediaQueryRun(api_wrapper=WikipediaAPIWrapper())
)

Три инструмента: объяснение

  1. add_prompt_to_state 📝

Этот инструмент запоминает вопросы посетителей зоопарка. Когда посетитель спрашивает: «Где львы?», инструмент сохраняет этот конкретный вопрос в памяти агента, чтобы другие агенты в рабочем процессе знали, что нужно исследовать.

Как это работает: это функция на Python, которая записывает подсказку посетителя в общий словарь tool_context.state . Этот контекст инструмента представляет собой кратковременную память агента для одного разговора. Данные, сохраненные в состоянии одним агентом, могут быть прочитаны следующим агентом в рабочем процессе.

  1. MCPToolset 🦁

Это используется для подключения агента-экскурсовода к серверу MCP зоопарка, созданному в Лаборатории 1. На этом сервере есть специальные инструменты для поиска конкретной информации о наших животных, такой как их имя, возраст и вольер.

Как это работает: система безопасно подключается к частному серверу зоопарка по указанному URL-адресу. Она использует get_id_token для автоматического получения защищенной «ключ-карты» (токена идентификатора учетной записи службы), чтобы подтвердить свою личность и получить доступ.

  1. LangchainTool 🌍

Это дает экскурсоводу общие знания о мире. Когда посетитель задает вопрос, которого нет в базе данных зоопарка, например: «Чем питаются львы в дикой природе?», этот инструмент позволяет экскурсоводу найти ответ в Википедии.

Как это работает: Он выступает в качестве адаптера, позволяя нашему агенту использовать встроенный инструмент WikipediaQueryRun из библиотеки LangChain.

Ресурсы:

Шаг 3: Определение агентов-специалистов

b8a9504b21920969.jpeg

Далее мы определим агента-исследователя и агента форматирования ответа. Агент-исследователь — это «мозг» нашей работы. Этот агент берет запрос пользователя из общего State , анализирует его мощные инструменты (инструмент MCP Server Tool и инструмент Wikipedia Tool) и решает, какие из них использовать для поиска ответа.

Роль агента, формирующего ответ, заключается в его представлении. Он не использует никаких инструментов для поиска новой информации. Вместо этого он берет необработанные данные, собранные агентом-исследователем (переданные через государство), и использует языковые навыки магистра права, чтобы преобразовать их в дружелюбный, разговорный ответ.

Добавьте следующий код в конец файла agent.py :

# 1. Researcher Agent
comprehensive_researcher = Agent(
    name="comprehensive_researcher",
    model=model_name,
    description="The primary researcher that can access both internal zoo data and external knowledge from Wikipedia.",
    instruction="""
    You are a helpful research assistant. Your goal is to fully answer the user's PROMPT.
    You have access to two tools:
    1. A tool for getting specific data about animals AT OUR ZOO (names, ages, locations).
    2. A tool for searching Wikipedia for general knowledge (facts, lifespan, diet, habitat).

    First, analyze the user's PROMPT.
    - If the prompt can be answered by only one tool, use that tool.
    - If the prompt is complex and requires information from both the zoo's database AND Wikipedia,
      you MUST use both tools to gather all necessary information.
    - Synthesize the results from the tool(s) you use into preliminary data outputs.

    PROMPT:
    {{ PROMPT }}
    """,
    tools=[
        mcp_tools,
        wikipedia_tool
    ],
    output_key="research_data" # A key to store the combined findings
)

# 2. Response Formatter Agent
response_formatter = Agent(
    name="response_formatter",
    model=model_name,
    description="Synthesizes all information into a friendly, readable response.",
    instruction="""
    You are the friendly voice of the Zoo Tour Guide. Your task is to take the
    RESEARCH_DATA and present it to the user in a complete and helpful answer.

    - First, present the specific information from the zoo (like names, ages, and where to find them).
    - Then, add the interesting general facts from the research.
    - If some information is missing, just present the information you have.
    - Be conversational and engaging.

    RESEARCH_DATA:
    {{ research_data }}
    """
)

Шаг 4: Агент рабочего процесса

Агент рабочего процесса выступает в роли «бэк-офиса» для экскурсии по зоопарку. Он принимает запрос на поиск информации и обеспечивает выполнение двумя описанными выше агентами своих задач в правильном порядке: сначала поиск, затем форматирование. Это создает предсказуемый и надежный процесс ответа на вопрос посетителя.

Как это работает: это SequentialAgent , особый тип агента, который не мыслит самостоятельно. Его единственная задача — запускать список sub_agents (исследователя и форматировщика) в фиксированной последовательности, автоматически передавая общую память от одного к другому.

Добавьте этот блок кода в конец файла agent.py :

tour_guide_workflow = SequentialAgent(
    name="tour_guide_workflow",
    description="The main workflow for handling a user's request about an animal.",
    sub_agents=[
        comprehensive_researcher, # Step 1: Gather all data
        response_formatter,       # Step 2: Format the final response
    ]
)

Заключительный этап: Сборка основного рабочего процесса. 1000b9d20f4e134b.jpeg

Этот агент обозначается как root_agent , который фреймворк ADK использует в качестве отправной точки для всех новых диалогов. Его основная роль — координация всего процесса. Он выступает в качестве начального контроллера, управляя первым этапом диалога.

Добавьте этот заключительный блок кода в конец файла agent.py :

root_agent = Agent(
    name="greeter",
    model=model_name,
    description="The main entry point for the Zoo Tour Guide.",
    instruction="""
    - Let the user know you will help them learn about the animals we have in the zoo.
    - When the user responds, use the 'add_prompt_to_state' tool to save their response.
    After using the tool, transfer control to the 'tour_guide_workflow' agent.
    """,
    tools=[add_prompt_to_state],
    sub_agents=[tour_guide_workflow]
)

Ваш файл agent.py готов! Собрав его таким образом, вы сможете увидеть, как каждый компонент — инструменты, рабочие агенты и агенты-менеджеры — играет свою конкретную роль в создании конечной интеллектуальной системы. Следующий шаг — развертывание!

7. Подготовьте приложение к развертыванию.

После подготовки локальной среды следующим шагом является подготовка проекта Google Cloud к развертыванию. Это включает в себя окончательную проверку структуры файлов вашего агента, чтобы убедиться в ее совместимости с командой развертывания. Что еще более важно, необходимо настроить критически важное разрешение IAM, которое позволит развернутой службе Cloud Run действовать от вашего имени и вызывать модели Vertex AI. Завершение этого шага гарантирует, что облачная среда будет готова к успешному запуску вашего агента.

Загрузите переменные в свою сессию командной оболочки, выполнив команду source.

source .env

Предоставьте учетной записи службы роль пользователя Vertex AI, которая даст ей разрешение на создание прогнозов и обращение к моделям Google.

# Grant the "Vertex AI User" role to your service account
gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT" \
  --role="roles/aiplatform.user"

8. Разверните агент с помощью интерфейса командной строки ADK.

После того, как ваш локальный код готов, а проект Google Cloud подготовлен, пришло время развернуть агент. Вы будете использовать команду adk deploy cloud_run — удобный инструмент, автоматизирующий весь процесс развертывания. Эта единственная команда упаковывает ваш код, создает образ контейнера, загружает его в реестр артефактов и запускает службу в Cloud Run, делая ее доступной через веб-интерфейс.

Развертывать

Выполните следующие команды для развертывания агента. Команда uvx позволяет запускать инструменты командной строки, опубликованные в виде пакетов Python, без необходимости глобальной установки этих инструментов.

# Run the deployment command
uvx --from google-adk==1.14.0 \
adk deploy cloud_run \
  --project=$PROJECT_ID \
  --region=europe-west1 \
  --service_name=zoo-tour-guide \
  --with_ui \
  . \
  -- \
  --labels=dev-tutorial=codelab-adk \
  --service-account=$SERVICE_ACCOUNT

Принять подсказки

Вам может быть предложено следующее:

Deploying from source requires an Artifact Registry Docker repository to store built containers. A repository named [cloud-run-source-deploy] in region 
[europe-west1] will be created.

Do you want to continue (Y/n)?

Введите Y и нажмите ENTER.

Вам может быть предложено следующее:

Allow unauthenticated invocations to [your-service-name] (y/N)?.

Для этой лабораторной работы мы хотим разрешить неаутентифицированные вызовы для упрощения тестирования. Введите y и нажмите Enter.

После успешного выполнения команда предоставит URL-адрес развернутой службы Cloud Run. (Он будет выглядеть примерно так: https://zoo-tour-guide-123456789.europe-west1.run.app ). Скопируйте этот URL-адрес для выполнения следующей задачи.

9. Протестируйте развернутого агента.

После запуска агента в Cloud Run вам потребуется провести тест, чтобы убедиться в успешности развертывания и корректной работе агента. Для доступа к веб-интерфейсу ADK и взаимодействия с агентом используйте общедоступный URL-адрес сервиса (например, https://zoo-tour-guide-123456789.europe-west1.run.app/ ).

Откройте общедоступный URL-адрес Cloud Run Service в веб-браузере. Поскольку вы использовали --with_ui flag , вы должны увидеть пользовательский интерфейс разработчика ADK.

Включите функцию Token Streaming в правом верхнем углу.

Теперь вы можете взаимодействовать с агентом зоопарка.

Напишите hello и нажмите Enter, чтобы начать новый разговор.

Обратите внимание на результат. Агент должен быстро ответить своим приветствием:

"Hello! I'm your Zoo Tour Guide. I can help you learn about the amazing animals we have here. What would you like to know or explore today?"

Задавайте агенту такие вопросы:

Where can I find the polar bears in the zoo and what is their diet?

3244d2f6c3b03088.pnge135694253b1be41.png

Объяснение потока агентов

Ваша система работает как интеллектуальная многоагентная команда. Процесс управляется в соответствии с четкой последовательностью, обеспечивающей плавный и эффективный переход от вопроса пользователя к окончательному, подробному ответу.

1. Сотрудник, встречающий посетителей зоопарка (стойка регистрации )

Весь процесс начинается с сотрудника, встречающего гостей.

Его задача: начать разговор. Его инструкция — поприветствовать пользователя и спросить, о каком животном он хотел бы узнать больше.

Инструмент: Когда пользователь отвечает, приветствующая система использует инструмент add_prompt_to_state, чтобы зафиксировать его точные слова (например, "расскажите мне о львах") и сохранить их в памяти системы.

Передача управления: После сохранения запроса управление немедленно передается его субагенту, рабочему процессу гида-экскурсовода.

2. Исследователь-всесторонне развитый (Супер-исследователь)

Это первый этап основного рабочего процесса и «мозг» операции. Вместо большой команды у вас теперь есть один высококвалифицированный агент, имеющий доступ ко всей доступной информации.

Его задача: проанализировать вопрос пользователя и сформировать интеллектуальный план. Он использует мощные инструменты языковой модели, чтобы определить, нуждается ли он в следующем:

  • Внутренние данные из архивов зоопарка (через сервер MCP).
  • Общие сведения из интернета (через API Википедии).
  • Или, в случае сложных вопросов, и то, и другое.

Его действие: Он запускает необходимые инструменты для сбора всех требуемых исходных данных. Например, если его спросят: «Сколько лет нашим львам и чем они питаются в дикой природе?», он обратится к серверу MCP за информацией о возрасте и к инструменту Википедии за информацией о рационе питания.

3. Форматировщик ответов (докладчик)

После того как исследователь соберет все необходимые данные, это будет последний агент, который необходимо запустить.

Его задача: выступать в роли дружелюбного голоса гида зоопарка. Он берет необработанные данные (которые могут поступать из одного или обоих источников) и обрабатывает их.

Принцип действия: Он синтезирует всю информацию в единый, связный и увлекательный ответ. Следуя инструкциям, он сначала представляет конкретную информацию о зоопарке, а затем добавляет интересные общие факты.

Итоговый результат: текст, сгенерированный этим агентом, представляет собой полный, подробный ответ, который пользователь видит в окне чата.

Если вас интересует более подробная информация о создании агентов, ознакомьтесь со следующими ресурсами:

  1. Документация ADK
  2. Создание пользовательских инструментов для агентов ADK

10. Очистка окружающей среды

gcloud run services delete zoo-tour-guide --region=europe-west1 --quiet
gcloud artifacts repositories delete cloud-run-source-deploy --location=europe-west1 --quiet

11. Поздравляем!

Поздравляем с завершением практического занятия!

Что мы рассмотрели

  • Как структурировать проект на Python для развертывания с помощью интерфейса командной строки ADK.
  • Как реализовать многоагентный рабочий процесс с использованием SequentialAgent и ParallelAgent.
  • Как подключиться к удалённому MCP-серверу с помощью MCPToolset для использования его инструментов.
  • Как расширить внутренние данные за счет интеграции внешних инструментов, таких как API Википедии.
  • Как развернуть агента в виде бессерверного контейнера в Cloud Run с помощью команды adk deploy.

12. Опрос

Выход:

Как вы будете использовать этот учебный материал?

Прочитайте только до конца. Прочитайте текст и выполните упражнения.