Todos os produtos
Search
Central de documentação

ApsaraDB RDS:Use ApsaraDB RDS for PostgreSQL to create a RAG application

Última atualização: Jun 26, 2026

O ApsaraDB RDS for PostgreSQL combina armazenamento vetorial com busca de texto completo, tornando-o um banco de dados vetorial robusto para aplicações de geração aumentada por recuperação (RAG). Este guia demonstra a construção de um chatbot de tickets para ilustrar como a recuperação multicaminho, a fusão de resultados e a geração de perguntas e respostas funcionam em conjunto em uma única instância PostgreSQL.

Ao concluir este guia, você entenderá:

  • Como ingerir e estruturar documentos para um pipeline RAG

  • O funcionamento de cada um dos quatro métodos de recuperação e quando utilizá-los

  • Como mesclar e reclassificar resultados usando o algoritmo de fusão de classificação recíproca (RRF) e o modelo bce-reranker-base_v1

Como funciona

O pipeline do chatbot de tickets possui quatro etapas:

  1. Processamento de dados — Divida os documentos de origem (documentação de ajuda, bases de conhecimento, tickets históricos) em fragmentos e gere embeddings. Armazene ambos na instância RDS.

  2. Recuperação multicaminho — Execute quatro buscas paralelas com base nas perguntas do usuário: baseada em palavras-chave do documento, baseada em palavras-chave do conteúdo, baseada em BM25 e baseada em embedding.

  3. Fusão de resultados — Mescle e reclassifique os resultados dos quatro caminhos utilizando o algoritmo RRF e o modelo bce-reranker-base_v1.

  4. Análise de perguntas e respostas — Pontue pares de perguntas e respostas durante os testes para avaliar e comparar políticas de recuperação.

Pré-requisitos

Antes de começar, certifique-se de ter:

  • Uma instância ApsaraDB RDS for PostgreSQL

  • Uma conta de banco de dados privilegiada para instalar extensões

  • Uma conta no Alibaba Cloud Model Studio e uma chave de API (para o modelo de text embedding). Consulte Obter uma chave de API

  • Um gateway NAT configurado para a Virtual Private Cloud (VPC) onde a instância RDS reside, permitindo que a instância acesse modelos externos. Consulte Usar a extensão rds_embedding para gerar vetores

Processamento de dados

Adquirir dados de source

Colete dados conforme a finalidade da sua aplicação RAG. O chatbot de tickets deste guia utiliza três fontes:

  • Documentação de ajuda

  • Artigos da base de conhecimento

  • Tickets de suporte históricos

Segmentar e incorporar documentos

Divida os documentos em fragmentos antes de gerar os embeddings. Utilize a classe HTMLHeaderTextSplitter do LangChain para segmentar documentos de ajuda pela hierarquia HTML (H1, H2). Configure o tamanho do fragmento e a sobreposição para equilibrar precisão e recall na recuperação. Consulte Splitters de texto do LangChain para visualizar a lista completa de opções de divisão.

Para documentos Markdown, utilize MarkdownHeaderTextSplitter com marcadores de título como # e ## para realizar a divisão hierárquica.

Armazenar dados

Armazene os dados processados em duas tabelas principais: document e embedding.

Instruções SQL para criar um trigger

A tabela document armazena metadados no nível do documento e palavras-chave usadas para correspondência de texto completo.

Coluna

Tipo

Descrição

id

bigint

Chave primária, incremento automático

title

varchar(255)

Título do documento (único — usado para deduplicação na atualização)

url

varchar(255)

URL de source

key_word

varchar(255)

Palavras-chave para correspondência de texto completo

tag

varchar(255)

Tag de source (ex.: direct, aone); controla o peso do tsvector

created

timestamp

Hora de criação

modified

timestamp

Hora da última modificação

key_word_tsvector

tsvector

tsvector ponderado construído a partir de key_word; usado na recuperação baseada em palavras-chave

product_name

varchar(255)

Nome do produto para filtragem

Índices:

Índice

Tipo

Finalidade

document_pkey

btree (id)

Chave primária

document_title_key

btree (title), UNIQUE

Deduplicação — documentos são atualizados pelo título

document_product_name_key

btree (product_name)

Filtragem por escopo de produto

document_key_word_tsvector_gin

GIN (key_word_tsvector)

Correspondência rápida de palavras-chave

Trigger: trigger_update_tsvector é executado em INSERT ou UPDATE e reconstrói key_word_tsvector automaticamente.

O trigger atribui pesos de A a D com base na coluna tag, garantindo que documentos direct tenham prioridade sobre documentos aone na recuperação por palavras-chave. Os pesos A, B, C e D seguem ordem decrescente de prioridade:

CREATE OR REPLACE FUNCTION update_tsvector()
RETURNS TRIGGER AS $$
BEGIN
    IF TG_OP = 'INSERT' OR TG_OP = 'UPDATE' THEN
        IF NEW.tag = 'direct' THEN
            NEW.key_word_tsvector := setweight(to_tsvector('jiebacfg', NEW.key_word), 'A');
        ELSIF NEW.tag = 'aone' THEN
            NEW.key_word_tsvector := setweight(to_tsvector('jiebacfg', NEW.key_word), 'B');
        ELSIF NEW.tag IS NOT NULL THEN
            NEW.key_word_tsvector := setweight(to_tsvector('jiebacfg', NEW.key_word), 'C');
        ELSE
            NEW.key_word_tsvector := setweight(to_tsvector('jiebacfg', NEW.key_word), 'D');
        END IF;
    END IF;

    RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE FUNCTION

CREATE TRIGGER trigger_update_tsvector
BEFORE INSERT OR UPDATE ON document
FOR EACH ROW
EXECUTE FUNCTION update_tsvector();
CREATE TRIGGER

Instruções SQL para criar um trigger

A tabela embedding armazena os fragmentos dos documentos e suas representações vetoriais.

Coluna

Tipo

Descrição

id

bigint

Chave primária, incremento automático

doc_id

integer

Chave estrangeira para document.id

content_chunk

text

Fragmento de texto após segmentação

content_embedding

vector(1536)

Embedding gerado a partir de content_chunk

created

timestamp

Hora de criação

modified

timestamp

Hora da última modificação

ts_vector_extra

tsvector

tsvector construído a partir de content_chunk; usado na recuperação baseada em palavras-chave do conteúdo

Índices:

Índice

Tipo

Finalidade

embedding_pkey

btree (id)

Chave primária

embedding_doc_id_key

btree (doc_id)

Busca de junção de documento

embedding_content_embedding_idx

HNSW (content_embedding, vector_cosine_ops), m=16, ef_construction=64

Busca vetorial de vizinho mais próximo aproximado (ANN)

embedding_rumidx

RUM (ts_vector_extra)

Busca de texto completo rápida com classificação

Trigger: embedding_tsvector_update reconstrói ts_vector_extra em operações de INSERT ou UPDATE:

CREATE TRIGGER embedding_tsvector_update
BEFORE INSERT OR UPDATE ON embedding
FOR EACH ROW
EXECUTE PROCEDURE tsvector_update_trigger('ts_vector_extra','public.jiebacfg','content_chunk');

Recuperação multicaminho

Quando usar cada método de recuperação

Cada método de recuperação adequa-se a diferentes características de consulta. Utilize esta tabela para escolher a estratégia ideal para o seu caso de uso:

Método

Mais indicado para

Limitações

Recuperação baseada em palavras-chave do documento

Consultas que correspondem a palavras-chave ou tags conhecidas do produto; rápido e preciso para termos exatos

Perde o significado semântico; falha em erros de digitação ou sinônimos

Recuperação baseada em palavras-chave do conteúdo

Consultas direcionadas ao corpo do texto do documento; beneficia-se do índice RUM para classificação rápida de texto completo

Mesmas limitações da correspondência por palavras-chave

Recuperação baseada em BM25

Consultas onde a frequência do termo e a importância no nível do documento são relevantes; complementa o ts_rank

Puramente estatístico; sem compreensão semântica

Recuperação baseada em embedding

Consultas expressas em linguagem natural; lida bem com sinônimos e paráfrases

Requer modelo de embedding; pode perder correspondências exatas de palavras-chave

Para a maioria das aplicações RAG, combine todos os quatro métodos e mescle os resultados usando o algoritmo RRF. Essa abordagem produz um recall superior ao uso de qualquer método isoladamente.

Recuperação baseada em palavras-chave do documento

A recuperação baseada em palavras-chave do documento compara as perguntas do usuário com as palavras-chave armazenadas na tabela document e retorna os N principais documentos por similaridade.

Etapa 1: Indexar palavras-chave do documento como valores tsvector ponderados.

Utilize to_tsvector para segmentar palavras-chave e setweight para atribuir pesos. As extensões de segmentação de palavras em chinês pg_jieba e zhparser processam textos em chinês. Consulte Gerenciar extensões para obter instruções de instalação.

SELECT setweight(to_tsvector('jiebacfg', 'PostgreSQL是世界上先进的开源关系型数据库'), 'A');
                                   setweight
-------------------------------------------------------------------------------
 'postgresql':1A '世界':3A '先进':5A '关系':8A '型':9A '开源':7A '数据库':10A

Etapa 2: Converter a pergunta do usuário em um tsquery e comparar com as palavras-chave.

SELECT
    id,
    title,
    url,
    key_word,
    ts_rank(
        key_word_tsvector,
        to_tsquery(replace(text(plainto_tsquery('jiebacfg', '%s')), '&', '|'))
    ) AS score
FROM
    public.document
WHERE
    key_word_tsvector @@ to_tsquery(replace(text(plainto_tsquery('jiebacfg', '%s')), '&', '|'))
    AND product_name = '%s'
ORDER BY
    score DESC
LIMIT 1;

Funções principais:

to_tsquery converte uma pergunta em um valor tsquery:

SELECT to_tsquery('jiebacfg', 'PostgreSQL是世界上先进的开源关系型数据库');
                                   to_tsquery
--------------------------------------------------------------------------------
 'postgresql' <2> '世界' <2> '先进' <2> '开源' <-> '关系' <-> '型' <-> '数据库'

<2> indica a distância entre palavras. <-> indica palavras adjacentes. Stopwords como e são removidas automaticamente. & significa AND, | significa OR e ! significa NOT.

Prefira plainto_tsquery em vez de to_tsquery quando a entrada do usuário puder conter operadores inválidos:

SELECT to_tsquery('jiebacfg','日志|&堆积');
ERROR:  syntax error in tsquery: "日志|&堆积"

SELECT plainto_tsquery('jiebacfg','日志|&堆积');
 plainto_tsquery
-----------------
 '日志' & '堆积'

Utilize a função text para converter o resultado de plainto_tsquery em uma string e, em seguida, use replace para alterar & para | visando à correspondência OR:

-- Use the plainto_tsquery function.
SELECT plainto_tsquery('jiebacfg', 'PostgreSQL是世界上先进的开源关系型数据库');
                          plainto_tsquery
--------------------------------------------------------------------
 'postgresql' & '世界' & '先进' & '开源' & '关系' & '型' & '数据库'

-- Use the plainto_tsquery, text, and replace functions.
SELECT replace(text(plainto_tsquery('jiebacfg', 'PostgreSQL是世界上先进的开源关系型数据库')), '&', '|');
                            replace
--------------------------------------------------------------------
 'postgresql' | '世界' | '先进' | '开源' | '关系' | '型' | '数据库'

-- Use the to_tsquery, plainto_tsquery, text, and replace functions.
SELECT to_tsquery(replace(text(plainto_tsquery('jiebacfg', 'PostgreSQL是世界上先进的开源关系型数据库')), '&', '|'));
                           to_tsquery
--------------------------------------------------------------------
 'postgresql' | '世界' | '先进' | '开源' | '关系' | '型' | '数据库'

Adicione termos personalizados ao dicionário pg_jieba para melhorar a segmentação de frases específicas do domínio:

-- Automatically add 关系型 to custom dictionary 0 with a weight of 100000.
INSERT INTO jieba_user_dict VALUES ('关系型',0,100000);

-- Load custom dictionary 0. The first 0 is the dictionary sequence number; the second 0 loads the default dictionary.
SELECT jieba_load_user_dict(0,0);
 jieba_load_user_dict
----------------------

-- Convert a user question into a tsquery value.
SELECT to_tsquery(replace(text(plainto_tsquery('jiebacfg', 'PostgreSQL是世界上先进的开源关系型数据库')), '&', '|'));
                           to_tsquery
--------------------------------------------------------------------
 'postgresql' | '世界' | '先进' | '开源' | '关系型' | '数据库'

Antes de adicionar 关系型 ao dicionário, o termo era dividido em '关系' & '型'. Após a adição, o dicionário retorna o termo composto '关系型' como um único token.

Operadores e pontuação:

Use @@ para verificar se um tsvector corresponde a um tsquery. A correspondência de pesos afeta os resultados:

SELECT to_tsvector('jiebacfg', 'PostgreSQL是世界上先进的开源关系型数据库') @@ to_tsquery('jiebacfg', 'postgresql:A');
 ?column?
----------
 f

SELECT setweight(to_tsvector('jiebacfg', 'PostgreSQL是世界上先进的开源关系型数据库'),'A') @@ to_tsquery('jiebacfg', 'postgresql:A');
 ?column?
----------
 t

A consulta busca por postgresql com peso A. A primeira instrução retorna falso porque nenhum peso foi atribuído ao tsvector. Após aplicar setweight, a correspondência é bem-sucedida.

Utilize ts_rank para pontuar a qualidade da correspondência entre um tsvector e um tsquery:

WITH sentence AS (
    SELECT 'PostgreSQL是世界上先进的开源关系型数据库' AS content
    UNION ALL
    SELECT 'MySQL是应用广泛的开源关系数据库'
    UNION ALL
    SELECT 'MySQL在全球非常流行'
)
SELECT content,
       ts_rank(to_tsvector('jiebacfg', content), to_tsquery('jiebacfg', 'postgresql | 开源')) AS score
FROM sentence
WHERE to_tsvector('jiebacfg', content) @@ to_tsquery('jiebacfg', 'postgresql | 开源')
ORDER BY score DESC;

                  content                   |    score
--------------------------------------------+-------------
 PostgreSQL是世界上先进的开源关系型数据库 |  0.06079271
 MySQL是应用广泛的开源关系数据库          | 0.030396355

A primeira frase corresponde tanto a postgresql quanto a 开源, obtendo uma pontuação maior que a segunda frase, que corresponde apenas a 开源. A terceira frase é filtrada por @@ porque não corresponde a nenhum dos termos.

Tratamento de falhas de segmentação:

Se a segmentação de palavras falhar ou a entrada contiver caracteres inesperados, a correspondência de palavras-chave poderá não retornar resultados. Utilize a extensão pg_bigm para correspondência difusa como alternativa:

WITH sentence AS (
    SELECT 'PostgreSQL是世界上先进的开源关系型数据库' AS content
    UNION ALL
    SELECT 'MySQL是应用广泛的开源关系数据库'
    UNION ALL
    SELECT 'MySQL在全球非常流行'
)
SELECT
    content,
    bigm_similarity(content, 'postgres | 开源产品') AS score
FROM
    sentence
ORDER BY
    score DESC;

                  content                   |   score
--------------------------------------------+------------
  PostgreSQL是世界上先进的开源关系型数据库 | 0.23076923
  MySQL是应用广泛的开源关系数据库          | 0.05263158
  MySQL在全球非常流行                      |        0.0
(3 rows)

bigm_similarity converte ambas as strings em bigramas (pares de caracteres consecutivos) e calcula a sobreposição. O resultado varia de 0 a 1, onde 1 representa uma correspondência exata. Isso funciona bem quando há erros de digitação, abreviações ou erros de segmentação. Para mais informações, consulte Usar a extensão pg_bigm para realizar consultas baseadas em correspondência difusa.

Recuperação baseada em palavras-chave do conteúdo

A recuperação baseada em palavras-chave do conteúdo pesquisa o texto content_chunk na tabela embedding usando a mesma abordagem de busca de texto completo da recuperação baseada em palavras-chave do documento. Como os fragmentos de documento são mais longos que os campos de palavras-chave, a extensão RUM oferece uma vantagem significativa de desempenho.

Os três planos de consulta a seguir comparam a mesma consulta de busca de texto completo usando diferentes abordagens de indexação. Todos utilizam a consulta 'wal日志堆积怎么办'.

Usar a extensão RUM para executar buscas de texto completo

Opção 1: Índice RUM (recomendado)

O índice RUM armazena posições de palavras e timestamps junto com as entradas do índice invertido, permitindo classificar resultados por similaridade sem uma etapa separada de ordenação. Tempo de consulta: 3,2 ms.

EXPLAIN ANALYZE
SELECT
    id,
    doc_id,
    content_chunk,
    ts_vector_extra <=> to_tsquery(
        REPLACE(
            TEXT(plainto_tsquery('jiebacfg', 'wal日志堆积怎么办')),
            '&',
            '|'
        )
    ) AS similarity
FROM
    embedding
WHERE
    ts_vector_extra @@ to_tsquery(
        REPLACE(
            TEXT(plainto_tsquery('jiebacfg', 'wal日志堆积怎么办')),
            '&',
            '|'
        )
    )
ORDER BY
    similarity
LIMIT
    10;
                                                                     QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=10.15..22.14 rows=10 width=521) (actual time=3.117..3.182 rows=10 loops=1)
   ->  Index Scan using embedding_rumidx on embedding  (cost=10.15..6574.53 rows=5474 width=521) (actual time=3.115..3.179 rows=10 loops=1)
         Index Cond: (ts_vector_extra @@ to_tsquery('''wal'' | ''日志'' | ''堆积'''::text))
         Order By: (ts_vector_extra <=> to_tsquery('''wal'' | ''日志'' | ''堆积'''::text))
 Planning Time: 0.296 ms
 Execution Time: 3.219 ms
(6 rows)

Usar índices GIN nativos para acelerar consultas

Opção 2: Índice GIN na coluna ts_vector_extra

O índice GIN não armazena posições de palavras, então o PostgreSQL precisa realizar uma varredura de heap de bitmap seguida de uma classificação. Tempo de consulta: 14,2 ms.

EXPLAIN ANALYZE
SELECT
    id,
    doc_id,
    content_chunk,
    ts_rank(
        ts_vector_extra,
        to_tsquery(
            REPLACE(
                TEXT(plainto_tsquery('jiebacfg', 'wal日志堆积怎么办')),
                '&',
                '|'
            )
        )
    ) AS similarity
FROM
    embedding
WHERE
    ts_vector_extra @@ to_tsquery(
        REPLACE(
            TEXT(plainto_tsquery('jiebacfg', 'wal日志堆积怎么办')),
            '&',
            '|'
        )
    )
ORDER BY
    similarity
LIMIT
    10;
                                                                           QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=7178.59..7179.76 rows=10 width=520) (actual time=10.526..14.192 rows=10 loops=1)
   ->  Gather Merge  (cost=7178.59..7718.33 rows=4626 width=520) (actual time=10.525..14.189 rows=10 loops=1)
         Workers Planned: 2
         Workers Launched: 2
         ->  Sort  (cost=6178.57..6184.35 rows=2313 width=520) (actual time=6.879..6.880 rows=10 loops=3)
               Sort Key: (ts_rank(ts_vector_extra, to_tsquery('''wal'' | ''日志'' | ''堆积'''::text)))
               Sort Method: top-N heapsort  Memory: 37kB
               Worker 0:  Sort Method: top-N heapsort  Memory: 39kB
               Worker 1:  Sort Method: top-N heapsort  Memory: 40kB
               ->  Parallel Bitmap Heap Scan on embedding  (cost=56.47..6128.59 rows=2313 width=520) (actual time=0.567..6.367 rows=1637 loops=3)
                     Recheck Cond: (ts_vector_extra @@ to_tsquery('''wal'' | ''日志'' | ''堆积'''::text))
                     Heap Blocks: exact=1515
                     ->  Bitmap Index Scan on embedding_ts_vector_gin  (cost=0.00..55.08 rows=5551 width=0) (actual time=0.794..0.794 rows=4910 loops=1)
                           Index Cond: (ts_vector_extra @@ to_tsquery('''wal'' | ''日志'' | ''堆积'''::text))
 Planning Time: 0.291 ms
 Execution Time: 14.234 ms

Índices GIN construídos em to_tsvector('jiebacfg'::regconfig, content_chunk)

Opção 3: Índice GIN em uma expressão (não recomendado)

Construir um índice GIN em to_tsvector('jiebacfg'::regconfig, content_chunk) exige que o PostgreSQL execute novamente to_tsvector em cada linha correspondente para calcular ts_rank, pois o GIN não armazena informações de localização de palavras. Tempo de consulta: 1.081 ms — mais de 300 vezes mais lento que o índice RUM.

EXPLAIN ANALYZE
SELECT
    id,
    doc_id,
    content_chunk,
    ts_rank(
        to_tsvector('jiebacfg', content_chunk),
        to_tsquery(
            REPLACE(
                TEXT(plainto_tsquery('jiebacfg', 'wal日志堆积怎么办')),
                '&',
                '|'
            )
        )
    ) AS similarity
FROM
    embedding
WHERE
    to_tsvector('jiebacfg', content_chunk) @@ to_tsquery(
        REPLACE(
            TEXT(plainto_tsquery('jiebacfg', 'wal日志堆积怎么办')),
            '&',
            '|'
        )
    )
ORDER BY
    similarity
LIMIT
    10;
                                                                          QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=8253.58..8254.75 rows=10 width=521) (actual time=1079.289..1081.510 rows=10 loops=1)
   ->  Gather Merge  (cost=8253.58..8786.55 rows=4568 width=521) (actual time=1079.287..1081.508 rows=10 loops=1)
         Workers Planned: 2
         Workers Launched: 2
         ->  Sort  (cost=7253.56..7259.27 rows=2284 width=521) (actual time=1073.189..1073.191 rows=10 loops=3)
               Sort Key: (ts_rank(to_tsvector('jiebacfg'::regconfig, content_chunk), to_tsquery('''wal'' | ''日志'' | ''堆积'''::text)))
               Sort Method: top-N heapsort  Memory: 43kB
               Worker 0:  Sort Method: top-N heapsort  Memory: 42kB
               Worker 1:  Sort Method: top-N heapsort  Memory: 37kB
               ->  Parallel Bitmap Heap Scan on embedding  (cost=55.93..7204.20 rows=2284 width=521) (actual time=2.127..1072.159 rows=1637 loops=3)
                     Recheck Cond: (to_tsvector('jiebacfg'::regconfig, content_chunk) @@ to_tsquery('''wal'' | ''日志'' | ''堆积'''::text))
                     Heap Blocks: exact=1028
                     ->  Bitmap Index Scan on embedding_content_gin  (cost=0.00..54.56 rows=5481 width=0) (actual time=0.808..0.809 rows=4910 loops=1)
                           Index Cond: (to_tsvector('jiebacfg'::regconfig, content_chunk) @@ to_tsquery('''wal'' | ''日志'' | ''堆积'''::text))
 Planning Time: 0.459 ms
 Execution Time: 1081.547 ms
(16 rows)

Resumo da comparação de índices:

Tipo de índice

Tempo de execução

Funcionamento da classificação

RUM em ts_vector_extra

3,2 ms

Varredura de índice com ordenação integrada — sem etapa de classificação

GIN em ts_vector_extra

14,2 ms

Varredura de bitmap + classificação paralela

GIN em to_tsvector(content_chunk)

1.081 ms

Varredura de bitmap + recálculo de tsvector por linha + classificação

Utilize o índice RUM (embedding_rumidx) para recuperação baseada em palavras-chave do conteúdo. Ele é 4 vezes mais rápido que a abordagem GIN em coluna armazenada e 300 vezes mais rápido que a abordagem GIN em expressão.

Recuperação baseada em BM25

BM25 é um algoritmo clássico de correspondência de texto que pontua documentos com base na frequência do termo (TF) e na frequência inversa do documento (IDF). Uma pontuação TF alta indica que uma palavra aparece frequentemente em um documento. Uma pontuação IDF alta indica que uma palavra aparece em poucos documentos, sendo portanto mais distintiva.

O BM25 otimiza o modelo TF-IDF com parâmetros adicionais para melhorar a qualidade da recuperação. Sua saída complementa a recuperação por palavras-chave baseada em ts_rank, já que ambos operam sobre estatísticas de termos, mas ponderam os fatores de maneira diferente.

Recuperação baseada em embedding

O ApsaraDB RDS for PostgreSQL suporta a extensão pgvector para armazenamento vetorial e busca de similaridade, além da extensão rds_embedding para gerar embeddings a partir de texto.

A extensão pgvector suporta dois métodos de indexação para busca de vizinho mais próximo aproximado (ANN):

  • HNSW (Hierarchical Navigable Small World) — constrói o índice incrementalmente à medida que os dados são inseridos; não requer treinamento; velocidade de consulta mais rápida

  • IVFFlat (Inverted File with Flat Compression) — requer treinamento nos dados existentes antes da construção; velocidade de consulta ligeiramente menor

Este guia utiliza HNSW, que funciona sem pré-treinamento e oferece recuperação mais rápida. Para práticas recomendadas de IVFFlat, consulte Criar um chatbot dedicado orientado por LLM no ApsaraDB RDS for PostgreSQL.

SELECT
    embedding.id,
    doc_id,
    content_chunk,
    content_embedding <=> '%s' AS similarity
FROM
    public.embedding
LEFT JOIN
    document ON document.id = embedding.doc_id
WHERE
    product_name = '%s'
ORDER BY
    similarity
LIMIT %s;

Para benchmarks de desempenho do pgvector, consulte Usar a extensão pgvector para realizar buscas de similaridade vetorial de alta dimensão.

Fusão de resultados

Mescle e reclassifique os resultados dos quatro caminhos de recuperação usando o algoritmo RRF e o modelo bce-reranker-base_v1.

Algoritmo RRF:

O RRF atribui uma pontuação a cada documento com base em sua classificação em vários sistemas de recuperação:

RRF(d) = Σ 1/(k + r_i(d))

Onde d é o documento, r_i(d) é a classificação do documento no sistema i e k é uma constante de suavização (geralmente definida como 60). Documentos bem classificados em múltiplos sistemas acumulam pontuações RRF mais altas, produzindo uma classificação mesclada que reflete o consenso entre todos os caminhos de recuperação.

Modelo bce-reranker-base_v1:

O bce-reranker-base_v1 é um modelo de reclassificação semântica multilíngue que suporta chinês, inglês, japonês e coreano. Ele produz classificações mais precisas que o RRF isoladamente, mas leva mais tempo ao processar muitos fragmentos.

Escolha entre as abordagens com base nos seus requisitos de latência:

  • Para respostas rápidas, use apenas o RRF ou aplique o bce-reranker-base_v1 somente nos principais resultados do RRF.

  • Para máxima precisão, execute o bce-reranker-base_v1 diretamente no conjunto de resultados mesclado.

Análise de perguntas e respostas

O chatbot de tickets aplica políticas diferentes com base na fonte de dados para controlar como cada tipo de conteúdo flui para o prompt do modelo de linguagem grande (LLM):

  • Conteúdo da base de conhecimento — fornecido diretamente ao usuário sem processamento pelo LLM

  • Documentação de ajuda — resumida e formatada pelo LLM, pois o HTML segmentado pode conter artefatos de layout e texto duplicado

  • Fallback do LLM — o LLM responde diretamente apenas quando a base de conhecimento e a documentação não conseguem fornecer um resultado relevante

  • Tickets históricos — fornecidos apenas como título e URL, direcionando os usuários para o ticket completo

Cada resposta inclui links para os documentos de source. Se a resposta não resolver totalmente o problema, os usuários podem seguir os links para obter informações mais completas.

Durante os testes, pontue pares de perguntas e respostas para avaliar a eficácia da política de recuperação. Escreva várias variantes de políticas e compare as pontuações para determinar a configuração de recuperação mais eficaz:

prompt = f\'\'\'请整理并格式化下面的内容并整理输出格式,
        {prompt_content}
        基于自己的能力做出回答,我的问题是:{question}。
        \'\'\'

Conectar a um chatbot do DingTalk

Você pode usar o Streamlit para criar uma aplicação web ou conectar-se ao chatbot do DingTalk. A aplicação web serve para autoteste e gerenciamento de documentos. O chatbot do DingTalk permite que todos os usuários acessem o serviço.

Em cada sessão de perguntas e respostas em um grupo do DingTalk, é necessário estabelecer uma nova conexão no nível do banco de dados. Conexões frequentes e de curta duração consomem tempo e memória. Se as conexões não forem liberadas prontamente, a instância poderá atingir seu limite de conexões. Utilize um pool de conexões para evitar isso — implemente um em sua aplicação ou use o pool de conexões PgBouncer integrado do ApsaraDB RDS for PostgreSQL.

Exemplo

Este exemplo demonstra a recuperação multicaminho usando um conjunto de dados de amostra sobre PostgreSQL, MySQL e SQL Server. A consulta é 介绍一下postgresql.

Preparar dados

  1. Instale as extensões necessárias usando uma conta privilegiada:

    Importante
    • Antes de instalar o pg_jieba, adicione pg_jieba ao value do parâmetro shared_preload_libraries. Para mais informações sobre como modificar o parâmetro shared_preload_libraries, consulte Configurar parâmetros da instância.

    • Execute a instrução SELECT * FROM pg_extension; para visualizar as extensões instaladas.

    CREATE EXTENSION IF NOT EXISTS pg_jieba;
    CREATE EXTENSION IF NOT EXISTS vector;
    CREATE EXTENSION IF NOT EXISTS rum;
    CREATE EXTENSION IF NOT EXISTS rds_embedding;
  2. Crie um gateway NAT para a Virtual Private Cloud (VPC) onde a instância RDS reside, permitindo que a instância RDS acesse modelos externos. Para mais informações, consulte Usar a extensão rds_embedding para gerar vetores.

    Nota

    Por padrão, não é possível conectar-se a uma instância RDS pela Internet. Ao usar um modelo grande externo, como o modelo de text embedding fornecido pelo Alibaba Cloud Model Studio, você deve criar um gateway NAT para a VPC onde a instância RDS reside, permitindo que a instância RDS acesse modelos externos.

  3. Execute as seguintes instruções SQL no banco de dados desejado para criar as tabelas de teste chamadas doc e embed, e crie índices para as tabelas:

    --Create the doc test table and an index for the table.
    DROP TABLE IF EXISTS doc;
    
    CREATE TABLE doc (
        id bigserial PRIMARY KEY,
        title character varying(255) UNIQUE,
        key_word character varying(255) DEFAULT \'\'
    );
    
    CREATE INDEX doc_gin ON doc
    USING GIN (to_tsvector(\'jiebacfg\', key_word));
    
    --Create the embed test table and an index for the table.
    DROP TABLE IF EXISTS embed;
    
    CREATE TABLE embed (
        id bigserial PRIMARY KEY,
        doc_id integer,
        content text,
        embedding vector(1536),
        ts_vector_extra tsvector
    );
    
    CREATE INDEX ON embed
    USING hnsw (embedding vector_cosine_ops)
    WITH (
        m = 16,
        ef_construction = 64
    );
  4. Execute as seguintes instruções SQL para criar um trigger. Quando uma linha na tabela embed for atualizada ou dados forem inseridos na linha, a coluna ts_vector_extra será atualizada automaticamente.

    -- Convert the text into a tsvector value for full-text search based on keywords.
    CREATE TRIGGER embed_tsvector_update
    BEFORE UPDATE OR INSERT
    ON embed
    FOR EACH ROW
    EXECUTE PROCEDURE tsvector_update_trigger(\'ts_vector_extra\', \'public.jiebacfg\', \'content\');
  5. Execute as seguintes instruções SQL. Sempre que uma inserção ou atualização for realizada na tabela embed, um embedding será gerado com base no conteúdo inserido ou atualizado e armazenado na coluna embedding.

    Importante

    Neste exemplo, utiliza-se o modelo de text embedding fornecido pelo Alibaba Cloud Model Studio. Você deve ativar o Alibaba Cloud Model Studio e obter a chave de API necessária. Para mais informações, consulte Obter uma chave de API.

    -- Convert the question into an embedding. Specify the api_key parameter based on your business requirements.
    CREATE OR REPLACE FUNCTION update_embedding()
    RETURNS TRIGGER AS $$
    BEGIN
        NEW.embedding := rds_embedding.get_embedding_by_model(\'dashscope\', \'sk-****\', NEW.content)::real[];
        RETURN NEW;
    END;
    $$ LANGUAGE plpgsql;
    CREATE TRIGGER set_embedding BEFORE INSERT OR UPDATE ON embed FOR EACH ROW EXECUTE FUNCTION update_embedding();
  6. Insira dados de teste na tabela.

    INSERT INTO doc(id, title, key_word) VALUES
    (1, \'PostgreSQL介绍\', \'PostgreSQL 插件\'),
    (2, \'MySQL介绍\', \'MySQL MGR\'),
    (3, \'SQL Server介绍\', \'SQL Server Microsoft\');
    
    INSERT INTO embed(doc_id, content) VALUES
    (1, \'PostgreSQL是以加州大学伯克利分校计算机系开发的POSTGRES,版本 4.2为基础的对象关系型数据库管理系统(ORDBMS)。 POSTGRES领先的许多概念在很久以后才出现在一些商业数据库系统中\'),
    (1, \'PostgreSQL是最初的伯克利代码的开源继承者。 它支持大部分SQL标准并且提供了许多现代特性:复杂查询、外键、触发器、可更新视图、事务完整性、多版本并发控制,同样,PostgreSQL可以用许多方法扩展,比如,通过增加新的:数据类型、函数、操作符、聚集函数、索引方法、过程语言\'),
    (1, \'并且,因为自由宽松的许可证,任何人都可以以任何目的免费使用、修改和分发PostgreSQL,不管是私用、商用还是学术研究目的。 \'),
    (1, \'Ganos插件和PostGIS插件不能安装在同一个Schema下\'),
    (1, \'丰富的生态系统:有大量现成的插件和扩展可供使用,比如PostGIS(地理信息处理)、TimescaleDB(时间序列数据库)、pg_stat_statements(性能监控)等,能够满足不同场景的需要\');
    
    INSERT INTO embed(doc_id, content) VALUES
    (2, \'MySQL名称的起源不明。 10多年来,我们的基本目录以及大量库和工具均采用了前缀"my"。 不过,共同创办人Monty Widenius的女儿名字也叫"My"。 时至今日,MySQL名称的起源仍是一个迷,即使对我们也一样\'),
    (2, \'MySQL软件采用双许可方式。 用户可根据GNU通用公共许可(http://www.fsf.org/licenses/)条款,将MySQL软件作为开放源码产品使用,或从MySQL AB公司购买标准的商业许可证。 关于我方许可策略的更多信息,请参见http://www.mysql.com/company/legal/licensing/。 \'),
    (2, \'组复制MySQL Group Replication(简称MGR)是MySQL官方在已有的Binlog复制框架之上,基于Paxos协议实现的一种分布式复制形态。 RDS MySQL集群系列实例支持组复制。 本文介绍如何使复制方式为组复制。 使用了组复制的MySQL集群能够基于分布式Paxos协议自我管理,具有很强的数据可靠性和数据一致性。 相比传统主备复制方式,组复制具有以下优势:数据的强一致性,数据的强可靠性,全局事务强一致性\');
    
    INSERT INTO embed(doc_id, content) VALUES
    (3, \'Microsoft SQL Server是一种关系数据库管理系统 (RDBMS)。 应用程序和工具连接到SQL Server实例或数据库,并使用Transact-SQL (T-SQL)进行通信。 \'),
    (3, \'SQL Server 2022 (16.x)在早期版本的基础上构建,旨在将SQL Server发展成一个平台,以提供开发语言、数据类型、本地或云环境以及操作系统选项。 \'),
    (3, \'SQL Server在企业级应用中广受欢迎,与其他Microsoft产品(如Excel、Power BI)无缝集成,便于数据分析\');

Executar recuperação multicaminho

Execute as seguintes instruções SQL para recuperar o texto da consulta "introduzir postgresql" usando múltiplos métodos e classificar os documentos relevantes por similaridade.

-- The text that you want to query.
WITH query AS (
    SELECT \'介绍一下postgresql\' AS query_text
),
-- Convert the question into an embedding. Replace sk-**** with the API key obtained from Alibaba Cloud Model Studio.
query_embedding AS (
    SELECT rds_embedding.get_embedding_by_model(\'dashscope\', \'sk-****\', query.query_text)::real[]::vector AS embedding
    FROM query
),
-- Document keyword search: score by ts_rank (higher = better match).
first_method AS (
    SELECT
        id,
        title,
        ts_rank(to_tsvector(\'jiebacfg\', doc.key_word),
                to_tsquery(replace(text(plainto_tsquery(\'jiebacfg\', (SELECT query_text FROM query))), \'&\', \'|\'))) AS score,
        \'doc_key_word\' AS method
    FROM doc
    WHERE
        to_tsvector(\'jiebacfg\', doc.key_word) @@
        to_tsquery(replace(text(plainto_tsquery(\'jiebacfg\', (SELECT query_text FROM query))), \'&\', \'|\'))
    ORDER BY
        score DESC
    LIMIT 3
),
-- Content keyword search: score by RUM <=> operator (lower = better match).
second_method AS (
    SELECT
        id,
        doc_id,
        content,
        to_tsvector(\'jiebacfg\', content) <=>
        to_tsquery(replace(text(plainto_tsquery(\'jiebacfg\', (SELECT query_text FROM query))), \'&\', \'|\')) AS score,
        \'content_key_word\' AS method
    FROM embed
    WHERE
        to_tsvector(\'jiebacfg\', content) @@
        to_tsquery(replace(text(plainto_tsquery(\'jiebacfg\', (SELECT query_text FROM query))), \'&\', \'|\'))
    ORDER BY
        score
    LIMIT 3
),
-- Embedding search: score by cosine distance <=> (lower = better match).
third_method AS (
    SELECT
        embed.id,
        embed.doc_id,
        embed.content,
        embedding <=> (SELECT embedding FROM query_embedding LIMIT 1) AS score,
        \'embedding\' AS method
    FROM embed
    ORDER BY score
    LIMIT 3
)
-- Join to retrieve document titles and combine results from all three methods.
SELECT
    first_method.title,
    embed.id AS chunk_id,
    SUBSTRING(embed.content FROM 1 FOR 30),
    first_method.score,
    first_method.method
FROM first_method
LEFT JOIN embed ON first_method.id = embed.doc_id
UNION
SELECT
    doc.title,
    second_method.id AS chunk_id,
    SUBSTRING(second_method.content FROM 1 FOR 30),
    second_method.score,
    second_method.method
FROM second_method
LEFT JOIN doc ON second_method.doc_id = doc.id
UNION
SELECT
    doc.title,
    third_method.id AS chunk_id,
    SUBSTRING(third_method.content FROM 1 FOR 30),
    third_method.score,
    third_method.method
FROM third_method
LEFT JOIN doc ON third_method.doc_id = doc.id
ORDER BY method, score;

Saída:

     title      | chunk_id |                          substring                           |        score         |      method
----------------+----------+--------------------------------------------------------------+----------------------+------------------
 PostgreSQL介绍 |        3 | PostgreSQL是最初的伯克利代码的开源继承者。 它支持大           |   13.159472465515137 | content_key_word
 PostgreSQL介绍 |        2 | PostgreSQL是以加州大学伯克利分校计算机系开发的PO             |     16.4493408203125 | content_key_word
 PostgreSQL介绍 |        4 | 并且,因为自由宽松的许可证,任何人都可以以任何目的免费使用、 |     16.4493408203125 | content_key_word
 PostgreSQL介绍 |        6 | 丰富的生态系统:有大量现成的插件和扩展可供使用,比如Post     | 0.020264236256480217 | doc_key_word
 PostgreSQL介绍 |        5 | Ganos插件和PostGIS插件不能安装在同一个Schem                  | 0.020264236256480217 | doc_key_word
 PostgreSQL介绍 |        3 | PostgreSQL是最初的伯克利代码的开源继承者。 它支持大           | 0.020264236256480217 | doc_key_word
 PostgreSQL介绍 |        2 | PostgreSQL是以加州大学伯克利分校计算机系开发的PO             | 0.020264236256480217 | doc_key_word
 PostgreSQL介绍 |        4 | 并且,因为自由宽松的许可证,任何人都可以以任何目的免费使用、 | 0.020264236256480217 | doc_key_word
 PostgreSQL介绍 |        2 | PostgreSQL是以加州大学伯克利分校计算机系开发的PO             |   0.2546271233144539 | embedding
 PostgreSQL介绍 |        3 | PostgreSQL是最初的伯克利代码的开源继承者。 它支持大           |  0.28679098231865074 | embedding
 PostgreSQL介绍 |        6 | 丰富的生态系统:有大量现成的插件和扩展可供使用,比如Post     |  0.41783296077761967 | embedding

Todos os três métodos de recuperação retornam resultados para PostgreSQL介绍, confirmando que as abordagens baseadas em palavras-chave, conteúdo e embedding identificam corretamente o documento.

Tópicos relacionados

Para mais informações sobre as melhores práticas do ApsaraDB RDS for PostgreSQL em RAG, consulte os seguintes tópicos: