GuideAbout 10 minutes

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

CallUse it forReturns
choose(state, q, options)Pick one of 2–8 actions, categories or planschoice, index, confidence, probs
yes_no(state, q)Approve? Release? Within policy?yes, confidence
score(state, q)Risk, urgency, fit on a 1–5 scalescore (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 automaticallyAccuracy of those
always100%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 None

Recipes

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

WhereHowGood for
Laptop or desktopOllama, LM Studio, llama.cpp (macOS, Windows, Linux)Building, testing, private data
Your serverDocker + Ollama or llama-server on any CPU VPSProduction APIs, no GPU needed
GPU servingvLLM, TGI or SGLang with the safetensors weightsHigh volume, batching
Inside your appllama.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 takes OLLAMA_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_alive in Ollama).