Перейти к содержимому
EN

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, MessagesPlaceholder
from langchain_core.output_parsers import StrOutputParser
from langchain_core.messages import HumanMessage, AIMessage
from 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.
  • Системный промпт первый и неизменный — это осознанно: стабильный префикс попадает в промпт-кэш (см. экономику).

Тот же результат потоково — на цепочке целиком:

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: индекс на 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/modelscapabilities.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() с ручной нарезкой готовой строки.