Colectica-routing-toolkit

Extrai e valida cruzadamente a lógica de roteamento de questionários a partir de exportações Colectica DDI e Forsta+ (Confirmit Horizons) — extração de esquema, grafos de roteamento, diff estrutural de roteamento, simulação de entrevista e um servidor MCP somente leitura sobre os resultados.

Documentação

Sistema de Questionários Flowise

Um aplicativo Django para analisar e simular arquivos JSON de questionários de pesquisa no formato Colectica (por exemplo, ondas do Understanding Society Mainstage). Ele permite que designers enviem um módulo de questionário, extraiam seu esquema de perguntas e lógica de roteamento, construam um grafo visual de fluxo, executem uma revisão consultiva assistida por IA via Flowise e simulem interativamente a navegação pelo questionário como um respondente.

Ele também pode ingerir uma exportação XML Forsta+ (Confirmit Horizons) da mesma onda de questionário e comparar estruturalmente seu roteamento com o roteamento derivado do Colectica, evidenciando quaisquer discrepâncias — ramificações ausentes, condições incompatíveis, ramificações "else" exclusivas do Forsta+ — em uma GUI dedicada de diff de roteamento, com uma visualização lado a lado dos grafos por discrepância.

Princípio arquitetural central

O Django detém todos os dados, a lógica de roteamento e a validação. O Flowise é apenas consultivo.

O Flowise (uma plataforma externa de agentes LLM) é usado para dois propósitos restritos, e sua saída é sempre validada/pós-processada pelo Django antes de ser confiável:

  1. Agentflow de Revisão de Módulo — revisa roteamento/cobertura para problemas de design. O Django envia um payload compacto e pós-valida a resposta contra fatos conhecidos, rejeitando qualquer coisa que invente nomes de perguntas ou modifique o roteamento.
  2. Agentflow de Redação de Entrevista — reformata o texto/opções das perguntas voltadas ao respondente durante o simulador de entrevista. O Django valida a resposta e recorre a uma mensagem determinística construída localmente se o Flowise estiver indisponível ou retornar algo inválido — o simulador sempre funciona mesmo se o Flowise estiver fora do ar.

Pipeline de processamento

Imposto na UI/views como uma ordem estrita por módulo enviado:

  1. Upload JSON → QuestionnaireModule
  2. Extrair esquema (schema_extractor.ColecticaSchemaExtractor) → NormalizedQuestion linhas
  3. Extrair roteamento (routing_extractor.ColecticaRoutingExtractor) → RoutingEdge linhas (condicional / sequencial / loop)
  4. Construir grafo (graph_builder.py + graph_enrichment.py) → QuestionnaireGraph (JSON de nós/arestas + texto Mermaid)
  5. Executar revisão Flowise (opcional) → ModuleAIReview

Tudo é executado de forma síncrona no ciclo requisição/resposta — não há fila de tarefas Celery/assíncrona.

Diff de roteamento Colectica vs Forsta+

Um segundo pipeline independente é executado contra um segundo QuestionnaireModule (source_format auto-detectado como forsta_xml a partir da extensão .xml no upload — mesmo formulário de upload do Colectica), validando de forma cruzada a exportação XML Forsta+ (Confirmit Horizons) de uma agência de campo contra o roteamento derivado do Colectica para a mesma onda:

  1. Upload XML Forsta+ → QuestionnaireModule
  2. Extrair esquema (forsta_xml_schema_extractor.ForstaXmlSchemaExtractor) → NormalizedQuestion linhas
  3. Extrair roteamento (forsta_xml_routing_extractor.ForstaXmlRoutingExtractor) → RoutingEdge linhas
  4. Corresponder perguntas (question_matcher.build_question_matches) → QuestionMatch linhas, emparelhando cada pergunta do Colectica com sua melhor contraparte Forsta+ em três passadas: correspondência exata de texto normalizado, depois fallback difuso (difflib, limite de 0,75) e, em seguida, uma etapa de reconciliação desempate por nome — se a correspondência atual de uma pergunta tiver um nome diferente do dela, e uma pergunta não utilizada com o mesmo nome existir no outro lado cuja própria redação também ultrapasse o limite difuso (ou seja uma correspondência limpa de prefixo/substring — o texto-fonte do Forsta+ às vezes incorpora instruções do entrevistador inline onde o Colectica as mantém separadas), a de mesmo nome assume. Nome é um desempate, nunca uma sobreposição: um "falso amigo" de mesmo nome, mas com conteúdo não relacionado, é deixado intacto.
  5. Comparar roteamento (routing_comparator.compare_routing_for_modules) → RoutingDiscrepancy linhas — um diff estrutural (apenas presença do nó-alvo da aresta, não semântica de condição). Tanto a pergunta de origem quanto a de destino de cada aresta são resolvidas por meio de QuestionMatch antes da comparação, não comparadas como strings de nome brutas, de modo que um destino presente em ambos os lados sob um nome diferente (maiúsculas/minúsculas, um sufixo Forsta+, etc.) não seja erroneamente reportado como ausente.

Navegável em /questionnaires/routing-diff/<colectica_module_id>/<forsta_module_id>/, com uma página de detalhes por discrepância renderizando os grafos de roteamento de ambos os sistemas lado a lado.

Servidor de ferramentas MCP

Junto ao aplicativo Django principal, mcp_server expõe um subconjunto selecionado e somente leitura dos mesmos dados como ferramentas MCP (Model Context Protocol) via HTTP transmissível, para clientes MCP como o Claude Desktop — seu próprio aplicativo Django, seu próprio processo independente, sua própria porta, nunca o runserver principal.

python manage.py runmcp                 # own process, port 8765 by default

Cada requisição deve carregar um token de acesso pessoal no caminho da URL (https://<host>/t/<token>/mcp) em vez de um cabeçalho, já que as UIs de conectores de clientes MCP geralmente aceitam apenas uma URL. Usuários da equipe geram/revogam tokens em /questionnaires/mcp-tokens/ — sem segredo compartilhado, sem necessidade de comando de terminal para integrar uma nova pessoa, e revogar o token de uma pessoa não afeta o de ninguém mais.

Solicitando acesso: não há cadastro self-service por design. Abra uma issue neste repositório ou entre em contato com o mantenedor para solicitar um token.

Ferramentas (mcp_server/tools.py), cada uma com um prompt MCP correspondente (mcp_server/prompts.py) que os clientes MCP podem expor como um atalho estilo comando de barra:

FerramentaPromptNotas
list_moduleslistModulesSomente Colectica
get_module_summaryshowModuleSummarySomente Colectica
list_questionslistQuestionsSomente Colectica
get_questionshowQuestionSomente Colectica
get_routing_edgeslistRoutingEdgesSomente Colectica
trace_variabletraceVariableSomente Colectica
get_module_graphshowModuleGraphColectica + Forsta+; também retorna sintaxe de fluxograma Mermaid para que um cliente MCP possa renderizar um diagrama real; módulos grandes são auto-resumidos em vez de retornar um grafo enorme
evaluate_edge_conditionevaluateConditionColectica + Forsta+; avalia qualquer string de condição contra respostas hipotéticas
get_routing_diff_reportshowRoutingDiffReportPar Colectica + Forsta+; espelha a página de relatório de diff de roteamento
get_routing_discrepancy_detailshowColecticaForstaDiscrepancyPar Colectica + Forsta+; espelha a página de detalhes por discrepância, incluindo os grafos de ambos os sistemas
get_routing_simulationshowRoutingSimulationSomente Colectica

Nunca escreve no banco de dados e nunca aciona uma etapa de pipeline computacionalmente pesada (extração, construção de grafo, correspondência/comparação, revisão de IA) por conta própria — toda ferramenta lê dados que outra parte do aplicativo já computou e persistiu.

Pilha tecnológica

  • Django 6.0 (projeto config/; dois aplicativos, flowise_questionnaire e mcp_server)
  • PostgreSQL (flowise_questionnaire_db)
  • Flowise (externo, auto-hospedado ou em nuvem) para revisão/redação consultiva de IA
  • Sem framework frontend — templates Django renderizados no servidor (grafos de roteamento renderizados no lado do cliente via vis-network, carregados de um CDN)

Começando

Pré-requisitos

  • Python 3.12+
  • PostgreSQL, com um banco de dados flowise_questionnaire_db disponível
  • Uma instância Flowise em execução (opcional — necessária apenas para os recursos de revisão de IA / redação de entrevista; o restante do aplicativo funciona sem ela)

Configuração

git clone https://github.com/amiravarzamani/colectica-forsta-routing-toolkit.git
cd flowise-questionnaire-system

python3 -m venv venv
source venv/bin/activate        # venv\Scripts\activate on Windows
pip install -r requirements.txt

Copie .env.example para .env e preencha SECRET_KEY/DB_PASSWORD/DB_HOSTconfig/settings.py não tem padrões para estes e falhará ruidosamente na inicialização se estiverem ausentes. Ajuste as configurações DATABASES e FLOWISE_* em config/settings.py para corresponder ao seu ambiente antes de executar as migrações.

python manage.py migrate
python manage.py createsuperuser   # first user, since login is required app-wide
python manage.py runserver

O aplicativo está montado em /questionnaires/ e exige login (LOGIN_URL = /questionnaires/login/).

Executando testes

python manage.py test

Estrutura do projeto

config/                         Django project settings, URLs, WSGI/ASGI
flowise_questionnaire/
  models.py                     QuestionnaireModule, NormalizedQuestion, RoutingEdge,
                                 QuestionnaireGraph, ModuleAIReview, InterviewSimulatorSession/Turn,
                                 QuestionMatch, RoutingDiscrepancy
  services/                     Pipeline logic, in order:
    schema_extractor.py           parse questions out of the Colectica JSON
    routing_extractor.py          parse conditional/sequential/loop routing (Colectica)
    forsta_xml_schema_extractor.py   parse questions out of the Forsta+ XML
    forsta_xml_routing_extractor.py  parse conditional/sequential/loop routing (Forsta+)
    graph_builder.py               build the routing graph
    graph_enrichment.py            annotate the graph
    condition_evaluator.py         evaluate Colectica-syntax routing conditions against answers
    forsta_condition_evaluator.py  evaluate Forsta+-syntax routing conditions against answers
    coverage_intent_builder.py     generate deterministic test-case seed inputs
    routing_simulator.py           check routing coverage
    question_matcher.py            pair Colectica and Forsta+ questions (exact + fuzzy + name-tiebreak)
    routing_comparator.py          structural diff of matched questions' routing edges (source + target resolved via QuestionMatch)
    routing_diff_explainer.py      plain-language explanation text for the routing-diff GUI
    agentflow_payload_builder.py   build the Module Review Flowise payload
    flowise_client.py              send/receive the Module Review agentflow
    interview_router.py            deterministic routing engine for the simulator
    interview_simulator_service.py orchestrate simulator sessions
    answer_validation.py           validate respondent A/B/C input
    question_presentation.py       convert questions to respondent-facing text
    flowise_interview_wording.py   Interview Wording agentflow client + caching/fallback
    interview_simulator_contracts.py  shared dataclasses
  views/
    module_views.py               upload / extract / build-graph / review / graph
    interview_simulator_views.py  start / state / answer / abandon
    routing_simulation_views.py
    routing_diff_views.py         Colectica-vs-Forsta+ report / run / discrepancy-detail
    auth_views.py
mcp_server/
  models.py                      McpAccessToken (per-person access token)
  auth_middleware.py              TokenAuthMiddleware -- validates /t/<token>/mcp on every request
  tools.py                        the MCP tools (see "MCP tool server" above)
  prompts.py                      matching MCP prompts (slash-command shortcuts)
  server.py                       MCPServer instance, tool/prompt registration
  views.py / urls.py              staff-only token management UI (/questionnaires/mcp-tokens/)
  management/commands/runmcp.py   standalone streamable-HTTP server command

Grafo de conhecimento (graphify)

O código-fonte pode ser explorado via graphify, uma ferramenta que transforma o repositório em um grafo de conhecimento consultável (nós god, estrutura de comunidade, relacionamentos entre arquivos) em vez de depender de grep/navegação bruta. A saída é gravada em graphify-out/ (ignorada pelo git — é um artefato local regenerável, não código-fonte commitado).

pip install graphifyy
graphify .                 # build the graph (AST + semantic extraction)
graphify query "<question>"          # BFS/DFS traversal, answers from the graph
graphify path "<A>" "<B>"            # shortest path between two concepts/symbols
graphify explain "<concept>"         # plain-language explanation of a node
graphify update .                    # incremental re-extract after code changes

graphify-out/graph.html abre como uma visualização interativa independente; GRAPH_REPORT.md é uma auditoria em linguagem simples de nós god, conexões surpreendentes e perguntas sugeridas.

Status / trabalho em andamento

  • Os modelos AgentRun, SyntheticProfile, SimulationRun, SimulationCase, ValidationIssue estão definidos, mas ainda não conectados a nenhuma view.
  • O pipeline de importação XML Forsta+ (Confirmit Horizons) e diff de roteamento Colectica-vs-Forsta+ (veja acima) está implementado e em uso ativo. Veja forsta_xml_routing_validation_plan.md para o documento de pesquisa original e a justificativa de design (agora também contém uma seção de notas "pós-construção" documentando onde a implementação real divergiu do design inicial).
  • RoutingDiscrepancy.DiscrepancyType.CONDITION_MISMATCH está definido no modelo (para um futuro diff semântico/avaliação de condição, em oposição ao diff estrutural atual), mas não é atualmente produzido por routing_comparator.py — reservado, não é um bug.
  • Uma ferramenta mcp_server para o resultado mais recente de ModuleAIReview está projetada, mas ainda não construída — intencionalmente em espera até uma aprovação separada, não uma omissão.
  • Limitação conhecida de question_matcher.py: duas perguntas do Colectica com redação genérica reutilizada byte-idêntica (por exemplo, um acompanhamento de carta-formulário como "E em que cidade é isso?" feita em mais de um contexto de roteamento) não podem ser desambiguadas apenas por similaridade de texto, então a errada pode vencer uma correspondência. O desempate por nome não ajuda aqui, pois os nomes das perguntas não colidem, apenas o texto delas. Não corrigido atualmente — precisaria de um sinal diferente (por exemplo, posição no grafo de roteamento) do que similaridade de texto.

Licença

Nenhum arquivo de licença ainda — todos os direitos reservados por padrão até que um seja adicionado.