Skip to content

Commit d6c8794

Browse files
spoons-and-mirrorsTarquinen
authored andcommitted
feat: intra response synth instruction injection
cleanup fix
1 parent 4f6dc96 commit d6c8794

File tree

2 files changed

+81
-1
lines changed

2 files changed

+81
-1
lines changed

lib/prompts/tool-result-nudge.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<instruction name=agent_nudge>
2+
You have accumulated several tool outputs. If you have completed a discrete unit of work and distilled relevant understanding in writting for the user to keep, use the context_pruning tool to remove obsolete tool outputs from this conversation and optimize token usage.
3+
</instruction>

lib/synth-instruction.ts

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,73 @@
1+
export interface ToolResultTracker {
2+
seenToolResultIds: Set<string>
3+
toolResultCount: number
4+
}
5+
6+
export function createToolResultTracker(): ToolResultTracker {
7+
return {
8+
seenToolResultIds: new Set(),
9+
toolResultCount: 0
10+
}
11+
}
12+
13+
function countNewToolResults(messages: any[], tracker: ToolResultTracker): number {
14+
let newCount = 0
15+
16+
for (const m of messages) {
17+
if (m.role === 'tool' && m.tool_call_id) {
18+
const id = String(m.tool_call_id).toLowerCase()
19+
if (!tracker.seenToolResultIds.has(id)) {
20+
tracker.seenToolResultIds.add(id)
21+
newCount++
22+
}
23+
} else if (m.role === 'user' && Array.isArray(m.content)) {
24+
for (const part of m.content) {
25+
if (part.type === 'tool_result' && part.tool_use_id) {
26+
const id = String(part.tool_use_id).toLowerCase()
27+
if (!tracker.seenToolResultIds.has(id)) {
28+
tracker.seenToolResultIds.add(id)
29+
newCount++
30+
}
31+
}
32+
}
33+
}
34+
}
35+
36+
tracker.toolResultCount += newCount
37+
return newCount
38+
}
39+
40+
/**
41+
* Counts new tool results and injects nudge instruction every 5th tool result.
42+
* Returns true if injection happened.
43+
*/
44+
export function maybeInjectToolResultNudge(
45+
messages: any[],
46+
tracker: ToolResultTracker,
47+
nudgeText: string
48+
): boolean {
49+
const prevCount = tracker.toolResultCount
50+
const newCount = countNewToolResults(messages, tracker)
51+
52+
if (newCount > 0) {
53+
// Check if we crossed a multiple of 5
54+
const prevBucket = Math.floor(prevCount / 5)
55+
const newBucket = Math.floor(tracker.toolResultCount / 5)
56+
if (newBucket > prevBucket) {
57+
// Inject at the END of messages so it's in immediate context
58+
return injectNudgeAtEnd(messages, nudgeText)
59+
}
60+
}
61+
return false
62+
}
63+
164
export function isIgnoredUserMessage(msg: any): boolean {
265
if (!msg || msg.role !== 'user') {
366
return false
467
}
568

6-
if (msg.ignored || msg.info?.ignored) {
69+
// Skip ignored or synthetic messages
70+
if (msg.ignored || msg.info?.ignored || msg.synthetic) {
771
return true
872
}
973

@@ -17,6 +81,19 @@ export function isIgnoredUserMessage(msg: any): boolean {
1781
return false
1882
}
1983

84+
/**
85+
* Injects a nudge message at the END of the messages array as a new user message.
86+
* This ensures it's in the model's immediate context, not buried in old messages.
87+
*/
88+
export function injectNudgeAtEnd(messages: any[], nudgeText: string): boolean {
89+
messages.push({
90+
role: 'user',
91+
content: nudgeText,
92+
synthetic: true
93+
})
94+
return true
95+
}
96+
2097
export function injectSynthInstruction(messages: any[], instruction: string): boolean {
2198
// Find the last user message that is not ignored
2299
for (let i = messages.length - 1; i >= 0; i--) {

0 commit comments

Comments
 (0)