Parliamentary AI Monitor Implementation Plan

Context

Answers confirmed:

The Shared Core Pipeline

All three paths run the same brain. The only difference is what happens at the output step.

[Diagram]

The one question this prototype needs to answer:

"Does the legal analysis logic work accurately enough to be useful in a real session — and what's the fastest way to find out?"

Everything else (speaker ID, dashboards, archive systems) is downstream of this.

Audio Input — Two Modes, Same Pipeline

[Diagram]

Switching between mic and livestream is a single function swap. The rest of the pipeline is unchanged.

3 Prototype Paths

[Diagram]

Path A — Local Web App (Streamlit)

The idea: Python + Streamlit running entirely on a laptop in the committee room. No server, no deploy, no accounts. One terminal command → browser tab opens.

Who accesses it: The laptop screen directly. Optionally expose to the MP's phone via ngrok — one extra terminal command, zero code change.

[Diagram]

Build sequence:

[Diagram]

Stack:

Python 3.11+
├── sounddevice       # mic capture
├── openai            # Whisper API
├── google-generativeai  # Gemini API
└── streamlit         # UI

Core code sketch:

import sounddevice as sd
import tempfile, wave, time
from openai import OpenAI
import google.generativeai as genai

CHUNK_SECONDS = 30
SAMPLE_RATE = 16000

openai_client = OpenAI()
genai.configure(api_key="YOUR_GEMINI_KEY")
model = genai.GenerativeModel("gemini-1.5-pro")

SYSTEM_PROMPT = """
คุณคือ Smart MP Advisor ผู้เชี่ยวชาญด้านข้อบังคับการประชุมรัฐสภาและรัฐธรรมนูญ
[paste Smart MP Advisor instructions here]

วิเคราะห์ข้อความต่อไปนี้ และตอบกลับในรูปแบบ:
สถานะ: NORMAL หรือ AT_RISK
ข้อบังคับที่เกี่ยวข้อง: (ถ้ามี)
เหตุผล: (สั้นๆ)
"""

def record_chunk():
    audio = sd.rec(int(CHUNK_SECONDS * SAMPLE_RATE),
                   samplerate=SAMPLE_RATE, channels=1, dtype='int16')
    sd.wait()
    with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as f:
        with wave.open(f.name, 'wb') as wf:
            wf.setnchannels(1); wf.setsampwidth(2); wf.setframerate(SAMPLE_RATE)
            wf.writeframes(audio.tobytes())
        return f.name

def transcribe(path):
    with open(path, "rb") as f:
        return openai_client.audio.transcriptions.create(
            model="whisper-1", file=f, language="th"
        ).text

def analyze(transcript):
    return model.generate_content(
        f"{SYSTEM_PROMPT}\n\nข้อความ: {transcript}"
    ).text

while True:
    path = record_chunk()
    transcript = transcribe(path)
    analysis = analyze(transcript)
    print(f"[{time.strftime('%H:%M:%S')}] {transcript}")
    print(f"→ {analysis}\n")
    time.sleep(1)

For phone access (zero code change):

# Terminal 1
streamlit run app.py

# Terminal 2
ngrok http 8501

Pros: Fastest to build. Easiest to iterate. Everything visible on screen. Cons: Laptop must stay open. Phone access needs ngrok (one extra step).

Path B — LINE Bot

The idea: Same Python backend. Output is pushed as a LINE message to the MP's phone. No new interface — the alert arrives where the MP is already looking.

Why this fits: LINE penetration in Thai political circles is near 100%. Zero setup required on the MP's side.

[Diagram]

Build sequence:

[Diagram]

Two LINE options:

Option Setup time Cost Best for
LINE Notify 5 min, personal token Free 1 recipient, fastest test
LINE Messaging API 30 min, channel setup Free tier Multiple recipients, richer format

Core push function:

import requests

LINE_NOTIFY_TOKEN = "YOUR_TOKEN"

def push_to_line(status: str, transcript: str, article: str, reason: str):
    icon = "🟢" if status == "NORMAL" else "🔴"
    msg = f"""
{icon} {status}
──────────────────
📝 {transcript[:150]}{'...' if len(transcript) > 150 else ''}
⚖️ {article if article else '-'}
💬 {reason}
    """.strip()
    requests.post(
        "https://notify-api.line.me/api/notify",
        headers={"Authorization": f"Bearer {LINE_NOTIFY_TOKEN}"},
        data={"message": msg}
    )

What the MP sees:

🔴 AT_RISK
──────────────────
📝 "ท่านประธานที่หน้าตาดีที่นั่งอยู่..."
⚖️ ข้อบังคับการประชุม ข้อ 45
💬 อาจเข้าข่ายการใช้ถ้อยคำเสียดสี

Pros: Zero new interface for the MP. Most socially invisible. Fastest to working demo. Cons: LINE Notify deprecated end-2025 (Messaging API is the upgrade path). No running log view.

Path C — n8n Workflow

The idea: Build the pipeline as an n8n workflow. Entire logic visible and editable on the n8n canvas. No Python to maintain beyond the 10-line audio recorder.

Why this fits: n8n is already running in this environment. The workflow can be handed off to, or modified by, non-developers.

[Diagram]

Build sequence:

[Diagram]

Companion recorder (10 lines — run once, leave running):

# recorder.py
import sounddevice as sd, wave, time

while True:
    audio = sd.rec(30 * 16000, samplerate=16000, channels=1, dtype='int16')
    sd.wait()
    with wave.open("/tmp/chunk.wav", "wb") as f:
        f.setnchannels(1); f.setsampwidth(2); f.setframerate(16000)
        f.writeframes(audio.tobytes())
    time.sleep(1)

n8n Gemini HTTP node config:

{
  "method": "POST",
  "url": "https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-pro:generateContent",
  "headers": { "x-goog-api-key": "{{ $env.GEMINI_KEY }}" },
  "body": {
    "contents": [{
      "parts": [{ "text": "[Smart MP Advisor system prompt]\n\nข้อความ: {{ $json.transcript }}" }]
    }]
  }
}

Pros: Entire logic visible on canvas. Easy to hand off. Logging and multi-output trivial to add. Cons: Audio capture still needs the 10-line Python companion.

Decision Matrix

Path A — Streamlit Path B — LINE Bot Path C — n8n
Build time 2–3 days 1–2 days 2–3 days
Code required Medium (Python + Streamlit) Low (Python + API call) Minimal (10-line recorder)
Infrastructure Laptop only Laptop + LINE account Laptop + n8n instance
MP experience Watch screen / open URL Check LINE (already open) Check LINE / email
Visual running log ✅ Yes ❌ Push per alert ❌ Push per alert
Modifiable by non-dev ❌ No ❌ No ✅ Yes (n8n canvas)
Livestream input ✅ Easy swap ✅ Easy swap ✅ Easy swap
Multi-recipient Via ngrok URL share ✅ Native (LINE group) ✅ Native (multiple nodes)
Auto-logging Manual Manual ✅ Google Sheets node
Best for Demos + iteration Deployment + daily use Handoff + auditability

All three paths use the same Whisper + Gemini + Smart MP Advisor core. Switching between paths costs 1 day, not 1 week.

Recommendation

[Diagram]

Given the confirmed answers — MP checks their phone, n8n already running, no live dashboard needed:

Path A becomes relevant only if a visual running log proves necessary after the first test.

Livestream Input — Same Pipeline, Swapped Source

[Diagram]
def capture_livestream_chunk(url, duration=30, output="/tmp/chunk.wav"):
    subprocess.run([
        "ffmpeg", "-y", "-i", url,
        "-t", str(duration), "-ar", "16000", "-ac", "1", output
    ], capture_output=True)

Replace record_chunk() with capture_livestream_chunk(url) in any path. Everything downstream is unchanged.

What "Done" Looks Like

The prototype is done when all four are true:

  1. Real Thai speech from a real microphone → Whisper transcript
  2. Transcript → Gemini + Smart MP Advisor → structured analysis
  3. AT_RISK result → alert reaches the MP within 15 seconds of speaking
  4. A real person in a real committee session says: "มันทำงานได้จริง"

Everything beyond that is the next sprint.