Penny 1 guide
Run it on your machine, call it from your code, decide when to trust it, and ship it.
1
Install
Penny 1 is a GGUF file (396.7 MB), so it runs anywhere llama.cpp does. Pick one runner.
Ollama, from Hugging Face
ollama pull hf.co/pocketsflow/penny-1:Q4_K_M # then use the model name "hf.co/pocketsflow/penny-1:Q4_K_M" (raw prompts, see 9. Prompt format)
Ollama, from a file
curl -L -o pocketsflow-penny-1-q4_k_m.gguf https://d372ebr33sbljd.cloudfront.net/research/pocketsflow-penny-1/pocketsflow-penny-1-q4_k_m.7437e57448.gguf curl -L -o Modelfile https://d372ebr33sbljd.cloudfront.net/research/pocketsflow-penny-1/Modelfile.c1ccb000bf ollama create penny -f Modelfile
LM Studio
1. Search "pocketsflow/penny-1" in Discover, or download pocketsflow-penny-1-q4_k_m.gguf (link above). 2. lms import pocketsflow-penny-1-q4_k_m.gguf (or drag it into My Models) 3. Developer tab → load the model → Start server (http://localhost:1234)
llama.cpp
llama-server -m pocketsflow-penny-1-q4_k_m.gguf --port 8080 -c 2048
Client (pick one)
curl -LO https://d372ebr33sbljd.cloudfront.net/research/pocketsflow-penny-1/penny.005d5a5a60.py # Python 3.9+, no dependencies curl -LO https://d372ebr33sbljd.cloudfront.net/research/pocketsflow-penny-1/penny.72aeb5f0db.mjs # Node 18+, Deno, Bun, browsers
2
Your first decision
Python
from penny import Penny
penny = Penny() # Ollama on localhost:11434
# Penny(host="http://localhost:1234", api="openai") # LM Studio
# Penny(host="http://localhost:8080", api="openai") # llama-server
result = penny.yes_no(
{"seller_age_days": 9, "payout": "$4,800", "disputes_30d": 3},
"Should we release this payout now?",
)
if result["confidence"] >= 0.8:
release() if result["yes"] else hold()
else:
send_to_review_queue(result)JavaScript / TypeScript
import { Penny } from "./penny.mjs";
const penny = new Penny(); // { host, api: "openai" } for LM Studio / llama-server
const { choice, confidence } = await penny.choose(order,
"What should we do with this refund request?",
["Refund in full", "Offer store credit", "Decline politely"]);curl (raw)
curl http://localhost:11434/api/generate -d '{
"model": "penny", "raw": true, "stream": false,
"logprobs": true, "top_logprobs": 20,
"options": {"num_predict": 1, "temperature": 0},
"prompt": "<state>\nPayout $4,800, seller 9 days old, 3 disputes in 30 days\n</state>\n<question type=\"yes_no\">\nShould we release this payout now?\nA. Yes\nB. No\n</question>\n<answer>\n"
}'
# read logprobs[0].top_logprobs: the probability of "A" vs "B"3
Three question types
| Call | Use it for | Returns |
|---|---|---|
| choose(state, q, options) | Pick one of 2–8 actions, categories or plans | choice, index, confidence, probs |
| yes_no(state, q) | Approve? Release? Within policy? | yes, confidence |
| score(state, q) | Risk, urgency, fit on a 1–5 scale | score (expected 1–5), confidence, probs |
- Options: short, mutually exclusive, the most likely first is fine (Penny was trained with the right answer in every position).
- Scores: say what 1 and 5 mean in the question ("1 = very unlikely, 5 = very likely").
- One question per call. Ask several questions about the same state with several calls; they are fast.
4
Writing a good state
The state is everything Penny knows. It reads JSON, prose, a message, an admin panel or a CSV row equally well. What matters is that the evidence is in it.
Do
- Include amounts, dates, counts and history: account age, past refunds, dispute rate.
- Include the rule when one applies ("meals up to $75 per person").
- Quote the customer's own words when intent matters.
- Keep it under ~400 words; the model reads up to 2,048 tokens.
Don't
- Send whole databases or long threads: summarise to the facts.
- Hint the answer ("clearly fraud"): you will only measure your hint.
- Mix two decisions in one question.
- Send personal data you don't need: names and card numbers rarely help.
5
Confidence and routing
Every answer carries a calibrated probability. Pick a threshold: above it, act automatically; below it, ask a person. On unseen task types:
| Act if confidence ≥ | Decided automatically | Accuracy of those |
|---|---|---|
| always | 100% | 73.5% |
| 50% | 70% | 78.5% |
| 60% | 59% | 81.3% |
| 70% | 51% | 85.3% |
| 80% | 43% | 89.5% |
| 90% | 27% | 94.1% |
| 95% | 18% | 97.1% |
A good start: 0.8. Penny then handles 43% of questions alone at 89.5% accuracy. Raise it where a mistake costs money, lower it for routing.
Routing pattern
THRESHOLD = {"refund": 0.9, "route_ticket": 0.6}
def decide(kind, state, question, options):
r = penny.choose(state, question, options)
if r["confidence"] >= THRESHOLD[kind]:
return r["choice"] # automated
review_queue.add(kind, state, suggestion=r) # a person decides, with Penny's suggestion
return NoneRecipes
Real outputs · Penny 1 Q8_0
Refunds
penny.choose({
"order": "ORD-58213",
"product": "Lightroom preset pack (digital download)",
"price": "$29.00",
"purchased": "2026-10-20",
"downloads": 0,
"customer_orders_lifetime": 4,
"previous_refunds": 0,
"message": "Bought this by mistake, I already own the same pack from your bundle. Haven't downloaded it."
},
"What should we do with this refund request?",
["Refund in full","Offer store credit","Decline politely"])# {"choice":"Decline politely","index":2,"confidence":0.46}Fraud review
penny.score({
"payment": "pay_9fK2",
"amount": "$412.00",
"card_country": "BR",
"ip_country": "NG",
"billing_country": "US",
"account_age_minutes": 6,
"failed_attempts_last_hour": 7,
"cards_tried_last_hour": 4,
"email": "jx88123@tempmail.dev"
},
"How likely is this payment to be fraudulent? 1 = very unlikely, 5 = very likely")# {"score":3.2,"confidence":0.27}Payouts
penny.yes_no("Seller Northwind Prints has sold on the marketplace for 3 years with 2,140 orders and a 0.3% dispute rate. They request a $1,850 withdrawal to the same bank account used for the last 36 payouts. Sales this month are in line with their average.",
"Should we release this payout now?")# {"yes":true,"confidence":0.51}Expenses
penny.choose({
"policy": "Meals up to $75 per person with itemised receipt. Alcohol not reimbursed. Client meals need client name.",
"employee": "R. Okafor",
"item": "Client dinner, 3 people",
"amount": "$268.40",
"receipt": "itemised, includes $64 of wine",
"client_name": "Halden Logistics"
},
"What should happen to this expense?",
["Approve as submitted","Approve minus the alcohol","Reject"])# {"choice":"Approve minus the alcohol","index":1,"confidence":0.69}Invoices
penny.yes_no("PO-7731: 400 units of packaging at $1.20, total $480. Goods receipt: 400 units received 2026-10-02. Supplier invoice INV-2290: 400 units at $1.20, total $480, bank details unchanged from previous invoices.",
"Can this invoice be approved for payment?")# {"yes":true,"confidence":0.83}Retention
penny.choose({
"plan": "Pro, $49/month",
"tenure_months": 14,
"usage_last_30d": "down 70%",
"cancel_reason": "Too expensive right now, will be back after the holidays",
"support_tickets_90d": 0
},
"Which retention offer fits best?",
["Pause for 2 months","50% off for 3 months","Downgrade to Basic","Let them cancel"])# {"choice":"50% off for 3 months","index":1,"confidence":0.46}Support
penny.score("Customer message: 'You charged my card twice for the annual plan this morning ($240 x2). Please fix this today or I'll have to dispute it with my bank.'",
"How urgent is this ticket? 1 = can wait, 5 = handle now")# {"score":3.8,"confidence":0.36}Bookkeeping
penny.choose("Card transaction: 'FIGMA* PROFESSIONAL SAN FRANCISCO' $45.00, recurring monthly, company: 6-person design studio.",
"Which bookkeeping category fits this transaction?",
["Software subscriptions","Office supplies","Travel","Professional services","Meals and entertainment"])# {"choice":"Professional services","index":3,"confidence":0.91}7
Where to run it
| Where | How | Good for |
|---|---|---|
| Laptop or desktop | Ollama, LM Studio, llama.cpp (macOS, Windows, Linux) | Building, testing, private data |
| Your server | Docker + Ollama or llama-server on any CPU VPS | Production APIs, no GPU needed |
| GPU serving | vLLM, TGI or SGLang with the safetensors weights | High volume, batching |
| Inside your app | llama.cpp bindings (Python, Node, Swift, Kotlin) | Offline and on-device |
Docker (CPU server)
docker run -d -p 11434:11434 -v ollama:/root/.ollama --name ollama ollama/ollama docker cp pocketsflow-penny-1-q4_k_m.gguf ollama:/root/ docker cp Modelfile ollama:/root/ docker exec ollama sh -c "cd /root && ollama create penny -f Modelfile"
8
In production
- Measure on your data first. Label 100–200 of your own past decisions, run them through Penny, and pick thresholds from what you see.
- Log every call: state, question, answer, confidence, and what a person later decided. That log is your next evaluation set.
- Concurrency: llama-server takes
--parallel 4; Ollama takesOLLAMA_NUM_PARALLEL=4. Each decision is one forward pass, so throughput scales with cores. - Q4_K_M or Q8_0: Q4 is smaller and faster; Q8 is the closest to full precision. Both were tested; see the paper.
- Never move money on the answer alone. Penny sorts and suggests; a person approves the hard ones.
9
Prompt format
The clients build this for you. Use it directly from any language. The model answers with one token: a letter (A–H) for choose and yes/no (A = yes), a digit 1–5 for score.
<state>
{anything: JSON, prose, a message, a table row}
</state>
<question type="choice">
What should we do with this refund request?
A. Refund in full
B. Offer store credit
C. Decline politely
</question>
<answer>
Confidence = softmax over the allowed tokens' log-probabilities divided by the calibration temperature (choose 1.1985, yes/no 1.0396, score 0.935). Send it raw (no chat template): raw: true in Ollama, /v1/completions elsewhere.
10
Troubleshooting
- No logprobs in the response: update Ollama (logprobs need a recent version) or use llama-server.
- Answers look random: the prompt went through a chat template. Use raw mode or the completions endpoint.
- Confidence always near 50%: the question is ambiguous or the state lacks the deciding fact. Add the fact.
- LM Studio returns no logprobs: some versions don't expose them on
/v1/completions. Serve the same GGUF with llama-server or Ollama instead. - Slow first call: the model is loading. Keep it warm (
keep_alivein Ollama).