Spaces:
Paused
A newer version of the Gradio SDK is available: 6.26.0
DPR AI Simulator β Architecture
1. Goals & Non-Goals
Goals (what we WILL build)
- AI-powered DPR simulation β Simulate the three core functions of Indonesia's House of Representatives (DPR RI): absorbing, compiling, and following up on citizen aspirations.
- Multi-agent parliamentary deliberation β Each DPR member is an independent AI agent with unique persona (faction ideology, electoral district, expertise).
- Cost-efficient processing β Process 50β575 agents for under $0.10 using gpt-4.1-nano, with real-time cost tracking.
- Interactive web interface β Gradio-based UI for inputting aspirations and visualizing simulation results with data tables and council transcripts.
- Realistic parliamentary dynamics β Council Discussion stage simulates multi-round debates with faction positions, coalition vs. opposition dynamics, and consensus building.
Non-Goals (what we WON'T build β prevents scope creep)
- Real legislative drafting β The system generates action plans and recommendations, not actual RUU (Rancangan Undang-Undang) drafts with legal force.
- Persistent database storage β No database layer; all simulation state is in-memory per request.
- Authentication / user management β No login system; API keys are entered per session via the UI.
- Real-time streaming LLM responses β Each agent call is a complete async request; no streaming tokens to the UI.
- Integration with actual DPR systems β This is a simulation/educational tool, not connected to any government API.
2. Core Principles
- Markdown and JSON are the universal intermediate formats β All agent prompts and outputs use structured JSON; UI renders markdown.
- Batch parallel processing over individual sequential calls β Agent invocations are grouped into batches (default 10) with async
gather()to maximize throughput. - Every agent is a persona, not a generic LLM β Each agent prompt injects faction ideology, commission scope, and electoral district context to produce politically realistic responses.
- Cost transparency by design β Every API call tracks token usage and calculates USD cost; totals are surfaced in the UI and logs.
- Fail-soft per agent β If one agent fails (network, JSON parse error), the pipeline continues with that agent marked as error; no single failure aborts the simulation.
3. System Overview
βββββββββββββββ ββββββββββββββββ βββββββββββββββββββββββ βββββββββββββββ
β User ββββββΆβ Gradio UI ββββββΆβ DPRSimulator ββββββΆβ Results β
β (Browser) β β (src/ui) β β (src/core) β β (UI + Log) β
βββββββββββββββ ββββββββββββββββ βββββββββββββββββββββββ βββββββββββββββ
β
βββββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββ
β β β
βΌ βΌ βΌ
ββββββββββββββββββ ββββββββββββββββββ ββββββββββββββββββ
β MemberFactory β β Agent Pool β β Komisi Data β
β (create/filter)β β (4 agents) β β (13 komisi) β
ββββββββββββββββββ ββββββββββββββββββ ββββββββββββββββββ
Data Flow:
User submits aspiration
β
βΌ
ββββββββββββββββββββ
β 1. CREATE MEMBERSβ βββΆ DPRMemberFactory generates N members with
β (MemberFactory)β faction, komisi, dapil, expertise, province
ββββββββββββββββββββ
β
βΌ
ββββββββββββββββββββ
β 2. FILTER β βββΆ Get relevant members by komisi (primary)
β (Relevance) β and province (secondary), up to sample_size
ββββββββββββββββββββ
β
βΌ
ββββββββββββββββββββ
β 3. ABSORB β βββΆ AbsorbAgent (N parallel batch calls)
β (Stage 1) β Output: AbsorpsiResponse per member
β β (relevansi, sentiment, quote, poin_kunci)
ββββββββββββββββββββ
β
βΌ
ββββββββββββββββββββ
β 4. COMPILE β βββΆ CompileAgent (1 call)
β (Stage 2) β Output: KompilasiResponse
β β (ringkasan, tema_utama, fraksi_terlibat)
ββββββββββββββββββββ
β
βΌ
ββββββββββββββββββββ
β 5. COUNCIL β βββΆ CouncilDiscussionAgent (1 call)
β DISCUSSION β Output: CouncilDiscussionResponse
β (Stage 3) β (diskusi multi-putaran, posisi_fraksi, konsensus)
ββββββββββββββββββββ
β
βΌ
ββββββββββββββββββββ
β 6. FOLLOW-UP β βββΆ FollowUpAgent (1 call)
β (Stage 4) β Output: TindakLanjutResponse
β β (langkah, timeline, anggaran, pihak_terlibat)
ββββββββββββββββββββ
β
βΌ
ββββββββββββββββββββ
β 7. AGGREGATE β βββΆ PipelineResult with all stages + SimulationDetails
β (Result) β Displayed in Gradio UI as chat + dataframes
ββββββββββββββββββββ
4. Tech Stack
| Component | Library | Version | Role |
|---|---|---|---|
| LLM Framework | LangChain | >=1.2.7 | Agent orchestration, prompt templates, output parsing |
| LLM Provider | OpenAI (via langchain-openai) | >=2.16.0 | gpt-4.1-nano for all agent inference |
| Web UI | Gradio | >=6.4.0 | Interactive web interface with chatbot and data tables |
| Data Validation | Pydantic | >=2.12.5 | Model schemas, settings management, response parsing |
| Settings | pydantic-settings | >=2.12.0 | Environment-based configuration |
| Data Processing | pandas | (via gradio) | DataFrame conversion for member tables |
| Package Manager | uv | latest | Fast Python dependency management |
| Python Runtime | CPython | >=3.12 | Async/await, type hints, modern syntax |
Avoided alternatives:
- FastAPI + React β Would add frontend complexity; Gradio is sufficient for this data-science-style UI.
- CrewAI / AutoGen β Overkill for this fixed pipeline; LangChain gives precise control over prompts and batching.
- Local LLMs (Ollama) β Would require GPU infrastructure; OpenAI API keeps costs low and setup minimal.
- Streamlit β Less flexible for custom CSS/theming compared to Gradio 6.x.
5. Project Structure
dpr-simulator/
βββ main.py # Entry point: initializes logging, launches Gradio
βββ pyproject.toml # Project metadata + dependencies (uv)
βββ README.md # User-facing documentation (Indonesian)
βββ uv.lock # Locked dependency versions
βββ requirements.txt # Fallback requirements file
βββ .env # Local environment variables (gitignored)
βββ logs/
β βββ app.log # Runtime application logs
βββ assets/
β βββ project_header.png # README header image
βββ others/ # Documentation and marketing artifacts
β βββ linkedin_post.md
β βββ komisi_dpr.md
β βββ api_call_breakdown.md
β βββ response_example.md
β βββ additional_info*.md
βββ src/
β βββ __init__.py
β βββ config/
β β βββ __init__.py # Exports settings, setup_logger
β β βββ settings.py # Pydantic Settings (env vars, defaults)
β β βββ logging_config.py # Structured logging setup
β β βββ examples.py # Pre-loaded aspiration examples for UI
β βββ models/
β β βββ __init__.py # Exports all Pydantic models
β β βββ dpr_member.py # DPRMember: id, name, faction, komisi, dapil, province, expertise
β β βββ aspirasi.py # Aspirasi: id, source, category, content, priority, timestamp
β β βββ responses.py # All response models:
β β # AbsorpsiResponse, KompilasiResponse,
β β # CouncilDiscussionResponse, TindakLanjutResponse,
β β # SimulationDetails, PipelineResult
β βββ core/
β β βββ __init__.py # Exports DPRSimulator, DPRMemberFactory
β β βββ simulator.py # DPRSimulator: pipeline orchestrator
β β βββ member_factory.py # DPRMemberFactory: member creation + relevance filtering
β β βββ faction_data.py # FACTION_PERSONAS: 17 faction ideologies
β β βββ komisi_data.py # 13 Komisi definitions, categoryβkomisi mapping
β β βββ agents/
β β βββ __init__.py # Exports all agents
β β βββ base.py # BaseAgent: LLM init, cost calc, abstract methods
β β βββ absorb_agent.py # AbsorbAgent: Stage 1 β member-level aspiration analysis
β β βββ compile_agent.py # CompileAgent: Stage 2 β aggregate responses into consensus
β β βββ council_discussion_agent.py # CouncilDiscussionAgent: Stage 3 β multi-member deliberation
β β βββ followup_agent.py # FollowUpAgent: Stage 4 β concrete action plan + budget
β βββ ui/
β βββ __init__.py # Exports launch_app
β βββ app.py # Gradio UI: create_app(), process_aspirasi_async/sync
βββ .github/
β βββ workflows/
β βββ sync.yml # GitHub Actions: sync to Hugging Face Spaces
βββ .cursor/
βββ rules/
βββ development-agent-rules.mdc # Cursor IDE rules for AI coding agents
6. Data Model
Core Entities
DPRMember β Represents a simulated parliament member.
id,name,faction(fraksi),komisi,dapil,province,expertise[]to_prompt_context()formats member data for LLM prompts.
Aspirasi β Represents a citizen aspiration submitted for processing.
id,source(province),category,content,priority(Tinggi/Sedang/Rendah),timestampto_prompt_context()formats aspiration data for LLM prompts.
Pipeline Response Models
AbsorpsiResponse β Output of Stage 1 (per member)
relevansi: Tinggi/Sedang/Rendahalasan_relevansi: technical explanationsentiment: Positif/Negatif/Netral/Kritisquote: verbal political statement in faction persona stylepoin_kunci: list of key pointsrekomendasi_awal: initial recommendationcost_usd: API call cost
KompilasiResponse β Output of Stage 2
status: terkumpul / tidak_relevanringkasan: consensus summarytema_utama: main themesfraksi_terlibat: involved factionsrekomendasi_tindak_lanjut: follow-up recommendation
CouncilDiscussionResponse β Output of Stage 3
diskusi: list of rounds, each with interventions (pemaparan/tanggapan)posisi_fraksi: faction β position mappingkonsensus: sepenuhnya/setengah/terbagi/deadlockrekomendasi_kolektif: collective recommendation
TindakLanjutResponse β Output of Stage 4
langkah_tindak_lanjut: concrete stepskomisi_penanggung_jawab: responsible commissiontimeline: estimated timelineestimasi_anggaran: budget estimate in Rupiahrincian_anggaran: per-item budget breakdownsumber_dana: funding sources (APBN/APBD)pihak_terlibat: stakeholders to involve
PipelineResult β Aggregates all stages
aspirasi,tanggapan_anggota[],kompilasi,council_discussion,tindak_lanjutsimulation_details: member selection stats, relevance breakdown, costtotal_cost_usd: sum of all API call costs
7. Agent Design
All agents inherit from BaseAgent and follow the same pattern:
- System Prompt β Defines role and rules (in Indonesian)
- User Prompt Builder β Injects dynamic context (member data, aspiration, prior responses)
- LLM Invocation β
ainvoke()via LangChain ChatOpenAI - JSON Parsing β Strip markdown fences,
json.loads(), map to Pydantic model - Cost Calculation β Extract
token_usagefromresponse_metadata, compute USD cost - Error Handling β Catch exceptions, return response with
errorfield set
Agent Specializations
| Agent | Temperature | Input | Output | Key Behavior |
|---|---|---|---|---|
| AbsorbAgent | 0.7 | Member + Aspirasi | AbsorpsiResponse | Injects faction persona via get_faction_persona(). Prioritizes Komisi over Dapil for relevance. Generates natural political quotes. |
| CompileAgent | 0.7 | Aspirasi + AbsorpsiResponse[] | KompilasiResponse | Filters to Tinggi/Sedang relevance only. Aggregates themes and factions. |
| CouncilDiscussionAgent | 0.8 | Aspirasi + AbsorpsiResponse[] + DPRMember[] | CouncilDiscussionResponse | Simulates multi-round (default 2) parliamentary debate. Each intervention includes member_id, name, faction, type (pemaparan/tanggapan), and content. |
| FollowUpAgent | 0.7 | Aspirasi + KompilasiResponse | TindakLanjutResponse | Generates realistic Indonesian government budget estimates with per-item breakdowns and funding sources. |
8. Pipeline Orchestration
DPRSimulator is the central orchestrator:
# Simplified flow
async def process_aspirasi(aspirasi, sample_size, komisi_filter):
# 1. Filter members
relevant = MemberFactory.get_relevant_members(members, category, source, komisi_filter, sample_size)
# 2. Absorb (batched parallel)
for batch in relevant[::batch_size]:
responses += await gather([absorb_agent.invoke(m, aspirasi) for m in batch])
await sleep(rate_limit_delay)
# 3. Compile
kompilasi = await compile_agent.invoke(aspirasi, responses)
# 4. Council Discussion (only if compilation succeeded)
if kompilasi.status == "terkumpul":
council = await council_discussion_agent.invoke(aspirasi, responses, relevant_members)
# 5. Follow-up (only if compilation succeeded)
if kompilasi.status == "terkumpul":
tindak_lanjut = await followup_agent.invoke(aspirasi, kompilasi)
# 6. Aggregate
return PipelineResult(...)
Batching Strategy:
- Default batch size: 10 members
- Default rate limit delay: 1 second between batches
- Uses
asyncio.gather()within each batch for parallel execution - Total API calls formula:
N + 3where N = sample size
9. Member Factory & Relevance Engine
DPRMemberFactory.create_members(count)
- Generates
countmembers with cyclical distribution across:- 17 factions (PDI-P, Golkar, Gerindra, PKB, Nasdem, PKS, Demokrat, PAN, PPP, PSI, Perindo, Hanura, Garuda, PBB, PKPI, Gelora, Ummat)
- 13 Komisi (Komisi I β Komisi XIII)
- 33 provinces
- 16 expertise areas
- Members are named
Anggota_DPR_{id}for deterministic generation.
DPRMemberFactory.get_relevant_members(members, category, source, komisi_filter, limit)
- Determine target Komisi from
CATEGORY_TO_KOMISImapping (or explicit filter) - Filter members whose
komisiis in target list - Fallback to expertise match if no Komisi match (defensive)
- Sort by province match (source province members first)
- Return top
limitmembers
10. Configuration
All configuration is via pydantic-settings with environment variable fallbacks:
| Variable | Default | Description |
|---|---|---|
OPENAI_API_KEY |
"" |
OpenAI API key (also accepted via UI) |
OPENAI_MODEL |
gpt-4.1-nano |
Model for all agents |
PROMPT_COST_PER_1K |
0.0001 |
Prompt token cost (USD) |
COMPLETION_COST_PER_1K |
0.0004 |
Completion token cost (USD) |
DEFAULT_MEMBER_COUNT |
50 |
Default members generated |
BATCH_SIZE |
10 |
Parallel batch size for absorb stage |
RATE_LIMIT_DELAY |
1.0 |
Seconds between batches |
GRADIO_SERVER_NAME |
127.0.0.1 |
Gradio host |
GRADIO_SERVER_PORT |
7860 |
Gradio port |
GRADIO_SHARE |
False |
Public Gradio share |
COUNCIL_DISCUSSION_ROUNDS |
2 |
Number of council debate rounds |
Security note: The application does not use .env files for the API key in production. The key is entered per session via the Gradio UI and is never persisted to disk.
11. UI Design
Gradio Layout (2-column):
| Left Column (Input) | Right Column (Output) |
|---|---|
| OpenAI API Key input (password) | Chatbot (progress + results) |
| Simulation settings (sliders) | Simulation details (accordions) |
| Aspiration content (textarea) | All members dataframe |
| Category / Komisi / Priority dropdowns | Relevant members dataframe |
| Source province dropdown | Responding members dataframe |
| Submit button | Council discussion transcript |
| Example aspirations (7 presets) | API call breakdown (dev info) |
Visual Design:
- Dark theme with slate/blue gradient background
- Custom CSS variables for theming
- IBM Plex Sans font
- Orange accent color for primary actions and highlights
- Data tables use Gradio's native Dataframe component
12. Cost Model
Using gpt-4.1-nano (default):
| Sample Size | Stage 1 (Absorb) | Stage 2 (Compile) | Stage 3 (Council) | Stage 4 (Follow-up) | Total API Calls | Est. Cost |
|---|---|---|---|---|---|---|
| 20 | 20 | 1 | 1 | 1 | 23 | ~$0.002β0.005 |
| 50 | 50 | 1 | 1 | 1 | 53 | ~$0.005β0.01 |
| 100 | 100 | 1 | 1 | 1 | 103 | ~$0.01β0.02 |
| 575 | 575 | 1 | 1 | 1 | 578 | ~$0.05β0.10 |
Comparison: Actual DPR RI annual budget is approximately Rp 5 Trillion.
13. Error Handling Strategy
| Layer | Strategy |
|---|---|
| Agent level | Try/except around ainvoke() and JSON parsing; return response with error field populated |
| Batch level | asyncio.gather() with individual task exceptions; failed agents logged but don't stop batch |
| Pipeline level | If compilation returns tidak_relevan, skip Council and Follow-up with warning messages |
| UI level | Try/except around entire pipeline; display error in chatbot with β prefix |
| Logging | Structured logging with logging.getLogger("dpr_simulator.{module}"); debug-level token usage, info-level stage transitions |
14. Deployment
Primary target: Hugging Face Spaces
- SDK: Gradio
- Entry point:
main.py - Sync via GitHub Actions (
.github/workflows/sync.yml)
Local development:
uv sync
uv run python main.py
# Access at http://127.0.0.1:7860
15. Observability
Logging hierarchy:
dpr_simulatorβ App lifecycle (startup, shutdown)dpr_simulator.simulatorβ Pipeline stage transitions, member counts, costsdpr_simulator.agentsβ Agent initializationdpr_simulator.agents.{absorb,compile,council,followup}β Per-agent invocations, token usage, errorsdpr_simulator.membersβ Member factory operationsdpr_simulator.uiβ UI events (submissions, errors)
Log output: logs/app.log (file) + stdout (console, via Gradio).
16. Implementation Status
| Phase | Status | Description |
|---|---|---|
| Core Pipeline | β Done | 4-stage pipeline (Absorb, Compile, Council, Follow-up) fully implemented |
| Agent System | β Done | All 4 agents with persona injection, JSON parsing, cost tracking |
| Member Factory | β Done | 17 factions, 13 komisi, 33 provinces, relevance filtering |
| Gradio UI | β Done | Dark-themed UI with chatbot, data tables, council transcript panel |
| Cost Tracking | β Done | Per-call and total cost calculation with USD and IDR display |
| Example Data | β Done | 7 pre-loaded aspiration examples |
| Logging | β Done | Structured logging across all modules |
| HF Spaces Deploy | β Done | Live deployment with GitHub Actions sync |
17. Decision Log
| Decision | Chosen | Rejected | Reason |
|---|---|---|---|
| LLM Provider | OpenAI API (gpt-4.1-nano) | Local LLMs, Claude, Gemini | Lowest cost ($0.05 for 575 agents), zero infrastructure, consistent JSON output |
| UI Framework | Gradio 6.x | Streamlit, FastAPI+React | Built-in chatbot, dataframe, and theme support; fastest path to interactive demo |
| Agent Framework | LangChain | CrewAI, AutoGen, raw OpenAI | Precise prompt control, easy JSON output parsing, familiar async patterns |
| Config Management | pydantic-settings | python-dotenv, dynaconf | Type-safe, validated, auto-documented; integrates with Pydantic models |
| Package Manager | uv | pip, poetry | Fastest installs, lockfile support, modern Python tooling |
| Member Naming | Anggota_DPR_{id} |
Real names (fictional), UUIDs | Deterministic, no cultural bias, easy debugging |
| Council Simulation | Single LLM call with full context | Multi-agent debate loop | 1 call vs N calls = massive cost savings; LLM can simulate multiple personas effectively |
| Language | Indonesian (prompts + UI) | English | Target audience is Indonesian citizens; political quotes must feel authentic |
| API Key Input | UI per-session | .env file | Security: no keys in repo, no disk persistence in shared environments (HF Spaces) |
| Data Persistence | None (in-memory) | SQLite, PostgreSQL | Stateless by design; each request is independent, no user accounts |
18. Project Status
This project is complete. All core functionality has been implemented, tested, and deployed. No further upgrades or new features are planned.
The system is stable and ready for use as-is. Any future changes would be limited to maintenance (dependency updates, bug fixes) rather than feature expansion.