
Initial Release
IRIS Political Insight is a platform for querying and analyzing Brazilian public electoral and parliamentary data. Built with InterSystems IRIS, Hybrid Search, and RAG (Retrieval-Augmented Generation), it integrates, structures, links, indexes, retrieves, and contextualizes official evidence from the Superior Electoral Court (TSE) and the Chamber of Deputies, with traceability back to the source.

Brazil is campaigning for the 2026 General Elections. What if exploring public electoral data were as simple as asking a question?
Portuguese version Β· Technical pipeline Β· Article in Portuguese
On October 4, 2026, 158,745,463 voters will be eligible to participate in Brazilβs General Elections, choosing representatives for six elective offices: President of the Republic, state governors, the Federal Senate, the Chamber of Deputies, state legislative assemblies, and, in the Federal District, the Legislative Chamber. A runoff election for president and governors will be held on October 25. Source: Superior Electoral Court (TSE).
Brazil publishes a significant amount of data on candidacies and parliamentary activity. The challenge is to transform distributed files, APIs, identifiers, and documents into information that people can actually explore.
Public data does not automatically mean accessible information. An investigation may require knowledge of government APIs, ZIP files, CSV files, PDFs, SQL, different identifiers for the same person, and multiple official systems. The goal is to reduce this barrier without replacing the original sources.
The project brings together two complementary dimensions:
A candidacy tells us who is running. Legislative data can add context about those who have already served as federal deputies. The link between the databases is deterministic and auditable, and parliamentary data is collected only when the link is classified as MATCHED.
On top of this foundation, the system combines structured filters, lexical search, and vector similarity; retrieves evidence; enriches each passage with data from its source entity; and asks the language model for a synthesis limited to the retrieved context.
The system does not try to answer:
Who is the best candidate?
The goal is to answer exploratory questions using RAG:
βWhat do the available official sources say about this candidate and the topic being researched?β
βWhat is this candidateβs position on protecting children and adolescents online?β
βDoes this candidate present proposals to combat violence in schools? What are they?β
βWhich candidates have proposals related to reducing the 6x1 work schedule?β
βWhich candidates present proposals for regulating social media and combating disinformation?β
βWhich candidates have proposals related to the use of artificial intelligence in the public sector?β
These questions correspond to paths available in the implementation. The answer depends on the ingested scope and the available evidence.
The application does not:
The system provides information + context + evidence.
The implemented flow is collect β validate β link β persist β split β represent β retrieve β contextualize β explain.
Public sourcesTSE Chamber β β ββββββββββ¬ββββββββββ β Ingestion β βΌ InterSystems IRIS βββββββββββββββΌββββββββββββββ β β β βΌ βΌ βΌ Candidate Proposition PoliticalChunk Relational Relational VECTOR β β β βββββββββββββββΌββββββββββββββ β Hybrid Search ββββββββββ΄βββββββββ β β Keyword Search Vector Search β β ββββββββββ¬βββββββββ β RRF β Top K β LLM β βΌ Answer + sources
There is no api container or Waitress. IRIS serves the Flask API; the second container contains only the Streamlit UI.
| Capability | Effective use |
|---|---|
| Persistent classes | Eight %Persistent classes model candidacies, history, legislative proposals, authors, topics, documents, chunks, and runs. |
| SQL | Parameterized SQL is used for filters, aggregations, relationships, structured context, and auditing. |
| Object API | Embedded Python uses _OpenId(), _New(), and _Save() in specific Candidate and IngestionRun operations. |
| Streams | Extracted PDFs and history JSON are stored in %Stream.GlobalCharacter. |
| Vector Search | 1,536-dimensional embeddings are stored in %Vector; IRIS calculates VECTOR_COSINE. |
| Multimodel | Relationships, objects, streams, and vectors make up a single database, with no separate vector database. |
| Embedded Python | Ingestion, API, retrieval, and RAG run in the IRISAPP namespace. |
| Native WSGI | The Web Gateway hosts Flask through %SYS.Python.WSGI at /api. |
| Transactions/auditing | Commit, rollback, and IngestionRun make the process observable and repeatable. |
SQL handles what is deterministic, streams preserve documents, vectors approximate meanings, and the RAG context brings these representations together in the IRIS core.
Electoral data contains names, acronyms, parties, states, offices, numbers, and IDs that require exact matching. Human questions may also express the same concept using different words.
| Mechanism | Role |
|---|---|
| Lexical search | Favors exact phrases and terms after case and accent normalization. |
| Vector Search | Uses the question embedding and VECTOR_COSINE in IRIS. |
| Hybrid Search | Combines rankings using RRF (k=60) without mixing incompatible scores. |
| RAG | Provides evidence to the LLM so it can produce a cited synthesis conditioned on the sources. |
The LLM is not called when there is no valid evidence. The prompt requires neutral language, exclusive use of the [E1] and [E2] blocks, citations, and an explicit statement when context is insufficient. RAG does not eliminate hallucinations; it seeks to reduce them by conditioning generation on the retrieved material.
| Source | Content | Access |
|---|---|---|
| TSE Open Data | 2026 candidacies and government platforms when available | CKAN, ZIP, Latin-1 CSV, and PDFs |
| Chamber of Deputies Open Data | Deputies, history, mandates, legislative proposals, authors, and topics | Paginated REST v2 JSON |
Each chunk retains its type, external ID, official URL, candidate, metadata, hash, and passage.
A single command processes TSE candidates, government platforms, Chamber matching and collection, and the RAG index. Downloads are validated, writes are idempotent, chunks use 700 tokens with an overlap of 100, and text-embedding-3-small produces 1,536 dimensions.
See PIPELINE.pt.md for contracts, matching, transactions, pagination, hashes, failures, chunking, and retrieval. The README does not duplicate that documentation.
After ingestion, open http://localhost:8501, select a candidate β or βAll candidatesβ β and ask a question. The interface displays the answer and sources.
Test the flow through the API without selecting an ID manually:
$candidates = Invoke-RestMethod http://localhost:52773/api/candidates
$candidate = $candidates.items | Select-Object -First 1
$body = @{ question = "Which topics appear most frequently in this candidate's legislative proposals?"; candidateId = [int]$candidate.id } | ConvertTo-Json
Invoke-RestMethod -Method Post -Uri http://localhost:52773/api/ask -ContentType "application/json" -Body $body
question
β deterministic planning
β structured query or Hybrid Search
β chunk + structured source data
β context [E1]...[En]
β LLM
β answer + sources[] with official URL
The repository does not contain screenshots, so no prefabricated answer is presented as an actual result. Outputs vary according to scope, collection, and official data.
intersystems/iris-community:latest-cd;/ask;1972, 52773, and 8501;This checkout does not have an origin configured; use the HTTPS URL displayed on the published page:
$repositoryUrl = Read-Host "Repository HTTPS URL"
git clone $repositoryUrl
Set-Location tse-iris-rag
Copy-Item .env.example .env
notepad .env
On Linux/macOS, use cp .env.example .env. Fill in at least:
LLM_API_KEY=your-openai-key
The example uses the 2026 election, the state of SP, and the offices in INGEST_OFFICES. Reduce the number of states, offices, and Chamber limits if you want a smaller load.
docker compose up --build -d
docker compose ps
Wait for iris to become healthy and for ui to be running.
Invoke-RestMethod http://localhost:52773/api/health
Invoke-WebRequest -UseBasicParsing http://localhost:8501/_stcore/health
Expected results: {βstatusβ:βokβ} and ok.
docker compose exec iris irispython -m app.ingestion.pipeline
The command queries public services and generates embeddings; duration and volume vary. To rebuild only the already persisted chunks and embeddings:
docker compose exec -T iris irispython -m app.ingestion.chunk_index
Invoke-RestMethod http://localhost:52773/api/candidates
http://localhost:8501http://localhost:52773/apipython -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install -r requirements-dev.txt
pytest -m unit
ruff check app tests wsgi_app.py
mypy app wsgi_app.py
Integration and smoke tests require IRIS/Docker to be running and the RUN_IRIS_TESTS=1 and RUN_SMOKE_TESTS=1 variables, respectively.
| Symptom | Action |
|---|---|
| IRIS is not healthy | docker compose logs βtail 200 iris |
| API returns 500 | docker compose exec -T iris sh -lc βtail -100 /usr/irissys/mgr/WSGI.logβ |
| Candidate list is empty | Run the pipeline and check the key, INGEST_* filters, and logs. |
Partial RAG_INDEX |
Check the key; some chunks may not have embeddings. |
| UI cannot reach the API | The container uses http://iris:52773/api; the host uses http://localhost:52773/api. |
| Outdated WSGI code | Rebuild/recreate iris; modules may be cached. |
docker compose down preserves the volume. docker compose down -v deletes the IRIS data.
app/
βββ api/ # Flask and HTTP contracts
βββ config/ # validated configuration
βββ database/ # SQL, DB-API, Object API, and transactions
βββ embeddings/ # embeddings
βββ ingestion/ # TSE, Chamber, matching, and chunking
βββ rag/ # context, prompt, and generation
βββ repositories/ # IRIS persistence
βββ retrieval/ # structured, lexical, vector, and RRF
βββ ui/ # Streamlit
iris/ # eight ObjectScript classes
tests/ # unit, integration, and smoke tests
docs/ # specifications, decisions, and audits
The project competes in the RAG category of the InterSystems PT 2026 Contest.
| Criterion | Evidence | Status |
|---|---|---|
| RAG | Retrieval β context β prompt β Responses API β answer with sources | Implemented |
| Hybrid Search | Lexical + vector combined using RRF | Implemented |
| Vector Search | %Vector(DOUBLE, 1536) + VECTOR_COSINE |
Implemented |
| Public APIs | TSE CKAN and Chamber REST v2 | Implemented |
| Multimodel data | Relational/objects + streams + vectors in IRIS | Implemented |
| Chunking | 700/100 tokens, hash, provenance, and pages | Implemented and analyzed |
| Embeddings | text-embedding-3-small, using the same model for corpus and query |
Implemented and analyzed |
| Explicit pipeline | Ingestion, chunking, vector, retrieval, prompt, and generation | Implemented and documented |
| Native WSGI | %SYS.Python.WSGI at /api |
Implemented; the formal bonus is in the PyProd category |
The development method using OpenAI Codex, specification prompts, human review, and actual corrections is described in ARTICLE.pt.md.
Code released under the MIT License.
Independent project, with no affiliation with the TSE, the Chamber, candidacies, or political parties. It organizes public information and does not replace official sources or the voterβs judgment.