LangChain
Подключите LangChain к Mixen — оплата с баланса в рублях, каталог из 105+ моделей через привычный ChatOpenAI.
Установка
Заголовок раздела «Установка»pip install -U langchain-openaiКонфигурация
Заголовок раздела «Конфигурация»Mixen совместим с протоколом OpenAI — направьте ChatOpenAI на наш base URL и передайте ключ:
from langchain_openai import ChatOpenAI
llm = ChatOpenAI( model="openai/gpt-5.6-sol", base_url="https://api.mixen.ai/v1", api_key="ваш-api-ключ",)
print(llm.invoke("Что такое векторный поиск?").content)Стриминг — дельты приходят по мере генерации:
for chunk in llm.stream("Расскажи про RAG в трёх абзацах"): print(chunk.content, end="", flush=True)Ключевые моменты:
- В актуальных версиях
langchain-openaiпараметры называютсяbase_urlиapi_key;openai_api_baseиopenai_api_key— легаси-псевдонимы. - Вместо аргументов можно задать переменные окружения
OPENAI_BASE_URLиOPENAI_API_KEY— явные аргументы имеют приоритет. - Инструменты работают:
llm.bind_tools([...])— наш/v1/chat/completionsподдерживает tool calling.
Сценарий: цепочка с системным промптом и историей
Заголовок раздела «Сценарий: цепочка с системным промптом и историей»Одиночный invoke хорош для проверки ключа. Для продукта собирается LCEL-цепочка: шаблон промпта, модель и парсер вывода, соединённые оператором |:
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholderfrom langchain_core.output_parsers import StrOutputParserfrom langchain_core.messages import HumanMessage, AIMessagefrom langchain_openai import ChatOpenAI
llm = ChatOpenAI( model="anthropic/claude-sonnet-5", base_url="https://api.mixen.ai/v1", api_key="ваш-api-ключ",)
prompt = ChatPromptTemplate.from_messages([ ("system", "Ты — консультант магазина чая. Отвечай на русском, коротко: " "сорт, вкус, как заваривать. Не выдумывай позиции ассортимента."), MessagesPlaceholder("history"), ("human", "{input}"),])
chain = prompt | llm | StrOutputParser()
history: list = []
def ask(question: str) -> str: answer = chain.invoke({"history": history, "input": question}) history.extend([HumanMessage(content=question), AIMessage(content=answer)]) return answer
print(ask("Посоветуйте чай для вечернего чаепития"))print(ask("А есть такой же, но с меньшей терпкостью?")) # «такой же» — из historyЧто здесь важно:
StrOutputParserснимает обёрткуAIMessage— на выходе сразу строка.MessagesPlaceholder("history")вставляет список сообщений между системным промптом и вопросом; в примере история ведётся вручную, при желании замените её наRunnableWithMessageHistory.- Системный промпт первый и неизменный — это осознанно: стабильный префикс попадает в промпт-кэш (см. экономику).
Стриминг и async
Заголовок раздела «Стриминг и async»Тот же результат потоково — на цепочке целиком:
for chunk in chain.astream({"history": history, "input": question}): print(chunk, end="", flush=True)И полностью асинхронный вариант — для сервисов на asyncio:
import asyncio
async def main() -> None: answer = await chain.ainvoke({"history": history, "input": question}) print(answer)
asyncio.run(main())Стриминг у нас настоящий: токены уходят из модели по мере генерации, время до первого токена не зависит от длины ответа.
RAG на наших эмбеддингах
Заголовок раздела «RAG на наших эмбеддингах»Эмбеддинги и реранк поверх того же ключа — в отдельном гайде RAG: индекс на POST /v1/embeddings, поиск, LLM-as-judge для реранкинга и цитаты в ответе. Модель ответа там — тот же ChatOpenAI с нашим base_url.
Выбор модели и экономика
Заголовок раздела «Выбор модели и экономика»| Задача | Модель | Цена за 1M токенов |
|---|---|---|
| Массовые вызовы: классификация, извлечение, черновики | z-ai/glm-5.3-flash |
8.4 ₽ вход / 27.9 ₽ выход |
| Сложные задачи, длинный контекст, аккуратный код | anthropic/claude-sonnet-5 |
213.9 / 1069.5 ₽ |
Промпт-кэш — главный рычаг экономии в цепочках с историей: каждый следующий ход повторяет системный промпт и прошлые сообщения, и этот повторяющийся префикс считается по ~10% цены входа (у claude-sonnet-5 — 21.4 ₽ за 1M вместо 213.9 ₽, у glm-5.3-flash — 1.7 ₽). Кэш живёт 5 минут с продлением от каждого попадания, поэтому плотный диалог дешевеет ход за ходом. Правило то же, что в примере выше: неизменное — в начало промпта, новое — в конец.
Глубина рассуждения регулируется параметром reasoning_effort API (от off до max: off, low, medium, high, xhigh, max). Допустимые уровни конкретной модели — в GET /v1/models → capabilities.reasoning_efforts. Помните, что рассуждение расходует бюджет max_tokens: под длинный структурированный вывод задавайте потолок с запасом.
Рекомендуемые модели
Заголовок раздела «Рекомендуемые модели»| Модель | ID |
|---|---|
| GPT-5.6 Sol | openai/gpt-5.6-sol |
| Claude Opus 5 | anthropic/claude-opus-5 |
| GLM 5.3 | z-ai/glm-5.3 |
| DeepSeek V4 Pro | deepseek/deepseek-v4-pro |
| Kimi K3 | moonshotai/kimi-k3 |
Актуальный список — в каталоге.
Типичные проблемы
Заголовок раздела «Типичные проблемы»- 401 / Invalid API Key — ключ скопирован не целиком или это не API-ключ Mixen; выпустите новый в кабинете, он начинается с
mxn-. - «model not found» — ID модели передавайте посимвольно из каталога, включая префикс вендора (
openai/…,anthropic/…). - Запрос уходит не с тем ключом — если
api_keyне передан, SDK возьмёт переменную окруженияOPENAI_API_KEY(например, настоящий ключ OpenAI), и Mixen ответит 401. - Ответ обрезался на середине — у моделей с рассуждением подуманная часть расходует
max_tokens; поднимите потолок и проверьтеfinish_reason("length"означает обрезку, а не конец мысли). - Стриминг отдаёт всё разом — проверьте, что запрос идёт через
stream()/astream(), а неinvoke()с ручной нарезкой готовой строки.