docs: refresh translations for recent English changes (#3379)
This commit is contained in:
parent
d8b638304e
commit
c5d7d0b6d0
6 changed files with 3149 additions and 1047 deletions
|
|
@ -1,13 +1,13 @@
|
|||
---
|
||||
translation:
|
||||
sections: [e33d441f12d50535, 7099694c603e0f5f, c1df4cf9673433e6, c9cd294541422e6e, 6cec073617bfd037, efa92b8f99e908c8, 6a22a29e27fb4601]
|
||||
sections: [7be05607887e6853, e7375894888d9750, c36f73fc7e3af13b, 2fec2d7e129e62fe, 809b0e0a7c27295a, b4395a04d2a5d906, 1a436007f5f54779, c6b2078ed1e63ba5]
|
||||
tool: 1
|
||||
---
|
||||
# Обробка помилок {#handling-errors}
|
||||
|
||||
Інструмент може завершитися невдачею двома способами, і SDK обробляє їх зовсім по-різному.
|
||||
Інструмент може завершитися невдачею трьома способами, і SDK обробляє кожен по-різному.
|
||||
|
||||
Викиньте звичайний виняток — і його побачить **модель**. Викиньте `MCPError` — і його побачить **протокол**.
|
||||
Викиньте `ToolError` — і ваше повідомлення побачить **модель**. Викиньте `MCPError` — і його побачить **протокол**. Викиньте будь-що інше — і це збій: модель дізнається лише, що виклик не вдався, а трасування потрапить у ваш лог.
|
||||
|
||||
Ця сторінка — про те, як вибрати.
|
||||
|
||||
|
|
@ -15,11 +15,11 @@ translation:
|
|||
|
||||
Візьмімо інструмент, який щось шукає, і нехай пошук нічого не знайде:
|
||||
|
||||
```python title="server.py" hl_lines="11-12"
|
||||
```python title="server.py" hl_lines="2 12-13"
|
||||
--8<-- "docs_src/handling_errors/tutorial001.py"
|
||||
```
|
||||
|
||||
У цих двох рядках немає нічого специфічного для MCP. `get_author` викидає звичайний `ValueError`, як це зробила б будь-яка функція Python.
|
||||
`ToolError` з `mcp.server.mcpserver.exceptions` — це спосіб, яким інструмент повідомляє моделі, що щось пішло не так.
|
||||
|
||||
Викличте його з назвою, якої немає в каталозі, і подивіться на результат:
|
||||
|
||||
|
|
@ -30,13 +30,15 @@ result.structured_content # None
|
|||
```
|
||||
|
||||
* Запит **виконався успішно**. Результат є; на боці того, хто викликав, нічого не викинуто.
|
||||
* `is_error` дорівнює `True`, а повідомлення вашого винятку (з назвою інструмента на початку) лежить у `content` — саме там, де читає модель.
|
||||
* `is_error` дорівнює `True`, а ваше повідомлення (з назвою інструмента на початку) лежить у `content` — саме там, де читає модель.
|
||||
* `structured_content` дорівнює `None`. У невдалого виклику немає значення, яке можна було б структурувати.
|
||||
|
||||
Це **помилка інструмента**, і так за замовчуванням обробляється *будь-який* виняток, який викидає ваш інструмент. І майже завжди це саме те, що потрібно.
|
||||
Це **помилка інструмента**, і майже завжди це саме те, що потрібно.
|
||||
|
||||
Ваш інструмент викликає саме модель. Це вона обрала аргументи. Тож помилка інструмента — це репліка в розмові: модель читає *«No book titled 'Nothing' in the catalog.»*, розуміє, що не вгадала назву, і викликає знову з кращою. Один `raise` — і маєте агента, що сам виправляє свої помилки.
|
||||
|
||||
На сервері `ToolError` — це один рядок рівня `INFO` у лозі, без трасування. Ви цього очікували, тож розслідувати нічого.
|
||||
|
||||
!!! tip
|
||||
Ніколи не повертайте повідомлення про помилку з інструмента через `return`. Повернутий рядок має `is_error=False`, тож для
|
||||
моделі (і для кожного клієнтського інтерфейсу) це виглядає так, ніби інструмент спрацював і цей рядок і є відповіддю.
|
||||
|
|
@ -44,7 +46,7 @@ result.structured_content # None
|
|||
|
||||
## Помилка, яку модель не може виправити {#an-error-the-model-cannot-fix}
|
||||
|
||||
Тепер замініть `ValueError` на `MCPError`.
|
||||
Тепер замініть `ToolError` на `MCPError`.
|
||||
|
||||
```python title="server.py" hl_lines="1 3 14"
|
||||
--8<-- "docs_src/handling_errors/tutorial002.py"
|
||||
|
|
@ -77,10 +79,10 @@ result.structured_content # None
|
|||
|
||||
Ці два шляхи відповідають на два різні запитання.
|
||||
|
||||
* **Викидайте будь-який виняток** у разі збою *виконання*: те, що інструмент намагався зробити, не вдалося. Виклик обрала модель, тож саме модель має побачити наслідок і отримати шанс виправитися. Назва з помилкою, зовнішній API, що не відповів вчасно, рядок, якого не існує, — усе це помилки інструмента.
|
||||
* **Викидайте `ToolError`** у разі збою *виконання*: те, що інструмент намагався зробити, не вдалося. Виклик обрала модель, тож саме модель має побачити наслідок і отримати шанс виправитися. Назва з помилкою, зовнішній API, що не відповів вчасно, рядок, якого не існує, — усе це помилки інструмента.
|
||||
* **Викидайте `MCPError`**, коли слід відхилити *сам запит*: клієнтові бракує можливості, від якої залежить інструмент, сервер не в тому стані, щоб обслуговувати будь-кого, той, хто викликає, пропустив обов'язковий крок. Жодна повторна спроба моделі нічого з цього не виправить, тож передавати їй повідомлення немає сенсу.
|
||||
|
||||
Вирішує одне запитання: **чи могла б розумніша модель цього уникнути?** Так -> звичайний виняток. Ні -> `MCPError`.
|
||||
Вирішує одне запитання: **чи могла б розумніша модель цього уникнути?** Так -> `ToolError`. Ні -> `MCPError`.
|
||||
|
||||
За цим критерієм друга версія `get_author` зробила хибний вибір: краща назва все виправляє, тож модель заслуговувала побачити повідомлення. Ця версія тут, щоб показати механізм, а не щоб його рекомендувати.
|
||||
|
||||
|
|
@ -89,6 +91,25 @@ result.structured_content # None
|
|||
корисне навантаження `data`. Усе, що ви в них покладете, клієнт і отримає: SDK пересилає викинутий
|
||||
`MCPError` дослівно, не очищуючи його.
|
||||
|
||||
## Будь-який інший виняток {#any-other-exception}
|
||||
|
||||
Тепер приберіть перевірку й дайте пошуку в словнику завершитися невдачею самому по собі:
|
||||
|
||||
```python title="server.py" hl_lines="11"
|
||||
--8<-- "docs_src/handling_errors/tutorial004.py"
|
||||
```
|
||||
|
||||
`CATALOG[title]` викидає `KeyError`. Ви його не передбачали, тож SDK вважає це збоєм:
|
||||
|
||||
```python
|
||||
result.is_error # True
|
||||
result.content # [TextContent(text="Error executing tool get_author")]
|
||||
```
|
||||
|
||||
Виклик усе одно повертає `is_error=True`, тож модель знає, що він не вдався, і може рухатися далі. Чого вона не отримує — то це тексту винятку: `KeyError` з вашого коду чи купа SQL від драйвера на три бібліотеки глибше можуть описувати нутрощі вашого сервера, тому за його межі вони ніколи не виходять.
|
||||
|
||||
Натомість їх отримуєте ви. Сервер записує збій у лог на рівні `ERROR` з повним трасуванням як `Tool 'get_author' raised an unexpected exception`. Тож лог робочого середовища на рівні `WARNING` мовчить під час кожної `ToolError` і озивається, щойно щось справді зламалося.
|
||||
|
||||
## Ресурс, якого не існує {#a-resource-that-doesnt-exist}
|
||||
|
||||
Ресурси проводять ту саму межу й мають один іменований виняток для типового випадку.
|
||||
|
|
@ -109,7 +130,7 @@ result.structured_content # None
|
|||
}
|
||||
```
|
||||
|
||||
Зверніть увагу: тут немає напіврезультату з `is_error=True`. Читання ресурсу або повертає вміст, або завершується помилкою: у ресурсів є лише протокольний шлях. Про шаблони й усе інше, що стосується ресурсів, — на сторінці **[Ресурси](resources.md)**.
|
||||
Зверніть увагу: тут немає напіврезультату з `is_error=True`. Читання ресурсу або повертає вміст, або завершується помилкою: у ресурсів є лише протокольний шлях. `ResourceError` — те саме для збою, який не є «не знайдено» (`-32603`, ваше повідомлення), і обидва — це один рядок рівня `INFO` у вашому лозі. Будь-який інший виняток, окрім `MCPError`, — це збій: клієнт отримує `-32603`, де вказано лише URI, а трасування йде у ваш лог на рівні `ERROR`. Про шаблони й усе інше, що стосується ресурсів, — на сторінці **[Ресурси](resources.md)**.
|
||||
|
||||
## Помилки, які ви ніколи не викидаєте {#errors-you-never-raise}
|
||||
|
||||
|
|
@ -120,19 +141,21 @@ result.structured_content # None
|
|||
Це означає цілий клас інструкцій `raise`, які писати не треба: не перевіряйте повторно власні анотації типів.
|
||||
|
||||
!!! info
|
||||
Усе на цій сторінці — те, що бачить **клієнт**, і `Client` у пам'яті, з яким ви писатимете
|
||||
тести, бачить рівно те саме. Навіть `raise_exceptions=True` не перетворює помилку інструмента
|
||||
назад на трасування: до моменту, коли цей прапорець міг би спрацювати, ваш виняток уже став
|
||||
результатом з `is_error=True`. Перевіряйте результат через assert. Цей підхід описано на сторінці **[Тестування](../get-started/testing.md)**.
|
||||
Усе, що на цій сторінці бачить **клієнт**, бачить і `Client` у пам'яті, з яким ви писатимете
|
||||
тести. Навіть `raise_exceptions=True` не повертає виняток інструмента, що впав,
|
||||
тому, хто викликав: до моменту, коли цей прапорець міг би спрацювати, ваш виняток уже став
|
||||
результатом з `is_error=True`. Перевіряйте результат через assert. Якщо потрібне трасування збою, воно
|
||||
в лозі сервера, і `caplog` з pytest його перехоплює. Цей підхід описано на сторінці **[Тестування](../get-started/testing.md)**.
|
||||
|
||||
## Підсумки {#recap}
|
||||
|
||||
* Викиньте **будь-який виняток** в інструменті -> виклик повертає `is_error=True` з вашим повідомленням у `content`. Модель читає його й може повторити спробу. Це поведінка за замовчуванням.
|
||||
* Викиньте **`ToolError`** в інструменті -> виклик повертає `is_error=True` з вашим повідомленням у `content`. Модель читає його й може повторити спробу.
|
||||
* Викиньте **`MCPError`** -> сам виклик завершується помилкою JSON-RPC. Модель нічого не бачить; розбирається хост. `code`, `message` і `data` доходять без змін.
|
||||
* Вирішальне запитання: *чи могла б розумніша модель цього уникнути?* Так -> виняток. Ні -> `MCPError`.
|
||||
* Вирішальне запитання: *чи могла б розумніша модель цього уникнути?* Так -> `ToolError`. Ні -> `MCPError`.
|
||||
* Будь-який **інший виняток** — це збій -> `is_error=True` лише з `Error executing tool <name>` для моделі та запис рівня `ERROR` із трасуванням для вас.
|
||||
* `ResourceNotFoundError` з обробника ресурсу -> протокольний `-32602` з URI в `data`.
|
||||
* Некоректні аргументи відхиляються за схемою ще до запуску вашої функції; для них `raise` не потрібен.
|
||||
* `from mcp import MCPError`; константи кодів помилок — з `mcp.types`.
|
||||
* Імпорти: `from mcp import MCPError`, `from mcp.server.mcpserver.exceptions import ToolError, ResourceError, ResourceNotFoundError` і константи кодів помилок з `mcp.types`.
|
||||
|
||||
З помилками розібралися. Це все, що сервер *надає назовні*. Про те, що кожен обробник може читати і що робити у відповідь клієнтові під час роботи, — наступний розділ: **[Усередині обробника](../handlers/index.md)**.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
translation:
|
||||
sections: [496394d24d221bf1, 4ceb4591180dc6c3, 0fd63e4682d02e0c, 969ede0bd3686a16, 043f526230dd243d, 6ee3e9bcfd24047a]
|
||||
sections: [496394d24d221bf1, 4ceb4591180dc6c3, 0fd63e4682d02e0c, 969ede0bd3686a16, 864137b5e9c61e91, 043f526230dd243d, db1ef91db7d6b3f3]
|
||||
tool: 1
|
||||
---
|
||||
# Медіа {#media}
|
||||
|
|
@ -86,6 +86,24 @@ result.structured_content # None
|
|||
так `Audio` з байтів MP3 — і клієнту повідомлять `mime_type="audio/wav"`, після чого
|
||||
він сумлінно не зможе це декодувати. Передаєте `data=` — передавайте й `format=`.
|
||||
|
||||
## Вбудовування ресурсу {#embedding-a-resource}
|
||||
|
||||
Інструмент може повернути й документ: текст або байти разом з URI, за яким він доступний, і MIME-типом. Це **`EmbeddedResource`**, ще один різновид блока вмісту. На відміну від звичайного `str`, він повідомляє клієнту, що саме це за вміст, тож клієнт може показати його як вкладення або впізнати ресурс, який уже знає.
|
||||
|
||||
```python title="server.py" hl_lines="7 14 16-18"
|
||||
--8<-- "docs_src/media/tutorial005.py"
|
||||
```
|
||||
|
||||
* `brand://guidelines` — звичайний ресурс (про них — на сторінці **[Ресурси](resources.md)**). Інструмент на запит передає моделі той самий документ, а прямий виклик `guidelines()` зберігає єдине джерело істини.
|
||||
* `EmbeddedResource` і `TextResourceContents` беруться з `mcp.types`. Допоміжного класу, як для зображень, немає: побудований блок потрапляє в результат без змін, і `structured_content` теж немає.
|
||||
* Використовуйте URI, під яким ресурс зареєстровано, щоб клієнт міг зрозуміти, що вкладення й `brand://guidelines` — той самий документ. Дозволений будь-який URI, зареєстрований чи ні.
|
||||
|
||||
```python
|
||||
result.content # [EmbeddedResource(type="resource", resource=TextResourceContents(uri="brand://guidelines", mime_type="text/markdown", text="# Brand guidelines\n\n..."))]
|
||||
```
|
||||
|
||||
Для двійкового вмісту замість `TextResourceContents` використовуйте `BlobResourceContents(uri=..., mime_type=..., blob=...)` з байтами в кодуванні base64 у полі `blob`. Щоб надіслати лише вказівник, за яким клієнт зможе пізніше виконати `resources/read`, поверніть натомість `ResourceLink(name=..., uri=...)` — це теж блок вмісту.
|
||||
|
||||
## Іконки {#icons}
|
||||
|
||||
`Icon` — це метадані, а не вміст. Він не містить зображення, а вказує на нього через URI, і клієнт може завантажити його й показати поруч із назвою сервера, інструментом, ресурсом чи промптом.
|
||||
|
|
@ -115,6 +133,7 @@ client.server_info.icons # [Icon(src="https://example.com/brand-kit.png", mime_
|
|||
|
||||
* Поверніть з інструмента `Image` або `Audio` — і клієнт отримає блок `ImageContent` / `AudioContent`: ваші байти в кодуванні base64 з MIME-типом.
|
||||
* Створюйте їх із `path=`, і тоді MIME-тип визначить розширення, або з `data=` у пам'яті плюс явний `format=`.
|
||||
* Поверніть `EmbeddedResource`, щоб покласти в результат документ (текст або blob у base64 з його URI та MIME-типом), або `ResourceLink`, щоб надіслати лише вказівник.
|
||||
* Медіарезультати не мають ні `structured_content`, ні схеми виводу.
|
||||
* `Icon` — це вказівник: URI `src` плюс необов'язкові `mime_type`, `sizes` і `theme`.
|
||||
* `icons=[...]` працює на сервері, інструментах, ресурсах і промптах, а клієнти знаходять їх у відповідних об'єктах.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
translation:
|
||||
sections: [d65c098f37f5b6c3, dd0c2724d6f2877e, 6835bb3570c6714c, ffe823cb0fedd488, f33651add1b59094]
|
||||
sections: [d65c098f37f5b6c3, dd0c2724d6f2877e, 6835bb3570c6714c, d30d3c20168b88b2, f5ef38dad59d6f76, 6e38a699ba57fbdf, 2b984a3bf37a0ddd]
|
||||
tool: 1
|
||||
---
|
||||
# Промпти {#prompts}
|
||||
|
|
@ -139,10 +139,55 @@ uv run mcp dev server.py
|
|||
```
|
||||
|
||||
!!! info
|
||||
Якщо ви читали сторінку **[Інструменти](tools.md)**, то вже знаєте все, що є на цій. Той самий декоратор, той самий
|
||||
Якщо ви читали сторінку **[Інструменти](tools.md)**, то вже знаєте все, про що йшлося досі. Той самий декоратор, той самий
|
||||
docstring як опис, ті самі `Annotated`/`Field`. Змінюється лише те, хто
|
||||
його запускає (користувач) і куди йде результат (у розмову).
|
||||
|
||||
## Більше ніж текст {#more-than-text}
|
||||
|
||||
`UserMessage` і `AssistantMessage` також приймають блок вмісту або допоміжний об'єкт `Image` / `Audio` всюди, де приймають `str`. У промптах трапляються два випадки: прикріпити документ і прикріпити зображення.
|
||||
|
||||
### Вбудовування файлу {#embedding-a-file}
|
||||
|
||||
```python title="server.py" hl_lines="5 12 21 23"
|
||||
--8<-- "docs_src/prompts/tutorial004.py"
|
||||
```
|
||||
|
||||
* Посібник зі стилю — це ресурс за адресою `style://python` (про них — на сторінці **[Ресурси](resources.md)**), який читається з файлу `style-guide.md` поруч із `server.py`. Покладіть туди будь-який Markdown-файл.
|
||||
* `EmbeddedResource(resource=TextResourceContents(...))`, обидва з `mcp.types`, несе файл разом із його URI та MIME-типом як перше повідомлення; запит, що на нього посилається, іде слідом як звичайний текст.
|
||||
* Вбудовування замість вставлення посібника в f-рядок дає клієнту змогу показати його як вкладення й пізніше знову відкрити `style://python`, а модель отримує файл дослівно. Для двійкового файлу використовуйте `BlobResourceContents` із `blob` у base64.
|
||||
|
||||
Після генерування `content` першого повідомлення — це блок `resource`:
|
||||
|
||||
```json
|
||||
{"type": "resource", "resource": {"uri": "style://python", "mimeType": "text/markdown", "text": "* Prefer early returns.\n..."}}
|
||||
```
|
||||
|
||||
### Прикріплення зображення {#attaching-an-image}
|
||||
|
||||
```python title="server.py" hl_lines="4 15"
|
||||
--8<-- "docs_src/prompts/tutorial005.py"
|
||||
```
|
||||
|
||||
* `Image` — допоміжний клас зі сторінки **[Зображення, аудіо та піктограми](media.md)**. `UserMessage` перетворює його на блок `ImageContent` (файл закодовано в base64, MIME-тип вгадано з `.png`), коли промпт генерується; `Audio` так само стає `AudioContent`.
|
||||
* Покладіть будь-який PNG з іменем `architecture.png` поруч із `server.py`. Аргументи промпту — рядки, тому зображення завжди надходить із сервера; `component` лише дає слова.
|
||||
|
||||
```json
|
||||
{"type": "image", "data": "iVBORw0KGgoAAAANSUhEUg...", "mimeType": "image/png"}
|
||||
```
|
||||
|
||||
## Зміна списку під час роботи {#changing-the-list-at-runtime}
|
||||
|
||||
Промпти можна додавати, поки клієнти під'єднані, наприклад щоб користувач міг зберегти інструкцію як власний пункт меню. Зареєструйте промпт, а тоді надішліть сповіщення:
|
||||
|
||||
```python title="server.py" hl_lines="5 23-27"
|
||||
--8<-- "docs_src/prompts/tutorial006.py"
|
||||
```
|
||||
|
||||
* `mcp.add_prompt(Prompt.from_function(fn, name=..., description=...))` реєструє функцію точно так, як це зробив би `@mcp.prompt()`, а `mcp.remove_prompt(name)` — зворотна дія. `add_prompt` залишає наявний запис із тим самим іменем, а не перезаписує його, тому інструмент спершу видаляє старий, щоб збереження працювало як заміна. `prompts/list` відображає зміну одразу.
|
||||
* `await ctx.notify_prompts_changed()` надсилає `notifications/prompts/list_changed` кожному клієнту `2026-07-28`, що слухає потік `subscriptions/listen` (**[Підписки](../handlers/subscriptions.md)**). `await ctx.session.send_prompt_list_changed()` надсилає його клієнту, який зробив виклик, якщо той старший за 2026 (**[Обслуговування клієнтів старого покоління](../run/legacy-clients.md)**). Викликайте обидва; кожен нічого не робить, коли сповіщати нікого.
|
||||
* Клієнт, що отримав сповіщення, знову викликає `prompts/list`. У Python-класі `Client` це `async with client.listen(prompts_list_changed=True) as sub:`, що видає подію `PromptsListChanged`.
|
||||
|
||||
## Підсумки {#recap}
|
||||
|
||||
* `@mcp.prompt()` над функцією робить її промптом. Ім'я — з функції, опис — з docstring.
|
||||
|
|
@ -151,5 +196,7 @@ uv run mcp dev server.py
|
|||
* Поверніть `str` — і він стане одним повідомленням користувача. Поверніть список `UserMessage` / `AssistantMessage`, щоб закласти багатоходову розмову.
|
||||
* `title=` і `Field(description=...)` — це те, що клієнт показує у своєму інтерфейсі.
|
||||
* Відсутній обов'язковий аргумент провалює весь запит. Окремого результату з помилкою для промпту немає.
|
||||
* Загорніть `EmbeddedResource` або `Image` у `UserMessage`, щоб прикріпити документ чи зображення.
|
||||
* Додавайте або видаляйте промпти під час роботи через `mcp.add_prompt(...)` / `mcp.remove_prompt(...)`, а тоді викликайте `await ctx.notify_prompts_changed()` і `await ctx.session.send_prompt_list_changed()`.
|
||||
|
||||
Серверне автодоповнення аргументів промпту (або шаблону ресурсу) — це **[Автодоповнення](completions.md)**.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
translation:
|
||||
sections: [a838d57f003aed44, 857d03886a0137ed, 42d9efcb9f542867, 2290ff08435b5573, e866c192e11d1c14, 6cdbad079f7b47f0, d4b607372fb28b51, 18dbf726ac45e0b7, c6f7d2a148aa49f4, c851964bb3301907, d715db6f8dccc9cc, ef86634aa70498a7]
|
||||
sections: [a838d57f003aed44, 857d03886a0137ed, 42d9efcb9f542867, 2290ff08435b5573, 91be9b73602abcf1, 6cdbad079f7b47f0, d4b607372fb28b51, 18dbf726ac45e0b7, c7eff2a5698225fa, c851964bb3301907, 8f296f1f09e4c400, d715db6f8dccc9cc, a0c344a48450dbe4]
|
||||
tool: 1
|
||||
---
|
||||
# Структурований вивід {#structured-output}
|
||||
|
|
@ -105,7 +105,7 @@ result.structured_content # {"temperature": 16.2, "humidity": 0.83, "conditions
|
|||
--8<-- "docs_src/structured_output/tutorial003.py"
|
||||
```
|
||||
|
||||
Під час виконання `TypedDict` — це звичайний `dict`, тож саме його ви будуєте й повертаєте. Схема, валідація і `structured_content` ідентичні версії з `BaseModel` (за винятком описів, для яких у `TypedDict` немає місця).
|
||||
Під час виконання `TypedDict` — це звичайний `dict`, тож саме його ви будуєте й повертаєте. Схема, валідація і `structured_content` підпорядковуються тим самим правилам, що й у версії з `BaseModel`: додайте docstring класу або `Annotated[..., Field(description=...)]` — і вони стануть описами, а ключ `NotRequired`, якого немає в словнику, не потрапить і до `structured_content`.
|
||||
|
||||
## Dataclass {#a-dataclass}
|
||||
|
||||
|
|
@ -187,18 +187,19 @@ result.structured_content # {"London": 16.2, "Reykjavik": 4.4}
|
|||
Анотація обіцяє `WeatherData`. Відповідь зовнішнього сервісу перестала надсилати `humidity`.
|
||||
|
||||
!!! check
|
||||
Викличте `get_weather` — і він не передасть клієнту тихцем напівпорожній об'єкт. Виклик завершується помилкою,
|
||||
і перші рядки помилки називають поле:
|
||||
Викличте `get_weather` — і він не передасть клієнту тихцем напівпорожній об'єкт. Виклик завершується помилкою:
|
||||
клієнт отримує `is_error=True` з `Error executing tool get_weather`, тож модель знає, що виклик
|
||||
не вдався, замість того щоб упевнено читати погоду, якої немає. Назва поля — для вас,
|
||||
у лозі сервера на рівні `ERROR`:
|
||||
|
||||
```text
|
||||
Error executing tool get_weather: 1 validation error for WeatherData
|
||||
Tool 'get_weather' raised an unexpected exception
|
||||
...
|
||||
pydantic_core._pydantic_core.ValidationError: 1 validation error for WeatherData
|
||||
humidity
|
||||
Field required [type=missing, input_value={'temperature': 16.2, 'conditions': 'Overcast'}, input_type=dict]
|
||||
```
|
||||
|
||||
Цей текст повертається як результат інструмента з `is_error=True`, тож модель знає, що виклик не вдався,
|
||||
замість того щоб упевнено читати погоду, якої немає.
|
||||
|
||||
До речі, повертати звичайний `dict` з інструмента з `-> WeatherData` цілком можна. Саме це й видав `json.loads`. Перевіряється значення, а не тип Python.
|
||||
|
||||
## Відмова від структурованого виводу {#opting-out}
|
||||
|
|
@ -213,6 +214,10 @@ result.structured_content # {"London": 16.2, "Reykjavik": 4.4}
|
|||
|
||||
Протилежне, `structured_output=True`, перетворює автоматичне визначення на вимогу: інструмент, чий тип повернення не може дати схему, викидає виняток під час імпорту замість відкоту до тексту.
|
||||
|
||||
## Блоки вмісту й медіа {#content-blocks-and-media}
|
||||
|
||||
Блоки вмісту й медіа (`TextContent`, `EmbeddedResource`, `Image`, `Audio` та подібні — самі по собі, як елементи `list`, `tuple` чи `Sequence` або як варіанти об'єднання типів) вимкнено за вас: вони призначені для читання моделлю, тож автоматичне визначення не виводить із них схеми (`Image` та `Audio` описано на сторінці **[Зображення, аудіо та іконки](media.md)**). `structured_output=True` усе одно примусово створює схему для класів блоків вмісту.
|
||||
|
||||
## Клас без анотацій типів {#a-class-without-type-hints}
|
||||
|
||||
Є один спосіб опинитися без структурованого виводу, не просивши про це: повернути клас, у **тілі якого немає анотацій**.
|
||||
|
|
@ -245,6 +250,6 @@ result.structured_content # {"London": 16.2, "Reykjavik": 4.4}
|
|||
* Скаляри, списки, кортежі й об'єднання типів загортаються в `{"result": ...}`. Моделі, `TypedDict`, dataclass, анотовані класи й `dict[str, ...]` — уже об'єкти й лишаються як є.
|
||||
* Кожен результат несе `content` (текст, для моделі) **і** `structured_content` (дані, для застосунку).
|
||||
* Те, що ви повертаєте, перевіряється на відповідність схемі. Невідповідність — це помилка інструмента, а не зіпсований результат.
|
||||
* `structured_output=False` вимикає це для інструмента. Клас без анотацій типів вимикає це мовчки; пильнуйте.
|
||||
* `structured_output=False` вимикає це для інструмента. Блоки вмісту, `Image` та `Audio` вимкнено за замовчуванням; клас без анотацій типів вимикає це мовчки, тож пильнуйте.
|
||||
|
||||
Тепер ви володієте всім, що інструмент може сказати у відповідь. Далі — другий примітив: **[Ресурси](resources.md)**.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
translation:
|
||||
sections: [e4cc390d56573409, 8566e2b68594e9ad, 2c97b9f888398951, 048e5471dfa71aea, 3076b1e16ad95950, edbedf2a16e71311, 3d8ef8da89fa87c1, f6c0e02e6ea5a363]
|
||||
sections: [e4cc390d56573409, f30cf8103a6e918c, 2c97b9f888398951, 048e5471dfa71aea, 3076b1e16ad95950, edbedf2a16e71311, 3d8ef8da89fa87c1, f6c0e02e6ea5a363]
|
||||
tool: 1
|
||||
---
|
||||
# Інструменти {#tools}
|
||||
|
|
@ -39,6 +39,8 @@ translation:
|
|||
|
||||
Обидва аргументи потрапили в `required`, бо жоден не має типового значення. За мить ви це виправите. (Ключі `title` — артефакти Pydantic; контракт складають властивості, їхні типи та `required`.)
|
||||
|
||||
Ключа `$schema` теж немає: схему без нього MCP трактує як **JSON Schema 2020-12** — саме її й генерує Pydantic, тож вибирати нічого, доки не візьметеся писати схеми вручну для **[низькорівневого Server](../advanced/low-level-server.md#the-dialect-is-json-schema-2020-12)**.
|
||||
|
||||
!!! tip
|
||||
Анотації типів тут — не документація. Вони і є **контракт**. Якщо клієнт надішле `"limit": "ten"`,
|
||||
SDK відхилить запит ще до того, як ваша функція запуститься.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
translation:
|
||||
sections: [4a7033e1ed8ad602, 55dcbfff0c6271bf, 101ef9d14bf4ec46, 4b6c4a845438abc7, f98b46bafbee4acd]
|
||||
sections: [4a7033e1ed8ad602, 55dcbfff0c6271bf, 317f4256a650cab6, 4b6c4a845438abc7, f98b46bafbee4acd]
|
||||
tool: 1
|
||||
---
|
||||
# URI-шаблони та безпека шляхів {#uri-templates-and-path-safety}
|
||||
|
|
@ -170,7 +170,7 @@ SDK підтримує підмножину, відібрану для зіст
|
|||
`safe_join`, щоб розв'язати шлях і впевнитися, що він лишається всередині
|
||||
базового каталогу:
|
||||
|
||||
```python title="server.py" hl_lines="4 14"
|
||||
```python title="server.py" hl_lines="5 15"
|
||||
--8<-- "docs_src/uri_templates/tutorial002.py"
|
||||
```
|
||||
|
||||
|
|
@ -211,9 +211,10 @@ SDK підтримує підмножину, відібрану для зіст
|
|||
|
||||
!!! tip
|
||||
Якщо обробник не може виконати запит (файл не існує, ідентифікатор
|
||||
невідомий), викиньте виняток. SDK перетворить його на відповідь з
|
||||
помилкою. Про різницю між помилкою протоколу та помилкою інструмента —
|
||||
на сторінці **[Обробка помилок](handling-errors.md)**.
|
||||
невідомий), викиньте `ResourceNotFoundError`, як це робить `read_manual`
|
||||
вище. Клієнт отримає `-32602` з вашим повідомленням і URI. Неочікуваний
|
||||
виняток натомість стає загальною помилкою `-32603`. Див.
|
||||
**[Обробка помилок](handling-errors.md#a-resource-that-doesnt-exist)**.
|
||||
|
||||
## Ресурси на низькорівневому Server {#resources-on-the-low-level-server}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue