Uso de SQL para gerar automaticamente descrições de objetos

O recurso Descrições de objetos baseadas no Cortex permite que você use a função COMPLETE do Snowflake Cortex para gerar automaticamente descrições para tabelas, exibições e colunas. Este recurso utiliza modelos de linguagem grande (LLMs) hospedados pelo Snowflake para avaliar metadados de objetos e, se desejado, dados de amostra para gerar uma descrição.

Este tópico descreve como usar um procedimento armazenado para gerar descrições programaticamente. Para obter informações sobre como usar Snowsight para gerar as descrições, consulte Gerar descrições com o Snowflake Cortex.

Geração de uma descrição

O procedimento armazenado AI_GENERATE_TABLE_DESC gera automaticamente uma descrição para uma tabela e exibição. Ele também pode gerar descrições para as colunas dessa tabela ou exibição.

O procedimento armazenado AI_GENERATE_TABLE_DESC aceita dois argumentos:

  • O nome da tabela ou exibição para a qual você deseja gerar uma descrição.

  • Um objeto de configuração opcional que permite fazer o seguinte:

    • Gerar descrições para as colunas da tabela ou exibição especificada.

    • Usar dados de amostra da tabela ou exibição para potencialmente melhorar a precisão das descrições das colunas.

Exemplo: Gerar uma descrição de tabela
CALL AI_GENERATE_TABLE_DESC( 'my_table');
Copy
Exemplo: Gerar descrições de tabela e coluna sem usar dados de amostra
CALL AI_GENERATE_TABLE_DESC(
  'mydb.sch1.hr_data',
  {
    'describe_columns': true,
    'use_table_data': false
  });
Copy
Exemplo: Gerar descrições de exibição e coluna usando dados de amostra para melhorar a precisão
CALL AI_GENERATE_TABLE_DESC(
  'mydb.sch1.v1',
  {
    'describe_columns': true,
    'use_table_data': true
  });
Copy

Para obter a sintaxe completa do procedimento armazenado, consulte AI_GENERATE_TABLE_DESC.

Como trabalhar com a resposta

O procedimento armazenado AI_GENERATE_TABLE_DESC retorna um objeto JSON que contém as descrições geradas, juntamente com informações gerais sobre a tabela e as colunas. Dentro desse objeto, o campo description contém a descrição gerada.

Suponha que você tenha criado a seguinte tabela:

CREATE OR REPLACE TABLE mydb.sch1.hr_data (fname VARCHAR, age INTEGER);

INSERT INTO hr_data (fname, age)
    VALUES
        ('Thomas',    44),
        ('Katherine', 29),
        ('Lisa',      29);
Copy

Dada essa tabela, veja a seguir um exemplo do objeto JSON retornado por AI_GENERATE_TABLE_DESC:

{
  "COLUMNS": [
    {
      "database_name": "mydb",
      "description": "The first name of the employee.",
      "name": "FNAME",
      "schema_name": "sch1",
      "table_name": "hr_data"
    }
    {
      "database_name": "mydb",
      "description": "A column holding data of type DecimalType representing age values.",
      "name": "AGE",
      "schema_name": "sch1",
      "table_name": "hr_data"
    },
  ],
  "TABLE": [
    {
      "database_name": "mydb",
      "description": " The table contains records of employee data, specifically demographic information. Each record includes an employee's age and name.",
      "name": "hr_data",
      "schema_name": "sch1"
    }
  ]
}

Para obter mais informações sobre cada campo JSON, consulte Retornos.

Definir descrições geradas como comentários

Para definir uma descrição gerada como um comentário em uma tabela, exibição ou coluna, você deve executar manualmente uma instrução SQL que inclua o parâmetro SET COMMENT. Por exemplo, para salvar uma descrição gerada para uma tabela t1, execute ALTER TABLE t1 SET COMMENT = 'ai generated description';.

Você pode escrever um código personalizado para gerar e salvar descrições automaticamente. Para obter exemplos de procedimentos armazenados que fazem isso, consulte Exemplos.

Requisitos de controle de acesso

Os usuários devem ter os seguintes privilégios e funções para chamar o procedimento armazenado AI_GENERATE_TABLE_DESC:

  • Privilégio SELECT na tabela ou exibição.

  • Função de banco de dados SNOWFLAKE.CORTEX_USER.

Disponibilidade do recurso

Sua região deve oferecer suporte ao LLM usado pelo Snowflake Cortex (como o Mistral-7b e o Llama 3.1-8b) para gerar as descrições. Verifique a disponibilidade da função COMPLETE. Se a função COMPLETE não for aceita em sua região, você deverá ativar a inferência entre regiões para usar o recurso.

Uso de dados de amostra

Ao gerar uma descrição para uma coluna, é possível se basear apenas em metadados ou optar por usar dados de amostra para melhorar a descrição alimentada pelo Snowflake Cortex. Os dados de amostra referem-se aos dados de uma coluna específica que é avaliada quando você usa o Snowflake Cortex para gerar descrições. Se optar por usar dados de amostra, o Snowflake usará uma parte dos dados de amostra para gerar a descrição, o que resulta em descrições mais precisas. Os dados de amostra não são armazenados pelo Snowflake como Dados de Uso.

Considerações sobre custo

A geração de descrições incorre nos seguintes custos:

  • Créditos consumidos pelo warehouse em uso.

  • Créditos cobrados pelo uso do Snowflake Cortex com LLMs menores, como Mistral-7b e Llama 3.1-8b. Essas cobranças aparecem em uma fatura como AI-Services, incluindo todos os usos do Snowflake Cortex.

Limitações

Você não pode gerar descrições de colunas para objetos com mais de mil colunas.

Exemplos

Exemplo: Gerar descrições e defini-las como comentários

Etapa 1: Criar um procedimento armazenado

O procedimento armazenado a seguir faz o seguinte:

  • Gera automaticamente descrições para todas as tabelas (e as colunas delas) em um esquema.

  • Define essas descrições como comentários nas tabelas e colunas.

CREATE OR REPLACE PROCEDURE DESCRIBE_TABLES_SET_COMMENT (database_name STRING, schema_name STRING,
  set_table_comment BOOLEAN,
  set_column_comment BOOLEAN)
  RETURNS STRING
  LANGUAGE PYTHON
  RUNTIME_VERSION = '3.10'
  PACKAGES=('snowflake-snowpark-python','joblib')
  HANDLER = 'main'
AS
$$
import json
from joblib import Parallel, delayed
import multiprocessing

def generate_descr(session, database_name, schema_name, table, set_table_comment, set_column_comment):
  table_name =  table['TABLE_NAME']
  async_job = session.sql(f"CALL AI_GENERATE_TABLE_DESC( '{database_name}.{schema_name}.{table_name}',{{'describe_columns': true, 'use_table_data': true}})").collect_nowait()
  result = async_job.result()
  output = json.loads(result[0][0])
  columns_ret = output["COLUMNS"]
  table_ret = output["TABLE"][0]

  table_description = table_ret["description"]
  table_name = table_ret["name"]
  database_name = table_ret["database_name"]
  schema_name = table_ret["schema_name"]

  if (set_table_comment):
      table_description = table_description.replace("'", "\\'")
      session.sql(f"""ALTER TABLE {database_name}.{schema_name}.{table_name} SET COMMENT = '{table_description}'""").collect()

  for column in columns_ret:
      column_description = column["description"];
      column_name = column["name"];
      if not column_name.isupper():
        column_name = '"' + column_name + '"'

      if (set_column_comment):
          column_description = column_description.replace("'", "\\'")
          session.sql(f"""ALTER TABLE  {database_name}.{schema_name}.{table_name} MODIFY COLUMN {column_name}  COMMENT '{column_description}'""").collect()

  return 'Success';

def main(session, database_name, schema_name, set_table_comment, set_column_comment):

    schema_name = schema_name.upper()
    database_name = database_name.upper()
    tablenames = session.sql(f"""SELECT table_name
                      FROM {database_name}.information_schema.tables
                      WHERE table_schema = '{schema_name}'
                      AND table_type = 'BASE TABLE'""").collect()
    try:
        Parallel(n_jobs=multiprocessing.cpu_count(), backend="threading")(
                delayed(generate_descr)(
                    session,
                    database_name,
                    schema_name,
                    table,
                    set_table_comment,
                    set_column_comment,
                ) for table in tablenames
            )
        return 'Success'
    except Exception as e:
        # Catch and return the error message
        return f"An error occurred: {str(e)}"
$$;
Copy

Etapa 2: Chamar o procedimento armazenado

Supondo que seu esquema seja denominado my_db.sch1, chame o procedimento armazenado da seguinte maneira para gerar descrições para tabelas e colunas:

CALL describe_tables_set_comment('my_db', 'sch1', true, true);
Copy

Você pode executar um comando DESC TABLE para verificar se as descrições geradas foram definidas como comentários em uma tabela.

Exemplo: Gerar descrições e salvá-las em uma tabela de catálogo

Etapa 1: Criar um procedimento armazenado

O procedimento armazenado a seguir faz o seguinte:

  • Gera automaticamente descrições para todas as tabelas (e as colunas delas) em um esquema.

  • Preenche uma tabela de catálogo, em que cada linha representa uma tabela ou coluna com uma descrição gerada.

CREATE OR REPLACE PROCEDURE DESCRIBE_TABLES_SET_CATALOG (database_name string, schema_name string, catalog_table string)
  RETURNS STRING
  LANGUAGE PYTHON
  RUNTIME_VERSION = '3.10'
  PACKAGES=('snowflake-snowpark-python','joblib')
  HANDLER = 'main'
AS
$$
import json
from joblib import Parallel, delayed
import multiprocessing

def generate_descr(session, database_name, schema_name, table, catalog_table):
    table_name =  table['TABLE_NAME']
    async_job = session.sql(f"CALL AI_GENERATE_TABLE_DESC( '{database_name}.{schema_name}.{table_name}',{{'describe_columns': true, 'use_table_data': true}})").collect_nowait()
    result = async_job.result()
    output = json.loads(result[0][0])
    columns_ret = output["COLUMNS"]
    table_ret = output["TABLE"][0]

    table_description = table_ret["description"]
    table_description = table_description.replace("'", "\\'")
    table_name = table_ret["name"]
    database_name = table_ret["database_name"]
    schema_name = table_ret["schema_name"]

    session.sql(f"""INSERT INTO {catalog_table} (domain, description, name, database_name, schema_name, table_name)
                          VALUES ('TABLE', '{table_description}', '{table_name}', '{database_name}', '{schema_name}', null)""").collect()

    for column in columns_ret:
        column_description = column["description"];
        column_description = column_description.replace("'", "\\'")
        column_name = column["name"];
        if not column_name.isupper():
            column_name = '"' + column_name + '"'
        session.sql(f"""INSERT INTO {catalog_table} (domain, description, name, database_name, schema_name, table_name)
                          VALUES ('COLUMN', '{column_description}', '{column_name}', '{database_name}', '{schema_name}', '{table_name}')""").collect()

    return 'Success';

def main(session, database_name, schema_name, catalog_table):

    schema_name = schema_name.upper()
    database_name = database_name.upper()
    catalog_table_upper = catalog_table.upper()
    tablenames = session.sql(f"""SELECT table_name
                      FROM {database_name}.information_schema.tables
                      WHERE table_schema = '{schema_name}'
                      AND table_type = 'BASE TABLE'
                      AND table_name !='{catalog_table_upper}'""").collect()
    try:
        Parallel(n_jobs=multiprocessing.cpu_count(), backend="threading")(
                delayed(generate_descr)(
                    session,
                    database_name,
                    schema_name,
                    table,
                    catalog_table,
                ) for table in tablenames
            )
        return 'Success'
    except Exception as e:
        # Catch and return the error message
        return f"An error occurred: {str(e)}"
$$;
Copy

Etapa 2: Criar a tabela de catálogo a ser preenchida

Use o código a seguir para criar a tabela de catálogo em que as descrições das tabelas e colunas são armazenadas.

CREATE OR REPLACE TABLE catalog_table (
  domain VARCHAR,
  description VARCHAR,
  name VARCHAR,
  database_name VARCHAR,
  schema_name VARCHAR,
  table_name VARCHAR
  );
Copy

Etapa 3: Chamar os procedimentos armazenados

Supondo que seu esquema seja denominado my_db.sch1, chame o procedimento armazenado da seguinte forma:

CALL describe_tables_set_catalog('my_db', 'sch1', 'catalog_table');
Copy