This is a submission for the Hermes Agent Challenge
TL;DRβ Type a one-line bill topic. Twenty-five Hermes agents (1 Speaker, 19 ministries, 5 parties) run a full Polish legislative session in 2 minutes. Vote tally, social impact, party tweets β and a side-by-side"current law vs proposed amendment"with every clause cited to a real statute. Built ondelegate_task
for parallel ministry consultation.
π Live: https://web-production-53027.up.railway.app/
π₯ Walkthrough: https://www.loom.com/share/92cdac7da31c471088a4e569b0cfe1ed
π¦ Repo: https://github.com/monsad/ai-politics (MIT)
Watch a politician debate a new tax law on TV. They argue whether it's fair, whether it'll work, whether the other side is lying. Nobody ever shows you the diff β which paragraph of which statute actually changes, and from what to what. The conversation is theatre on top of an invisible legal document.
So I built the theatre AND the legal document.
Virtual Parliament is a multi-agent simulation of the Polish Sejm. You type something like "four-day work week" or "flat income tax", and 25 Hermes agents run a full legislative session:
delegate_task
The frontend surfaces the diff as a Current law vs proposed change panel. Left column: what's in force today, quoted from the statute. Right column: what the AI just proposed. A non-lawyer can finally see the actual edit.
This is the part of legislation that's normally invisible. The whole point is to make it visible.
π₯ Video walkthrough (~2 min): https://www.loom.com/share/92cdac7da31c471088a4e569b0cfe1ed
π Live URL: https://web-production-53027.up.railway.app/
Two buttons:
google/gemini-3.1-flash-lite
calls.Try "four-day work week" (Demo) β it's the demo fixture I ship in the Docker image. Lewica and Konfederacja vote on opposite sides (pro-labour vs free-market), which is the political-coherence check I built into the acceptance tests.
Local one-command run:
git clone https://github.com/monsad/ai-politics && cd ai-politics && make setup && parliament "four-day work week"
π¦ Repo: https://github.com/monsad/ai-politics (MIT)
skills/ # 25 Hermes Agent skills, validated by skills-ref
marszalek-sejmu/ # the orchestrator β owns the bill-drafting template
ministry-finansow/ # 19 ministry experts (Finance, Health, Climate, ...)
...
party-ko/ # 5 party agents (KO, PiS, TD, Konfederacja, Lewica)
parliament/
session.py # subprocess launcher around `hermes chat -s <skill>`
transcript_parser.py # splits orchestrator stdout into per-speaker utterances
citation_validator.py # every [node:...] must resolve back to a real statute
api.py # FastAPI: POST /sessions, polling SSE /stream/{id}
cli.py # `parliament "<topic>"` (typer)
web/ # Next.js 16 static export, served by FastAPI
deploy/ # Dockerfile entrypoint + Hermes config + demo fixture
| Layer | Tech | Notes |
|---|---|---|
| Agent framework | ||
| hermes-agent 0.14.0 | ||
the load-bearing piece β pip install hermes-agent==0.14.0 |
||
| Skills spec | ||
Anthropic Agent Skills + skills-ref@0.1.5 |
||
| 25 skills, lowercase-hyphen, validated in CI | ||
| RAG | ||
| PageIndex Cloud via MCP | ||
| vectorless retrieval over Polish Constitution + ~50 statutes; every citation traces to a real document | ||
| Models | ||
google/gemini-3.1-flash-lite via OpenRouter |
||
| ~$0.04 per full session, fast enough for live demo | ||
| Orchestrator | ||
| Python 3.11 + FastAPI + uvicorn | subprocess launcher around hermes chat |
|
| Stream | ||
| sse-starlette + polling SQLite | per-speaker utterances pushed as event: utterance |
|
| Frontend | ||
| Next.js 16 (App Router, static export) + Tailwind | served from /app/* by the same FastAPI |
|
| Deploy | ||
| Railway (single Docker container) | public HTTPS, ~$5/month |
There's one Hermes property the whole project is built on:
delegate_task
lets a parent skill fan out to N child skills in parallel as a single tool call.
Without that, this project isn't tractable. With it, the entire 25-agent pipeline is 24 LLM calls in a tight DAG, runs in 2 minutes, and the orchestrator never has to manage thread pools or async gathers itself.
Here's the shape:
βββββββββββββββββββββββββββββββ
β marszalek-sejmu (skill) β
β Topic β ministry selection β
ββββββββββββββββ¬βββββββββββββββ
β delegate_task(tasks=[...]) β Hermes batch mode
ββββββββββββββββββΌβββββββββββββββββ
βΌ βΌ βΌ
ββββββββββββββββββ ββββββββββββββββββ ββββββββββββββββββ
β ministry- β β ministry- β β ministry- β
β finansow β β klimatu β β rodziny-pracy β
ββββββββββ¬ββββββββ ββββββββββ¬ββββββββ ββββββββββ¬ββββββββ
β PageIndex RAG β (cite real statutes)
ββββββββββ¬ββββββββββ΄ββββββββββ¬ββββββββ
βΌ βΌ
Synthesized findings β MarszaΕek
β
βΌ 5 Γ party debate, Γ2 readings
ββββββββββββββββ
β KO PiS TD β
β Konf Lewica β
ββββββββ¬ββββββββ
βΌ
Seat-weighted vote β Draft bill
delegate_task
was the right primitive
ThreadPoolExecutor
to spawn AIAgent
children. I don't have to mix asyncio with hermes-agent's threaded subagents β a known foot-gun if you roll your own.pageindex-rag
). The MarszaΕek doesn't pollute their context.Skills as the unit of expertise. Every agent is one SKILL.md
. The MarszaΕek has the bill-drafting template (assets/bill-draft-template.md
). The parties have their actual policy positions. None of this fits in one big system prompt β but as 25 separate skills, it's maintainable. I can rewrite Lewica's economic stance without touching Konfederacja.
MCP toolsets for retrieval. Every skill that cites Polish law declares toolsets: ["pageindex-rag"]
and gets retrieval for free. Zero Python integration code. The PageIndex MCP server is one config-yaml entry.
Subprocess as the integration surface. Hermes is a CLI first. The cleanest way to embed it in FastAPI is subprocess.Popen(["hermes", "chat", "-s", skill, "-q", topic, "-Q", "--accept-hooks", "--yolo"])
. My session.py
is essentially that subprocess launcher plus a stdout parser that splits the result into per-speaker utterances for SSE streaming.
Bake-time config for the container. For Railway, the Dockerfile copies hermes-config.yaml
to /root/.hermes/config.yaml
and skills/*
to /root/.hermes/skills/
. An entrypoint script materializes OPENROUTER_API_KEY
into ~/.hermes/.env
at boot. Crucially: ** disabled_toolsets: [browser, computer-use, voice, terminal-modal]** β otherwise Hermes hangs at startup looking for a Chromium binary that isn't in
python:3.11-slim
. I only found that via a /diag
endpoint I added to introspect the running container.If I had to write the parallel fan-out + tool registry + skill by hand, I'd still be debugging deadlocks instead of arguing with my own bill drafts.
Hermes let me spend my time on the simulation design (how does a MarszaΕek pick ministries? what does each party's house style sound like? how do you parse "Article 129 Β§1 is amended to readβ¦" out of free-form markdown?) and the legal-diff UX (the Current law vs proposed change panel) β not on the orchestration framework.
That's the right division of labour for a 5-day contest project, and frankly for most agent projects.
gemini-flash-lite
β llama-3.3-70b
changes vocabulary, barely changes the structure of the debate.Czas pracy nie moΕΌe przekraczaΔ 8 godzin na dobΔ i przeciΔtnie 40 godzinβ¦
"π΅π± Built in Ε»ory. MIT-licensed. Educational simulation only β no real Members of Parliament are represented, no hate speech is produced, and a disclaimer is emitted at the top and bottom of every session.