Putting a Courtroom on a Language Model

The Idea

Most LLM demos are one person talking to one model. I wanted to see what happens when a single human has to share the room with several AI characters who must stay consistent with each other over a long session.

A courtroom turned out to be a good place to try it. It has clear roles, a fixed order of events, a lot of talking and a decision at the end.

The interesting question was not whether a model can play a judge. It was which parts of a trial the model should be trusted with, and which parts it should not.

You are handed a freshly generated fictional criminal case, choose to prosecute or defend, argue it in your own words and wait for the jury to decide.

Image Text

The Build

Every game starts with a case invented by the model from a random topic and setting: a charge, a defendant, evidence, witnesses and a twist. The case is written to leave room for both guilt and reasonable doubt, so neither side has an easy win.

From there the trial follows a fixed script. The judge opens the court, you and your AI opponent give opening statements and present your evidence, and the judge instructs the jury. You write your own statements. The opponent and the judge respond in character, and each one sees everything that has been said so far.

Then twelve jurors vote independently. Each has a name, age and occupation, and gives a written rationale alongside their decision. A majority decides, and a 6-6 tie is a hung jury that votes again. You can edit the jurors in the app, and a juror who “deeply distrusts authority figures” makes for a very different trial.

The whole thing runs on a local model through Ollama, so there are no accounts, no API keys and no usage costs. Twelve simultaneous model calls would be expensive on a hosted API. Here they are free to try, which shaped how the jury works.

Image Text

Each juror’s rationale is shown next to their vote, so you can see why the jury decided as it did, not just what it decided.

Behind the Build

Designed and built end-to-end by me, from the trial flow and prompts to the data model and interface. It is a personal project with no commercial deployment, and it is open source under the MIT licence.

The design choice I would point to is a simple division of labour. The model writes. Code decides. The model produces the case, the speeches and the jurors’ reasoning, but it never counts a vote, never tracks where the trial is up to and never gets to decide what counts as valid output. Cases and votes have to fit a schema or they are retried. The tally is a plain function. The position in the trial is worked out from the saved transcript, so a refresh or a crash cannot leave the game in an impossible state.

Two smaller decisions made a visible difference. Jurors have to give their rationale before their decision, so the vote is justified rather than rationalised after the fact. And the randomness in each case comes from a topic picked by code, because a model asked to “be random” keeps producing the same few cases.

What I Took From It

This is a small a game, not a model of real juries. All twelve jurors are the same model with different personas, so their votes are correlated. What does carry over to product work is the pattern. Models are very good at producing things that read well, such as a story, a speech or an opinion. They are not what you want counting votes or guaranteeing a format. Let the model write, and let validated structure and plain code decide what is accepted, what happens next and who wins.

Under the Hood

Read the technical deep dive on GitHub →

View the code on GitHub →