Руководства
RAG
Эмбеддинги, rerank и чат одним ключом. Модель отвечает по вашей базе, а не только по своим весам.
RAG (Retrieval-Augmented Generation) подставляет в контекст модели фрагменты вашей базы знаний. Ответ опирается на найденные документы: меньше выдумок, данные можно обновлять без дообучения.
Три эндпоинта AITUNNEL закрывают весь пайплайн:
- Эмбеддинги —
POST /embeddings: векторы документов и запроса - Ранжирование —
POST /rerank: точный скоринг кандидатов - Чат —
POST /chat/completions: ответ по найденному контексту
Как устроен пайплайн
Индекс
Режете документы на чанки и считаете эмбеддинги.
Поиск
Эмбеддинг запроса, ближайшие чанки по косинусу.
Rerank
По желанию: кросс-энкодер оставляет лучшие 3–5. Можно пропустить.
Генерация
Эти фрагменты уходят в чат как контекст.
Шаг 1. Индекс
Режьте документы на чанки и отправляйте их пакетом в /embeddings. Для прототипа векторы можно держать в памяти; в проде — в векторной базе.
curl https://api.aitunnel.ru/v1/embeddings \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-aitunnel-xxx" \
-d '{
"model": "text-embedding-3-small",
"input": [
"AITUNNEL — единый OpenAI-совместимый API к GPT, Claude, Gemini и другим.",
"RAG — Retrieval-Augmented Generation: модель отвечает по найденным фрагментам вашей базы.",
"Эмбеддинги превращают текст в вектор."
]
}'Векторная база
В проде храните эмбеддинги в Qdrant, pgvector, Weaviate, Pinecone и подобных. Пример выше — только иллюстрация.
Много однотипных чанков без картинок можно прогнать через Batch запросы, если у модели в каталоге есть batch.
Шаг 2. Поиск
Эмбеддинг запроса и косинус с каждым чанком. Сравнивайте косинусом, не евклидовым расстоянием.
import numpy as np
def cosine(a, b):
a, b = np.array(a), np.array(b)
return float(a @ b / (np.linalg.norm(a) * np.linalg.norm(b)))
def retrieve(query, documents, top_n=5):
query_vec = client.embeddings.create(
model="text-embedding-3-small",
input=query,
).data[0].embedding
scored = [
{"text": doc["text"], "score": cosine(query_vec, doc["embedding"])}
for doc in documents
]
scored.sort(key=lambda item: item["score"], reverse=True)
return scored[:top_n]
query = "Как RAG улучшает ответы модели?"
results = retrieve(query, documents, top_n=5)
for i, item in enumerate(results, 1):
print(f"{i}. {item['score']:.4f} {item['text']}")Та же модель
Тот же model, что при индексации. Смешивать модели нельзя: пространства векторов несовместимы.
Шаг 3. Rerank
Косинус по эмбеддингам быстрый и приблизительный. Rerank смотрит на пару «запрос + документ» и ставит более точную оценку. Имеет смысл, когда кандидатов много (например 20) и нужны лучшие 3.
В OpenAI SDK метода rerank нет — обычный HTTP. Подробнее — ранжирование.
curl https://api.aitunnel.ru/v1/rerank \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-aitunnel-xxx" \
-d '{
"model": "rerank-4-pro",
"query": "Как RAG улучшает ответы модели?",
"documents": [
"RAG — Retrieval-Augmented Generation: модель отвечает по найденным фрагментам вашей базы, а не только по своим весам.",
"Эмбеддинги превращают текст в вектор. Похожие по смыслу фрагменты оказываются рядом.",
"Rerank сравнивает запрос с каждым документом напрямую и ставит более точные оценки, чем косинус эмбеддингов.",
"AITUNNEL — единый OpenAI-совместимый API к GPT, Claude, Gemini и другим. Один ключ, оплата в рублях.",
"Векторные базы вроде Qdrant, pgvector и Weaviate хранят эмбеддинги и ищут ближайших соседей."
],
"top_n": 3
}'Картинки в rerank
Часть моделей принимает не только строки, но и объекты { "text", "image" }. image — URL или data-URI. Так можно ранжировать схемы и скриншоты, у которых смысл не в подписи. В каталоге AITUNNEL сейчас текстовые rerank-модели; мультимодальные вызываются слагом OpenRouter, если у модели во входе есть image.
Строки и объекты можно смешивать в одном documents.
Шаг 4. Ответ по контексту
Топ после rerank (или сразу после поиска) — в системный или пользовательский промпт. Просите цитировать источники и молчать, если контекста мало.
def generate(query, context_docs):
context = "\n\n".join(
f"[{i}] {item['document']['text']}"
for i, item in enumerate(context_docs, 1)
)
answer = client.chat.completions.create(
model="gpt-5-nano",
messages=[
{
"role": "system",
"content": (
"Отвечай только по контексту. Ссылайся на источники как [n]. "
"Если в контексте нет ответа — так и скажи."
),
},
{
"role": "user",
"content": f"Контекст:\n{context}\n\nВопрос: {query}",
},
],
)
return answer.choices[0].message.content
print(generate(query, ranked))Повторяющийся системный префикс («отвечай только по контексту, цитируй [n]») стоит держать стабильным — его подхватит кеш промпта.
Полный пример
Все четыре шага подряд. Эмбеддинги и чат — через OpenAI SDK, rerank — HTTP.
import numpy as np
import requests
from openai import OpenAI
client = OpenAI(
api_key="sk-aitunnel-xxx",
base_url="https://api.aitunnel.ru/v1/",
)
EMBED_MODEL = "text-embedding-3-small"
RERANK_MODEL = "rerank-4-pro"
CHAT_MODEL = "gpt-5-nano"
def cosine(a, b):
a, b = np.array(a), np.array(b)
return float(a @ b / (np.linalg.norm(a) * np.linalg.norm(b)))
def embed(texts):
return [
item.embedding
for item in client.embeddings.create(model=EMBED_MODEL, input=texts).data
]
def retrieve(query_vec, doc_vecs, top_n=5):
scored = [(i, cosine(query_vec, vec)) for i, vec in enumerate(doc_vecs)]
scored.sort(key=lambda item: item[1], reverse=True)
return scored[:top_n]
def rerank(query, documents, top_n=3):
response = requests.post(
"https://api.aitunnel.ru/v1/rerank",
headers={"Authorization": "Bearer sk-aitunnel-xxx"},
json={
"model": RERANK_MODEL,
"query": query,
"documents": documents,
"top_n": top_n,
},
)
response.raise_for_status()
return response.json()["results"]
def generate(query, context_docs):
context = "\n\n".join(f"[{i}] {text}" for i, text in enumerate(context_docs, 1))
answer = client.chat.completions.create(
model=CHAT_MODEL,
messages=[
{
"role": "system",
"content": (
"Отвечай только по контексту. Ссылайся на источники как [n]. "
"Если в контексте нет ответа — так и скажи."
),
},
{
"role": "user",
"content": f"Контекст:\n{context}\n\nВопрос: {query}",
},
],
)
return answer.choices[0].message.content
chunks = [
"AITUNNEL — единый OpenAI-совместимый API к GPT, Claude, Gemini и другим. Один ключ, оплата в рублях.",
"RAG — Retrieval-Augmented Generation: модель отвечает по найденным фрагментам вашей базы, а не только по своим весам.",
"Эмбеддинги превращают текст в вектор. Похожие по смыслу фрагменты оказываются рядом.",
"Rerank сравнивает запрос с каждым документом напрямую и ставит более точные оценки, чем косинус эмбеддингов.",
"Векторные базы вроде Qdrant, pgvector и Weaviate хранят эмбеддинги и ищут ближайших соседей.",
"Кеш промпта удешевляет повтор одного и того же префикса — например системные правила цитирования.",
"Имя модели без префикса провайдера берёт цену из каталога AITUNNEL. Слаг provider/model уходит в OpenRouter."
]
# 1. Индекс
doc_vecs = embed(chunks)
# 2. Поиск
query = "Как RAG улучшает ответы модели?"
matches = retrieve(embed([query])[0], doc_vecs, top_n=5)
retrieved = [chunks[i] for i, _ in matches]
# 3. Rerank
ranked = rerank(query, retrieved, top_n=3)
context = [item["document"]["text"] for item in ranked]
# 4. Ответ
print(generate(query, context))Когда нужен rerank
Rerank — отдельный запрос. Имеет смысл, когда:
- база большая, косинус возвращает шум
- важна точность (клиентский FAQ, юридические или медицинские тексты)
- вы берёте 10–20 кандидатов и оставляете 3–5
Можно обойтись без него, когда:
- база маленькая и косинус уже попадает
- важна минимальная задержка
- вы только собираете прототип
Нарезка документов
От нарезки зависит качество поиска не меньше, чем от модели:
- По абзацам и заголовкам — сохраняет смысл, хорошо для структурированных текстов
- Фиксированный размер с перекрытием — 200–500 токенов и ~50 токенов overlap, чтобы мысль не обрывалась на границе
- По смысловым границам — предложения, секции, разрывы глав
Размер чанка
Мелкие чанки (200–300 токенов) точнее попадают в запрос, но теряют окружение. Крупные (500–1000) держат контекст, но размывают сигнал. Подбирайте на своих данных.
Практика
- Одна и та же модель эмбеддингов для индекса и запроса.
- Пакет в
input, не по одному тексту. - Кешируйте векторы — повтор того же текста даёт тот же вектор.
- Сначала широко, потом rerank: 10–20 кандидатов косинусом, 3–5 после rerank.
- Метаданные в промпт: заголовок, раздел, URL рядом с текстом — так проще цитировать.
- Порог релевантности: после rerank отбрасывайте слабые документы, чтобы не путать модель.
- Имена моделей без префикса провайдера:
text-embedding-3-small,rerank-4-pro,gpt-5-nano. Слагprovider/model— OpenRouter.
Модели
Актуальные списки без ключа:
curl https://api.aitunnel.ru/public/aitunnel/models/embeddings
curl https://api.aitunnel.ru/public/aitunnel/models/rerank
curl https://api.aitunnel.ru/public/aitunnel/models/chatНа сайте: каталог. Фильтры по выходной модальности «Эмбеддинг» и «Ранжирование».
Смотрите также
- Эмбеддинги —
POST /embeddings - Ранжирование —
POST /rerank - Кеширование промпта
- Структурированный вывод — JSON по схеме из найденного контекста
- Выбор провайдера
- Batch запросы — массовая индексация