---
name: azion-responda-perguntas-a-partir-de-uma-tabela-vetorial
description: >-
  Responda a uma pergunta a partir de uma function com os trechos mais próximos de uma tabela vetorial do SQL Database e um modelo de chat que os cita.
---

# Responda perguntas a partir de uma tabela vetorial

Você responde a uma pergunta a partir de uma function: ela gera o embedding da pergunta com um modelo no AI Inference, recupera os trechos mais próximos de uma tabela vetorial do SQL Database com `vector_top_k` e pede a um modelo de chat que responda a partir deles e os cite. Os trechos vêm de documentos em um bucket do Object Storage, que uma segunda function na mesma aplicação ingere. Para o padrão geral de ingestão, consulte [Gere embeddings de documentos em uma tabela vetorial com AI Inference](/pt-br/documentacao/guias/ai/agentes-e-rag/gerar-embeddings-de-documentos-em-uma-tabela-vetorial/).

---

## Pré-requisitos

- Uma aplicação e um workload que servem o seu domínio, com o **Application Accelerator** ativado, que o behavior **Run Function** exige. Para criá-los, consulte [Primeiros passos com Applications](/pt-br/documentacao/plataforma/applications/primeiros-passos/).
- SQL Database habilitado na conta. O produto está em Preview e não vem habilitado por padrão, então solicite acesso pelo [Technical Support](/pt-br/documentacao/suporte/).
- Uma tabela `passages` com um índice vetorial `passages_idx` para vetores de 1.024 dimensões. Para criá-los, consulte [Crie a tabela vetorial](/pt-br/documentacao/guias/ai/agentes-e-rag/gerar-embeddings-de-documentos-em-uma-tabela-vetorial/#crie-a-tabela-vetorial).
- Um bucket do Object Storage com `workloads_access` definido como `read_only`, que guarda os documentos como arquivos de texto UTF-8 ou Markdown. Para criar o bucket e enviar os arquivos, consulte [Primeiros passos com Object Storage](/pt-br/documentacao/plataforma/object-storage/primeiros-passos/).
- Um personal token com a permissão **Edit SQL Database**, para as chamadas ao banco de dados. Para criar um, consulte [Gerencie personal tokens](/pt-br/documentacao/guias/plataforma/conta-e-billing/personal-tokens/).
- A [Azion CLI](/pt-br/documentacao/devtools/cli/), instalada e autorizada, para armazenar as variáveis de ambiente.

Os exemplos usam `support-docs` para o bucket, `support-kb` para o banco de dados, `returns-policy.md` para um documento dentro dele, `/admin/ingest` e `/api/ask` para os dois caminhos e `www.example.com` para o domínio. Substitua-os pelos seus valores.

---

## Faça a ingestão dos documentos de um bucket

A function de ingestão transforma um documento em linhas de `passages`: ela lê o documento no bucket, o divide em trechos de até 1.500 caracteres, gera os embeddings de todos eles em uma chamada e os grava pela API da Azion, porque a conexão do runtime com um banco de dados é somente leitura. A rota grava no banco de dados, então ela recusa uma requisição sem o segredo de ingestão.

Para armazenar os quatro valores que a function lê, execute estes comandos com a Azion CLI. Uma key que contém `token` ou `secret` é armazenada como secret por padrão:

```bash
azion create variables --key "AZION_TOKEN" --value "[TOKEN VALUE]"
azion create variables --key "INGEST_SECRET" --value "<ingest-secret>"
azion create variables --key "SQL_DATABASE_ID" --value "<database-id>" --secret false
azion create variables --key "DOCS_BUCKET" --value "support-docs" --secret false
```

Crie as variáveis antes da function: uma function que já está em execução não lê um valor alterado até receber um novo deploy.

Crie uma function chamada `support-ingest` com este código:

```javascript
import Storage from "azion:storage";

const EMBEDDING_MODEL = "Qwen/Qwen3-Embedding-4B";
const DIMENSIONS = 1024;
const MAX_PASSAGE = 1500;
const MAX_PASSAGES = 99;

function split(text) {
  const passages = [];
  let current = "";
  for (const paragraph of text.split(/\n\s*\n/)) {
    const p = paragraph.trim();
    if (!p) continue;
    if (current && current.length + p.length > MAX_PASSAGE) {
      passages.push(current);
      current = "";
    }
    current = current ? current + "\n\n" + p : p;
  }
  if (current) passages.push(current);
  return passages;
}

const quote = (value) => "'" + value.replaceAll("'", "''") + "'";

export default {
  async fetch(request, env, ctx) {
    if (request.method !== "POST") {
      return new Response("Method not allowed", { status: 405 });
    }
    if (request.headers.get("Authorization") !== `Bearer ${Azion.env.get("INGEST_SECRET")}`) {
      return new Response("Unauthorized", { status: 401 });
    }
    const key = new URL(request.url).searchParams.get("key");
    if (!key) {
      return Response.json({ error: "key is required" }, { status: 400 });
    }

    const object = await new Storage(Azion.env.get("DOCS_BUCKET")).get(key);
    const passages = split(new TextDecoder().decode(await object.arrayBuffer()));
    if (passages.length === 0 || passages.length > MAX_PASSAGES) {
      return Response.json({ error: `${passages.length} passages; split the document` }, { status: 413 });
    }

    const embedded = await Azion.AI.run(EMBEDDING_MODEL, {
      "input": passages,
      "encoding_format": "float",
      "dimensions": DIMENSIONS
    });

    const statements = [`DELETE FROM passages WHERE source = ${quote(key)};`];
    for (const item of embedded.data) {
      statements.push(
        `INSERT INTO passages (source, content, embedding) VALUES (${quote(key)}, ${quote(passages[item.index])}, vector('[${item.embedding.join(",")}]'));`
      );
    }

    const result = await fetch(
      `https://api.azion.com/v4/workspace/sql/databases/${Azion.env.get("SQL_DATABASE_ID")}/query`,
      {
        method: "POST",
        headers: {
          "Accept": "application/json",
          "Authorization": `Token ${Azion.env.get("AZION_TOKEN")}`,
          "Content-Type": "application/json"
        },
        body: JSON.stringify({ statements })
      }
    );
    const body = await result.json();
    const failed = (body.data ?? []).find((entry) => entry.error);
    if (!result.ok || failed) {
      return Response.json({ error: failed?.error ?? `SQL API answered ${result.status}` }, { status: 502 });
    }
    return Response.json({ key, passages: passages.length });
  },
};
```

Execute a function no caminho dela com estes valores, seguindo [Primeiros passos com Functions](/pt-br/documentacao/plataforma/functions/primeiros-passos/):

- **Instância da function**: `support-ingest`, sem Args.
- **Regra**: uma regra de Request Phase chamada `support - ingest`, com o critério `${uri}` *starts with* `/admin/ingest` e o behavior **Run Function** selecionando a instância `support-ingest`.

Para criar a regra no Azion Console ou pela API, consulte [Adicione a regra que executa a função](/pt-br/documentacao/guias/desenvolvimento-de-aplicacoes/functions-e-runtime/funcoes-serverless/#adicione-a-regra-que-executa-a-funcao).

Um `POST` para `/admin/ingest` com o segredo e a key de um documento substitui os trechos desse documento em `passages` e responde com o número de trechos armazenados. O `DELETE` faz com que uma segunda ingestão da mesma key substitua as linhas dela em vez de duplicá-las.

Para fazer a ingestão do documento de exemplo:

```bash
curl -X POST 'https://www.example.com/admin/ingest?key=returns-policy.md' \
  -H 'Authorization: Bearer <ingest-secret>'
```

A function responde com a key e o número de trechos que armazenou, como `{"key":"returns-policy.md","passages":3}`. Uma requisição sem o header `Authorization` responde `401`. Para contar as linhas do documento pela API do SQL Database:

```bash
curl --request POST \
  --url https://api.azion.com/v4/workspace/sql/databases/<database-id>/query \
  --header 'Accept: application/json' \
  --header 'Authorization: Token [TOKEN VALUE]' \
  --header 'Content-Type: application/json' \
  --data '{"statements":["SELECT COUNT(*) FROM passages WHERE source = '\''returns-policy.md'\'' AND embedding IS NOT NULL;"]}'
```

A única entrada em `data` traz `results`, cuja única linha guarda o mesmo número que a ingestão retornou.

O caso de uso [Criar e executar assistentes de IA para suporte ao cliente](/pt-br/documentacao/casos-de-uso/construir-e-executar-workloads-de-ai/criar-e-executar-assistentes-de-ia-para-suporte-ao-cliente/) usa os valores deste exemplo.

Estas verificações confirmam o caso de uso [Criar e executar assistentes de IA para suporte ao cliente](/pt-br/documentacao/casos-de-uso/construir-e-executar-workloads-de-ai/criar-e-executar-assistentes-de-ia-para-suporte-ao-cliente/).

---

## Armazene o nome do banco de dados

A function abre o banco de dados pelo nome, por uma réplica de leitura.

Para armazenar o nome do banco de dados que a function abre, execute:

```bash
azion create variables --key "SQL_DATABASE_NAME" --value "support-kb" --secret false
```

A conta guarda a variável `SQL_DATABASE_NAME`, que a function lê com `Azion.env.get()`.

O caso de uso [Criar e executar assistentes de IA para suporte ao cliente](/pt-br/documentacao/casos-de-uso/construir-e-executar-workloads-de-ai/criar-e-executar-assistentes-de-ia-para-suporte-ao-cliente/) usa os valores deste exemplo.

---

## Crie a function do assistente

A function gera o embedding da pergunta com o mesmo modelo e a mesma largura dos trechos, recupera os quatro trechos mais próximos e os envia ao modelo de chat com as seis últimas mensagens de `history` e a pergunta. Ela grava uma linha de log `"event":"answer"` por resposta, registrando se a resposta cita um trecho.

Crie uma function chamada `support-assistant` com este código:

```javascript
const EMBEDDING_MODEL = "Qwen/Qwen3-Embedding-4B";
const CHAT_MODEL = "Qwen/Qwen3-30B-A3B-Instruct-2507-FP8";
const DIMENSIONS = 1024;
const TOP_K = 4;

const SYSTEM_PROMPT =
  "You answer customer questions using only the numbered passages in the user message. " +
  "Cite every passage you use as [n]. If the passages do not contain the answer, " +
  "say that you do not know and suggest contacting support.";

async function retrieve(question) {
  const embedded = await Azion.AI.run(EMBEDDING_MODEL, {
    "input": question,
    "encoding_format": "float",
    "dimensions": DIMENSIONS
  });
  const vector = embedded.data[0].embedding.join(",");

  const { Database } = Azion.Sql;
  const connection = await Database.open(Azion.env.get("SQL_DATABASE_NAME"));
  const rows = await connection.query(
    `SELECT passages.source, passages.content FROM vector_top_k('passages_idx', vector('[${vector}]'), ${TOP_K}) JOIN passages ON passages.rowid = id;`
  );

  const passages = [];
  let row = await rows.next();
  while (row) {
    passages.push({ source: row.getString(0), content: row.getString(1) });
    row = await rows.next();
  }
  return passages;
}

export default {
  async fetch(request, env, ctx) {
    if (request.method !== "POST") {
      return new Response("Method not allowed", { status: 405 });
    }
    const { question, history = [] } = await request.json();
    if (typeof question !== "string" || question.trim() === "") {
      return Response.json({ error: "question is required" }, { status: 400 });
    }

    const passages = await retrieve(question);
    const context = passages
      .map((p, i) => `[${i + 1}] (${p.source})\n${p.content}`)
      .join("\n\n");

    const modelResponse = await Azion.AI.run(CHAT_MODEL, {
      "stream": false,
      "max_tokens": 800,
      "messages": [
        { "role": "system", "content": SYSTEM_PROMPT },
        ...history.slice(-6),
        { "role": "user", "content": `Passages:\n${context}\n\nQuestion: ${question}` }
      ]
    });

    const answer = modelResponse?.choices?.[0]?.message?.content ?? "";
    const cited = /\[\d+\]/.test(answer);
    console.log(JSON.stringify({ event: "answer", cited, passages: passages.length }));

    return Response.json({
      answer,
      sources: passages.map((p, i) => ({ n: i + 1, source: p.source }))
    });
  },
};
```

Para criar a function e a sua instância, siga [Primeiros passos com Functions](/pt-br/documentacao/plataforma/functions/primeiros-passos/) com o nome `support-assistant` e dê à instância o nome `support-assistant`, sem Args.

A aplicação tem uma instância `support-assistant` que responde a uma pergunta a partir dos trechos mais próximos dela.

O caso de uso [Criar e executar assistentes de IA para suporte ao cliente](/pt-br/documentacao/casos-de-uso/construir-e-executar-workloads-de-ai/criar-e-executar-assistentes-de-ia-para-suporte-ao-cliente/) usa os valores deste exemplo.

---

## Execute a function no caminho

Crie uma regra de Request Phase como mostra [Adicione a regra que executa a função](/pt-br/documentacao/guias/desenvolvimento-de-aplicacoes/functions-e-runtime/funcoes-serverless/#adicione-a-regra-que-executa-a-funcao), com o nome `support - ask`, o critério `${uri}` *is equal* `/api/ask` e o behavior **Run Function** selecionando a instância `support-assistant`.

Um `POST` para `/api/ask` com uma pergunta responde com a resposta do modelo, que cita trechos como `[n]`, e com o documento de onde veio cada trecho citado. Uma regra nova leva alguns minutos para se propagar.

---

## Confirme que as respostas citam os documentos

Para fazer uma pergunta que o documento responde:

```bash
curl -X POST https://www.example.com/api/ask \
  -H 'Content-Type: application/json' \
  -d '{"question":"How many days do I have to return an item?"}'
```

A resposta traz `answer`, com pelo menos uma citação `[n]`, e `sources`, cujas entradas indicam `returns-policy.md`.

Faça uma pergunta que nenhum documento cobre, como `What is the capital of France?`. A resposta diz que o assistente não sabe e não traz nenhuma citação.

A function responde a partir dos trechos da tabela, e apenas deles.

Estas verificações confirmam o caso de uso [Criar e executar assistentes de IA para suporte ao cliente](/pt-br/documentacao/casos-de-uso/construir-e-executar-workloads-de-ai/criar-e-executar-assistentes-de-ia-para-suporte-ao-cliente/).

---

## Próximos passos

- [Vector search](/pt-br/documentacao/plataforma/sql-database/vector-search.md): A função vector\_top\_k, as funções de distância e os limites de um índice vetorial.
- [Criar e executar assistentes de IA para suporte ao cliente](/pt-br/documentacao/casos-de-uso/construir-e-executar-workloads-de-ai/criar-e-executar-assistentes-de-ia-para-suporte-ao-cliente.md): O design a que esta function serve: o tamanho dos trechos, quantos são recuperados e o limite da resposta, com o motivo de cada um.
