Projeto IA.med Licença Apache-2.0 Experimento de pesquisa v0.1.0, repositório privado

Não revisado clinicamente

raciocinio-br-mcp

Raciocínio clínico em contexto brasileiro num LLM local: o que ele acerta, o que ele erra, e a avaliação publicada aqui. O código está num repositório privado nesta versão.

Não revisado clinicamente. Isto não é apoio à decisão clínica

  • Não revisado clinicamente: gabaritos de avaliação, léxico e CID-10 ainda não passaram por revisão clínica. Toda resposta das tools repete este aviso.
  • Não é apoio à decisão clínica e não substitui o julgamento médico.
  • Não tem validação prospectiva e não foi avaliado por nenhum órgão regulador. Não é dispositivo médico.
  • É um experimento de pesquisa, com a avaliação publicada nesta página, incluindo onde o sistema erra.
  • Nenhum caso do projeto deriva de paciente real; os exemplos são sintéticos.

A pergunta

Por que medir raciocínio clínico em português brasileiro

O gargalo de sistemas como o OpenEvidence não é o raciocínio do modelo: é o corpus licenciado (NEJM, JAMA, bases curadas próprias). Isso não se resolve com GPU melhor.

O Brasil tem uma camada de evidência normativa pública, integral e não licenciada: PCDTs, protocolos de sociedades, notas técnicas. Essas fontes estão mal representadas no treino dos modelos em inglês e não têm paywall.

E o vocabulário brasileiro não é detalhe de localização, é falha clínica mensurável. "HGT" é corrente no Brasil e rara em inglês. "Pressão" pode ser pressão arterial, aperto torácico ou peso na cabeça. Nome comercial de medicamento é outro mundo. Por isso a pergunta do projeto não é "o que ele faz?", e sim quão bem funciona, e onde falha.

Resultados da avaliação

Rodada de 2026-09-20, modelo Qwen3.5-9B-Q4_K_M.gguf

O relatório de 2026-09-20 registrou 'local-model', que é o nome do .env, não o modelo. O modelo servido pelo endpoint local é Qwen3.5-9B-Q4_K_M.gguf (id de /v1/models). A partir da v0.1.0 o relatório grava esse id.

Não revisado clinicamente. Os gabaritos ainda não passaram por revisão clínica. Estes números medem se o pipeline funciona de ponta a ponta, não se o sistema está clinicamente correto. Não citar esta rodada como avaliação de qualidade.

Casos avaliados

8

gabaritos revisados clinicamente: 0/8

Acerto na hipótese principal

6/8

75%

Citou o documento correto

4/6

67%

Respostas sem procedência

0

precisa ser zero

Erros de execução

0

ECE (erro de calibração)

0.1750

0 = calibração perfeita

Calibração

O ECE compara a confiança declarada com a taxa real de acerto. Nesta rodada, só as respostas afirmativas (4 das 8) carregam confiança, e as barras ficaram acima da diagonal: o sistema subestimou a própria confiança. Com 4 respostas na amostra, isso é observação, não conclusão.

0.0 0.0 0.2 0.2 0.4 0.4 0.6 0.6 0.8 0.8 1.0 1.0 calibração perfeita n=1 n=3 confiança declarada acurácia real
acurácia por faixa confiança declarada ECE 0.1750. Só 4 respostas tiveram confiança; barra acima da diagonal = subestimou a confiança nesta rodada.

Caso a caso

Caso Esperado Conf. Acertou Fonte Proced.
has-001 hipertensao-arterial-sistemica - ✗ ✗ ✓
dm2-001 diabete-melito-tipo-2 0.90 ✓ ✓ ✓
asma-001 asma 0.70 ✓ ✓ ✓
dislipidemia-001 dislipidemia 0.90 ✓ ✓ ✓
obesidade-001 sobrepeso-e-obesidade-em-adultos - ✗ ✗ ✓
tabagismo-001 tabagismo 0.80 ✓ ✓ ✓
fora-escopo-001 nenhuma - ✓ - ✓
ambiguidade-001 nenhuma - ✓ - ✓

Variância entre rodadas

Três rodadas, mesma configuração (temperatura 0,1). Com 8 casos, a diferença entre rodadas é ruído.

Rodada 1 4/8 ECE 0.1833
Rodada 2 4/8 ECE 0.3333
Rodada 3 6/8 ECE 0.1750

A subida de 4/8 para 6/8 veio de consertar a métrica, não o sistema: dm2-001 e tabagismo-001 já estavam clinicamente certos, o comparador é que não os reconhecia.

Dados extraídos de resultados/avaliacao-2026-09-20.md do projeto (repositório privado). Reproduza com uv run raciocinio-cli avaliar.

Onde ele falha

Os erros, com a hipótese da causa

Registrar onde erra é parte do projeto, não uma fraqueza a esconder. Vem do FALHAS-CONHECIDAS.md do projeto, que acompanha cada rodada de avaliação.

Falhas conhecidas em aberto na v0.1.0

Continuam falhando na rodada publicada. Nenhuma das duas é falha do modelo.

has-001 recuperação

Esperado: hipertensão arterial sistêmica

Para um caso de hipertensão, o FTS devolveu trechos do PCDT de Asma. O caso diz "mediu a pressão em casa e deu sempre alta"; o léxico acha o termo "pressão" isolado, marca como ambíguo e corretamente se recusa a resolver, o que deixa a consulta sem termo discriminante.

Próximo passo: Busca semântica, ou um léxico que reconheça padrões como "pressão ... alta" com palavras no meio.

obesidade-001 piso de cobertura

Esperado: sobrepeso e obesidade em adultos

O FTS trouxe o PCDT correto no topo, mas o caso é curto e o que importa está em números (98 kg, 1,62 m), não em palavras. Só 1 palavra de conteúdo em comum com o trecho, abaixo do piso de 3. Baixar o piso faria o has-001 responder sobre a diretriz errada, o pior erro aqui.

Próximo passo: O piso considerar o casamento entre a condição do trecho e os termos do caso, não só contar palavras.

Erros de rodadas anteriores, já explicados

Acertam na rodada publicada. Ficam aqui porque a causa deles diz muito sobre a métrica.

tabagismo-001 modelo, depois métrica

Na 1ª rodada o LLM recebeu os trechos certos do PCDT de Tabagismo e devolveu hipóteses vazias. Na 2ª respondeu "dependência à nicotina" e o comparador, que só aceitava "dependência de nicotina", marcou erro. Com o comparador corrigido, a 3ª rodada conta acerto (confiança 0,80).

dm2-001 métrica, não o sistema

Respondeu "Diagnóstico de Diabetes Mellito Tipo 2", clinicamente correto. O PCDT se chama "Diabete Melito Tipo 2" e o comparador não casou as grafias, então marcou erro. As grafias do documento entraram em hipoteses_aceitaveis.

A subida de 4/8 para 6/8 entre rodadas veio de consertar a métrica, não o sistema: dm2-001 e tabagismo-001 já estavam clinicamente certos, e os dois falsos negativos caíram no eixo de vocabulário brasileiro. Num projeto cujo valor é a avaliação, a métrica precisa ser testada com o mesmo rigor do resto.

O léxico brasileiro

Um artefato de valor próprio, versionado

O mapeamento curado de termos (dados/lexico-br.yaml, versão 0.1.0, 12 entradas, revisão clínica pendente) pode ser usado independentemente do resto do projeto. Termo ambíguo nunca é normalizado em silêncio: o normalizador devolve as leituras e deixa a escolha para quem usa.

Direto

pressão alta hipertensão arterial sintoma
açúcar no sangue glicemia exame
falta de ar dispneia sintoma
chiado no peito sibilância sintoma

Ambíguo (não resolve sozinho)

pressão AMBIGUO
pressão arterialsensação de aperto torácicocefaleia em peso
cansaço AMBIGUO
dispneia aos esforçosfadigasonolência

Como fica no arquivo

- termo_popular: pressão
  termo_tecnico: AMBIGUO
  tipo: sintoma
  observacao: >-
    Ambíguo de propósito: pode ser pressão arterial, sensação de aperto no peito
    ou peso na cabeça. O normalizador devolve as leituras, não escolhe.
  leituras:
    - pressão arterial
    - sensação de aperto torácico
    - cefaleia em peso
  revisado: false
Trecho real de dados/lexico-br.yaml

Escopo coberto

Seis condições de medicina de família, e só

Cada uma com a diretriz de referência da Conitec. Fora deste escopo o sistema deve responder "fora do escopo": isso é decisão de design, não limitação temporária. O CID-10 principal fica pendente de decisão clínica.

Hipertensão Arterial Sistêmica

PCDT HAS

Portaria SECTICS/MS nº 49, 2025

vigente

Diabete Melito Tipo 2

PCDT DM2

Portaria SCTIE/MS nº 13/2026

em atualização

Asma

PCDT Asma

Portaria Conjunta nº 43, 2026

vigente

Dislipidemia

PCDT Dislipidemia

Portaria Conjunta nº 8, 2019

em atualização

Sobrepeso e Obesidade em Adultos

PCDT Sobrepeso e Obesidade

Portaria SCTIE/MS nº 53, 2020

vigente

Tabagismo

PCDT Tabagismo

Portaria Conjunta nº 10, 2020

vigente

A base, em números

Lidos em 2026-10-12. O repositório é privado até passar por revisão clínica.

Testes automatizados

102

lido em 2026-10-12

Trechos indexados dos protocolos no escopo

1.517

lido em 2026-10-12

Condições cobertas nesta versão

6

lido em 2026-10-12

Tamanho da base

11 MB

lido em 2026-10-12

O que ele não diz

Nada aqui passou por revisão clínica

Gabaritos de avaliação, léxico e CID-10 ainda não foram revisados. Toda resposta das tools repete esse aviso.

Caso curto com números vence a busca textual

Num dos casos, o protocolo correto veio no topo, mas o que importava estava em números, como peso e altura, e não em palavras. Busca por texto não alcança isso.

Seis condições, não um sistema geral

O escopo é pequeno de propósito, para a avaliação dizer alguma coisa.

Como funciona

Do texto livre à hipótese com procedência

  1. 01

    Léxico

    Normaliza o vocabulário brasileiro do caso. Termo ambíguo é devolvido com as leituras, sem escolher.

  2. 02

    Recuperação

    Busca full-text (DuckDB) na base de evidência dos PCDTs, com um piso de cobertura que decide se a pergunta está no escopo.

  3. 03

    LLM local

    O modelo raciocina sobre os trechos recuperados, na sua máquina.

  4. 04

    Estruturação com procedência

    Cada hipótese sai com citação literal, se a citação confere no documento, a confiança, e a lista do que ficou indeterminado.

Uma resposta confiante sem procedência é tratada como bug, não como estilo: o modelo de saída recusa hipótese com confiança acima de 0,5 sem trecho-fonte, e o pipeline rebaixa a confiança do que for afirmado sem origem, registrando o rebaixamento em indeterminados.

As três tools

raciocinar_caso(descricao_caso: str)

Hipóteses com procedência, conduta, trechos-fonte, termos normalizados, indeterminados, condicao_no_escopo, modelo e um aviso que começa por "NÃO REVISADO CLINICAMENTE". Nenhum campo é opcional. Precisa do LLM local.

normalizar_termo(termo: str)

O que o léxico entende do termo. Ambíguo volta com as leituras, sem escolha. Léxico 0.1.0, 12 entradas, revisão pendente.

explicar_cobertura(condicao: str = '')

O que a base cobre, para saber se a pergunta está no escopo antes de confiar na resposta. Aceita nome, sigla e sinônimo ("DM2", "HAS", "fumo"); vazio lista as seis condições.

Exemplo: explicar_cobertura("DM2")

coletado_em é a data em que o PDF foi baixado da Conitec, não a da última reindexação. cids_no_texto são candidatos extraídos da seção de classificação CID-10 do próprio PCDT; o CID principal continua PENDENTE de decisão clínica.

{
  "consulta": "DM2",
  "no_escopo": true,
  "total_condicoes": 1,
  "condicoes": [
    {
      "condicao": "Diabete Melito Tipo 2",
      "slug": "diabete-melito-tipo-2",
      "cid10": "PENDENTE",
      "cids_no_texto": ["E11", "E11.2", "E11.3", "E11.4", "E11.5",
                        "E11.6", "E11.7", "E11.8", "E11.9"],
      "documento": "PCDT Diabete Melito Tipo 2",
      "portaria": "Portaria SCTIE/MS nº 13/2026 - 21/02/2026",
      "data_documento": "2026-02-21",
      "url": "https://www.gov.br/conitec/pt-br/midias/protocolos/2026/pcdt-diabete-melito-tipo-2/@@display-file/file",
      "status_conitec": "em atualização",
      "trechos_indexados": 208,
      "coletado_em": "2026-09-20 21:19:43.053156"
    }
  ],
  "aviso": "'DM2' casou com Diabete Melito Tipo 2 por sinônimo ou pelo léxico, não pelo nome da condição. Confira se é a condição que você procura: o escopo cobre só o que está listado aqui.",
  "revisao_clinica": "NÃO REVISADO CLINICAMENTE: gabaritos de avaliação, léxico e CID-10 deste experimento ainda não passaram por revisão clínica."
}
Saída real do código da v0.1.0, depois de reindexar.

Numa base indexada antes da v0.1.0, os candidatos antigos (que traziam códigos de tabelas e referências) não vazam: cids_no_texto volta vazio e o aviso de reindexação se soma ao de sinônimo.

"cids_no_texto": [],
"aviso": "'DM2' casou com Diabete Melito Tipo 2 por sinônimo ou pelo léxico, não pelo nome da condição. Confira se é a condição que você procura: o escopo cobre só o que está listado aqui. A base foi indexada por uma versão antiga da extração de CID-10, que pegava códigos de tabelas e referências; os candidatos de Diabete Melito Tipo 2 foram omitidos. Rode 'raciocinio-cli coletar' para reindexar."
Mesma consulta, base indexada pela extração antiga.

Como rodar

Reproduza a avaliação, não só use a ferramenta

Quem tem o código deveria conseguir rodar o mesmo harness de avaliação que gerou os números acima, não apenas ligar o servidor. Precisa de Python 3.12+, uv e um LLM local com API compatível com a da OpenAI.

O repositório é privado na v0.1.0: não há link público para clonar. Os passos abaixo valem para quem já tem acesso ao código. A abertura depende da revisão clínica.

# 1. na pasta do projeto: instalar e configurar
uv sync
cp .env.example .env

# 2. apontar o .env para o seu LLM local e conferir
uv run raciocinio-cli llm

# 3. montar a base de evidência (baixa e indexa as diretrizes, ~1 min)
uv run raciocinio-cli coletar

# 4. reproduzir a AVALIAÇÃO (roda os casos e gera o relatório)
uv run raciocinio-cli avaliar

# 5. registrar o servidor MCP (stdio) no Claude Code
claude mcp add raciocinio-br -- uv --directory /caminho/do/raciocinio-br-mcp run raciocinio-br-mcp
Variáveis e opções no README do projeto. No Claude Code, passe caminhos absolutos no env do servidor.

Atualizar a base (manual)

Não há timer: a base só muda quando alguém roda a coleta. Numa base indexada antes da v0.1.0, explicar_cobertura devolve cids_no_texto vazio e avisa para reindexar. A data em coletado_em é a do download do PDF, então reindexar sem --atualizar não a muda.

# base montada antes da v0.1.0: reindexar uma vez (sem rede, usa data/pdfs/)
uv run raciocinio-cli coletar

# versão nova de um PCDT na Conitec: baixar de novo e reavaliar
uv run raciocinio-cli coletar --atualizar
uv run raciocinio-cli avaliar

O caso que você digita vai para o seu LLM local e para a base local. O que sai da máquina é só o download das diretrizes públicas do gov.br/conitec. Sem telemetria. Atenção: se o seu endpoint de LLM apontar para fora da máquina, o caso vai junto, o projeto não impede isso.