Discourse AI + Explorador de Dados?

Decidi que queria uma consulta específica hoje. Sem querer incomodar meus engenheiros (demais), decidi pedir ao ChatGPT para escrever uma consulta de explorador de dados para mim com base no que eu sabia e no que eu sabia que queria. Fiquei bastante impressionado que ele acertou tudo (além de adicionar um ponto e vírgula no final que não deveria ter).

Aqui está a consulta que ele construiu para mim quando dei alguns parâmetros e pedi para ele construir uma consulta que me desse um total acumulado de todos os usuários por mês:

SELECT 
    timeframe "Mês, Ano",
    user_count as "Usuários Adicionados Neste Mês",
    SUM(user_count) OVER (ORDER BY TO_DATE(timeframe, 'Month, YYYY'))::integer as "Usuários (Total Acumulado)"
FROM (
    SELECT 
        TO_CHAR(u.created_at, 'Month, YYYY') as "timeframe",
        count(u.id) as "user_count"
    FROM 
        users u
    GROUP BY 
        timeframe
    ORDER BY
        timeframe
) subquery
ORDER BY 
    TO_DATE(timeframe, 'Month, YYYY')

Houve alguma consideração para integrar IA na área do explorador de dados? :slight_smile:

12 curtidas

Uma postagem foi dividida em um novo tópico: Poderíamos reabrir o tópico para compartilhar consultas do Explorer?

Gostaria de compartilhar o prompt, a versão do ChatGPT e o modelo usado com o ChatGPT. :slightly_smiling_face:

Por exemplo


ChatGPT Versão de Agosto de 3
Modelo: GPT-3.5
Prompt

O que é o Discourse Data Explorer?

Conclusão

Até minha última atualização em setembro de 2021, não há uma ferramenta ou produto específico chamado “Discourse Data Explorer” que eu conheça. No entanto, posso fornecer uma explicação geral com base nas informações disponíveis até aquele momento.

Discourse é um software de fórum moderno e de código aberto que permite que comunidades online tenham discussões e interações. É amplamente utilizado para hospedar discussões, fóruns de perguntas e respostas e comunidades de suporte. O Discourse oferece um rico conjunto de recursos para gerenciar discussões, engajamento de usuários e moderação.

Um “Data Explorer” (Explorador de Dados) geralmente se refere a uma ferramenta ou recurso que permite aos usuários consultar e explorar dados dentro de um sistema ou plataforma. É frequentemente usado no contexto de bancos de dados ou plataformas de análise de dados.

Considerando os dois componentes, “Discourse Data Explorer” poderia potencialmente ser uma ferramenta ou plugin hipotético que oferece aos usuários a capacidade de explorar e consultar dados dentro de um fórum Discourse. Isso poderia ser útil para administradores de comunidade ou analistas de dados que desejam obter insights sobre o comportamento do usuário, tendências de conteúdo ou outras métricas relacionadas às discussões que ocorrem no fórum.

É importante notar que minhas informações podem estar desatualizadas e novos desenvolvimentos podem ter ocorrido após minha última atualização. Se “Discourse Data Explorer” for de fato um produto ou recurso real, recomendo pesquisar as informações mais recentes para obter detalhes precisos sobre suas capacidades e uso.


O motivo da pergunta é que você tem uma excelente ideia e foi bom vê-la compartilhada, mas no futuro outros podem tentar fazer outra pergunta e o compartilhamento do prompt, da conclusão e dos detalhes do ChatGPT são necessários se a ajuda for necessária para entender quaisquer problemas.

Observação: Sou um moderador de categoria no fórum Discourse da OpenAI, então tenho muita experiência com aqueles que buscam ajuda com prompts do ChatGPT. (ref)


A versão do ChatGPT pode ser encontrada no canto inferior direito, abaixo de onde um prompt é inserido

O modelo pode ser encontrado perto do topo da página, no meio

image

1 curtida

Eu…não gosto deste bot.

1 curtida

Expandindo um pouco essa ideia, permitir que os usuários interajam com aplicativos usando linguagem natural é praticamente o meu sonho em relação à IA. Com o Discourse e o plugin Data Explorer, seria possível obter resultados de prompts como “retornar uma lista de usuários que expressaram interesse em moderação”. Ou ainda melhor, “enviar um convite para o grupo community_moderation para todos os usuários que expressaram interesse em moderação”.

Voltando ao que é possível agora:

Testando com ChatGPT 3.0 e 3.5, obtive bons resultados ao pedir para ele retornar uma consulta PostgreSQL e copiar a estrutura das tabelas de meu interesse para o prompt. Por exemplo:

Resumo

Eu: Retorne uma consulta postgresql que liste todos os nomes de usuário para usuários que estão no grupo “community_moderators” para a seguinte estrutura de banco de dados:

# == Schema Information
#
# Table name: groups
#
#  id                                 :integer          not null, primary key
#  name                               :string           not null
#
# == Schema Information
#
# Table name: group_users
#
#  id                 :integer          not null, primary key
#  group_id           :integer          not null
#
# == Schema Information
#
# Table name: users
#
#  id                        :integer          not null, primary key
#  username                  :string(60)       not null

ChatGPT 3.5: Claro, aqui está uma consulta PostgreSQL que lista todos os nomes de usuário para usuários que estão no grupo “community_moderators” com base na estrutura de banco de dados fornecida:

SELECT u.username
FROM users u
JOIN group_users gu ON u.id = gu.user_id
JOIN groups g ON gu.group_id = g.id
WHERE g.name = 'community_moderators';

Esta consulta recupera os nomes de usuário da tabela users juntando-a com a tabela group_users pelo ID do usuário e, em seguida, juntando-a com a tabela groups pelo ID do grupo. Ela filtra os resultados para incluir apenas os nomes de usuário de usuários que estão no grupo “community_moderators”.

Eu: Aqui está a estrutura das tabelas de tópicos e posts da aplicação:

# == Schema Information
#
# Table name: topics
#
#  id                        :integer          not null, primary key
#  title                     :string           not null
#
# == Schema Information
#
# Table name: posts
#
#  id                      :integer          not null, primary key
#  user_id                 :integer
#  topic_id                :integer          not null
#  post_number             :integer          not null
#  raw                     :text             not null

Retorne uma consulta que liste todos os IDs de tópicos, IDs de posts e nomes de usuários para todos os posts criados por membros do grupo “community_moderators” que deem alguma indicação de que o autor está interessado em como moderar discussões polarizadas.

ChatGPT 3.5: Para conseguir isso, você precisaria juntar as tabelas posts, topics, users e group_users com base no esquema fornecido. Aqui está uma consulta PostgreSQL que recupera as informações solicitadas:

SELECT t.id AS topic_id, p.id AS post_id, u.username
FROM posts p
JOIN topics t ON p.topic_id = t.id
JOIN users u ON p.user_id = u.id
JOIN group_users gu ON u.id = gu.user_id
JOIN groups g ON gu.group_id = g.id
WHERE g.name = 'community_moderators'
  AND (p.raw ILIKE '%moderate%' OR p.raw ILIKE '%polarizing%' OR p.raw ILIKE '%discussion%');

Esta consulta recupera o topic_id, post_id e username para todos os posts criados por membros do grupo “community_moderators” que contêm certas palavras-chave indicando interesse em moderar discussões polarizadas. O operador ILIKE é usado para correspondência de padrões sem distinção entre maiúsculas e minúsculas na coluna raw da tabela posts.

Por favor, note que esta consulta assume que as palavras-chave que você forneceu são indicativas dos posts desejados. Pode ser necessário ajustar ou expandir as palavras-chave com base em seus critérios específicos.

Eu: Escreva uma consulta que retorne uma contagem de posts criados em cada período semanal desde setembro de 2022, criados por membros do grupo community_moderators que indicam algum interesse em como moderar discussões polarizadas.

ChatGPT 3.5: Para conseguir isso, você pode usar a seguinte consulta PostgreSQL para retornar uma contagem de posts criados em cada período semanal desde setembro de 2022, criados por membros do grupo “community_moderators” que indicam interesse em como moderar discussões polarizadas:

SELECT
    DATE_TRUNC('week', p.created_at) AS week_start,
    COUNT(p.id) AS post_count
FROM
    posts p
JOIN
    users u ON p.user_id = u.id
JOIN
    group_users gu ON u.id = gu.user_id
JOIN
    groups g ON gu.group_id = g.id
WHERE
    g.name = 'community_moderators'
    AND (p.raw ILIKE '%moderate%'
         OR p.raw ILIKE '%polarizing%'
         OR p.raw ILIKE '%political%'
         OR p.raw ILIKE '%contentious%'
         OR p.raw ILIKE '%debate%'
         OR p.raw ILIKE '%controversial%'
         OR p.raw ILIKE '%discussion%')
    AND p.created_at >= '2022-09-01' -- Data de início: Setembro de 2022
GROUP BY
    week_start
ORDER BY
    week_start;

Esta consulta calcula a contagem de posts criados por membros do grupo “community_moderators” que contêm as palavras-chave especificadas em cada período semanal desde setembro de 2022. Ela agrupa os resultados por semana e os ordena cronologicamente. Por favor, certifique-se de ajustar os nomes das colunas e as relações entre as tabelas com base no seu esquema de banco de dados real.


Os resultados parecem bons para mim e representam os tipos de consultas que me levaram um bom tempo para escrever no passado. Assumo que seria possível treinar um modelo na estrutura do banco de dados do Discourse para que detalhes sobre a estrutura pudessem ser omitidos dos prompts.

4 curtidas

ah, isso é ótimo, eu tive resultados mistos com as consultas SQL do explorador de dados do chatGPT, mas fazer isso dessa forma faz muito sentido. eu nem pensei em fornecer a estrutura da tabela e pedir consultas PostgreSQL. :woman_facepalming:t2: obrigado pela dica @simon :slight_smile:

1 curtida

Existe algum lugar onde eu possa ver um esquema completo do(s) banco(s) de dados do Discourse?

1 curtida

oi @so-jordan-violet, eu só uso a ferramenta de tabela expansível (não sei o nome! kkk) no lado direito na interface de edição de consulta do explorador de dados:

2 curtidas

Se conseguirmos um documento baseado em texto (especialmente se estiver no formato de criação de tabela SQL), poderíamos fornecê-lo como instruções e provavelmente obter resultados bastante confiáveis.

1 curtida

Sim, temos este item no backlog da equipe de IA desde o início deste ano. A ideia é ingerir todo o esquema do Discourse (ou a parte importante) no prompt para permitir que ele crie quaisquer consultas.

Temos construído GitHub - discourse/data-explorer-examples: Examples for Discourse Data Explorer para ajudar a fornecer exemplos ao modelo, para que ele possa ser melhor fundamentado.

7 curtidas

Gostaria de ter mais tempo para analisar a base de código, mas você tem o SQL armazenado em algum lugar (publicamente) para os esquemas da tabela crest?

Estou trabalhando com LLM no meu próprio produto/empresa agora e adoraria brincar com isso.

1 curtida

ah, isso é ótimo. obrigado pelo link Falco! :slight_smile:

3 curtidas

Eu apenas gero isso diretamente do esquema

Algo como isto deve ajudá-lo a começar:


Tenho explorado todos os tipos de abordagens para fazer isso funcionar. Um problema, no entanto, é que o GPT 3.5 é tão direcionável que fica confuso.

Fornecer o esquema certamente ajuda, mas estou descobrindo que preciso ser ultra estratégico com exemplos.

Apenas contar uma história sobre “como contar os tópicos arquivados para um usuário” acaba direcionando uma consulta como “quantas postagens um usuário teve” para tópicos arquivados.

Eu estava pensando:

graph TD
A[Adicionar informações genéricas úteis] --> B[Gerar embeddings para a pergunta]
B --> C[Fazer uma busca de similaridade entre exemplos]
C --> D[Adicionar 3 exemplos ao prompt]
D --> E[Adicionar esquema ao prompt]
E --> F[Fazer a pergunta]
F --> G[Obter SQL]

Mas acontece que, a menos que os exemplos sejam espetaculares e você não inclua exemplos contrários… as coisas ficam ruins.

Estou começando a pensar que o melhor que temos por enquanto é apenas fornecer o esquema e um preâmbulo. Talvez adicionar outra etapa no final para validação e nova tentativa da consulta.

5 curtidas

Você teria um esquema completo que mostrasse os tipos de dados?

Não tenho certeza se cabe no orçamento de tokens, vou tentar

With ChatCPT 3.5 I’m getting unexpected results when I provide the full schema. For example, it keeps trying to find the topic author in the topic_users table.

I get successful results by limiting the information in the prompt to the tables and columns that are required to write the query. Also by adding some additional information about how the columns are used. For example:

# Table name: user_actions
#
#  id              :integer          not null, primary key
#  action_type     :integer          not null (:like=>1,:was_liked=>2,:new_topic=>4,:reply=>5,:response=>6,:mention=>7,:quote=>9,:edit=>11,:new_private_message=>12,:got_private_message=>13,:solved=>15,:assigned=>16)
#  user_id         :integer          not null (the user who is to be credited with the action)
#  target_topic_id :integer
#  acting_user_id  :integer (the user who performed the action, for example, a staff user can perform an action on behalf of a regular user)
#  created_at      :datetime         not null

Another way to look at getting this to work with the Data Explorer plugin would be to have users fill out a dynamically generate form that lists the data they are looking for, and what conditions they want to apply to the data. The prompt could be generated programmatically by Discourse, then sent to the LLM to have the query written.

Edit: @jordan-violet, this might be a useful starting point. Here’s an annotated partial schema that’s working for me. It works well as long as I limit my queries so that they can be answered by the provided schema. I’ve added additional details to the schema to clarify details that seem to confuse ChatGPT. The obvious downside of this approach is that a fully annotated schema will exceed ChatGPT 3.5’s token limit. I’ve included some information in the schema that could be omitted to reduce the number of tokens that are used.

Summary
# == Schema Information
#
# Table name: application_requests
#
#  id       :integer          not null, primary key
#  date     :date             not null
#  req_type :integer          not null ("http_total"=>0,"http_2xx"=>1,"http_background"=>2,"http_3xx"=>3,"http_4xx"=>4,"http_5xx"=>5,"page_view_crawler"=>6,"page_view_logged_in"=>7,"page_view_anon"=>8,"page_view_logged_in_mobile"=>9,"page_view_anon_mobile"=>10,"api"=>11,"user_api"=>12)
#  count    :integer          default(0), not null
#
# Table name: users
#
#  id                        :integer          not null, primary key
#  username                  :string(60)       not null
#  created_at                :datetime         not null
#  updated_at                :datetime         not null
#  name                      :string           (the user's real name)
#  last_posted_at            :datetime
#  active                    :boolean          default(FALSE), not null
#  username_lower            :string(60)       not null
#  last_seen_at              :datetime
#  admin                     :boolean          default(FALSE), not null
#  trust_level               :integer          not null
#  approved                  :boolean          default(FALSE), not null
#  approved_by_id            :integer
#  approved_at               :datetime
#  previous_visit_at         :datetime
#  suspended_at              :datetime
#  suspended_till            :datetime
#  date_of_birth             :date
#  ip_address                :inet
#  moderator                 :boolean          default(FALSE)
#  title                     :string
#  locale                    :string(10)
#  primary_group_id          :integer
#  registration_ip_address   :inet
#  staged                    :boolean          default(FALSE), not null
#  first_seen_at             :datetime
#  silenced_till             :datetime
#
# Table name: topics
#
#  id                        :integer          not null, primary key
#  title                     :string           not null
#  last_posted_at            :datetime
#  created_at                :datetime         not null
#  updated_at                :datetime         not null
#  views                     :integer          default(0), not null
#  posts_count               :integer          default(0), not null
#  user_id                   :integer          (the id of the user who created the topic)
#  last_post_user_id         :integer          not null (the id of the user who created the last post in the topic)
#  reply_count               :integer          default(0), not null
#  deleted_at                :datetime
#  highest_post_number       :integer          default(0), not null
#  like_count                :integer          default(0), not null
#  category_id               :integer
#  visible                   :boolean          default(TRUE), not null
#  moderator_posts_count     :integer          default(0), not null
#  closed                    :boolean          default(FALSE), not null
#  archived                  :boolean          default(FALSE), not null
#  bumped_at                 :datetime         not null
#  archetype                 :string           default("regular"), not null (can be set to either "regular" or "private_message")
#  slug                      :string
#  deleted_by_id             :integer          (the id of the user who deleted the topic)
#  participant_count         :integer          default(1)
#  word_count                :integer
#  excerpt                   :string
#  highest_staff_post_number :integer          default(0), not null
#
# Table name: posts
#
#  id                      :integer          not null, primary key
#  user_id                 :integer          (the id of the user who created the post)
#  topic_id                :integer          not null
#  post_number             :integer          not null (indicates the post's order in its topic)
#  raw                     :text             not null (the post's content)
#  created_at              :datetime         not null
#  updated_at              :datetime         not null
#  reply_to_post_number    :integer          (the post_number that the post is a reply to)
#  reply_count             :integer          default(0), not null
#  deleted_at              :datetime
#  like_count              :integer          default(0), not null
#  bookmark_count          :integer          default(0), not null
#  reads                   :integer          default(0), not null (the number of times the post has been read)
#  post_type               :integer          default(1), not null (:regular=>1, :moderator_action=>2, :small_action=>3, :whisper=>4)
#  last_editor_id          :integer          (the id of the user who last edited the post)
#  hidden                  :boolean          default(FALSE), not null
#  hidden_reason_id        :integer          (:flag_threshold_reached=>1,:flag_threshold_reached_again=>2,:new_user_spam_threshold_reached=>3,:flagged_by_tl3_user=>4,:email_spam_header_found=>5,:flagged_by_tl4_user=>6,:email_authentication_result_header=>7,:imported_as_unlisted=>8)
#  edit_reason             :string
#  word_count              :integer
#  wiki                    :boolean          default(FALSE), not null
#
# Table name: categories
#
#  id                                        :integer          not null, primary key
#  name                                      :string(50)       not null
#  topic_id                                  :integer          (the id of the topic that is used for the category's description)
#  topic_count                               :integer          default(0), not null
#  created_at                                :datetime         not null
#  updated_at                                :datetime         not null
#  user_id                                   :integer          not null (the id of the user who created the topic)
#  topics_year                               :integer          default(0)
#  topics_month                              :integer          default(0)
#  topics_week                               :integer          default(0)
#  slug                                      :string           not null
#  description                               :text
#  text_color                                :string(6)        default("FFFFFF"), not null
#  read_restricted                           :boolean          default(FALSE), not null
#  auto_close_hours                          :float
#  post_count                                :integer          default(0), not null
#  latest_post_id                            :integer
#  latest_topic_id                           :integer
#  position                                  :integer
#  parent_category_id                        :integer
#  posts_year                                :integer          default(0)
#  posts_month                               :integer          default(0)
#  posts_week                                :integer          default(0)
#  topics_day                                :integer          default(0)
#  posts_day                                 :integer          default(0)
#
# Table name: groups
#
#  id                                 :integer          not null, primary key
#  name                               :string           not null
#  created_at                         :datetime         not null
#  automatic                          :boolean          default(FALSE), not null
#  user_count                         :integer          default(0), not null
#  title                              :string
#  bio_raw                            :text             (the group's description)
#  allow_membership_requests          :boolean          default(FALSE), not null
#  full_name                          :string
#  visibility_level                   :integer          default(0), not null (who can see the group :public=>0, :logged_on_users=>1, :members=>2, :staff=>3, :owners=>4)
#  messageable_level                  :integer          default(0) (who can message the group :public=>0, :logged_on_users=>1, :members=>2, :staff=>3, :owners=>4)
#  mentionable_level                  :integer          default(0) (who can mention the group :public=>0, :logged_on_users=>1, :members=>2, :staff=>3, :owners=>4)
#  members_visibility_level           :integer          default(0), not null (who see the group's members :public=>0, :logged_on_users=>1, :members=>2, :staff=>3, :owners=>4)
#
# Table name: group_users
#
#  id                 :integer          not null, primary key
#  group_id           :integer          not null
#  user_id            :integer          not null
#  created_at         :datetime         not null
#
# Table name: user_actions
#
#  id              :integer          not null, primary key
#  action_type     :integer          not null (:like=>1,:was_liked=>2,:new_topic=>4,:reply=>5,:response=>6,:mention=>7,:quote=>9,:edit=>11,:new_private_message=>12,:got_private_message=>13,:solved=>15,:assigned=>16)
#  user_id         :integer          not null (the user who is to be credited with the action)
#  target_topic_id :integer
#  target_post_id  :integer
#  target_user_id  :integer          (for example, the id of the user whose post was liked)
#  acting_user_id  :integer          (the user who performed the action, for example, a staff user can perform an action on behalf of a regular user)
#  created_at      :datetime         not null
#  updated_at      :datetime         not null
#
# Table name: topic_views
#
#  topic_id   :integer          not null
#  viewed_at  :date             not null
#  user_id    :integer         (will be set if the topic was viewed by a logged in user)
#  ip_address :inet            (will be set if the topic was viewed by an anonymous user)
#
# Table name: user_visits
#
#  id         :integer          not null, primary key
#  user_id    :integer          not null
#  visited_at :date             not null
#  posts_read :integer          default(0)
#  mobile     :boolean          default(FALSE) (will be set to TRUE if the user visited on a mobile device)
#  time_read  :integer          default(0), not null (the value returned is the number of seconds of the visit)

Here’s a link to a chat session where I tried it out: https://chat.openai.com/share/d108c104-3aa3-45d9-9161-6da21d5b3a77

If you want to try expanding on this list, the easiest way to do it is to copy the schema from the bottom of the model you’re interested in, directly from the Discourse code: discourse/app/models at main · discourse/discourse · GitHub.

3 curtidas

Seria ótimo ter exatamente isso, mas uma lista abrangente de todas as tabelas. De onde você tirou isso?

EDIT: Ah, vejo seu link no final, desculpe. Provavelmente não terei tempo de juntar tudo. Estava esperando que houvesse um esquema SQL mestre em algum lugar :confused:

O problema é que apenas fornecer o esquema não é informação suficiente para o ChatGPT. Você precisa fornecer detalhes sobre coisas como:

  • o que os códigos inteiros de req_type em application_requests significam
  • para que serve a coluna user_id em topics
  • o que os códigos de action_type na tabela user_actions significam e qual a diferença entre as colunas user_id, target_user_id e acting_user_id dessa tabela

Com esses tipos de detalhes, o GPT 3.5 parece fazer um bom trabalho sem nenhum treinamento adicional. O problema então se torna que fornecer esse nível de detalhe sobre todo o banco de dados resultará no prompt excedendo o limite de tokens do ChatGPT (4096 tokens, incluindo o texto do prompt e a saída gerada). Se esse tipo de abordagem fosse usada, haveria a necessidade de uma maneira de limitar o que é enviado no prompt com base nas informações que o usuário queria obter da consulta do Data Explorer.

1 curtida

Essa não tem sido nada da minha experiência. Tive uma experiência bastante agradável com isso esta noite. Compartilharei alguns resultados mais tarde.

Isso me dá a sensação de uma função competente, mas muito de nível básico/júnior. Ela chega perto, eu digo o que fez de errado e ela conserta. Repetir.

Na verdade, estou gostando muito do que ela está me dando e do tempo que está me poupando com base no meu uso esta noite.

1 curtida

O GPT 3.5 lida com 16k sem problemas hoje

O problema começa a acontecer quando as solicitações são complicadas, levando em conta casos extremos e assim por diante

1 curtida