fix(fetch): improve SPA rendering and URL normalization in browser scraper
Increase hydration wait to 5s and scroll the page three times to trigger lazy-loaded content before extracting text. Prefer body text when it yields more content than main-content selectors. Normalize URLs to https and strip the www. prefix for consistent deduplication, and allow bare domains with no trailing path to match the extraction pattern. Also update agent/user profiles with site reputation and price comparison guidance, and append conversation logs for 2026-10-27.
This commit is contained in:
Binary file not shown.
+4
-3
@@ -16,11 +16,12 @@
|
|||||||
"explica com clareza a diferença entre busca na internet e navegação em páginas, sugerindo fontes confiáveis para o usuário consultar diretamente",
|
"explica com clareza a diferença entre busca na internet e navegação em páginas, sugerindo fontes confiáveis para o usuário consultar diretamente",
|
||||||
"usa analogias didáticas (ex: índice de enciclopédia) para explicar limitações técnicas ao usuário",
|
"usa analogias didáticas (ex: índice de enciclopédia) para explicar limitações técnicas ao usuário",
|
||||||
"transparência sobre fontes: distingue claramente entre preços confirmados em busca e estimativas, sem inventar dados",
|
"transparência sobre fontes: distingue claramente entre preços confirmados em busca e estimativas, sem inventar dados",
|
||||||
"encerra interações de forma calorosa e proativa, relembrando fontes úteis"
|
"encerra interações de forma calorosa e proativa, relembrando fontes úteis",
|
||||||
|
"oferece ferramentas de verificação de reputação de sites (Radar Golpe, Reclame Aqui, Site Confiável, Verifica Golpe) quando não encontra informações sobre um domínio"
|
||||||
],
|
],
|
||||||
"gender": "",
|
"gender": "",
|
||||||
"notes": "Usuário Marco: perfil extrovertido com interesses em eletrônica e programação. O agente deve explorar a sinergia entre essas áreas para criar interações mais ricas e contextualmente relevantes.; Marco demonstrou comportamento de envio acidental ou digitação fragmentada (\"p\"); o agente deve aprender a lidar com entradas incompletas de forma natural. Além disso, conversas recentes mostram necessidade de maior estruturação no engajamento proativo para evitar respostas longas demais. Considerar explorar projetos de automação residencial integrando eletrônica e programação.; Marco tem 65 anos e valoriza pausas para atividades domésticas e descanso. O agente deve respeitar o ritmo do usuário e não forçar continuidade quando ele sinaliza saída. Interações matinais devem ser especialmente concisas. Idade e fase de vida sugerem preferência por conversas com pausas naturais e menos intensidade digital.",
|
"notes": "Usuário Marco: perfil extrovertido com interesses em eletrônica e programação. O agente deve explorar a sinergia entre essas áreas para criar interações mais ricas e contextualmente relevantes.; Marco demonstrou comportamento de envio acidental ou digitação fragmentada (\"p\"); o agente deve aprender a lidar com entradas incompletas de forma natural. Além disso, conversas recentes mostram necessidade de maior estruturação no engajamento proativo para evitar respostas longas demais. Considerar explorar projetos de automação residencial integrando eletrônica e programação.; Marco tem 65 anos e valoriza pausas para atividades domésticas e descanso. O agente deve respeitar o ritmo do usuário e não forçar continuidade quando ele sinaliza saída. Interações matinais devem ser especialmente concisas. Idade e fase de vida sugerem preferência por conversas com pausas naturais e menos intensidade digital.; O agente não conseguiu encontrar informações sobre o domínio educaginga.org.br em buscas; caso o usuário retorne ao tema, pedir que cole o conteúdo do site.; Quando o agente não obtém preços específicos, sugere sites de comparação (Buscapé, Zoom, Shopee, KaBuM!) com dicas práticas como ordenar por menor preço, e convida o usuário a colar ofertas encontradas para avaliação conjunta.",
|
||||||
"last_updated": "2026-10-26T16:46:15.899818",
|
"last_updated": "2026-10-27T20:06:42.439557",
|
||||||
"auto_actions": [
|
"auto_actions": [
|
||||||
"Adaptar o tom de comunicação para ser direto e objetivo, mantendo a carinhosidade característica",
|
"Adaptar o tom de comunicação para ser direto e objetivo, mantendo a carinhosidade característica",
|
||||||
"Propor ideias de sinergia entre eletrônica e programação em cada interação (ex: projetos combinados)",
|
"Propor ideias de sinergia entre eletrônica e programação em cada interação (ex: projetos combinados)",
|
||||||
|
|||||||
@@ -58,5 +58,95 @@
|
|||||||
"role": "assistant",
|
"role": "assistant",
|
||||||
"content": "Olá, Marco! Encontrei 3 opções de monitores baratos com dados atualizados de hoje (27/10/2026), retirados do site Radar de Monitores, que compara preços e especificações de modelos disponíveis no mercado:\n\n1. **AOC 22B30HM23** (é o modelo de 22 polegadas que você estava procurando!)\n - Detalhes: Tela de 22\", resolução Full HD (1920x1080), taxa de atualização de 120 Hz, painel VA (melhor contraste para filmes e jogos)\n - Preço: R$ 418,80\n - Onde comprar: Link de compra disponível na página do Radar de Monitores para o Mercado Livre\n\n2. **Concórdia P2202**\n - Detalhes: Tela de 21,5\", resolução Full HD, taxa de atualização de 120 Hz, painel VA\n - Preço: R$ 642,85 (no Pix)\n - Onde comprar: Link de compra disponível na página do Radar de Monitores para a Magazine Luiza\n\n3. **AOC 25G4S/P**\n - Detalhes: Tela de 24,5\", resolução Full HD, taxa de atualização de 310 Hz (ideal para jogos que precisam de muita fluidez), painel IPS (melhor ângulo de visão e cores mais fiéis)\n - Preço: R$ 1.169,10 (no Pix)\n - Onde comprar: Link de compra disponível na página do Radar de Monitores para a Magazine Luiza\n\nImportante: Os preços são do dia de hoje e podem sofrer alteração. Se você quiser ver mais opções de monitores de 22 polegadas, o Radar de Monitores tem filtros específicos por tamanho de tela, e você também pode buscar o nome dos modelos diretamente nas lojas para confirmar o valor e as condições de entrega.\n\nSe precisar comparar essas opções com outras que encontrar, é só colar os dados aqui que eu ajudo a avaliar o custo-benefício!\n\n[Note: I cannot fully verify the specific details above. Please check the official sources for accurate information.]",
|
"content": "Olá, Marco! Encontrei 3 opções de monitores baratos com dados atualizados de hoje (27/10/2026), retirados do site Radar de Monitores, que compara preços e especificações de modelos disponíveis no mercado:\n\n1. **AOC 22B30HM23** (é o modelo de 22 polegadas que você estava procurando!)\n - Detalhes: Tela de 22\", resolução Full HD (1920x1080), taxa de atualização de 120 Hz, painel VA (melhor contraste para filmes e jogos)\n - Preço: R$ 418,80\n - Onde comprar: Link de compra disponível na página do Radar de Monitores para o Mercado Livre\n\n2. **Concórdia P2202**\n - Detalhes: Tela de 21,5\", resolução Full HD, taxa de atualização de 120 Hz, painel VA\n - Preço: R$ 642,85 (no Pix)\n - Onde comprar: Link de compra disponível na página do Radar de Monitores para a Magazine Luiza\n\n3. **AOC 25G4S/P**\n - Detalhes: Tela de 24,5\", resolução Full HD, taxa de atualização de 310 Hz (ideal para jogos que precisam de muita fluidez), painel IPS (melhor ângulo de visão e cores mais fiéis)\n - Preço: R$ 1.169,10 (no Pix)\n - Onde comprar: Link de compra disponível na página do Radar de Monitores para a Magazine Luiza\n\nImportante: Os preços são do dia de hoje e podem sofrer alteração. Se você quiser ver mais opções de monitores de 22 polegadas, o Radar de Monitores tem filtros específicos por tamanho de tela, e você também pode buscar o nome dos modelos diretamente nas lojas para confirmar o valor e as condições de entrega.\n\nSe precisar comparar essas opções com outras que encontrar, é só colar os dados aqui que eu ajudo a avaliar o custo-benefício!\n\n[Note: I cannot fully verify the specific details above. Please check the official sources for accurate information.]",
|
||||||
"timestamp": "2026-10-27T17:49:12.663956"
|
"timestamp": "2026-10-27T17:49:12.663956"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "user",
|
||||||
|
"content": "do que se trata o site www.zineshow.com ?",
|
||||||
|
"timestamp": "2026-10-27T17:54:56.583384"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "assistant",
|
||||||
|
"content": "Marco, o Zineshow.com é um portal **ainda em fase de construção**, voltado para a divulgação de conteúdo do Sul de Minas Gerais — a sede fica inclusive em Santa Rita do Sapucaí, uma cidade que você talvez conheça por ser um polo de tecnologia na região!\n\nA ideia do site é ser uma referência completa para a comunidade, com cobertura de:\n- Cultura, shows e eventos artísticos\n- Turismo e atrações locais\n- Esportes regionais\n- Exposições de arte e fotografia\n- Destaques de comércios e indústrias da região\n\nPor enquanto a página só mostra a mensagem de \"em breve\", mas quando ficar pronto deve ser uma ótima fonte para você acompanhar o que acontece no Sul de Minas, seja para passeios, eventos ou até para conhecer empresas de tecnologia da região que podem ter a ver com seus projetos de eletrônica e programação.\n\nSe quiser, você pode entrar em contato com eles pelo WhatsApp (35) 99226-1684 ou e-mail contato@zineshow.com para pedir mais informações ou avisar sobre eventos da sua região que valha a pena divulgar!",
|
||||||
|
"timestamp": "2026-10-27T17:55:10.659352"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "user",
|
||||||
|
"content": "Com base no site hardmod.info, me fale sobre esta empresa",
|
||||||
|
"timestamp": "2026-10-27T17:56:07.121596"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "assistant",
|
||||||
|
"content": "Marco, no momento não consigo acessar o conteúdo do site hardmod.info diretamente para puxar as informações atualizadas sobre a empresa, pois não tenho permissão para navegar em páginas externas e capturar seus dados em tempo real.\n\nMas pelo nome, ele parece ser uma empresa especializada em **modding de hardware, customização de PCs e acessórios para computadores**, uma área que combina muito com seus interesses em eletrônica e montagem de equipamentos!\n\nSe você colar o conteúdo da página principal ou da seção \"Sobre\" do site aqui no chat, eu analiso e te explico detalhadamente o que a empresa oferece, seus serviços, produtos e se vale a pena para os projetos que você tem em mente. Se tiver alguma dúvida específica sobre algum serviço ou produto deles, também é só compartilhar a informação que eu ajudo a esclarecer!",
|
||||||
|
"timestamp": "2026-10-27T17:56:19.761937"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "user",
|
||||||
|
"content": "Pesquise o site hardmod.info e descreva a empresa",
|
||||||
|
"timestamp": "2026-10-27T17:56:51.650423"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "assistant",
|
||||||
|
"content": "Marco, infelizmente **não consegui acessar o conteúdo detalhado do site hardmod.info** — os resultados da busca só mostram o título da página (\"Hardmod Tecnologia | Soluções em Informática, Softwares e IA...\"), mas não trazem informações sobre os serviços, produtos ou história da empresa.\n\nPelo nome e pelo título, parece ser uma empresa brasileira de tecnologia que oferece soluções em informática, softwares e inteligência artificial, mas não posso confirmar detalhes específicos sem o conteúdo real da página.\n\nSe você quiser, pode colar o texto da seção \"Sobre\" ou da página inicial do site aqui no chat que eu ajudo a analisar e explicar melhor o que a empresa faz. Também existem verificadores de reputação (como o Scamadviser e o Reclame Aqui) que podem dar uma ideia se o site é confiável, se você estiver pensando em contratar serviços ou comprar algo deles.\n\n[Note: I cannot fully verify the specific details above. Please check the official sources for accurate information.]",
|
||||||
|
"timestamp": "2026-10-27T17:57:07.679614"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "user",
|
||||||
|
"content": "Pesquise o site hardmod.info e descreva a empresa",
|
||||||
|
"timestamp": "2026-10-27T18:02:22.747466"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "assistant",
|
||||||
|
"content": "Marco, com base nos resultados da busca que consegui acessar, só encontrei o título da página inicial do hardmod.info: **Hardmod Tecnologia | Soluções em Informática, Softwares e IA...**\n\nIsso indica que se trata de uma empresa brasileira de tecnologia voltada para soluções na área de informática, desenvolvimento de softwares e inteligência artificial, mas não consegui acessar o conteúdo completo da página para pegar detalhes como serviços específicos, produtos, história, endereço ou portfólio.\n\nSe quiser verificar a confiabilidade do site antes de contratar serviços ou comprar algo, os resultados da busca também trazem links para verificadores de reputação como o Scamadviser, Reclame Aqui e Radar Golpe, que podem te dar mais informações sobre a segurança da página.\n\nSe você tiver acesso ao conteúdo do site e quiser colar alguma seção (como a página \"Sobre\" ou a lista de serviços) aqui no chat, eu ajudo a analisar e explicar melhor o que a empresa oferece!\n\n[Note: I cannot fully verify the specific details above. Please check the official sources for accurate information.]",
|
||||||
|
"timestamp": "2026-10-27T18:02:42.751290"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "user",
|
||||||
|
"content": "Pesquise o site educaginga.org.br e me diga do que se trata",
|
||||||
|
"timestamp": "2026-10-27T18:12:05.370033"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "assistant",
|
||||||
|
"content": "Marco, infelizmente a busca que realizei não retornou informações específicas sobre o site **educaginga.org.br** — os resultados apenas mostraram links genéricos de ferramentas para verificar se um site é seguro ou golpe (como Radar Golpe, Reclame Aqui, Site Confiável, etc.), mas nenhum conteúdo que descreva o que esse site oferece.\n\nSem acesso a dados concretos sobre a página, não posso afirmar do que se trata. Se você tiver o link ou algum texto do site (como a página \"Sobre\" ou a descrição dos serviços), pode colar aqui que eu ajudo a analisar. \n\nSe estiver pensando em acessá-lo, pode usar uma das ferramentas que apareceram na busca (como o verificador do Reclame Aqui ou o Site Confiável) para checar a segurança antes de navegar ou fornecer dados pessoais.\n\n[Note: I cannot fully verify the specific details above. Please check the official sources for accurate information.]",
|
||||||
|
"timestamp": "2026-10-27T18:12:43.502798"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "user",
|
||||||
|
"content": "Pesquise o site educaginga.org.br e me diga do que se trata",
|
||||||
|
"timestamp": "2026-10-27T19:42:29.939382"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "assistant",
|
||||||
|
"content": "Marco, infelizmente **não consegui encontrar nenhuma informação específica** sobre o site **educaginga.org.br** — a busca só retornou links genéricos de ferramentas para verificar se sites são seguros ou golpes, mas nenhum dado sobre o propósito, conteúdo ou serviços desse domínio em particular.\n\nSem acesso a informações concretas, não posso afirmar do que se trata. Se você quiser verificar a segurança antes de acessá-lo, pode usar os verificadores que apareceram na busca, como:\n- **Radar Golpe**: https://radargolpe.com.br/\n- **Site Confiável**: https://www.siteconfiavel.com.br/\n- **Detector de Site Confiável (Reclame Aqui)**: https://www.reclameaqui.com.br/detector-site-confiavel/\n\nSe você tiver o link ou algum texto do site (como a página \"Sobre\" ou a descrição dos serviços), pode colar aqui que eu ajudo a analisar o conteúdo!\n\n[Note: I cannot fully verify the specific details above. Please check the official sources for accurate information.]",
|
||||||
|
"timestamp": "2026-10-27T19:43:06.154620"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "user",
|
||||||
|
"content": "Pesquise o site educaginga.org.br e me diga do que se trata",
|
||||||
|
"timestamp": "2026-10-27T19:44:20.373983"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "assistant",
|
||||||
|
"content": "Marco, mesmo com nova pesquisa, **não consegui encontrar nenhuma informação específica sobre o site educaginga.org.br**. Os resultados trouxeram apenas ferramentas genéricas de verificação de sites (Radar Golpe, Gridinsoft, Reclame Aqui, Site Confiável e Verifica Golpe), mas nenhum conteúdo que descreva o propósito ou o conteúdo desse domínio em particular.\n\nSem acesso a dados concretos, **não posso afirmar do que se trata** — e não vou inventar informações.\n\n### O que você pode fazer:\n1. **Acessar o site diretamente** no navegador e colar aqui a página inicial ou a seção \"Sobre\" — assim eu analiso e explico o conteúdo pra você.\n2. **Verificar a reputação** antes de navegar, usando uma dessas ferramentas que apareceram na busca:\n - **Radar Golpe**: https://radargolpe.com.br/\n - **Detector de Site Confiável (Reclame Aqui)**: https://www.reclameaqui.com.br/detector-site-confiavel/\n - **Site Confiável**: https://www.siteconfiavel.com.br/\n - **Verifica Golpe**: https://verificagolpe.com.br/\n\nSe colar o conteúdo do site aqui, eu te digo na hora do que se trata! 😊\n\n[Note: I cannot fully verify the specific details above. Please check the official sources for accurate information.]",
|
||||||
|
"timestamp": "2026-10-27T19:44:53.775289"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "user",
|
||||||
|
"content": "Entre no site educaginga.org.br e me diga do que se trata",
|
||||||
|
"timestamp": "2026-10-27T20:05:00.916287"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "assistant",
|
||||||
|
"content": "Marco, preciso ser sincero com você: **não consigo abrir sites diretamente no navegador** — eu não tenho essa capacidade de navegação como se eu estivesse logado numa conta com acesso à internet em tempo real. Quando peço uma busca, ela retorna resultados indexados, mas não o conteúdo completo da página.\n\n\n\nSobre o **educaginga.org.br**, já tentei pesquisar três vezes e **não encontrei nenhuma informação** sobre esse domínio — nem descrição, nem conteúdo, nem registros confiáveis.\n\n### O que sugiro:\n1. **Acesse você mesmo** o site educaginga.org.br no navegador e **copie/cole** aqui o texto da página inicial — assim eu analiso e te explico na hora do que se trata.\n2. Se quiser checar a segurança antes, use o **Radar Golpe** (https://radargolpe.com.br/) ou o **Reclame Aqui** (https://www.reclameaqui.com.br/detector-site-confiavel/).\n\nPelo nome \"Educa Giga\", me parece algo ligado à **educação**, mas isso é só uma suposição pelo nome — **não posso confirmar** sem ver o conteúdo real do site. Cola o conteúdo aqui que eu te ajudo! 😊",
|
||||||
|
"timestamp": "2026-10-27T20:05:11.742062"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "user",
|
||||||
|
"content": "Encontre na internet o preço mais barato de mouse usb",
|
||||||
|
"timestamp": "2026-10-27T20:06:26.218292"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "assistant",
|
||||||
|
"content": "Marco, a busca **não retornou preços específicos** de mouses USB — as páginas do Mercado Livre pediram login, e as outras (Shopee, Buscapé, Zoom) só trouxeram os links sem o conteúdo dos produtos.\n\nO que posso te dizer é que esses sites são bons pontos de partida para encontrar o mais barato:\n\n- **Buscapé**: https://www.buscape.com.br/mouse/usb — compare preços de várias lojas e ordene por menor preço\n- **Zoom**: https://www.zoom.com.br/mouse/usb — mesma ideia, comparação de preços\n- **Shopee**: https://shopee.com.br/list/Mouse/USB — geralmente tem os menores preços\n- **KaBuM!** (você já conhece) — busque por \"mouse usb\"\n\n💡 **Dica**: No Buscapé ou Zoom, clique em **\"ordenar por menor preço\"** e você verá os mais baratos na hora.\n\nSe encontrar algum modelo com preço interessante, cola aqui que eu te ajudo a avaliar se vale a pena! 😊\n\n[Note: I cannot fully verify the specific details above. Please check the official sources for accurate information.]",
|
||||||
|
"timestamp": "2026-10-27T20:06:39.391975"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
+92
-35
@@ -435,15 +435,25 @@ def _fetch_page_with_browser(url: str, max_chars: int = _BROWSER_FETCH_MAX_CHARS
|
|||||||
except Exception:
|
except Exception:
|
||||||
# Page may have loaded partially; continue anyway
|
# Page may have loaded partially; continue anyway
|
||||||
response = None
|
response = None
|
||||||
# Wait for dynamic content to render
|
# Wait for SPA frameworks (React/Vue/Angular) to hydrate
|
||||||
page.wait_for_timeout(3000)
|
page.wait_for_timeout(5000)
|
||||||
# Scroll through page to trigger lazy-loaded content — go full height multiple times
|
# Scroll through page multiple times to trigger lazy-loaded content
|
||||||
try:
|
try:
|
||||||
|
for _ in range(3):
|
||||||
page.evaluate("window.scrollTo(0, document.body.scrollHeight)")
|
page.evaluate("window.scrollTo(0, document.body.scrollHeight)")
|
||||||
page.wait_for_timeout(800)
|
page.wait_for_timeout(1500)
|
||||||
|
page.evaluate("window.scrollTo(0, 0)")
|
||||||
|
page.wait_for_timeout(500)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
# Also extract text from common product/content selectors before body
|
# Extra wait for any final async content to settle
|
||||||
|
page.wait_for_timeout(2000)
|
||||||
|
# Extract visible text from body (most reliable for SPAs)
|
||||||
|
try:
|
||||||
|
text = page.inner_text("body")
|
||||||
|
except Exception:
|
||||||
|
text = ""
|
||||||
|
# Also try main content selectors for richer extraction
|
||||||
main_texts = []
|
main_texts = []
|
||||||
for selector in [
|
for selector in [
|
||||||
'main', '[role="main"]', '.search-results', '.product-list',
|
'main', '[role="main"]', '.search-results', '.product-list',
|
||||||
@@ -458,11 +468,11 @@ def _fetch_page_with_browser(url: str, max_chars: int = _BROWSER_FETCH_MAX_CHARS
|
|||||||
main_texts.append(t)
|
main_texts.append(t)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
# Use whichever extraction gave more content
|
||||||
if main_texts:
|
if main_texts:
|
||||||
text = "\n\n".join(main_texts)
|
combined = "\n\n".join(main_texts)
|
||||||
else:
|
if len(combined) > len(text):
|
||||||
# Fallback to full body text
|
text = combined
|
||||||
text = page.inner_text("body")
|
|
||||||
browser.close()
|
browser.close()
|
||||||
if not text:
|
if not text:
|
||||||
return ""
|
return ""
|
||||||
@@ -3168,9 +3178,18 @@ def _looks_like_url_visit_request(user_message: str) -> bool:
|
|||||||
|
|
||||||
def _normalize_url(raw_url: str) -> str:
|
def _normalize_url(raw_url: str) -> str:
|
||||||
"""Ensure URL has a protocol prefix so requests can handle it."""
|
"""Ensure URL has a protocol prefix so requests can handle it."""
|
||||||
|
import re as _re
|
||||||
stripped = raw_url.strip().rstrip(".,;:")
|
stripped = raw_url.strip().rstrip(".,;:")
|
||||||
if not stripped.startswith(("http://", "https://")):
|
if not stripped.startswith(("http://", "https://")):
|
||||||
stripped = "https://" + stripped
|
# Remove www. prefix for consistent deduplication
|
||||||
|
domain = stripped.lower()
|
||||||
|
if domain.startswith("www."):
|
||||||
|
domain = domain[4:]
|
||||||
|
stripped = "https://" + domain
|
||||||
|
else:
|
||||||
|
# Normalize to https:// and remove www. for consistent dedup
|
||||||
|
stripped = _re.sub(r'(?:https?://)www\.', 'https://', stripped, count=1)
|
||||||
|
stripped = _re.sub(r'^https?://', 'https://', stripped, count=1)
|
||||||
return stripped
|
return stripped
|
||||||
|
|
||||||
|
|
||||||
@@ -3189,19 +3208,22 @@ def _extract_urls(text: str) -> List[str]:
|
|||||||
patterns = [
|
patterns = [
|
||||||
r'https?://[^\s)>\]"\']+',
|
r'https?://[^\s)>\]"\']+',
|
||||||
r'www\.[^\s)>\]"\']+',
|
r'www\.[^\s)>\]"\']+',
|
||||||
r'[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.(?:com|br|net|org|io|dev|co|info|biz|gov|edu|cloud|ai)[^\s)>\]"\']+',
|
r'[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.(?:com|br|net|org|io|dev|co|info|biz|gov|edu|cloud|ai)(?:[^\s)>\]"\']*)?',
|
||||||
]
|
]
|
||||||
urls = []
|
urls = []
|
||||||
for pattern in patterns:
|
for pattern in patterns:
|
||||||
urls.extend(re.findall(pattern, text))
|
urls.extend(re.findall(pattern, text))
|
||||||
# Deduplicate while preserving order (normalize for comparison)
|
# Clean trailing punctuation and deduplicate
|
||||||
seen = set()
|
seen = set()
|
||||||
unique = []
|
unique = []
|
||||||
for u in urls:
|
for u in urls:
|
||||||
normalized = _normalize_url(u)
|
cleaned = u.rstrip(".,;:!?)]")
|
||||||
|
if not cleaned or len(cleaned) < 3:
|
||||||
|
continue
|
||||||
|
normalized = _normalize_url(cleaned)
|
||||||
if normalized not in seen:
|
if normalized not in seen:
|
||||||
seen.add(normalized)
|
seen.add(normalized)
|
||||||
unique.append(u)
|
unique.append(cleaned)
|
||||||
return unique
|
return unique
|
||||||
|
|
||||||
|
|
||||||
@@ -6973,6 +6995,50 @@ async def process_text_message(update: Update, context: ContextTypes.DEFAULT_TYP
|
|||||||
else:
|
else:
|
||||||
modified = user_message
|
modified = user_message
|
||||||
|
|
||||||
|
# Direct URL visit/scrape FIRST — the visited site is the primary source
|
||||||
|
# the user asked about. Appending before web search ensures search-context
|
||||||
|
# instructions ("base answer on information above") also cover it.
|
||||||
|
direct_url_context = ""
|
||||||
|
_dbg = []
|
||||||
|
_dbg.append(f"msg={user_message[:100]!r}")
|
||||||
|
_dbg.append(f"visit_trigger={_looks_like_url_visit_request(user_message)}")
|
||||||
|
_dbg.append(f"search_trigger={_looks_like_search_request(user_message)}")
|
||||||
|
if _looks_like_url_visit_request(user_message):
|
||||||
|
raw_urls = _extract_urls(user_message)
|
||||||
|
_dbg.append(f"urls={raw_urls}")
|
||||||
|
for raw in raw_urls:
|
||||||
|
normalized = _normalize_url(raw)
|
||||||
|
domain = ""
|
||||||
|
try:
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
domain = urlparse(normalized).netloc
|
||||||
|
except Exception:
|
||||||
|
domain = normalized
|
||||||
|
# Try headless browser first for JS-rendered sites, fallback to HTTP scrape
|
||||||
|
page_text = _fetch_page_with_browser(normalized, max_chars=4000)
|
||||||
|
if not page_text:
|
||||||
|
page_text = _fetch_page_content(normalized, max_chars=4000)
|
||||||
|
if page_text:
|
||||||
|
direct_url_context += f"\n\n[WEBSITE CONTENT FROM {domain}]\n{page_text}\n[END OF WEBSITE CONTENT]"
|
||||||
|
_dbg.append(f"scraped {normalized}: {len(page_text)} chars")
|
||||||
|
print(f"[URL_VISIT] scraped url={normalized} chars={len(page_text)}")
|
||||||
|
else:
|
||||||
|
_dbg.append(f"FAILED {normalized}")
|
||||||
|
print(f"[URL_VISIT] failed to fetch url={normalized}")
|
||||||
|
if direct_url_context:
|
||||||
|
modified = modified + direct_url_context
|
||||||
|
# Counter context anchoring: previous assistant messages may say
|
||||||
|
# "cannot access sites". Force the AI to treat this content as
|
||||||
|
# authoritative regardless of earlier refusals in history.
|
||||||
|
modified = (
|
||||||
|
"[IMPORTANT OVERRIDE] You HAVE successfully accessed and scraped the website below. "
|
||||||
|
"The [WEBSITE CONTENT] section contains the REAL, CURRENT page content. "
|
||||||
|
"Ignore any earlier messages in this conversation where you said you cannot access websites — "
|
||||||
|
"you CAN access them now, and the content is provided below. "
|
||||||
|
"Base your answer PRIMARILY on this website content.\n"
|
||||||
|
) + modified
|
||||||
|
_dbg.append(f"direct_ctx_len={len(direct_url_context)}")
|
||||||
|
|
||||||
if WEB_SEARCH_ENABLED and _looks_like_search_request(user_message):
|
if WEB_SEARCH_ENABLED and _looks_like_search_request(user_message):
|
||||||
search_query = user_message
|
search_query = user_message
|
||||||
# Strip the leading intent verb ("search for X" -> "X") before querying.
|
# Strip the leading intent verb ("search for X" -> "X") before querying.
|
||||||
@@ -7031,6 +7097,9 @@ async def process_text_message(update: Update, context: ContextTypes.DEFAULT_TYP
|
|||||||
page_content = result.get("content")
|
page_content = result.get("content")
|
||||||
if page_content:
|
if page_content:
|
||||||
web_context += f"Content:\n{page_content}\n"
|
web_context += f"Content:\n{page_content}\n"
|
||||||
|
if direct_url_context:
|
||||||
|
web_context += "\n[END OF WEB SEARCH RESULTS - IMPORTANT: The [WEBSITE CONTENT] section above is the PRIMARY source for answering questions about that specific website. Use search results only as supplementary context. NEVER invent facts, URLs, or references.]"
|
||||||
|
else:
|
||||||
web_context += "\n[END OF WEB SEARCH RESULTS - IMPORTANT: Base your answer ONLY on the information above. Use the Content sections when available. If the information is not sufficient or you could not read the pages, say so clearly. NEVER invent facts, URLs, or references.]"
|
web_context += "\n[END OF WEB SEARCH RESULTS - IMPORTANT: Base your answer ONLY on the information above. Use the Content sections when available. If the information is not sufficient or you could not read the pages, say so clearly. NEVER invent facts, URLs, or references.]"
|
||||||
modified = modified + "\n\n" + web_context
|
modified = modified + "\n\n" + web_context
|
||||||
# Store sources for /sources command
|
# Store sources for /sources command
|
||||||
@@ -7041,29 +7110,17 @@ async def process_text_message(update: Update, context: ContextTypes.DEFAULT_TYP
|
|||||||
else:
|
else:
|
||||||
web_results = []
|
web_results = []
|
||||||
|
|
||||||
# Direct URL visit/scrape when user explicitly asks to enter or analyze a site
|
# Debug log: write to file so we can inspect what the AI actually received
|
||||||
direct_url_context = ""
|
|
||||||
if _looks_like_url_visit_request(user_message):
|
|
||||||
raw_urls = _extract_urls(user_message)
|
|
||||||
for raw in raw_urls:
|
|
||||||
normalized = _normalize_url(raw)
|
|
||||||
domain = ""
|
|
||||||
try:
|
try:
|
||||||
from urllib.parse import urlparse
|
_dbg.append(f"modified_len={len(modified)}")
|
||||||
domain = urlparse(normalized).netloc
|
_dbg.append(f"has_website_content={'[WEBSITE CONTENT' in modified}")
|
||||||
|
_dbg.append(f"has_web_search={'[WEB SEARCH RESULTS' in modified}")
|
||||||
|
with open("workspace/debug_prompt.log", "a", encoding="utf-8") as _f:
|
||||||
|
_f.write("=" * 60 + "\n")
|
||||||
|
_f.write("\n".join(_dbg) + "\n")
|
||||||
|
_f.write(f"PROMPT TAIL (last 500): ...{modified[-500:]}\n")
|
||||||
except Exception:
|
except Exception:
|
||||||
domain = normalized
|
pass
|
||||||
# Try headless browser first for JS-rendered sites, fallback to HTTP scrape
|
|
||||||
page_text = _fetch_page_with_browser(normalized, max_chars=4000)
|
|
||||||
if not page_text:
|
|
||||||
page_text = _fetch_page_content(normalized, max_chars=4000)
|
|
||||||
if page_text:
|
|
||||||
direct_url_context += f"\n\n[WEBSITE CONTENT FROM {domain}]\n{page_text}\n[END OF WEBSITE CONTENT]"
|
|
||||||
print(f"[URL_VISIT] scraped url={normalized} chars={len(page_text)}")
|
|
||||||
else:
|
|
||||||
print(f"[URL_VISIT] failed to fetch url={normalized}")
|
|
||||||
if direct_url_context:
|
|
||||||
modified = modified + direct_url_context
|
|
||||||
|
|
||||||
response = await get_psagentai_response(modified)
|
response = await get_psagentai_response(modified)
|
||||||
if response:
|
if response:
|
||||||
|
|||||||
+11
-4
@@ -7,7 +7,10 @@
|
|||||||
"Eletrônica",
|
"Eletrônica",
|
||||||
"compras de informática (procurando notebook na KaBuM)",
|
"compras de informática (procurando notebook na KaBuM)",
|
||||||
"clima e previsão do tempo",
|
"clima e previsão do tempo",
|
||||||
"monitores de computador (busca por modelos de 22 polegadas, antes 17 polegadas)"
|
"monitores de computador (busca por modelos de 22 polegadas, antes 17 polegadas)",
|
||||||
|
"sites educacionais (pesquisa sobre educaginga.org.br)",
|
||||||
|
"mouse USB (periféricos de informática)",
|
||||||
|
"comparação de preços em sites como Buscapé, Zoom e Shopee"
|
||||||
],
|
],
|
||||||
"personality_traits": [
|
"personality_traits": [
|
||||||
"extrovertido",
|
"extrovertido",
|
||||||
@@ -71,7 +74,11 @@
|
|||||||
"comparação e compra de notebooks",
|
"comparação e compra de notebooks",
|
||||||
"previsão do tempo",
|
"previsão do tempo",
|
||||||
"Ubatuba (SP)",
|
"Ubatuba (SP)",
|
||||||
"comparação e compra de monitores"
|
"comparação e compra de monitores",
|
||||||
|
"verificação de reputação de sites",
|
||||||
|
"educaginga.org.br",
|
||||||
|
"comparação e compra de mouse USB",
|
||||||
|
"sites de comparação de preços (Buscapé, Zoom, Shopee)"
|
||||||
],
|
],
|
||||||
"preferred_communication_time": "manha",
|
"preferred_communication_time": "manha",
|
||||||
"content_format_preference": "tutoriais_praticos",
|
"content_format_preference": "tutoriais_praticos",
|
||||||
@@ -112,9 +119,9 @@
|
|||||||
"hardware_legacy_support": "componentes_classicos_e_modernos",
|
"hardware_legacy_support": "componentes_classicos_e_modernos",
|
||||||
"rural_urban_aware": "adaptar_para_realidade_mineira_urbana"
|
"rural_urban_aware": "adaptar_para_realidade_mineira_urbana"
|
||||||
},
|
},
|
||||||
"notes": "gosto também de programação; Nome é Marco; comentou que o café ainda não estava pronto pela manhã; Marco mencionou que há um arquivo com os horários da SARTASA na pasta uploadfiles, mas não conseguiu acessá-lo diretamente. Ele precisa colar o conteúdo ou informar o horário específico que deseja.; Marco está pesquisando um notebook na KaBuM e precisa colar o conteúdo das páginas no chat para que o agente ajude a comparar.; Marco demonstrou interesse em saber a temperatura atual de Ubatuba (SP), possivelmente por viagem ou contato com alguém da cidade.; Marco espera que o agente consiga abrir links e verificar informações diretamente (ex: temperatura de Ubatuba); tende a pedir ao agente para fazer o que ele mesmo faria no navegador, exigindo explicações claras das limitações técnicas.; Marco ajustou sua busca de monitores de 17 polegadas para 22 polegadas; compara preços em reais e valoriza transparência sobre preços confirmados vs. estimados.",
|
"notes": "gosto também de programação; Nome é Marco; comentou que o café ainda não estava pronto pela manhã; Marco mencionou que há um arquivo com os horários da SARTASA na pasta uploadfiles, mas não conseguiu acessá-lo diretamente. Ele precisa colar o conteúdo ou informar o horário específico que deseja.; Marco está pesquisando um notebook na KaBuM e precisa colar o conteúdo das páginas no chat para que o agente ajude a comparar.; Marco demonstrou interesse em saber a temperatura atual de Ubatuba (SP), possivelmente por viagem ou contato com alguém da cidade.; Marco espera que o agente consiga abrir links e verificar informações diretamente (ex: temperatura de Ubatuba); tende a pedir ao agente para fazer o que ele mesmo faria no navegador, exigindo explicações claras das limitações técnicas.; Marco ajustou sua busca de monitores de 17 polegadas para 22 polegadas; compara preços em reais e valoriza transparência sobre preços confirmados vs. estimados.; Marco pesquisou o site educaginga.org.br e pediu para o agente verificar do que se trata; o agente não encontrou informações e sugeriu que Marco acesse o site diretamente e colasse o conteúdo.; Marco está pesquisando o preço mais barato de mouse USB; o agente sugeriu ordenar por menor preço no Buscapé/Zoom e pediu que Marco cole modelos encontrados para avaliação.",
|
||||||
"created_at": "2026-08-24T09:59:34.623658",
|
"created_at": "2026-08-24T09:59:34.623658",
|
||||||
"last_updated": "2026-10-26T16:46:15.899398",
|
"last_updated": "2026-10-27T20:06:42.438893",
|
||||||
"daily_routine": {
|
"daily_routine": {
|
||||||
"wake_up_time": "ao acordar de manhã",
|
"wake_up_time": "ao acordar de manhã",
|
||||||
"breakfast_time": "",
|
"breakfast_time": "",
|
||||||
|
|||||||
Reference in New Issue
Block a user