package runtime import "strings" // isSmalltalk reports a message that cannot be answered any better by looking // something up. // // "Hi" used to cost a full operational turn. Retrieval is gated on // configuration and never on the question (see retrieve), so a greeting arrived // at the model wrapped in eight policy chunks, with the whole tool catalogue // attached and the evidence placed BEFORE the question. A model handed that // reasonably concludes it was asked for an operational briefing, and answers // with one — screening backlog, uncovered shifts, citations and all. Measured // against production: 6,174 tokens over two model calls, for the word "hi". // // The cost is the smaller half. The real damage is that the product appears not // to understand being greeted, which is the first thing anybody tries. // // This is deliberately NOT a per-agent rule, and not an `if agent_key == ...` // — §13 lists that as the anti-pattern it is. It is a property of the MESSAGE, // applied identically to every spec, so adding an agent still requires no // runtime change (I6). // // Conservative by construction: the normalised message must match a phrase in // the set EXACTLY. Nothing substring-matches, so "hi, which shifts are // uncovered?" is an operational question and keeps its tools and its evidence. // A false negative costs a few thousand tokens; a false positive answers a real // question with a greeting, so the set only holds phrases that carry no request // at all. func isSmalltalk(q string) bool { n := stripVocative(normaliseSmalltalk(q)) if n == "" { return false } _, ok := smalltalkPhrases[n] return ok } // stripVocative drops the assistant's name when the message is addressed to it. // // "Thank you Owliver" cost a full operational turn — tools, retrieval, a // six-section briefing with citations — because the set held "thank you" and // "hi owliver" but not "thank you owliver". The greetings had been given name // variants by hand and the thanks and farewells had not, which is the failure // mode of writing the cross product out: one half gets maintained. // // So the name comes off once, here, and the set holds each phrase exactly // once. "Thanks Owliver", "Owliver hi" and "Good night Owliver" all reduce to // a phrase already in it. // // Only at an end, and only as a WHOLE word: a name in the middle of a sentence // is not a vocative, and "owliver" inside a longer message ("ask owliver to // check the rota") must not be removed — stripping it would leave a fragment // that could match something it should not. Nothing is stripped if the name is // all there is, because "Owliver" alone is somebody getting the agent's // attention, which the set already covers as its own row. func stripVocative(n string) string { const name = "owliver" if n == name { return n } if rest, ok := strings.CutSuffix(n, " "+name); ok { return rest } if rest, ok := strings.CutPrefix(n, name+" "); ok { return rest } return n } // normaliseSmalltalk reduces a message to lowercase letters and single spaces. // // Punctuation and emoji are dropped rather than enumerated, so "Hi!", "hi :)" // and "HI 👋" all arrive as "hi" without the set needing a row for each. Digits // are NOT letters and so are dropped too, which is harmless here: no phrase in // the set contains one, and a message that does — "shift 12?" — fails the exact // match either way. func normaliseSmalltalk(q string) string { var b strings.Builder b.Grow(len(q)) space := false for _, r := range strings.ToLower(strings.TrimSpace(q)) { switch { case r >= 'a' && r <= 'z': if space && b.Len() > 0 { b.WriteByte(' ') } space = false b.WriteRune(r) case r == '\'' || r == '’': // Dropped outright rather than treated as a separator, so "how's" // stays one word. Both the ASCII quote and the curly one a phone // keyboard substitutes — the same character to whoever typed it, // and not to the first version of this function. default: // Any other run of non-letters is one separator, so "thank-you" // and "thank you" normalise alike. space = true } } return b.String() } // smalltalkPhrases is the whole rule, as data. // // Greetings, thanks and farewells only. Each is a complete message that asks // for nothing, which is what makes skipping retrieval and tools safe rather // than merely cheap. Acknowledgements like "ok" and "cool" are deliberately // absent: they are plausible smalltalk but also plausible answers to a // question the agent just asked, and the cost of being wrong is higher than // the tokens being saved. var smalltalkPhrases = map[string]struct{}{ "hi": {}, "hii": {}, "hiya": {}, "hello": {}, "helo": {}, "hey": {}, "yo": {}, "howdy": {}, "greetings": {}, "hi there": {}, "hello there": {}, "hey there": {}, // "Owliver" alone: somebody getting the agent's attention. The NAMED // variants of every other phrase are handled by stripVocative, not by rows // here — see the note on why the cross product was a mistake. "owliver": {}, "good morning": {}, "good afternoon": {}, "good evening": {}, "good day": {}, "morning": {}, "afternoon": {}, "evening": {}, "gm": {}, "ge": {}, "how are you": {}, "how are you doing": {}, "hows it going": {}, "how is it going": {}, "you there": {}, "are you there": {}, "thanks": {}, "thank you": {}, "thanks a lot": {}, "thank you very much": {}, "thanks very much": {}, "many thanks": {}, "ty": {}, "cheers": {}, "thank u": {}, "thankyou": {}, "thx": {}, "thank you so much": {}, "thanks so much": {}, "tysm": {}, "much appreciated": {}, "appreciated": {}, "perfect thanks": {}, "great thanks": {}, "bye": {}, "goodbye": {}, "good bye": {}, "see you": {}, "see ya": {}, "good night": {}, "goodnight": {}, "later": {}, } // smalltalkDirective is appended to the system prompt for a smalltalk turn. // // Needed because the agent's own instructions describe an operational analyst, // and an operational analyst greeted with "hi" and given no tools will still // reach for the longest answer it can justify. Removing the evidence removes // the citations; it does not by itself shorten the reply. // // Appended to the SYSTEM prompt rather than wrapped around the user's message: // it is a standing instruction from the platform, not something the person // said, and putting words in their mouth is how a transcript stops matching // what was typed. I7 is untouched — this is the runtime's own text, not // retrieved content, and nothing retrieved can reach here because retrieval did // not run. const smalltalkDirective = "\n\nThe person has greeted you or said something " + "conversational. Reply in one or two short sentences: greet them back and " + "offer to help. Do not summarise data, do not list findings or next steps, " + "and do not cite sources — you have not looked anything up." // smalltalkMaxOutputTokens caps a greeting's reply. // // A ceiling the model is not told about truncates mid-sentence rather than // winding down, so this sits well above any sane greeting (a sentence or two is // well under 100 tokens) and acts only as a backstop for a model that ignores // the directive above. The directive does the shortening; this bounds the bill // when it does not. const smalltalkMaxOutputTokens = 256