RU ▾
Получить API-ключ

Uncensored VideoSora 2 API: Использование текста без цензуры для видеопайплайнов

Учебное пособие по Agentic AI в LangChain с OpenAI-совместимым API

Это руководство по LangChain подключает LangChain к OpenAI-совместимому эндпоинту, привязывает два инструмента и запускает цикл агента, который можно читать построчно. Каждый фрагмент кода использует тот же base URL и имя модели uncensored.

Обновлено

Роботизированная рука, циклически перемещающаяся между кубами инструментов, иллюстрирует вызов инструментов агентом LangChain

Ключевые моменты

  • ChatOpenAI работает без изменений: установите base_url, api_key и model в значение uncensored.
  • API поддерживает вызов инструментов (tool calling), поэтому bind_tools и встроенный агент работают корректно.
  • Ограничьте количество итераций цикла и проверяйте аргументы инструментов; модели с открытыми весами иногда ошибаются.
  • Учитывайте лимит в 8 параллельных запросов для полного ключа при запуске агентов параллельно.

Что вам нужно перед началом

Вам нужен Python 3.10 или новее, API-ключ и несколько пакетов. Ключ берётся на странице API-ключ: войдите через Google или по email и паролю, затем получите пробный баланс $0,50 в один клик. Пробный ключ позволяет выполнять 2 параллельных запроса и 60 запросов в минуту, чего достаточно для всего, что описано в этой статье, если запускать одного агента за раз.

pip install langchain-openai langchain-core langgraph
export API_KEY="paste-your-key-here"

API предоставляет эндпоинты POST /v1/chat/completions и GET /v1/models. LangChain нужен только первый. Модель представляет собой единственную модель с открытыми весами, дообученную для ответов без отказов по контенту при законном использовании взрослыми пользователями; её запускает независимый провайдер, и это не официальный API какого-либо вендора моделей. В документации перечислены заголовки и лимиты, если вам нужно держать их под рукой во время работы с редактором.

Как компоненты соотносятся с API

Понимание того, что LangChain делает в сети, помогает. Сообщение HumanMessage становится сообщением с role user. Сообщение AIMessage становится assistant, и когда оно содержит вызовы инструментов, они появляются в сообщении ассистента как массив tool_calls. Сообщение ToolMessage становится role tool с соответствующим ID вызова. Системный промпт идет первым с role system.

Это отображение важно для отладки. Если агент ведет себя странно, выведите список сообщений перед каждым вызовом и прочитайте его как стенограмму. Обычно вы обнаружите проблему за минуту: пустой результат инструмента, ID вызова, который не совпадает, или история, которая стала намного длиннее, чем вы ожидали. Слово agentic может звучать как магия, но каждый шаг — это обычный запрос chat completion.

Шаг 1: Подключение ChatOpenAI

Вся интеграция состоит из трех аргументов. LangChain формирует стандартные запросы в стиле OpenAI, поэтому сервер не может различить их.

import os
from langchain_openai import ChatOpenAI

llm = ChatOpenAI(
    model="uncensored",
    base_url="https://api.uncensoredvideo.cc/v1",
    api_key=os.environ["API_KEY"],
    temperature=0.7,
    max_retries=2,
)

print(llm.invoke("Name three beats of a heist scene, one line each.").content)

Если на экране появятся три строки, подключение работает. Если вы получаете 401, проверьте ключ. Если 404, убедитесь, что URL заканчивается на /v1 и не содержит лишних путей. max_retries — это опция клиента, которая помогает при временных ответах 429 с заголовком Retry-After: 5.

Потоковая передача работает так же, как и с любой моделью OpenAI: вызовите llm.stream(...) и итерируйте. Последний чанк содержит данные об использовании, поэтому вы можете логировать количество токенов для каждого вызова.

Шаг 2: Определение инструментов

Агент — это цикл: модель читает разговор, либо отвечает, либо запрашивает инструмент, ваш код запускает инструмент, и результат возвращается модели. Инструменты ниже — это обычные функции Python. Они намеренно скучные, чтобы показать механику.

from langchain_core.tools import tool

@tool
def word_count(text: str) -> int:
    """Count the words in a piece of text."""
    return len(text.split())

@tool
def spoken_minutes(words: int, words_per_minute: int = 150) -> float:
    """Estimate how many minutes a script takes to read aloud."""
    return round(words / words_per_minute, 2)

tools = [word_count, spoken_minutes]

Документация (docstring) и аннотации типов становятся описанием инструмента и его схемой, которые видит модель. Пишите их так, будто объясняете коллеге. Размытая документация — самая частая причина, по которой агент выбирает неверный инструмент. Если вы пишете сценарии для видео, тот же подход масштабируется на инструменты для поиска списков кадров или проверки бюджета длины, о чём рассказывается на странице генератора сценариев Sora 2.

Шаг 3: Запуск цикла агента вручную

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

from langchain_core.messages import HumanMessage, ToolMessage

by_name = {t.name: t for t in tools}
llm_tools = llm.bind_tools(tools)

messages = [HumanMessage(
    "Here is a draft: 'The vault door hums. Nina counts to three, "
    "then cuts the power.' How many words is it, and how long is it read aloud?"
)]

for step in range(6):
    ai = llm_tools.invoke(messages)
    messages.append(ai)
    if not ai.tool_calls:
        break
    for call in ai.tool_calls:
        result = by_name[call["name"]].invoke(call["args"])
        messages.append(ToolMessage(content=str(result), tool_call_id=call["id"]))

print(messages[-1].content)

Важны три детали. Ограничение range(6) предотвращает бесконечный цикл запутанной модели и также ограничивает ваши расходы. tool_call_id должен совпадать с вызовом, на который он отвечает. И модель может запросить несколько инструментов в одном ответе, поэтому внутренний цикл обрабатывает список.

Под капотом запрос содержит массив tools, а ответ содержит tool_calls, с результатами, возвращаемыми как сообщения с role: "tool". Вы можете принудительно вызвать конкретный инструмент с помощью tool_choice, который LangChain предоставляет как аргумент для bind_tools.

Шаг 4: Использование предварительно созданного агента LangGraph

Как только ручной цикл станет понятен, предварительно созданный агент сэкономит строки кода. Он принимает ту же модель и те же инструменты.

import os
from langchain_openai import ChatOpenAI
from langgraph.prebuilt import create_react_agent

llm = ChatOpenAI(
    model="uncensored",
    base_url="https://api.uncensoredvideo.cc/v1",
    api_key=os.environ["API_KEY"],
)

agent = create_react_agent(llm, tools)
out = agent.invoke({"messages": [("user", "How long is a 450 word script read aloud?")]})
print(out["messages"][-1].content)

LangChain и LangGraph меняют свои API между релизами, и некоторые помощники (helpers) были переименованы или перемещены. Если импорт не срабатывает, проверьте текущую документацию LangChain для поиска эквивалентного конструктора агента. Часть подключения, ChatOpenAI с базовым URL, остаётся прежней.

Выбор между циклом и графом

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

Что бы вы ни выбрали, делайте каждый узел небольшим и поручайте ему одну задачу. Узел планирования, выводящий короткий нумерованный список в формате JSON, узел выполнения, запускающий инструменты, и узел написания, создающий финальный текст, легче тестировать, чем один гигантский промпт. Они также позволяют использовать разные температуры: низкую для планирования и использования инструментов, более высокую для этапа написания. Поскольку каждый узел — это обычный вызов chat completion, тот же ключ, URL и имя модели обслуживают их все, а стоимость — это просто сумма вызовов.

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

Память, тестирование и структурированный вывод

Приведённый выше цикл забывает всё при завершении скрипта. Самая простая память — это сам список сообщений: сохраняйте его между ходами пользователя, добавляйте каждый новый вопрос и обрезайте самые старые сообщения, когда список становится длинным. Для длительных сессий заменяйте старые сообщения кратким саммари, сгенерированным моделью. Поскольку ввод тарифицируется за токен, саммари на 500 токенов выигрывает у 20 000 токенов сырой истории как по стоимости, так и по фокусу.

Тестирование агентов в основном заключается в проверке наблюдаемого поведения. Установите temperature в 0 или передайте фиксированный seed, который API передаёт дальше, чтобы запуски были более воспроизводимыми. Затем напишите несколько вопросов, где вы точно знаете, какой инструмент должен быть вызван, и проверьте (assert) имена инструментов и аргументы в списке сообщений, а не точную формулировку ответа. Запускайте эти тесты после каждого изменения промпта или описания инструмента. Изменение docstring может повлиять на выбор инструмента моделью, и тест — самый дешёвый способ это заметить.

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

Структурированный вывод и JSON-режим

Агентам часто нужно возвращать данные, а не текст. API поддерживает JSON-режим через response_format: {"type":"json_object"}. В LangChain вы можете запросить его с помощью llm.bind(response_format={"type": "json_object"}). Укажите в промпте модели, что ответ должен быть JSON-объектом, и назовите ожидаемые ключи, затем распарсите строку и проверьте её (validate) с помощью Pydantic. Не надейтесь, что шаг парсинга всегда будет успешным; оберните его в повторную попытку (retry), которая отправит текст ошибки модели один раз.

Типичные проблемы и способы их решения

  • Плохие аргументы инструментов. Модель с открытыми весами иногда возвращает строку там, где вы объявили целое число. Проверяйте аргументы, возвращайте короткое сообщение об ошибке из инструмента и позволяйте модели исправиться самостоятельно на следующем шаге.
  • Бесконечные циклы. Всегда ограничивайте количество итераций. Логируйте каждый шаг во время разработки.
  • Параллельные агенты. Полный ключ позволяет 8 параллельных запросов. Если вы запускаете пакетную обработку, установите config={"max_concurrency": 4} в llm.batch, чтобы оставить запас. Превышение лимита возвращает 429 с кодом concurrency; превышение 300 запросов в минуту возвращает rate_limited.
  • Потоковая передача плюс инструменты. Потоковый запрос удерживает слот параллельных запросов до конца потока, поэтому не начинайте новые вызовы из обработчика потока на пробном ключе.
  • Растущая история. Каждый шаг повторно отправляет полный список сообщений. Контекстное окно составляет 100 000 токенов, но вы платите за ввод при каждом вызове.

Сколько стоит запуск агента

Возьмите скромного агента, который делает 4 вызова модели для завершения задачи, со средним промптом в 3 000 токенов и 200 токенов вывода на вызов. Ввод: 4 x 3 000 = 12 000 токенов, что составляет 12 000 x $0,15 / 1 000 000 = $0,0018. Вывод: 4 x 200 = 800 токенов, что составляет $0,0004. Один запуск стоит $0,0022, поэтому 1 000 запусков стоят $2,20. Пробный баланс в $0,50 покрывает около 227 таких запусков. Неудачные или отклонённые запросы не тарифицируются, а баланс не сгорает. См. цены для сумм пополнения и бонусов. Этот сайт принимает только криптовалюту: USDT в сети TRC20 или USDC в сети Base.

Вопросы и ответы

Нужна ли LangChain специальная интеграция для этого API?

Нет. Класс ChatOpenAI из langchain-openai принимает base_url, поэтому вы указываете его на API и используете модель uncensored. Пользовательская обертка не требуется.

Поддерживает ли API вызов инструментов (tool calling) для агентов?

Да. Он принимает инструменты и параметр tool_choice, возвращает tool_calls (в том числе при потоковой передаче) и принимает результаты обратно в виде сообщений с ролью tool.

Почему мой агент завершается с ошибкой 429?

Вы превысили либо лимит частоты запросов, либо лимит параллельных запросов. Полный ключ позволяет выполнять 8 параллельных запросов и 300 запросов в минуту. Уменьшите значение max_concurrency или дождитесь окончания периода Retry-After.

Могу ли я использовать LangGraph вместо обычного цикла?

Да. Предварительно созданный агент LangGraph принимает тот же объект ChatOpenAI и тот же список инструментов, поэтому код подключения не меняется.

До вашего ключа — всего одна форма

Создайте аккаунт, скопируйте ключ, измените базовый URL. Это вся настройка.

Получить API-ключ