Ход: одна просьба клиента
Ход — это единица работы сервиса. Внутри него ассистент читает разговор, пишет изменение заказа, собирает его геометрией и проверяет; не сошлось — переспрашивает себя до трёх раз. Поэтому ход идёт десятки секунд, иногда минуты, и отвечает сервис заявкой, а не результатом.
POST /v1/conversations/{id}/turns → 202 {turn:{id, state:"queued"}}
│
queued → running → done | stopped | failed
│
вебхук turn.completed / .stopped / .failed
Один ход на разговор
Пока идёт ход, второй запрос по тому же разговору отвечает 409 conflict_busy. Это не очередь и не защита от нагрузки: следующая просьба человека почти всегда зависит от того, чем кончилась предыдущая, и собирать их параллельно значило бы собирать по устаревшему заказу.
Если клиент дописал, пока ход идёт, — у вас два честных пути: дождаться исхода и послать дописанное следующим ходом либо оборвать текущий (POST /v1/turns/{id}/stop) и послать просьбу целиком заново. Остановленный ход денег не стоит.
Идемпотентность
Заголовок Idempotency-Key — ваш id сообщения:
Idempotency-Key: msg-991
Повтор с тем же ключом возвращает тот же ход. Без заголовка повтор — это второй ход и вторые деньги. Ключ уникален в пределах разговора.
Исход
{
"status": "built",
"note": "Собрал кухню 3 метра: тумба под мойку 800, посудомойка 600, ящики. Сверху четыре навесных шкафа.",
"applied": 9,
"turn": 1,
"order_id": "lead-7781",
"findings": [
{ "code": "GEO-GAP", "severity": "warning", "path": "низ.шкаф2",
"msg": "…", "fixHint": "…" }
],
"why": "Ряд не сошёлся: мебели на 925 мм больше, чем стены…",
"usage": { "model": "…", "attempts": 1, "prompt_tokens": 12400,
"completion_tokens": 900, "cost_usd": 0.0231, "ms": 41200, "coins": 1 }
}
| Поле | Что это |
|---|---|
status | Чем кончилось — таблица ниже |
note | Ответ клиенту, по-русски. Единственное, что ему показывают |
applied | Сколько узлов заказа изменилось. 0 — заказ не тронут |
turn | Номер хода в разговоре |
findings | Находки проверки: code, severity (error/warning), path, msg, fixHint?. Есть не всегда |
why | Почему не вышло — по-русски, одной фразой. Только у неудачных ходов |
usage | Чем обошёлся ход. coins — сколько монет он стоит |
Статусы
status | Что случилось | Что делать |
|---|---|---|
built | Мебель собрана, заказ изменился | Показать note и обновить картинку |
parsed | Приложенный чертёж прочитан вслух; мебель НЕ собрана | Показать note — это разбор. Клиент поправляет словами или говорит «собирай» |
question | Ассистент ответил словами: спросил недостающее или отказался | Показать note и ждать ответа клиента. Картинка остаётся прежней |
rejected | Просьба спорит с нормой производства; переспрашивать нечем | Показать note и why. Решение за человеком: другое число или другой состав |
failed | Ход не состоялся: модель не поняла, оборвалась связь | Клиенту ничего не показывать. Предложить сказать иначе |
question — нормальный исход, а не ошибка. Ассистенту велено спрашивать то, чего он не может знать: вдоль какой стены мебель, какой длины стена, какая техника встраивается, что мешает в комнате. Спрашивает он по одному-два пункта за ход, а не анкетой.
Наоборот, то, что решается правилами (наполнение тумбы, сторона открывания, высота полки), он не спрашивает, а собирает по умолчанию и называет это строкой в note.
Картинку не отменяет ни один статус. Вид принадлежит разговору, а не ходу: пока в разговоре что-то собрано, GET .../render отдаёт его после ЛЮБОГО хода. built значит «картинка изменилась, возьмите новую», а не «только теперь она есть»: после question и rejected в силе прежняя, и ссылку на неё можно выписать заново хоть тем же запросом.
Картинки
К просьбе прикладывается до двух картинок, каждая не больше 5 МБ:
{ "text": "собери по этому плану",
"images": [{ "mime": "image/png", "b64": "iVBORw0…" }] }
Форматы: png, jpeg, webp, gif.
Чертёж сперва читается вслух. На картинку с составом (модули вдоль стены, в порядке и с размерами) ассистент отвечает не мебелью, а разбором по-русски: что стоит у какой стены, в каком порядке, на скольких миллиметрах, какие числа он взял не с чертежа и чего не разобрал. Статус такого хода — parsed, заказ при этом не меняется. Клиент правит разбор словами («вторая стена 3400», «бутылочницы нет») или говорит «собирай» — и следующий ход собирает всё разом.
Двух картинок хватает по сроку хода: чертёж разбирает медленная модель, и ход с картинкой идёт три-пять минут.
Мелкая картинка (меньше 600 пикселей по короткой стороне) не отбивается, но читается плохо: швы в пару пикселей превращают семь створок в пять, и шкаф по такому разбору собирается без единой ошибки. Есть крупнее — шлите крупнее.
Картинка помнится ровно один следующий ход. Нужна снова — приложите снова.
Деньги
| Что | Сколько |
|---|---|
| Просьба словами | 1 монета |
| Просьба с картинкой | 8 монет |
question, rejected, failed, остановленный ход | 0 |
Списывается ход, который дал работу: built и parsed.
Единственное исключение — ход с картинкой: он списывает свои восемь монет всегда, когда ассистент ответил, даже если в заказе не изменилось ничего. Работа там сделана разбором чертежа, и делает её самая дорогая модель сервиса. Ход с картинкой, который не состоялся вовсе (модель не ответила, связь оборвалась), не списывается и здесь.
Число картинок на цену не влияет: дорог разбор, а не изображение.
Не хватает монет — 402 quota_exceeded ещё до вопроса модели. В details лежит reason: out_of_coins, period_over, canceled, no_subscription.
Почём монета и как она покупается объёмом — в pricing.md.
Сроки
Ход живёт до четырёх минут; не уложился — failed. Разговор живёт 30 дней с последней просьбы и продлевается каждым ходом.
Если сервис перезапустили посреди хода, ход придёт failed вебхуком, а разговор освободится — «вечно занятых» разговоров не бывает.
Эта страница разметкой: /docs/api/turns.md · вся документация одним файлом: /llms-full.txt