Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

groundhog

Stop your AI companion from living the same day twice.

A prompt-composition layer for agents that send proactive, recurring messages — daily check-ins, companion bots, standup summaries, journal prompts. It keeps a ledger of what the persona has already said and composes tomorrow's prompt so it doesn't say it again.

groundhog does not call an LLM. It takes a persona, today's real material, and a history ledger; it returns a prompt string and records what came back. Bring your own model, your own scheduler, your own UI.

from groundhog import Composer, Ledger

led = Ledger("state/used.jsonl")
comp = Composer(ledger=led, genres=GENRES, moods=MOODS)

prompt = comp.build(persona=PERSONA, seeds=todays_feed(), facts=probe())
text = your_llm(prompt)          # groundhog doesn't do this part
led.record(text)                 # extract elements, start their cooldowns

Why this exists

Reactive companions are easy — you say something, it replies, and nobody notices repetition. Proactive ones are hard. Send one unprompted message a day and by week three the persona is writing the same message with different nouns: same opening, same emotional beat, same three metaphors, same sign-off.

Every proactive-agent framework I could find caps frequency (maxDaily: 5, quiet hours) and stops there. None of them track content. The closest one states the goal in its README — "reach out based on real data, not fabricated experiences" — and leaves the generator unimplemented.

This library is the missing piece, extracted from a companion agent that ran one message a day for a month and hit every one of these walls in production.

What it actually does

1. Element ledger with per-type cooldowns. Every message that goes out is mined for the concrete things it used — people, places, artworks, foods, activities, proper nouns, callback memories — and each one starts a cooldown. A 90-day cooldown on shared memories means the persona can't burn the good ones in week one. Forgetting is the anti-slop lever; frequency caps are not.

2. Format variety, not just word variety. A genre wheel (long letter / one-liner / pure complaint / question / late-night spiral / …) with per-genre length ceilings, plus a Markov mood chain so today's mood evolves from yesterday's instead of being rerolled. Most "anti-repetition" work happens at the token level; the thing readers actually notice is that every message has the same shape.

3. A constraint budget — the counterintuitive part.

Banning things fixes repetition and causes blandness.

Prohibitions can only push output away from the banned region; they can't push it anywhere good. Past a certain count they consume the instruction-following budget of a small model and the output collapses into safe, correct, lifeless prose. Worse, negative instructions inject the very tokens you're suppressing — listing "don't say I miss you" makes I miss you more available, not less.

Concretely: in the system this came from, the ban list had grown to 17 permanent phrases, of which 11 were the persona's entire emotional vocabulary, plus cooldowns that had swallowed her dorm, her city, her classmates, and every museum she'd ever mentioned. She had nothing left that was hers to talk about. Cutting the bans from 17 to 6 and the location blacklist from 15 entries to 2 is what brought the warmth back.

ConstraintBudget caps how many prohibitions reach the model, ranks them, drops the tail, and — the important half — makes you spend the freed room on positive instructions instead.

4. Real facts over invented ones. A Fact carries both a technical detail (for you) and a human line (for the model), and only human is ever rendered into the prompt. A companion that says "my messages aren't getting through to you" because a bot token really is being fought over by two pollers is grounded; one that says it because the script said to invent a problem is just going to send you looking for a file that was never there.

5. Voice/world separation. The persona's voice is free. Its claims about the world — what it did, what a file contains, whether something succeeded — must come from facts you passed in. groundhog renders an explicit permission to say "I don't know", because a persona with no licensed way to be uncertain will invent instead.

Status

Early. The ledger, extractor, wheel and budget are real; the composer's rendering is deliberately thin so you can replace it. Extracted from a production system, not designed in the abstract — see examples/companion.py.

Known gap

The ledger matches on exact element keys, so a persona that reworks the same observation into new words slips past it. Semantic near-duplicate detection (embedding similarity against the last K messages) is the obvious next layer and is not implemented yet.

Install

pip install -e .

Python 3.9+. No dependencies.

License

MIT

About

Stop your AI companion from living the same day twice. Anti-repetition prompt composition for agents that send proactive, recurring messages.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages