Agent Dev 101

Модуль 07 · Урок 7.1

Урок 7.1. Что такое Tool

На этой странице
  1. Зачем это нужно
  2. Основная идея
  3. Почему tool - это граница ответственности
  4. Ключевые термины
  5. Примеры tools
  6. Три Категории Tools
  7. Tool Call Lifecycle
  8. Tool как contract
  9. Input Schema
  10. Output Schema
  11. Tool errors
  12. Read, Write, Destructive
  13. Tool и agent loop
  14. Как агент должен выбирать tool
  15. Design example: run_tests(path)
  16. Bad tool design examples
  17. Tool design checklist
  18. Практика
  19. Типичные ошибки
  20. Критерии готовности
  21. Краткое резюме
  22. Вопросы для самопроверки
  23. Что дальше

Зачем это нужно

До этого момента мы говорили о контексте, правилах, specs и skills. Все эти элементы помогают агенту думать и действовать предсказуемо. Но сами по себе они не дают агенту возможности выполнить внешнее действие.

Агент может рассуждать:

text
Нужно найти файл.
Нужно запустить тест.
Нужно прочитать схему базы.
Нужно создать issue.
Нужно получить статус CI.

Но LLM не выполняет эти действия напрямую. Модель выбирает действие, формирует аргументы, а выполнение делает agent runtime через tool.

Tool - это граница между рассуждением модели и реальным миром. Именно поэтому tools требуют инженерного дизайна: schema, permissions, observability, errors, retries, audit и human approval для опасных действий.

Главная цель урока - понять tool как функцию действия, которую модель может вызвать, и научиться проектировать простой tool на примере run_tests(path).

Основная идея

Tool - это функция, доступная agent runtime, которую модель может выбрать для выполнения конкретного действия.

Простая формула:

text
Tool = name + description + input schema + execution + output + permission boundary

Skill отвечает:

text
Как выполнять повторяемую процедуру?

Tool отвечает:

text
Какое действие можно выполнить прямо сейчас?

Пример:

Задача агента Skill или tool?
"Как добавить endpoint с тестами" Skill
"Запустить тесты для файла" Tool
"Как проводить code review" Skill
"Получить diff текущей ветки" Tool
"Как безопасно делать migration" Skill
"Прочитать schema database" Tool или Resource

Важно:

text
LLM выбирает tool call.
Runtime выполняет tool call.
Модель получает result и продолжает работу.

Почему tool - это граница ответственности

Tool легко представить как обычную функцию: есть имя, аргументы и результат. Для agent development это слишком слабое представление. Tool - это место, где намерение модели превращается в действие системы.

До tool call модель находится в мире текста: она рассуждает, сравнивает варианты, строит план, формулирует аргументы. После tool call появляется внешний эффект: прочитан файл, запущен тест, отправлен комментарий, создан ticket, изменен код или затронута внешняя система. Поэтому tool всегда находится на границе ответственности.

На этой границе участвуют три стороны:

  • модель решает, что действие нужно;
  • runtime проверяет, можно ли выполнить действие и как именно;
  • project policy определяет, какие действия допустимы без approval.

Если все это смешать, tool становится опасным. Например, если модель сама формирует shell-команду, сама решает, безопасна ли она, и runtime выполняет ее без проверки, то у нас нет настоящей границы. Есть только доверие к следующему token'у модели.

Хороший tool design делает границу явной:

text
Model:
I need to run tests for this file.

Runtime:
The tool accepts only repository-relative paths and known modes.

Policy:
Targeted tests are allowed. Full integration suite may require confirmation.

Tool result:
The command ran, these tests passed or failed, here is evidence.

Такой подход особенно важен для coding agents. Агент может быть полезен только тогда, когда его действия проверяемы. Tool дает проверяемость, если он узкий, типизированный, observable и честно сообщает failures. Tool разрушает проверяемость, если он превращается в универсальную черную коробку.

Практический критерий:

text
Если вы не можете объяснить, что tool может сделать,
какие у него side effects и как выглядит failure,
его рано давать агенту.

Ключевые термины

Термин Значение
Tool Исполняемая функция, доступная агенту.
Tool Call Запрос модели на выполнение tool с аргументами.
Runtime Слой приложения, который валидирует, разрешает и выполняет tool.
Input Schema Формальное описание аргументов tool.
Output Schema Формальное или ожидаемое описание результата tool.
Structured Result Результат в машинно-читаемом формате, например JSON.
Permission Boundary Граница прав: что tool может делать и когда нужен approval.
Read Tool Tool, который только читает информацию.
Write Tool Tool, который изменяет состояние.
Destructive Tool Tool, который может удалить данные, деплоить, платить, отзывать доступ или ломать систему.
Idempotency Свойство повторного вызова без дополнительного изменения состояния.
Audit Log Запись о том, кто, когда и с какими аргументами вызвал tool.

Примеры tools

Для coding agent типичные tools:

text
read_file(path)
search_repository(query)
list_files(glob)
apply_patch(patch)
run_tests(command)
get_git_diff()

Для product/project workflow:

text
create_github_issue(title, body, labels)
comment_on_pull_request(pr_id, body)
get_ci_status(branch)
create_linear_ticket(title, description)

Для data/system workflow:

text
query_database(sql)
get_database_schema(database)
read_logs(service, time_range)
deploy_preview(branch)
rollback_deployment(deployment_id)

Не все эти tools одинаково безопасны. read_file и rollback_deployment нельзя проектировать одинаково.

Три Категории Tools

Для agent design удобно делить tools на три категории.

Категория Что делает Примеры Главный риск
Data tools Дают агенту context. read_file, search_docs, query_metrics, get_ci_status. Агент может получить лишние или sensitive данные.
Action tools Меняют состояние системы. apply_patch, create_ticket, send_email, deploy_preview. Side effects, которые трудно отменить.
Orchestration tools Запускают другой agent/workflow или передают задачу. run_reviewer_agent, handoff_to_support, start_eval_suite. Потеря контроля над context, cost и ownership.

В coding-agent репозитории чаще всего сначала появляются data tools и локальные action tools:

text
read/search -> edit scoped files -> run tests -> inspect diff

Orchestration tools стоит добавлять позже, когда уже есть:

  • task DAG;
  • role definitions;
  • run logs;
  • output contracts;
  • cost limits;
  • failure handling;
  • human approval gates.

Иначе "agent as a tool" превращается в черный ящик: один агент вызывает другого, тот делает diff, третий пишет summary, а в конце непонятно, кто за что отвечает.

Практический вывод:

text
Проектируйте data tools широко, action tools узко,
а orchestration tools только после появления process contracts.

Tool Call Lifecycle

flowchart TD A[User task] --> B[Model decides action is needed] B --> C[Select tool] C --> D[Build arguments] D --> E[Runtime validates schema] E --> F{Permission needed?} F -- No --> G[Execute tool] F -- Yes --> H[Request approval] H --> I{Approved?} I -- No --> J[Return denied result] I -- Yes --> G G --> K[Return structured result] K --> L[Model observes result] L --> M[Continue reasoning or next action]

Tool call не должен быть невидимой магией. Хороший runtime делает действия observable:

  • какой tool выбран;
  • какие аргументы переданы;
  • был ли approval;
  • какой результат получен;
  • был ли error;
  • что модель сделала после результата.

Tool как contract

Tool - это contract между моделью и внешним действием.

У хорошего tool есть:

  • понятное имя;
  • короткое description;
  • строгая input schema;
  • предсказуемый output;
  • понятные errors;
  • permission boundary;
  • документация side effects;
  • ограничения на scope;
  • auditability.

Пример плохого tool:

text
do_stuff(input: string) -> string

Проблемы:

  • непонятно, что делает;
  • input невалидируемый;
  • output неструктурированный;
  • side effects неизвестны;
  • permissions невозможно настроить.

Пример лучше:

text
run_tests(path: string, mode: "unit" | "integration" | "all") -> {
  exit_code: number,
  command: string,
  stdout_excerpt: string,
  stderr_excerpt: string,
  passed: boolean
}

Даже без реальной реализации видно, как agent runtime может проверять и объяснять действие.

Input Schema

Input schema ограничивает то, что модель может передать в tool.

Например, для run_tests(path):

json
{
  "type": "object",
  "properties": {
    "path": {
      "type": "string",
      "description": "Repository-relative path to a test file, test directory, or module."
    },
    "mode": {
      "type": "string",
      "enum": ["targeted", "module", "full"],
      "description": "How broad the test run should be."
    }
  },
  "required": ["path", "mode"],
  "additionalProperties": false
}

Почему schema важна:

  • не дает модели передавать случайные поля;
  • упрощает UI approvals;
  • делает logs понятнее;
  • помогает runtime валидировать аргументы до выполнения;
  • снижает вероятность dangerous command injection;
  • улучшает recoverability после errors.

Слабый вариант:

json
{
  "type": "object",
  "properties": {
    "command": { "type": "string" }
  }
}

Если tool принимает произвольную команду, это уже почти shell access. Такой tool требует гораздо более строгих permissions, чем run_tests(path).

Output Schema

Tool output должен быть полезен модели.

Плохо:

text
Done.

Лучше:

json
{
  "command": "npm test -- tests/projects.test.ts",
  "exit_code": 0,
  "passed": true,
  "duration_ms": 1840,
  "stdout_excerpt": "3 tests passed",
  "stderr_excerpt": "",
  "artifacts": []
}

Structured result помогает агенту:

  • понять, прошла ли проверка;
  • процитировать evidence пользователю;
  • решить, продолжать ли debugging;
  • отличить tool failure от failed test;
  • сохранять воспроизводимость.

Tool errors

Нужно различать два типа ошибок.

Тип ошибки Пример Что делать
Protocol/runtime error Tool не найден, schema invalid, permission denied Runtime возвращает ошибку, модель выбирает fallback или спрашивает.
Execution error Тесты упали, API вернул 400, файл не найден Tool возвращает actionable result, модель может исправлять задачу.

Для run_tests падение тестов не является "tool сломался". Tool сработал успешно и сообщил, что тесты failed.

Плохой дизайн:

text
Если tests failed, tool падает как runtime error.

Лучше:

json
{
  "passed": false,
  "exit_code": 1,
  "failure_summary": "Expected 201, got 400 in projects.test.ts:42"
}

Модель должна видеть failure, чтобы продолжить работу.

Read, Write, Destructive

Tools нужно классифицировать по уровню риска.

Категория Примеры Риск Policy
READ read_file, search_repository, get_schema Утечка данных, prompt injection Обычно можно auto-allow в доверенном scope.
WRITE apply_patch, create_issue, comment_pr Изменение состояния Нужны scope, logs, иногда approval.
DESTRUCTIVE delete_table, deploy_prod, charge_card, revoke_access Потеря данных, деньги, production impact Human approval по умолчанию.

Эта классификация появится снова в уроке 7.6. Пока важно запомнить: tool design начинается с risk model.

Tool и agent loop

В хорошем execution loop tool calls не случайны. Они встроены в workflow:

text
Read spec
  -> search_repository()
  -> read_file()
  -> plan
  -> apply_patch()
  -> run_tests()
  -> observe result
  -> fix if needed
  -> report verification

Tool не заменяет reasoning. Tool дает проверяемое действие или наблюдение.

Плохой agent loop:

text
Модель сразу пишет код.
Не читает файлы.
Не запускает tests.
В конце говорит "готово".

Хороший loop:

text
Модель читает контекст.
Выбирает минимальный action.
Выполняет tool.
Использует результат.
Доказывает outcome.

Как агент должен выбирать tool

Наличие tool не означает, что агент должен сразу его вызвать. Tool call - это часть reasoning loop, а не замена reasoning. Перед вызовом tool агент должен понимать, какую неопределенность он снимает или какой проверяемый шаг выполняет.

Примеры правильной мотивации:

text
Я не знаю, где реализован endpoint.
-> search_repository("password reset")

Я нашел файл, но не видел его содержимое.
-> read_file("app/api/auth/reset.ts")

Я изменил код и должен проверить behavior.
-> run_tests("tests/auth/reset.test.ts", mode="targeted")

Я хочу оставить comment во внешнем PR.
-> сначала подготовить preview, затем request approval, затем comment_on_pr()

Плохой pattern:

text
У меня есть tool, значит я попробую его вызвать.

Так агент начинает "тыкать" в среду, вместо того чтобы двигаться по задаче. Это приводит к лишним tool calls, noisy logs, случайным side effects и росту стоимости. Особенно опасно, если tools имеют write access.

Хорошая последовательность выбора:

  1. Сформулировать, какой факт или действие нужно.
  2. Проверить, нельзя ли получить ответ из уже доступного context.
  3. Выбрать самый узкий tool.
  4. Передать минимальные аргументы.
  5. Прочитать result как evidence, а не как повод игнорировать spec.
  6. Если result failed, отличить failed task от broken tool.

Например, если агенту нужно понять, почему тест упал, он не должен сразу запускать full suite. Сначала лучше прочитать failure summary, открыть relevant file, запустить targeted test и только потом расширять scope проверки.

Tool discipline - это часть управляемой разработки. Чем мощнее tools, тем важнее, чтобы агент вызывал их с понятной целью.

Design example: run_tests(path)

Практика урока - спроектировать tool run_tests(path).

Purpose

text
Run project tests for a repository-relative path or selected scope and return structured results for the agent.

Inputs

json
{
  "path": "tests/api/projects.test.ts",
  "mode": "targeted"
}

Output

json
{
  "command": "npm test -- tests/api/projects.test.ts",
  "exit_code": 0,
  "passed": true,
  "duration_ms": 2140,
  "summary": "12 tests passed",
  "failure_summary": null
}

Constraints

  • Принимать только repository-relative paths.
  • Не принимать произвольные shell commands от модели.
  • Не запускать full suite без явного выбора mode: "full".
  • Ограничивать timeout.
  • Возвращать stdout/stderr excerpt, а не бесконечный log.
  • Не скрывать failed tests.

Permission

run_tests обычно безопаснее, чем write tool, но все равно может:

  • тратить CPU;
  • менять cache;
  • создавать test artifacts;
  • запускать flaky integration tests;
  • обращаться к локальным services.

Поэтому policy может быть такой:

Mode Approval
targeted Auto-allow
module Auto-allow или soft confirmation
full Ask if expensive
integration with external services Ask

Bad tool design examples

1. Too much power

text
run_any_command(command: string)

Проблема: это не test tool, а общий shell. Его нельзя защищать так же, как run_tests(path).

2. No structured output

text
run_tests(path) -> "ok"

Проблема: агент не может отличить passed, failed, skipped, timeout.

3. Hidden side effects

text
get_ci_status(branch)

А внутри tool еще перезапускает pipeline.

Проблема: name обещает read, implementation делает write.

4. No permission boundary

text
deploy(environment)

Без distinctions между preview, staging, production.

Лучше:

text
deploy_preview(branch)
request_production_deploy(release_id)

Разные риски должны быть разными tools или разными policy paths.

Tool design checklist

Перед тем как дать tool агенту, ответьте:

  • Какое конкретное действие выполняет tool?
  • Можно ли сузить input schema?
  • Есть ли side effects?
  • Tool read-only, write или destructive?
  • Нужен ли human approval?
  • Что вернется при success?
  • Что вернется при expected failure?
  • Какой timeout?
  • Что логируется?
  • Может ли output содержать sensitive data?
  • Может ли input быть prompt injection carrier?
  • Есть ли safer alternative как resource?
  • Нужно ли это вообще делать tool, а не локальной командой?

Практика

Спроектируйте run_tests(path).

Заполните:

text
Name:
Description:
Inputs:
Output:
Permission policy:
Timeout:
Failure modes:
Audit fields:
When not to use:

Пример ответа:

text
Name:
run_tests

Description:
Run targeted project tests for a repository-relative path and return structured pass/fail output.

Inputs:
- path: repository-relative test path
- mode: targeted | module | full

Output:
- command
- exit_code
- passed
- duration_ms
- summary
- failure_summary

Permission policy:
- targeted auto-allowed
- full suite requires confirmation if expensive
- external integration tests require confirmation

When not to use:
- arbitrary shell commands
- deployment verification
- production smoke tests without explicit approval

Типичные ошибки

Ошибка Почему плохо Исправление
Tool принимает command: string Слишком широкая власть Делать domain-specific inputs.
Tool возвращает только plain text Модель плохо понимает outcome Вернуть structured result.
Нет distinction read/write/delete Permissions невозможно настроить Классифицировать risk level.
Tool скрывает side effects Пользователь теряет контроль Описать side effects и approvals.
Tool делает слишком много Непредсказуемость Разделить на маленькие tools.
Нет failure semantics Агент не знает, retry или stop Развести runtime error и execution failure.
Tool output слишком длинный Забивает context Возвращать summary + excerpts + artifact links.

Критерии готовности

После урока студент должен уметь:

  • объяснить tool как функцию действия;
  • отличить tool от skill;
  • описать lifecycle tool call;
  • спроектировать input/output schema;
  • классифицировать tool как READ, WRITE или DESTRUCTIVE;
  • объяснить, почему structured output важен;
  • спроектировать простой run_tests(path);
  • назвать основные риски плохого tool design.

Краткое резюме

Tool - это исполняемая capability агента. Модель выбирает tool и аргументы, runtime валидирует и выполняет действие, результат возвращается модели как наблюдение.

Хороший tool:

  • узкий;
  • typed;
  • observable;
  • проверяемый;
  • ограниченный по правам;
  • честно возвращает failures;
  • не скрывает side effects.

Tools делают агента полезным в реальном engineering workflow, но каждый tool расширяет поверхность риска. Поэтому следующий шаг - понять MCP как стандартный способ предоставлять tools, resources и prompts разным AI applications.

Вопросы для самопроверки

  1. Почему LLM не "выполняет" действие напрямую?
  2. Чем run_tests(path) безопаснее, чем run_command(command)?
  3. Почему failed tests должны быть execution result, а не runtime error?
  4. Какие поля вы включили бы в output tool get_ci_status(branch)?
  5. Какие tools в вашем проекте должны требовать human approval?

Что дальше

В следующем уроке мы разберем MCP - Model Context Protocol. Tool можно встроить в один agent runtime напрямую, но если capability должна быть переносимой между Codex, Claude Code, Cursor, внутренним agent host и другими клиентами, нужен стандартный способ подключения. MCP как раз решает эту задачу.

Обновлено 03.10.2026