AI Governance · Module 1

Prompt Evaluation

Do not just trust a prompt. Measure how well it works. Then see whether an optimiser, a program that tries changes to the prompt and keeps the ones that score best, can improve it. Everything runs on your own computer with a small open AI model. The example task is writing exam questions about AI governance. You can read the findings without running any code.

Start here

Two ways to use this page

I want the ideas, not the code

About 10 minutes, no setup. Read Why this matters, then What to do on Monday and the Lessons. Skip Setup and the code boxes; nothing on the page depends on them.

I want to run it

About 20 minutes to set up and a few minutes to run. You need a terminal, git, Python 3.11 or later and about 6 GB of disk. Go to Setup.

If you only read one thing
Write down what a good answer looks like as a short checklist, test your prompt against it on a handful of examples, and put the checklist’s rules into the prompt in plain words. In this experiment, those written rules did most of the improving; the automatic optimiser added polish.
Why this matters

A prompt is a component. Test it like one.

Most prompts are written, tried on two or three examples, and put to use because the output “looked fine”. After that, nobody can say how well the prompt works, or what the evidence is. Nobody can say if it still works after the AI model changes.

The governance view
AI governance is a clear record of who decided, on what evidence, and who answers for it. For a prompt, the evidence has three parts: a written description of what “good” means, a set of test cases, and a score that anyone can get again by rerunning the test.

This module uses DSPy, a Python toolkit. DSPy treats a prompt like a piece of software: you can test it, score it and improve it. You do not need DSPy for the ideas; the same steps work with any chat tool. The task is to write exam questions from short passages about AI governance principles. There are two kinds of question:

Short answer

One question and a short answer. Both must be grounded in the passage: based on what it actually says.

Multiple choice

One question, four options, and which option is correct.

Fully local

The model is Gemma 4 E2B, a small model from Google. It runs in llama.cpp, software that runs AI models on an ordinary computer. No data leaves your machine, and runs cost nothing.

The passages cover twelve principles that will not go out of date. They are: accountability, transparency, human oversight, risk-proportionate controls (stronger checks where the risk is higher), data minimisation (collecting only the data you need), bias testing, documentation, incident handling, supplier due diligence (checking the companies you buy AI from), record-keeping, contestability (letting people challenge a decision) and monitoring.

How it works

Five steps

The same five steps apply to any prompt you rely on, with or without DSPy. Non-coders can skip the code boxes; the text around them carries the idea.

01Describe the task, not the prompt

In DSPy you do not write the prompt itself. You list what goes in and what should come out. DSPy calls this list a signature, and it writes the actual prompt for you. This keeps what you want apart from how you ask. The “how you ask” part is what an optimiser can change.

class MultipleChoiceQuestion(dspy.Signature):
    """Write one multiple-choice exam question about the passage."""

    passage: str = dspy.InputField()
    question: str = dspy.OutputField()
    options: list[str] = dspy.OutputField()
    answer_index: int = dspy.OutputField()
02Collect examples, and keep some back

There are 24 hand-written passages. Each comes with a hand-written short-answer question and a hand-written multiple-choice question. These are the reference questions: examples of what good looks like. Twelve passages are for training, which means improving the prompt. The other twelve are held back: kept aside and used only for testing. Each principle appears once in each half.

The optimisers only ever see the training half. Every score on this page comes from the held-back half. So a prompt cannot score well by memorising its test.

03Write down what “good” means (the metric)

This is the most important step. The metric is the scoring rule: a small piece of code that gives one output a score. Here it is a checklist anyone can read. No second AI acts as a judge:

Short answer
  • It is a question
  • It stands alone: students will not see the passage
  • Answer is 12 words or fewer
  • Answer is grounded in the passage
  • Answer is not given away by the question
Multiple choice
  • It is a question, and it stands alone
  • Four different options, and the number of the correct option points to one of them
  • Correct option grounded in the passage
  • Wrong options not copied from the passage
  • Correct option not noticeably the longest
def short_answer_checks(passage, pred):
    q, a = str(pred.question).strip(), str(pred.answer).strip()
    return {
        "is_a_question": q.endswith("?") and len(q.split()) >= 5,
        "stands_alone": stands_alone(q),
        "answer_is_short": 0 < len(a.split()) <= 12,
        "answer_grounded": overlap(a, passage) >= 0.6,
        "answer_not_leaked": overlap(a, q) < 0.6,
    }

Before trusting it, the metric was run on the 48 hand-written reference questions, which should all pass. They did not, at first: see the lessons.

04Measure the starting point

Run the plain prompt on the held-back passages and score every output. This first score is the baseline: the starting point that every change is compared with. Then run the same test again. If the score moves between identical runs, that movement is noise (random variation). Differences between prompts that are smaller than the noise mean nothing.

evaluate = dspy.Evaluate(devset=devset, metric=mcq_metric, num_threads=4)
evaluate(dspy.Predict(MultipleChoiceQuestion))
05Optimise, then measure again on the held-back set

Here are four ways to improve the prompt. All four are scored the same way. The last three use a DSPy optimiser.

Hand-written rules

A person copies the checklist rules into the prompt’s instructions. This is traditional prompt engineering.

BootstrapFewShot

Runs the prompt on the training passages. It keeps up to three outputs that pass every check, and adds them to the prompt as worked examples.

MIPROv2

Also asks the model to suggest new instructions. It then tries different mixes of instructions and examples, and keeps the best one.

Hand-written + MIPROv2

MIPROv2 starting from the person's instructions rather than from scratch.

optimiser = dspy.MIPROv2(metric=mcq_metric, auto="light", seed=7)
better = optimiser.compile(dspy.Predict(MultipleChoiceQuestion), trainset=trainset)
better.save("results/mipro_mcq_program.json")

The saved file is the improved prompt: its instructions and its chosen examples. It is a plain text (JSON) file, so you can keep each version and review the changes.

Results

What each approach achieved

In short
On short-answer questions, writing the checklist into the prompt as plain rules raised the score from 76.7 to 93.3 out of 100. Adding the optimiser on top reached 98.3. The optimiser on its own scored 76.7. The person’s rules did most of the work.

Measured on the 12 held-back test passages the optimisers never saw (DSPy 3.4.0, Gemma 4 E2B (Q8) on llama.cpp, random seed 7). Each score is the share of checklist items passed, averaged over the passages, out of 100: 100 means every question passed every check. Where a prompt was run five times, the bracket shows the average and the typical spread. Single runs move by up to about 3 points by chance alone, so treat smaller gaps as ties.

PromptShort answerMultiple choice
Baseline
The plain prompt DSPy writes from the signature.
76.7 (5 reruns: 75.7 ± 0.9)85.4 (5 reruns: 85.6 ± 1.6)
Hand-written rules
A person copies the checklist rules into the prompt's instructions.
93.3 (5 reruns: 95.7 ± 1.9)89.6 (5 reruns: 87.9 ± 1.4)
BootstrapFewShot
Adds up to three worked examples that pass the metric.
81.784.4
MIPROv2
Proposes new instructions and picks the best instruction and examples.
It kept the plain prompt and no examples, so it scored the same as the baseline.
76.785.4
Hand-written + MIPROv2
MIPROv2 starting from the hand-written instructions.
98.389.6

Which checks each prompt passes

Short answer

CheckBaselineHand-written rulesBootstrapFewShotMIPROv2Hand-written + MIPROv2
Is a question100%100%100%100%100%
Stands alone (no “passage”)83%100%100%83%100%
Answer ≤ 12 words0%92%8%0%100%
Answer grounded in passage100%100%100%100%100%
Answer not in question100%75%100%100%92%

Multiple choice

CheckBaselineHand-written rulesBootstrapFewShotMIPROv2Hand-written + MIPROv2
Is a question100%100%100%100%100%
Stands alone (no “passage”)67%83%83%67%92%
Four options100%100%100%100%100%
Options distinct100%100%100%100%100%
Correct option identified100%100%92%100%100%
Correct option grounded92%50%33%92%92%
Wrong options not copied from passage92%100%83%92%100%
Correct option not the longest33%83%83%33%33%

Where the correct answer sits

Counts of which letter (A to D) held the correct answer. Across 12 multiple-choice questions a fair spread is about 3 per letter. No prompt fixed this, because each question is written on its own. Shuffle the options in code instead.

{"".join(rows)}
PromptABCD

The same passage, before and after

Short answer: baseline
Q: What is a common remedy for blurred accountability when several teams contribute to an AI system? A: A common remedy is a responsibility matrix that specifies who builds, approves, operates, and monitors the system.

Fails: Answer ≤ 12 words

Short answer: hand-written + MIPROv2
Q: What remedy addresses blurred accountability when multiple teams contribute to an AI system? A: A responsibility matrix stating who builds, approves, operates, and monitors

Passes every check

Multiple choice: baseline
Q: What is a common remedy mentioned for blurring accountability when several teams contribute to an AI system? A. Implementing stricter coding standards B. Establishing a responsibility matrix ← marked correct C. Increasing the number of contributing teams D. Focusing solely on system performance metrics

Fails: Stands alone (no “passage”)

Multiple choice: hand-written + MIPROv2
Q: What is a common solution for managing accountability when multiple teams contribute to an AI system? A. Relying solely on the supplier's documentation B. Implementing a responsibility matrix detailing roles for building, approving, operating, and monitoring ← marked correct C. Allowing affected people to contact all contributing teams equally D. Ignoring the issue until a problem arises

Fails: Correct option not the longest

The prompts the optimisers chose

Hand-written rules: Short answer
Write one exam question about the passage, with its answer. Rules: - Students will not see the passage, so never mention "the passage" or "the text". - The question must end with a question mark. - The answer must be 12 words or fewer, taken from the passage's own ideas. - Do not repeat the question's words in the answer.
Hand-written rules: Multiple choice
Write one multiple-choice exam question about the passage. Rules: - Students will not see the passage, so never mention "the passage" or "the text". - Give exactly four options: one correct, three plausible but wrong. - Keep all four options about the same length, so the answer does not stand out. - Write the wrong options in your own words, not copied from the passage. - answer_index is the 0-based position of the correct option.
BootstrapFewShot: Short answer
Write one exam question, answerable from the passage, with its short answer.

Plus 3 worked example(s).

BootstrapFewShot: Multiple choice
Write one multiple-choice exam question about the passage.

Plus 3 worked example(s).

MIPROv2: Short answer
Write one exam question, answerable from the passage, with its short answer.
MIPROv2: Multiple choice
Write one multiple-choice exam question about the passage.
Hand-written + MIPROv2: Short answer
Based on the provided text about AI ethics concepts, generate one exam question and its corresponding answer pair. Ensure the question requires extracting specific information, the answer is concise (12 words or fewer), and the answer is derived strictly from the text without repeating words from the question.

Plus 3 worked example(s).

Hand-written + MIPROv2: Multiple choice
Based on the provided text, generate one multiple-choice question about the main concept discussed. The question should test comprehension of the key idea. Provide exactly four options: one correct answer and three incorrect but plausible distractors. Ensure all options are of similar length and written in your own words. Finally, provide the 0-based index of the correct option.

Plus 3 worked example(s).

What MIPROv2 proposed, and threw away

Candidate instructions for the short-answer task, written by the same 2B model. None mentions answer length or the passage, because the proposer never sees the metric. Notice the narrowed topic list: the data covers twelve principles.

You are an expert question generation system. Your task is to analyze the provided descriptive passage on AI ethics and governance concepts (such as Accountability, Transparency, and Human oversight) and generate exactly one high-stakes, exam-style question based *only* on the information present in the text. Following the question, provide a concise, direct answer that is strictly derivable from the passage. Ensure the question tests a deep understanding of the ethical principle described. Passage: [Insert Descriptive Passage Here] Format your output strictly as: Question: [Your high-stakes question here] Answer: [Your concise answer here]
Given a passage about AI ethics and governance concepts such as Accountability, Transparency, and Human oversight, generate one concise question that can be answered strictly by extracting information from the passage, and provide the corresponding short answer.
Lessons

Prompt engineering lessons from one small experiment

Each lesson comes from something that happened while building this module.

01The metric is the specification

The first metric only checked that outputs had the right format. The plain prompt scored 83 and 96 out of 100. That looked good, but the questions were poor. Answers ran to 50 words. Questions said “according to the passage”, but students would never see the passage. A high score from a weak metric gives false comfort. Decide what good means, in terms you can check, before you try to improve anything.

02Test the checker before you trust it

Run the metric on answers you know are good. Also read a few outputs yourself. Here, one reference question failed because it was an instruction (“Give one way…”), not a question. A strict word match also marked a correct model answer as not grounded. The answer said “building” where the passage said “builds”. Then a stricter length check was added, and 15 of the 24 hand-written multiple-choice questions failed it. The author had made the longest option the correct one. That is exactly the flaw the check was added to catch in the model. People make the mistakes they look for in machines.

03Say the rule. Examples alone do not carry it.

BootstrapFewShot showed the model three short, correct answers, and it still wrote long ones. One line of instruction, “The answer must be 12 words or fewer”, fixed almost every answer. Small models copy the format of examples more easily than qualities nobody states, such as being short.

04An optimiser cannot find a rule it cannot see

MIPROv2 asks the model to suggest better instructions, based on the data and the program. But it never shows the model the metric. The suggestions added drama (“a high-stakes certification exam”). They also narrowed the topic to three principles the model happened to see. None mentioned answer length. When MIPROv2 started from the hand-written rules instead, it gave the highest short-answer score. That was a little above the rules alone. On multiple choice it tied. In this experiment the person’s rules did most of the work. The optimiser added examples and polish.

05Fixing one rule can break another

The hand-written rules told the model to keep all options the same length, and to write the wrong options in its own words. After that, fewer correct answers stood out. But the model also reworded the correct answer, so it drifted away from the passage. When MIPROv2 then rewrote those instructions, the correct answers matched the passage again. But the correct option was once again often the longest. Check every rule after every change, not only the one you were fixing.

06Do in code what code does reliably

Every prompt put the correct answer at B or C far more often than chance, and almost never at A. Each question is written separately, so no prompt can balance a set. Two lines of code can: shuffle the options and update the index. Use prompts for things that need judgement. Use code for fixed rules.

order = random.sample(range(4), 4)
options, answer_index = [options[i] for i in order], order.index(answer_index)
07Measure the noise before celebrating

The same prompt, run five times on the same passages, moved by up to about 3 points. That is the noise. With only twelve test passages, one question is worth several points. Differences smaller than the noise are ties, however good the chart looks. More test cases shrink the noise. More tests always beat cleverer tests.

08A passing score is not a good question

The metric checks format, and whether answers are grounded in the passage. It cannot tell if a question tests understanding or just memory. It cannot tell if a question is fair to every student, or even correct. That still needs a subject expert. Use the score to compare prompt versions, and to spot when a change makes things worse. Use people to approve the questions.

09Keep the record

Every run writes the model, DSPy version, seed, data split, prompt, outputs and scores to results/. That record is what lets someone else check the claim, and what you rerun when the model, the brief or the data changes. Without that record, a prompt is just an experiment. With it, the prompt is a governed part of your system.

Take it further
The AI Governance in the Workplace workshop applies the same ideas without code. It uses a generator (something that writes), an evaluator that checks the work against a written marking guide, tests where the right answer is already known, and a person who gives final approval.
No code needed

What to do on Monday

The same lessons, for a prompt you use in Copilot, ChatGPT or any chat tool.

1Write a checklist of what a good answer must do, in checkable terms (for example “12 words or fewer”, not “concise”).
2Collect 10 or more real inputs, including awkward ones, and keep them in a file you reuse.
3Try the checklist on answers you already know are good. If they fail, fix the checklist first.
4Put every rule in the prompt in plain words. Examples alone do not carry a rule.
5After any change, run all the inputs again and tick every item, not just the one you fixed.
6Run the same prompt twice before believing a small improvement.
7Do mechanical jobs (shuffling, counting, formatting) with a tool or spreadsheet, not the prompt.
8Keep the prompt, the date, the model and your checklist results together, and have a person sign off the output.
Setup

Download, serve, run

This section is for people who want to run the code. If you only want the ideas, skip to the lessons. Setup takes about 20 minutes. You need a terminal, git, Python 3.11 or later and about 6 GB of disk space. A graphics card helps but is not required.

1Install llama.cpp
2Download the Gemma model
3Start the model server
4Install DSPy and run

1. Install llama.cpp

llama.cpp runs open AI models on an ordinary computer. Other programs can then talk to the model in the same way they talk to OpenAI’s online service (an “OpenAI-compatible API”).

brew install llama.cpp
llama-server --version

Apple Silicon Macs use the GPU automatically.

winget install ggml.llamacpp
llama-server --version

Open a new terminal after installing so llama-server is on your PATH. Prebuilt downloads for CUDA and Vulkan are also on the releases page.

Build from source. Drop -DGGML_CUDA=ON if you have no NVIDIA card.

git clone --depth 1 https://github.com/ggml-org/llama.cpp.git ~/src/llama.cpp
cd ~/src/llama.cpp
cmake -B build -DGGML_CUDA=ON
cmake --build build --config Release -j --target llama-server
./build/bin/llama-server --version

Needs cmake and a C++ compiler (sudo apt install cmake build-essential), plus the CUDA toolkit for GPU builds.

2. Download Gemma 4 E2B

Gemma 4 E2B is the smallest model in Google's Gemma 4 family. It is built for laptops and phones. “E2B” stands for “effective 2 billion” parameters (the numbers the model learned in training). The model comes as a GGUF file, the file format llama.cpp reads.

pip install -U huggingface_hub
hf download ggml-org/gemma-4-E2B-it-GGUF gemma-4-E2B-it-Q8_0.gguf

Q8_0 is 5.0 GB. On a laptop with 8 GB of RAM, download gemma-4-E2B-it-Q4_0.gguf instead and point MODEL= at it in step 3.

3. Start the model server

git clone https://github.com/wmgmm/ai_governance.git
cd ai_governance/prompt_evaluation
./scripts/start_server.sh

The script finds the model you downloaded (in your Hugging Face cache). It makes the model available at http://localhost:8090, an address on your own computer. Leave it running. Open a second terminal window and check it is working:

curl http://localhost:8090/health

A reply containing "ok" means the server is ready. If it says the model is still loading, wait a few seconds and try again.

Two settings worth knowing
--reasoning off: by default, Gemma 4 “thinks” before it answers. For a five-word answer it used 269 tokens with thinking and 13 without (a token is a word or part of a word). For short answers in a fixed format, thinking mostly just costs time.
--parallel 4: the server handles four requests at a time, so DSPy can test four examples at once.

On Windows, run the command inside the script directly: llama-server -m PATH\to\gemma-4-E2B-it-Q8_0.gguf --alias gemma-4-e2b --port 8090 -c 16384 --parallel 4 -ngl 99 --jinja --reasoning off

4. Install DSPy and run the experiments

The project uses uv, a tool that installs Python packages. It installs DSPy 3.4 in a separate space (an “environment”), so it does not clash with other Python software on your computer.

cd ai_governance/prompt_evaluation
pip install uv
uv sync
uv run python run.py all
uv run python make_results_page.py

run.py all takes about three minutes on a desktop GPU. It writes scores, prompts and every model output to results/, and the second command rebuilds the results section of this page from them. Expect your scores to differ from these by a few points. The model picks each word with some randomness, so no two runs are the same.

Success looks like one line per stage and task, such as baseline short_answer dev score 76.7, then a results/ folder full of JSON files. make_results_page.py rewrites your local copy of this page, site/prompt_evaluation/index.html, not the published one.

If something goes wrong

“Model not found”: the download in step 2 did not finish, or you downloaded a different file. For the smaller Q4_0 file, give its path: MODEL=/path/to/gemma-4-E2B-it-Q4_0.gguf ./scripts/start_server.sh.

Port 8090 already in use: start on another port and tell the scripts where it is: PORT=8091 ./scripts/start_server.sh, then in the second terminal export LLM_API_BASE=http://localhost:8091/v1 before uv run.

hf or llama-server not found: open a new terminal so the install is on your PATH. On Linux, set LLAMA_SERVER=~/src/llama.cpp/build/bin/llama-server if you built it elsewhere.

No graphics card: everything still works on the CPU, only more slowly. The three-minute figure is for a desktop GPU; CPU times were not measured.

Scores differ from this page: expected, by a few points. See lesson 07.