dpr-simulator / ARCHITECTURE.md
mcikalmerdeka's picture
add ARCHITECTURE.md file
18e4e3b
|
Raw
History Blame Contribute Delete
27 kB
# DPR AI Simulator β€” Architecture
## 1. Goals & Non-Goals
### Goals (what we WILL build)
1. **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.
2. **Multi-agent parliamentary deliberation** β€” Each DPR member is an independent AI agent with unique persona (faction ideology, electoral district, expertise).
3. **Cost-efficient processing** β€” Process 50–575 agents for under $0.10 using gpt-4.1-nano, with real-time cost tracking.
4. **Interactive web interface** β€” Gradio-based UI for inputting aspirations and visualizing simulation results with data tables and council transcripts.
5. **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)
1. **Real legislative drafting** β€” The system generates action plans and recommendations, not actual RUU (Rancangan Undang-Undang) drafts with legal force.
2. **Persistent database storage** β€” No database layer; all simulation state is in-memory per request.
3. **Authentication / user management** β€” No login system; API keys are entered per session via the UI.
4. **Real-time streaming LLM responses** β€” Each agent call is a complete async request; no streaming tokens to the UI.
5. **Integration with actual DPR systems** β€” This is a simulation/educational tool, not connected to any government API.
## 2. Core Principles
1. **Markdown and JSON are the universal intermediate formats** β€” All agent prompts and outputs use structured JSON; UI renders markdown.
2. **Batch parallel processing over individual sequential calls** β€” Agent invocations are grouped into batches (default 10) with async `gather()` to maximize throughput.
3. **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.
4. **Cost transparency by design** β€” Every API call tracks token usage and calculates USD cost; totals are surfaced in the UI and logs.
5. **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), `timestamp`
- `to_prompt_context()` formats aspiration data for LLM prompts.
### Pipeline Response Models
**AbsorpsiResponse** β€” Output of Stage 1 (per member)
- `relevansi`: Tinggi/Sedang/Rendah
- `alasan_relevansi`: technical explanation
- `sentiment`: Positif/Negatif/Netral/Kritis
- `quote`: verbal political statement in faction persona style
- `poin_kunci`: list of key points
- `rekomendasi_awal`: initial recommendation
- `cost_usd`: API call cost
**KompilasiResponse** β€” Output of Stage 2
- `status`: terkumpul / tidak_relevan
- `ringkasan`: consensus summary
- `tema_utama`: main themes
- `fraksi_terlibat`: involved factions
- `rekomendasi_tindak_lanjut`: follow-up recommendation
**CouncilDiscussionResponse** β€” Output of Stage 3
- `diskusi`: list of rounds, each with interventions (pemaparan/tanggapan)
- `posisi_fraksi`: faction β†’ position mapping
- `konsensus`: sepenuhnya/setengah/terbagi/deadlock
- `rekomendasi_kolektif`: collective recommendation
**TindakLanjutResponse** β€” Output of Stage 4
- `langkah_tindak_lanjut`: concrete steps
- `komisi_penanggung_jawab`: responsible commission
- `timeline`: estimated timeline
- `estimasi_anggaran`: budget estimate in Rupiah
- `rincian_anggaran`: per-item budget breakdown
- `sumber_dana`: funding sources (APBN/APBD)
- `pihak_terlibat`: stakeholders to involve
**PipelineResult** β€” Aggregates all stages
- `aspirasi`, `tanggapan_anggota[]`, `kompilasi`, `council_discussion`, `tindak_lanjut`
- `simulation_details`: member selection stats, relevance breakdown, cost
- `total_cost_usd`: sum of all API call costs
## 7. Agent Design
All agents inherit from `BaseAgent` and follow the same pattern:
1. **System Prompt** β€” Defines role and rules (in Indonesian)
2. **User Prompt Builder** β€” Injects dynamic context (member data, aspiration, prior responses)
3. **LLM Invocation** β€” `ainvoke()` via LangChain ChatOpenAI
4. **JSON Parsing** β€” Strip markdown fences, `json.loads()`, map to Pydantic model
5. **Cost Calculation** β€” Extract `token_usage` from `response_metadata`, compute USD cost
6. **Error Handling** β€” Catch exceptions, return response with `error` field 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:
```python
# 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 + 3` where N = sample size
## 9. Member Factory & Relevance Engine
**DPRMemberFactory.create_members(count)**
- Generates `count` members 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)**
1. Determine target Komisi from `CATEGORY_TO_KOMISI` mapping (or explicit filter)
2. Filter members whose `komisi` is in target list
3. Fallback to expertise match if no Komisi match (defensive)
4. Sort by province match (source province members first)
5. Return top `limit` members
## 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](https://huggingface.co/spaces/mcikalmerdeka/dpr-simulator)
- SDK: Gradio
- Entry point: `main.py`
- Sync via GitHub Actions (`.github/workflows/sync.yml`)
**Local development:**
```bash
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, costs
- `dpr_simulator.agents` β€” Agent initialization
- `dpr_simulator.agents.{absorb,compile,council,followup}` β€” Per-agent invocations, token usage, errors
- `dpr_simulator.members` β€” Member factory operations
- `dpr_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.