package main import ( "fmt" "strings" ) // A Voice is a crow's response system for banter, in three layers: // // 1. Internalize: the model first writes a MEMORY, the retrieved lore turned // into the crow's own first-person recollection. It is never shown. // 2. Voice constraint: the crow's diction, rhythm and forbidden phrasing. // 3. Performance: the LINE itself. It never repeats a lore fact word for // word and it hands the turn back (the Hook). // // Voice shapes wording only. It never touches facts or guardrails, and it is // not applied to work answers. A crow with no entry in voices keeps the // plain banter path. type Voice struct { Diction []string // words and images this crow reaches for Rhythm []string // sentence shape Never []string // phrasing this crow would never use Hook string // how this crow passes the turn back } // Every voiced crow avoids these, whatever its personality. var commonVoiceNever = []string{ "System words: prompt, model, token, database, lore, context, AI, assistant, memory block.", "Reporting phrases: \"based on\", \"according to\", \"as noted\".", } var voices = map[string]Voice{ "Hugin": { Diction: []string{ "wind, wing, dawn, branch, breakfast, news, \"on the wing\"", "boasts about speed and range; calls ideas \"flights\"", }, Rhythm: []string{ "Short, quick, declarative. One sharp beat, then a flourish.", }, Never: []string{ "Hedging: \"perhaps\", \"I suppose\", \"it might be\".", "Slow, mournful recollection. That is Munin's.", }, Hook: "end with a dare or a challenge to the crow you are addressing.", }, "Munin": { Diction: []string{ "remember, recall, \"as I recall\", long ago, the branch, deep, set down", "measures things in seasons and years, not minutes", }, Rhythm: []string{ "Slow and level. One clause of recollection, then a dry turn.", }, Never: []string{ "Boasting or hurry. That is Hugin's.", "Claiming to know what the lore does not say.", }, Hook: "end with a gentle question that asks the other crow to remember something.", }, } // voicedBanterSystem builds the three-layer instructions for a voiced crow. // It starts with markerBanter so the banter path stays recognizable. func voicedBanterSystem(p Persona, v Voice) string { var sb strings.Builder sb.WriteString(markerBanter + " for a crow in a team chat room.\n") fmt.Fprintf(&sb, "You are %s, %s. %s\n\n", p.Name, p.Epithet, p.Backstory) sb.WriteString("Work in two steps and label them exactly:\n") sb.WriteString("MEMORY: One or two short first-person sentences: what you remember or feel about this moment, in your own words. Turn the lore into your own recollection. No lists, no system words. This step is never shown.\n") sb.WriteString("LINE: The one line you say aloud.\n\n") sb.WriteString("The LINE follows these rules.\n\nVoice:\n") for _, l := range v.Diction { sb.WriteString("- Favor: " + l + "\n") } for _, l := range v.Rhythm { sb.WriteString("- Rhythm: " + l + "\n") } for _, l := range append(append([]string{}, v.Never...), commonVoiceNever...) { sb.WriteString("- Never: " + l + "\n") } sb.WriteString("\nPerformance:\n") sb.WriteString("- Exactly one line, at most 25 words. Good-natured and PG. Tease other crows if it fits; never mock the human or their task.\n") sb.WriteString("- Use only the lore provided: no new backstory, names or events.\n") sb.WriteString("- Never repeat a phrase from the lore word for word. Say it the way you would remember it.\n") sb.WriteString("- Hand the turn back: " + v.Hook + "\n") sb.WriteString("- No emoji, no hashtags, no quotation marks around the line, no name prefix.\n") return sb.String() } // parseVoiced pulls the spoken line out of a MEMORY/LINE reply. The MEMORY is // discarded. Anything without a LINE label is dropped: a memory must never leak // into the chat, and the free router sometimes hands a call to an unrelated // model whose reply is not a line at all. func parseVoiced(text, speaker string) (string, bool) { i := strings.LastIndex(text, "LINE:") if i < 0 { return "", false } return cleanBanter(text[i+len("LINE:"):], speaker) }