Для получения чистого Markdown любой страницы добавьте .md к URL страницы.
Для полного индекса документации см. https://api.vega.chat/docs/llms.txt.
Для полного содержания документации см. https://api.vega.chat/docs/llms-full.txt.
Для интеграции AI‑клиентов (Claude Code, Cursor и др.) подключитесь к серверу MCP по адресу https://api.vega.chat/docs/_mcp/server.

RAG с эмбеддингами и переранжированием

MD версия

Retrieval‑Augmented Generation (RAG) привязывает ответы LLM к вашим собственным данным, извлекая релевантные документы перед генерацией ответа. Это предотвращает галлюцинации и поддерживает актуальность ответов без дообучения.

VEGA предоставляет все три строительных блока для RAG‑конвейера через единый API:

  1. Эмбеддинги — преобразуют документы и запросы в векторы для семантического поиска
  2. Переранжирование — переоценка полученных кандидатов для более высокой точности
  3. Chat Completions — генерируют окончательный ответ, используя извлечённый контекст

Как работает RAG

Типичный RAG‑конвейер включает следующие шаги:

  1. Индексирование — разбейте документы на фрагменты и сгенерируйте эмбеддинги для каждого фрагмента
  2. Извлечение — эмбеддите запрос пользователя и найдите наиболее похожие фрагменты документов
  3. Переранжирование (необязательно) — переоцените топ‑кандидаты с помощью кросс‑энкодера для лучшей точности
  4. Генерация — передайте топ‑документы в качестве контекста LLM для получения обоснованного ответа
User Query │ ▼ ┌──────────┐ ┌──────────────┐ ┌──────────┐ ┌──────────────┐ │ Embed │────▶│ Vector Search│────▶│ Rerank │────▶│ LLM Generate │ │ Query │ │ (Top N docs) │ │ (Top K) │ │ with Context │ └──────────┘ └──────────────┘ └──────────┘ └──────────────┘

Шаг 1: Индексирование ваших документов

Разбейте документы на фрагменты и сгенерируйте эмбеддинги для каждого фрагмента. Сохраните эмбеддинги в векторной базе данных (или в памяти для прототипирования).

python
import requests
import json

VEGA_API_KEY = "{{API_KEY_REF}}"

# Your documents, split into chunks
chunks = [
    "VEGA is a unified API gateway for LLMs. It aggregates models from multiple providers.",
    "RAG stands for Retrieval-Augmented Generation. It grounds LLM answers in external data.",
    "Embeddings convert text into numerical vectors that capture semantic meaning.",
    "Reranking uses a cross-encoder to re-score documents for a given query, improving precision.",
    "Vector databases like Pinecone, Weaviate, and Qdrant store embeddings for fast similarity search.",
    "Prompt caching can reduce costs by reusing previous computations for repeated prefixes.",
    "VEGA supports provider routing to control which providers serve your requests.",
]

# Generate embeddings for all chunks in one batch request
response = requests.post(
    "https://api.vega.chat/api/v1/embeddings",
    headers={
        "Authorization": f"Bearer {VEGA_API_KEY}",
        "Content-Type": "application/json",
    },
    json={
        "model": "{{MODEL}}",
        "input": chunks,
    },
)

data = response.json()
# Each item in data["data"] contains an "embedding" vector
# Store these alongside your chunks in a vector database
document_embeddings = [
    {"text": chunks[item["index"]], "embedding": item["embedding"]}
    for item in data["data"]
]

print(f"Indexed {len(document_embeddings)} chunks with {len(document_embeddings[0]['embedding'])}-dim embeddings")
typescript
const VEGA_API_KEY = '{{API_KEY_REF}}';

// Your documents, split into chunks
const chunks = [
  'VEGA is a unified API gateway for LLMs. It aggregates models from multiple providers.',
  'RAG stands for Retrieval-Augmented Generation. It grounds LLM answers in external data.',
  'Embeddings convert text into numerical vectors that capture semantic meaning.',
  'Reranking uses a cross-encoder to re-score documents for a given query, improving precision.',
  'Vector databases like Pinecone, Weaviate, and Qdrant store embeddings for fast similarity search.',
  'Prompt caching can reduce costs by reusing previous computations for repeated prefixes.',
  'VEGA supports provider routing to control which providers serve your requests.',
];

// Generate embeddings for all chunks in one batch request
const response = await fetch('https://api.vega.chat/api/v1/embeddings', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${VEGA_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    model: '{{MODEL}}',
    input: chunks,
  }),
});

const data = await response.json();
// Each item in data.data contains an "embedding" vector
// Store these alongside your chunks in a vector database
const documentEmbeddings = data.data.map((item: { index: number; embedding: number[] }) => ({
  text: chunks[item.index],
  embedding: item.embedding,
}));

console.log(`Indexed ${documentEmbeddings.length} chunks with ${documentEmbeddings[0].embedding.length}-dim embeddings`);
shell
curl https://api.vega.chat/api/v1/embeddings \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $VEGA_API_KEY" \
  -d '{
    "model": "{{MODEL}}",
    "input": [
      "VEGA is a unified API gateway for LLMs.",
      "RAG stands for Retrieval-Augmented Generation.",
      "Embeddings convert text into numerical vectors."
    ]
  }'

В продакшене используйте векторную базу данных (Pinecone, Weaviate, Qdrant, pgvector и др.) для эффективного хранения и поиска эмбеддингов. Подход с хранением в памяти показан лишь в иллюстративных целях.

Шаг 2: Извлечение релевантных документов

Когда пользователь задаёт вопрос, эмбеддите запрос и найдите наиболее похожие фрагменты документов, используя косинусное сходство.

python
import numpy as np

def cosine_similarity(a, b):
    return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))

def retrieve(query, document_embeddings, top_n=5):
    # Embed the query
    response = requests.post(
        "https://api.vega.chat/api/v1/embeddings",
        headers={
            "Authorization": f"Bearer {VEGA_API_KEY}",
            "Content-Type": "application/json",
        },
        json={
            "model": "{{MODEL}}",
            "input": query,
        },
    )

    query_embedding = np.array(response.json()["data"][0]["embedding"])

    # Score each document by cosine similarity
    scored = []
    for doc in document_embeddings:
        score = cosine_similarity(query_embedding, np.array(doc["embedding"]))
        scored.append({"text": doc["text"], "score": float(score)})

    # Return the top N most similar chunks
    scored.sort(key=lambda x: x["score"], reverse=True)
    return scored[:top_n]

query = "What is RAG and how does it work?"
results = retrieve(query, document_embeddings, top_n=5)

print("Retrieved documents:")
for i, r in enumerate(results):
    print(f"  {i+1}. (score: {r['score']:.4f}) {r['text']}")
typescript
function cosineSimilarity(a: number[], b: number[]): number {
  const dot = a.reduce((sum, val, i) => sum + val * b[i], 0);
  const magA = Math.sqrt(a.reduce((sum, val) => sum + val * val, 0));
  const magB = Math.sqrt(b.reduce((sum, val) => sum + val * val, 0));
  return dot / (magA * magB);
}

async function retrieve(
  query: string,
  documentEmbeddings: { text: string; embedding: number[] }[],
  topN = 5,
) {
  // Embed the query
  const response = await fetch('https://api.vega.chat/api/v1/embeddings', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${VEGA_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      model: '{{MODEL}}',
      input: query,
    }),
  });

  const data = await response.json();
  const queryEmbedding: number[] = data.data[0].embedding;

  // Score each document by cosine similarity
  const scored = documentEmbeddings.map((doc) => ({
    text: doc.text,
    score: cosineSimilarity(queryEmbedding, doc.embedding),
  }));

  // Return the top N most similar chunks
  scored.sort((a, b) => b.score - a.score);
  return scored.slice(0, topN);
}

const query = 'What is RAG and how does it work?';
const results = await retrieve(query, documentEmbeddings, 5);

console.log('Retrieved documents:');
results.forEach((r, i) => {
  console.log(`  ${i + 1}. (score: ${r.score.toFixed(4)}) ${r.text}`);
});

Шаг 3: Переранжирование для лучшей точности

Поиск по эмбеддингам быстрый, но приближённый. Модель переранжирования использует кросс‑энкодер, сравнивая каждый документ напрямую с запросом, что даёт более точные оценки релевантности. Это особенно ценно, когда вы извлекаете много кандидатов (например, 20) и хотите сузить их до лучших нескольких (например, 3).

python
def rerank(query, documents, top_n=3):
    response = requests.post(
        "https://api.vega.chat/api/v1/rerank",
        headers={
            "Authorization": f"Bearer {VEGA_API_KEY}",
            "Content-Type": "application/json",
        },
        json={
            "model": "{{RERANK_MODEL}}",
            "query": query,
            "documents": documents,
            "top_n": top_n,
        },
    )

    data = response.json()
    return data["results"]

# Use the texts from the retrieval step
retrieved_texts = [r["text"] for r in results]

reranked = rerank(query, retrieved_texts, top_n=3)

print("Reranked documents:")
for r in reranked:
    print(f"  Score: {r['relevance_score']:.4f} | {r['document']['text']}")
typescript
async function rerank(query: string, documents: string[], topN = 3) {
  const response = await fetch('https://api.vega.chat/api/v1/rerank', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${VEGA_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      model: '{{RERANK_MODEL}}',
      query,
      documents,
      top_n: topN,
    }),
  });

  const data = await response.json();
  return data.results;
}

// Use the texts from the retrieval step
const retrievedTexts = results.map((r) => r.text);

const reranked = await rerank(query, retrievedTexts, 3);

console.log('Reranked documents:');
for (const r of reranked) {
  console.log(`  Score: ${r.relevance_score.toFixed(4)} | ${r.document.text}`);
}
shell
curl https://api.vega.chat/api/v1/rerank \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $VEGA_API_KEY" \
  -d '{
    "model": "{{RERANK_MODEL}}",
    "query": "What is RAG and how does it work?",
    "documents": [
      "RAG stands for Retrieval-Augmented Generation. It grounds LLM answers in external data.",
      "Embeddings convert text into numerical vectors that capture semantic meaning.",
      "Reranking uses a cross-encoder to re-score documents for a given query, improving precision.",
      "VEGA is a unified API gateway for LLMs. It aggregates models from multiple providers.",
      "Vector databases like Pinecone, Weaviate, and Qdrant store embeddings for fast similarity search."
    ],
    "top_n": 3
  }'

Шаг 4: Генерация ответа с контекстом

Передайте топ‑документы в качестве контекста в чат‑модель. LLM сгенерирует обоснованный ответ, опираясь на извлечённую информацию.

python
def generate_answer(query, context_docs):
    # Build a context string from the reranked documents
    context = "\n\n".join(
        f"[{i+1}] {doc['document']['text']}"
        for i, doc in enumerate(context_docs)
    )

    response = requests.post(
        "https://api.vega.chat/api/v1/chat/completions",
        headers={
            "Authorization": f"Bearer {VEGA_API_KEY}",
            "Content-Type": "application/json",
        },
        json={
            "model": "{{CHAT_MODEL}}",
            "messages": [
                {
                    "role": "system",
                    "content": "Answer the user's question based on the provided context. "
                               "Cite the relevant source numbers in brackets. "
                               "If the context doesn't contain enough information, say so.",
                },
                {
                    "role": "user",
                    "content": f"Context:\n{context}\n\nQuestion: {query}",
                },
            ],
        },
    )

    return response.json()["choices"][0]["message"]["content"]

answer = generate_answer(query, reranked)
print(f"Question: {query}")
print(f"Answer: {answer}")
typescript
async function generateAnswer(
  query: string,
  contextDocs: { document: { text: string } }[],
) {
  // Build a context string from the reranked documents
  const context = contextDocs
    .map((doc, i) => `[${i + 1}] ${doc.document.text}`)
    .join('\n\n');

  const response = await fetch('https://api.vega.chat/api/v1/chat/completions', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${VEGA_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      model: '{{CHAT_MODEL}}',
      messages: [
        {
          role: 'system',
          content:
            "Answer the user's question based on the provided context. " +
            'Cite the relevant source numbers in brackets. ' +
            "If the context doesn't contain enough information, say so.",
        },
        {
          role: 'user',
          content: `Context:\n${context}\n\nQuestion: ${query}`,
        },
      ],
    }),
  });

  const data = await response.json();
  return data.choices[0].message.content;
}

const answer = await generateAnswer(query, reranked);
console.log(`Question: ${query}`);
console.log(`Answer: ${answer}`);

Полный пример

Ниже представлен полностью работающий конвейер RAG, объединяющий все четыре шага:

python
import requests
import numpy as np

VEGA_API_KEY = "{{API_KEY_REF}}"
EMBEDDING_MODEL = "{{EMBEDDING_MODEL}}"
RERANK_MODEL = "{{RERANK_MODEL}}"
CHAT_MODEL = "{{CHAT_MODEL}}"
BASE_URL = "https://api.vega.chat/api/v1"

def get_headers():
    return {
        "Authorization": f"Bearer {VEGA_API_KEY}",
        "Content-Type": "application/json",
    }

def embed(texts):
    """Generate embeddings for a list of texts."""
    response = requests.post(
        f"{BASE_URL}/embeddings",
        headers=get_headers(),
        json={"model": EMBEDDING_MODEL, "input": texts},
    )
    return [item["embedding"] for item in response.json()["data"]]

def cosine_similarity(a, b):
    return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))

def retrieve(query_embedding, doc_embeddings, top_n=10):
    """Find the top N most similar documents by cosine similarity."""
    scored = [
        (i, cosine_similarity(query_embedding, emb))
        for i, emb in enumerate(doc_embeddings)
    ]
    scored.sort(key=lambda x: x[1], reverse=True)
    return scored[:top_n]

def rerank(query, documents, top_n=3):
    """Rerank documents using a cross-encoder model."""
    response = requests.post(
        f"{BASE_URL}/rerank",
        headers=get_headers(),
        json={
            "model": RERANK_MODEL,
            "query": query,
            "documents": documents,
            "top_n": top_n,
        },
    )
    return response.json()["results"]

def generate(query, context_docs):
    """Generate an answer grounded in the provided context."""
    context = "\n\n".join(
        f"[{i+1}] {doc}"
        for i, doc in enumerate(context_docs)
    )
    response = requests.post(
        f"{BASE_URL}/chat/completions",
        headers=get_headers(),
        json={
            "model": CHAT_MODEL,
            "messages": [
                {
                    "role": "system",
                    "content": (
                        "Answer the user's question using only the provided context. "
                        "Cite sources with [n]. If the context is insufficient, say so."
                    ),
                },
                {
                    "role": "user",
                    "content": f"Context:\n{context}\n\nQuestion: {query}",
                },
            ],
        },
    )
    return response.json()["choices"][0]["message"]["content"]

# --- Pipeline ---

# 1. Index: chunk your knowledge base and embed
chunks = [
    "VEGA is a unified API gateway for LLMs. It aggregates models from multiple providers into a single OpenAI-compatible interface.",
    "RAG stands for Retrieval-Augmented Generation. It retrieves relevant documents and uses them as context for LLM generation.",
    "Embeddings convert text into high-dimensional vectors where semantically similar texts are closer together.",
    "Reranking uses a cross-encoder to compare each document directly against the query, producing more accurate relevance scores than embedding similarity alone.",
    "Vector databases like Pinecone, Weaviate, and Qdrant are optimized for storing and querying embedding vectors at scale.",
    "Prompt caching reduces costs by reusing computations for repeated prompt prefixes across requests.",
    "VEGA supports provider routing to control which upstream providers serve your requests based on cost, latency, or privacy preferences.",
]
doc_embeddings = embed(chunks)

# 2. Retrieve: embed the query and find similar chunks
query = "How does RAG improve LLM responses?"
query_embedding = embed([query])[0]
top_matches = retrieve(query_embedding, doc_embeddings, top_n=5)
retrieved_texts = [chunks[i] for i, _ in top_matches]

# 3. Rerank: re-score with a cross-encoder for better precision
reranked = rerank(query, retrieved_texts, top_n=3)
context_texts = [r["document"]["text"] for r in reranked]

# 4. Generate: produce a grounded answer
answer = generate(query, context_texts)
print(f"Q: {query}\nA: {answer}")
typescript
const VEGA_API_KEY = '{{API_KEY_REF}}';
const EMBEDDING_MODEL = '{{EMBEDDING_MODEL}}';
const RERANK_MODEL = '{{RERANK_MODEL}}';
const CHAT_MODEL = '{{CHAT_MODEL}}';
const BASE_URL = 'https://api.vega.chat/api/v1';

function getHeaders() {
  return {
    'Authorization': `Bearer ${VEGA_API_KEY}`,
    'Content-Type': 'application/json',
  };
}

async function embed(texts: string[]): Promise<number[][]> {
  const response = await fetch(`${BASE_URL}/embeddings`, {
    method: 'POST',
    headers: getHeaders(),
    body: JSON.stringify({ model: EMBEDDING_MODEL, input: texts }),
  });
  const data = await response.json();
  return data.data.map((item: { embedding: number[] }) => item.embedding);
}

function cosineSimilarity(a: number[], b: number[]): number {
  const dot = a.reduce((sum, val, i) => sum + val * b[i], 0);
  const magA = Math.sqrt(a.reduce((sum, val) => sum + val * val, 0));
  const magB = Math.sqrt(b.reduce((sum, val) => sum + val * val, 0));
  return dot / (magA * magB);
}

function retrieve(
  queryEmbedding: number[],
  docEmbeddings: number[][],
  topN = 10,
): { index: number; score: number }[] {
  const scored = docEmbeddings.map((emb, i) => ({
    index: i,
    score: cosineSimilarity(queryEmbedding, emb),
  }));
  scored.sort((a, b) => b.score - a.score);
  return scored.slice(0, topN);
}

async function rerank(query: string, documents: string[], topN = 3) {
  const response = await fetch(`${BASE_URL}/rerank`, {
    method: 'POST',
    headers: getHeaders(),
    body: JSON.stringify({
      model: RERANK_MODEL,
      query,
      documents,
      top_n: topN,
    }),
  });
  const data = await response.json();
  return data.results as { index: number; relevance_score: number; document: { text: string } }[];
}

async function generate(query: string, contextDocs: string[]): Promise<string> {
  const context = contextDocs
    .map((doc, i) => `[${i + 1}] ${doc}`)
    .join('\n\n');
  const response = await fetch(`${BASE_URL}/chat/completions`, {
    method: 'POST',
    headers: getHeaders(),
    body: JSON.stringify({
      model: CHAT_MODEL,
      messages: [
        {
          role: 'system',
          content:
            "Answer the user's question using only the provided context. " +
            'Cite sources with [n]. If the context is insufficient, say so.',
        },
        {
          role: 'user',
          content: `Context:\n${context}\n\nQuestion: ${query}`,
        },
      ],
    }),
  });
  const data = await response.json();
  return data.choices[0].message.content;
}

// --- Pipeline ---

// 1. Index: chunk your knowledge base and embed
const chunks = [
  'VEGA is a unified API gateway for LLMs. It aggregates models from multiple providers into a single OpenAI-compatible interface.',
  'RAG stands for Retrieval-Augmented Generation. It retrieves relevant documents and uses them as context for LLM generation.',
  'Embeddings convert text into high-dimensional vectors where semantically similar texts are closer together.',
  'Reranking uses a cross-encoder to compare each document directly against the query, producing more accurate relevance scores than embedding similarity alone.',
  'Vector databases like Pinecone, Weaviate, and Qdrant are optimized for storing and querying embedding vectors at scale.',
  'Prompt caching reduces costs by reusing computations for repeated prompt prefixes across requests.',
  'VEGA supports provider routing to control which upstream providers serve your requests based on cost, latency, or privacy preferences.',
];
const docEmbeddings = await embed(chunks);

// 2. Retrieve: embed the query and find similar chunks
const query = 'How does RAG improve LLM responses?';
const queryEmbedding = (await embed([query]))[0];
const topMatches = retrieve(queryEmbedding, docEmbeddings, 5);
const retrievedTexts = topMatches.map((m) => chunks[m.index]);

// 3. Rerank: re-score with a cross-encoder for better precision
const reranked = await rerank(query, retrievedTexts, 3);
const contextTexts = reranked.map((r) => r.document.text);

// 4. Generate: produce a grounded answer
const answer = await generate(query, contextTexts);
console.log(`Q: ${query}\nA: ${answer}`);

Когда использовать переранжирование

Переранжирование добавляет дополнительный вызов API, поэтому важно понимать, когда оно действительно полезно:

Используйте переранжирование, когда:

  • Ваши знания велики (сотни или тысячи фрагментов), и поиск по эмбеддингам даёт шумные результаты
  • Точность важнее задержки (например, клиентские Q&A, юридические или медицинские документы)
  • Вы извлекаете много кандидатов (например, топ‑20) и хотите сузить их до лучших 3‑5

Пропускайте переранжирование, когда:

  • База знаний небольшая, и поиск по эмбеддингам уже выдаёт высоко релевантные результаты
  • Требуется минимальная задержка (переранжирование добавляет ещё один запрос)
  • Вы создаёте прототип и хотите упростить конвейер

Стратегии фрагментации

То, как вы разбиваете документы, существенно влияет на качество поиска:

  • По абзацу или разделу — сохраняет семантическую связность, хорошо подходит для структурированных документов
  • Фиксированный размер с перекрытием — фрагменты по ~200‑500 токенов с перекрытием ~50 токенов, чтобы не терять контекст на границах
  • По семантическим границам — используйте заголовки, разрывы разделов или границы предложений для создания естественных фрагментов

Меньшие фрагменты (200‑300 токенов) обычно дают более точный поиск, но могут терять окружающий контекст. Большие фрагменты (500‑1000 токенов) сохраняют больше контекста, но могут размывать сигналы релевантности. Экспериментируйте с вашими данными, чтобы найти оптимальный баланс.

Лучшие практики

Используйте одну и ту же модель эмбеддингов для индексации и запросов. Смешивание моделей приводит к несовместимым векторным пространствам и ухудшает результаты поиска.

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

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

Извлекайте больше, чем нужно, а затем переранжируйте. Распространённый паттерн: извлечь 10‑20 кандидатов по эмбеддингам, затем переранжировать до топ‑3‑5. Это сочетает скорость поиска по эмбеддингам с точностью кросс‑энкодера.

Включайте метаданные в запрос. При генерации добавляйте метаданные источника (название документа, раздел, URL) рядом с текстом, чтобы LLM мог корректно цитировать.

Устанавливайте порог релевантности. После переранжирования отфильтровывайте документы ниже минимального порога, чтобы не вводить нерелевантный контекст, который может запутать LLM.

Доступные модели

Просмотрите доступные модели для каждого шага:

Связанные ресурсы

  • Embeddings API — полная ссылка на API генерации эмбеддингов
  • Provider Routing — управление тем, какие провайдеры обслуживают ваши запросы
  • Prompt Caching — снижение стоимости при повторяющихся префиксах запросов
  • Structured Outputs — принудительное соблюдение JSON‑схемы в ответах LLM для структурированного вывода RAG