joaopedrocgf commited on
Commit
46e8b1c
·
1 Parent(s): fada342

Adicionando novas funcionalidades

Browse files
Files changed (4) hide show
  1. app.py +39 -7
  2. chat.py +49 -7
  3. know_graph.json +76 -0
  4. prompts.py +19 -0
app.py CHANGED
@@ -1,6 +1,6 @@
1
  import streamlit as st
2
  import os
3
- import shutil
4
  from dotenv import load_dotenv
5
  from langchain_openai import ChatOpenAI
6
  from langchain_core.prompts import ChatPromptTemplate
@@ -8,7 +8,7 @@ from langchain_community.document_loaders import DirectoryLoader, TextLoader
8
  from langchain_text_splitters import RecursiveCharacterTextSplitter
9
  from langchain_chroma import Chroma
10
  from langchain_huggingface import HuggingFaceEmbeddings
11
- from prompts import PROMPT_TEMPLATE
12
 
13
  # Configuração da página Streamlit
14
  st.set_page_config(page_title="Central da Visão - Assistente", page_icon="👁️")
@@ -26,6 +26,9 @@ if not OPENAI_API_KEY:
26
  CHROMA_PATH = "chroma_db"
27
  DOCS_PATH = "docs"
28
 
 
 
 
29
  @st.cache_resource
30
  def load_or_create_db():
31
  """
@@ -102,19 +105,42 @@ def get_response(query):
102
  prefix = "Usuário: " if msg["role"] == "user" else "Consultor: "
103
  chat_history_str += f"{prefix}{msg['content']}\n"
104
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105
  # Busca de documentos relevantes
106
  docs_chroma = db_chroma.similarity_search(query, k=5)
107
  context_text = "\n\n".join([doc.page_content for doc in docs_chroma])
108
 
 
 
 
 
 
 
109
  # Prompt
110
  prompt_template = ChatPromptTemplate.from_template(PROMPT_TEMPLATE)
111
- prompt = prompt_template.format(context=context_text, chat_history=chat_history_str, question=query)
112
-
 
113
  # Modelo de linguagem
114
- model = ChatOpenAI(model_name='gpt-3.5-turbo', openai_api_key=OPENAI_API_KEY)
115
  response = model.invoke(prompt)
116
 
117
- return response.content
118
 
119
  # Interface
120
  for message in st.session_state.messages:
@@ -129,7 +155,13 @@ if query := st.chat_input("Digite sua dúvida aqui..."):
129
  with st.chat_message("assistant"):
130
  with st.spinner("Consultando base de dados..."):
131
  try:
132
- response_text = get_response(query)
 
 
 
 
 
 
133
  st.markdown(response_text)
134
  st.session_state.messages.append({"role": "assistant", "content": response_text})
135
  except Exception as e:
 
1
  import streamlit as st
2
  import os
3
+ import json
4
  from dotenv import load_dotenv
5
  from langchain_openai import ChatOpenAI
6
  from langchain_core.prompts import ChatPromptTemplate
 
8
  from langchain_text_splitters import RecursiveCharacterTextSplitter
9
  from langchain_chroma import Chroma
10
  from langchain_huggingface import HuggingFaceEmbeddings
11
+ from prompts import PROMPT_TEMPLATE, CLASSIFIER_PROMPT
12
 
13
  # Configuração da página Streamlit
14
  st.set_page_config(page_title="Central da Visão - Assistente", page_icon="👁️")
 
26
  CHROMA_PATH = "chroma_db"
27
  DOCS_PATH = "docs"
28
 
29
+ with open('know_graph.json', 'r', encoding='utf-8') as f:
30
+ knowledge_graph_data = json.dumps(json.load(f), ensure_ascii=False)
31
+
32
  @st.cache_resource
33
  def load_or_create_db():
34
  """
 
105
  prefix = "Usuário: " if msg["role"] == "user" else "Consultor: "
106
  chat_history_str += f"{prefix}{msg['content']}\n"
107
 
108
+ model = ChatOpenAI(model_name='gpt-4o-mini', openai_api_key=OPENAI_API_KEY)
109
+
110
+ prompt_classifier = ChatPromptTemplate.from_template(CLASSIFIER_PROMPT)
111
+ classifier_input = prompt_classifier.format(
112
+ knowledge_graph=knowledge_graph_data,
113
+ chat_history=chat_history_str,
114
+ question=query
115
+ )
116
+
117
+ classification_response = model.invoke(classifier_input).content
118
+
119
+ cleaned_res = classification_response.replace("```json", "").replace("```", "").strip()
120
+ try:
121
+ classification = json.loads(cleaned_res)
122
+ except:
123
+ classification = {"estagio": "Nao identificado", "objecao_id": "nenhuma", "argumento_base": ""}
124
+
125
  # Busca de documentos relevantes
126
  docs_chroma = db_chroma.similarity_search(query, k=5)
127
  context_text = "\n\n".join([doc.page_content for doc in docs_chroma])
128
 
129
+ diretriz_venda = f"\n[DIRETRIZ ESTRATÉGICA]: Estágio: {classification.get('estagio')}. " \
130
+ f"Objeção: {classification.get('objecao_id')}. " \
131
+ f"Argumento sugerido: {classification.get('argumento_base')}"
132
+
133
+ full_context = context_text + diretriz_venda
134
+
135
  # Prompt
136
  prompt_template = ChatPromptTemplate.from_template(PROMPT_TEMPLATE)
137
+ prompt = prompt_template.format(context=full_context,
138
+ chat_history=chat_history_str,
139
+ question=query)
140
  # Modelo de linguagem
 
141
  response = model.invoke(prompt)
142
 
143
+ return response.content, classification
144
 
145
  # Interface
146
  for message in st.session_state.messages:
 
155
  with st.chat_message("assistant"):
156
  with st.spinner("Consultando base de dados..."):
157
  try:
158
+ response_text, info_vendas = get_response(query)
159
+
160
+ with st.expander("📊 Log de Vendas (Inteligência)"):
161
+ st.write(f"**Estágio:** {info_vendas.get('estagio')}")
162
+ st.write(f"**Objeção Identificada:** {info_vendas.get('objecao_id')}")
163
+ st.caption(f"Argumento base: {info_vendas.get('argumento_base')}")
164
+
165
  st.markdown(response_text)
166
  st.session_state.messages.append({"role": "assistant", "content": response_text})
167
  except Exception as e:
chat.py CHANGED
@@ -1,7 +1,8 @@
1
  import os
 
2
  from dotenv import load_dotenv
3
  from retriever import db_chroma
4
- from prompts import PROMPT_TEMPLATE
5
  from langchain_core.prompts import ChatPromptTemplate
6
  from langchain_openai import ChatOpenAI
7
 
@@ -10,22 +11,63 @@ load_dotenv()
10
  OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
11
 
12
  def main():
13
- model = ChatOpenAI(model_name='gpt-3.5-turbo', openai_api_key=OPENAI_API_KEY)
14
- prompt_template = ChatPromptTemplate.from_template(PROMPT_TEMPLATE)
 
 
 
 
 
15
 
16
  while True:
17
  query = input("Digite sua pergunta (para sair digite 'sair'): ")
18
-
19
  if query.lower() == 'sair':
20
  break
21
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  docs_chroma = db_chroma.similarity_search(query, k=5)
23
  context_text = "\n\n".join([doc.page_content for doc in docs_chroma])
24
 
25
- prompt = prompt_template.format(context=context_text, question=query)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
- response = model.invoke(prompt)
28
- print('\n***Resposta:*** \n\n' + response.content)
29
 
30
  print("\n" + "-"*50 + "\n")
31
  if __name__ == "__main__":
 
1
  import os
2
+ import json
3
  from dotenv import load_dotenv
4
  from retriever import db_chroma
5
+ from prompts import PROMPT_TEMPLATE, CLASSIFIER_PROMPT
6
  from langchain_core.prompts import ChatPromptTemplate
7
  from langchain_openai import ChatOpenAI
8
 
 
11
  OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
12
 
13
  def main():
14
+ # Bom modelo para classificação e respostas
15
+ model = ChatOpenAI(model_name='gpt-4o-mini', openai_api_key=os.getenv("OPENAI_API_KEY"))
16
+
17
+ with open('know_graph.json', 'r', encoding='utf-8') as f:
18
+ knowledge_graph_data = json.dumps(json.load(f), ensure_ascii=False)
19
+
20
+ chat_history = "" # Histórico inicial
21
 
22
  while True:
23
  query = input("Digite sua pergunta (para sair digite 'sair'): ")
 
24
  if query.lower() == 'sair':
25
  break
26
 
27
+ # Classificação inicial
28
+ prompt_classifier = ChatPromptTemplate.from_template(CLASSIFIER_PROMPT)
29
+ classifier_input = prompt_classifier.format(
30
+ knowledge_graph=knowledge_graph_data,
31
+ chat_history=chat_history,
32
+ question=query
33
+ )
34
+
35
+ classification_response = model.invoke(classifier_input).content
36
+
37
+ cleaned_response = classification_response.replace("```json", "").replace("```", "").strip()
38
+ try:
39
+ classification = json.loads(cleaned_response)
40
+ except:
41
+ classification = {"estagio": "Nao identificado", "objecao_id": "nenhuma", "argumento_base": ""}
42
+
43
+ # Busca de contexto relevante
44
  docs_chroma = db_chroma.similarity_search(query, k=5)
45
  context_text = "\n\n".join([doc.page_content for doc in docs_chroma])
46
 
47
+ diretriz_venda = f"\n[DIRETRIZ ESTRATÉGICA]: Estágio: {classification.get('estagio')}. " \
48
+ f"Objeção: {classification.get('objecao_id')}. " \
49
+ f"Argumento sugerido: {classification.get('argumento_base')}"
50
+
51
+ full_context = context_text + diretriz_venda
52
+
53
+ prompt_final = ChatPromptTemplate.from_template(PROMPT_TEMPLATE)
54
+ final_input = prompt_final.format(
55
+ context=full_context,
56
+ chat_history=chat_history,
57
+ question=query
58
+ )
59
+
60
+ response = model.invoke(final_input)
61
+
62
+ print(f"\n--- LOG DE VENDAS ---")
63
+ print(f"📍 Estágio: {classification.get('estagio')}")
64
+ print(f"⚠️ Objeção: {classification.get('objecao_id')}")
65
+ print(f"---------------------\n")
66
+ print('***Resposta:*** \n\n' + response.content)
67
+
68
+ # Atualizar histórico (simplificado)
69
+ chat_history += f"\nUsuário: {query}\nAssistente: {response.content}"
70
 
 
 
71
 
72
  print("\n" + "-"*50 + "\n")
73
  if __name__ == "__main__":
know_graph.json ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "empresa": "Central da Visão",
3
+ "versao": "1.0",
4
+ "funil_de_vendas": [
5
+ {
6
+ "estagio": "1. Prospecção/Saudação",
7
+ "objetivo": "Acolhimento inicial e identificação do canal.",
8
+ "acoes_chave": ["Saudação humanizada", "Uso do nome do paciente", "Explicação do projeto social"],
9
+ "script_base": "A Central da Visão é um projeto que oferece cirurgias em clínicas renomadas com valores mais acessíveis utilizando horários ociosos."
10
+ },
11
+ {
12
+ "estagio": "2. Sondagem/Qualificação",
13
+ "objetivo": "Identificar a necessidade (Catarata, Refrativa, etc.) e o nível de urgência.",
14
+ "perguntas_chave": [
15
+ "Para quem seria a cirurgia?",
16
+ "Já possui indicação cirúrgica?",
17
+ "Qual a maior dificuldade que a visão está causando hoje?"
18
+ ],
19
+ "gatilhos_de_entrada": [
20
+ "visão embaçada", "visão turva", "nuvem no olho",
21
+ "dificuldade para ler", "sensibilidade à luz", "não enxergo direito"
22
+ ]
23
+ },
24
+ {
25
+ "estagio": "3. Orientação/Consideração",
26
+ "objetivo": "Apresentar diferenciais técnicos e converter sintomas em solução.",
27
+ "diferenciais": ["Clínicas com +10 anos", "Médicos especialistas (não residentes)", "Imparcialidade nos orçamentos"]
28
+ },
29
+ {
30
+ "estagio": "4. Fechamento/Encaminhamento",
31
+ "objetivo": "Agendamento da consulta de avaliação.",
32
+ "gatilhos": ["Escassez de agenda", "Senso de urgência (evitar agravamento)", "Facilitação de pagamento"]
33
+ }
34
+ ],
35
+ "mapeamento_de_objecoes": [
36
+ {
37
+ "id": "obj_preco_alto",
38
+ "categoria": "Financeiro",
39
+ "gatilhos": ["caro", "muito alto", "não tenho condições", "preço", "valor"],
40
+ "argumento_chave": "Focar no impacto social e no custo-benefício. Comparar o valor da cirurgia com a perda de autonomia. Oferecer parcelamento em 10x/12x.",
41
+ "diretriz": "Não dar desconto de imediato. Explicar que usamos tecnologia de ponta e médicos experientes."
42
+ },
43
+ {
44
+ "id": "obj_ja_tem_exames",
45
+ "categoria": "Técnica",
46
+ "gatilhos": ["já tenho exames", "fiz os exames", "quero saber se serve", "só quero o preço"],
47
+ "argumento_chave": "Segurança e responsabilidade. O cirurgião da Central precisa validar os exames no protocolo da clínica para garantir o resultado.",
48
+ "fonte": "Cenário H - Treinamento de Argumentação"
49
+ },
50
+ {
51
+ "id": "obj_distancia",
52
+ "categoria": "Logística",
53
+ "gatilhos": ["longe", "outra cidade", "distante", "não tenho como ir"],
54
+ "argumento_chave": "Explicar que a jornada é curta (poucas visitas à clínica) e que o benefício de operar em um centro de excelência compensa o deslocamento.",
55
+ "fonte": "Cenário C - Treinamento de Argumentação"
56
+ },
57
+ {
58
+ "id": "obj_medo_cirurgia",
59
+ "categoria": "Emocional",
60
+ "gatilhos": ["medo", "perigoso", "ficar cego", "anestesia", "risco"],
61
+ "argumento_chave": "Empatia radical. Explicar que é um procedimento seguro, rápido e que a Central acompanha do pré ao pós-operatório.",
62
+ "fonte": "Cenário B - Treinamento de Argumentação"
63
+ },
64
+ {
65
+ "id": "obj_comparacao_concorrente",
66
+ "categoria": "Mercado",
67
+ "gatilhos": ["vi mais barato", "mutirão", "concorrente", "outra clínica"],
68
+ "argumento_chave": "Diferenciação por qualidade. Não trabalhamos com mutirões ou residentes. Nossas clínicas têm rigorosos padrões de afiliação.",
69
+ "fonte": "Posicionamento de Marketing"
70
+ }
71
+ ],
72
+ "regras_de_ouro": {
73
+ "termos_proibidos": ["Atendimento humanizado", "Equipamentos modernos", "Médicos maravilhosos"],
74
+ "postura": "Empatia radical e precisão técnica. Chame sempre pelo nome."
75
+ }
76
+ }
prompts.py CHANGED
@@ -46,4 +46,23 @@ Baseie-se nos documentos para contornar objeções:
46
 
47
  ## REGRA DE OURO:
48
  Analise o HISTÓRICO DA CONVERSA. Se você já cumprimentou o usuário anteriormente ou se já se apresentou, NÃO diga "Olá", "Bom dia" ou "Sou o consultor" novamente. Responda diretamente à NOVA MENSAGEM, mantendo o tom acolhedor.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  """
 
46
 
47
  ## REGRA DE OURO:
48
  Analise o HISTÓRICO DA CONVERSA. Se você já cumprimentou o usuário anteriormente ou se já se apresentou, NÃO diga "Olá", "Bom dia" ou "Sou o consultor" novamente. Responda diretamente à NOVA MENSAGEM, mantendo o tom acolhedor.
49
+ """
50
+
51
+ CLASSIFIER_PROMPT = """
52
+ Você é um analista de vendas da Central da Visão. Sua tarefa é analisar a mensagem do usuário e o histórico para classificar o estágio no funil e identificar possíveis objeções, baseando-se na Estrutura JSON fornecida.
53
+
54
+ ESTRUTURA JSON DE REFERÊNCIA:
55
+ {knowledge_graph}
56
+
57
+ HISTÓRICO:
58
+ {chat_history}
59
+
60
+ MENSAGEM DO USUÁRIO:
61
+ {question}
62
+
63
+ INSTRUÇÃO:
64
+ Responda EXCLUSIVAMENTE em formato JSON com as seguintes chaves:
65
+ - "estagio": O nome do estágio identificado (conforme o JSON).
66
+ - "objecao_id": O ID da objeção detectada ou "nenhuma".
67
+ - "argumento_base": O 'argumento_chave' ou 'script_base' correspondente encontrado no JSON para ajudar na resposta.
68
  """