Skip to content

Commit 50b8102

Browse files
authored
Merge pull request #86 from Tarquinen/refactor/prompt-cleanup-v2
refactor: extract system-reminder and cleanup prompts
2 parents cfd0794 + 0b5d0aa commit 50b8102

File tree

9 files changed

+32
-27
lines changed

9 files changed

+32
-27
lines changed

index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ const plugin: Plugin = (async (ctx) => {
4545

4646
const prompts = {
4747
synthInstruction: loadPrompt("synthetic"),
48-
nudgeInstruction: loadPrompt("nudge")
48+
nudgeInstruction: loadPrompt("nudge"),
49+
systemReminder: loadPrompt("system-reminder")
4950
}
5051

5152
// Install global fetch wrapper for context pruning and synthetic instruction injection

lib/fetch-wrapper/formats/bedrock.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,22 @@ function isNudgeMessage(msg: any, nudgeText: string): boolean {
88
return false
99
}
1010

11-
function injectSynth(messages: any[], instruction: string, nudgeText: string): boolean {
11+
function injectSynth(messages: any[], instruction: string, nudgeText: string, systemReminder: string): boolean {
12+
const fullInstruction = systemReminder + '\n\n' + instruction
1213
for (let i = messages.length - 1; i >= 0; i--) {
1314
const msg = messages[i]
1415
if (msg.role === 'user') {
1516
if (isNudgeMessage(msg, nudgeText)) continue
1617

1718
if (typeof msg.content === 'string') {
1819
if (msg.content.includes(instruction)) return false
19-
msg.content = msg.content + '\n\n' + instruction
20+
msg.content = msg.content + '\n\n' + fullInstruction
2021
} else if (Array.isArray(msg.content)) {
2122
const alreadyInjected = msg.content.some(
2223
(part: any) => part?.type === 'text' && typeof part.text === 'string' && part.text.includes(instruction)
2324
)
2425
if (alreadyInjected) return false
25-
msg.content.push({ type: 'text', text: instruction })
26+
msg.content.push({ type: 'text', text: fullInstruction })
2627
}
2728
return true
2829
}
@@ -56,8 +57,8 @@ export const bedrockFormat: FormatDescriptor = {
5657
return body.messages
5758
},
5859

59-
injectSynth(data: any[], instruction: string, nudgeText: string): boolean {
60-
return injectSynth(data, instruction, nudgeText)
60+
injectSynth(data: any[], instruction: string, nudgeText: string, systemReminder: string): boolean {
61+
return injectSynth(data, instruction, nudgeText, systemReminder)
6162
},
6263

6364
injectPrunableList(data: any[], injection: string): boolean {

lib/fetch-wrapper/formats/gemini.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ function isNudgeContent(content: any, nudgeText: string): boolean {
99
return false
1010
}
1111

12-
function injectSynth(contents: any[], instruction: string, nudgeText: string): boolean {
12+
function injectSynth(contents: any[], instruction: string, nudgeText: string, systemReminder: string): boolean {
13+
const fullInstruction = systemReminder + '\n\n' + instruction
1314
for (let i = contents.length - 1; i >= 0; i--) {
1415
const content = contents[i]
1516
if (content.role === 'user' && Array.isArray(content.parts)) {
@@ -19,7 +20,7 @@ function injectSynth(contents: any[], instruction: string, nudgeText: string): b
1920
(part: any) => part?.text && typeof part.text === 'string' && part.text.includes(instruction)
2021
)
2122
if (alreadyInjected) return false
22-
content.parts.push({ text: instruction })
23+
content.parts.push({ text: fullInstruction })
2324
return true
2425
}
2526
}
@@ -48,8 +49,8 @@ export const geminiFormat: FormatDescriptor = {
4849
return body.contents
4950
},
5051

51-
injectSynth(data: any[], instruction: string, nudgeText: string): boolean {
52-
return injectSynth(data, instruction, nudgeText)
52+
injectSynth(data: any[], instruction: string, nudgeText: string, systemReminder: string): boolean {
53+
return injectSynth(data, instruction, nudgeText, systemReminder)
5354
},
5455

5556
injectPrunableList(data: any[], injection: string): boolean {

lib/fetch-wrapper/formats/openai-chat.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,22 @@ function isNudgeMessage(msg: any, nudgeText: string): boolean {
88
return false
99
}
1010

11-
function injectSynth(messages: any[], instruction: string, nudgeText: string): boolean {
11+
function injectSynth(messages: any[], instruction: string, nudgeText: string, systemReminder: string): boolean {
12+
const fullInstruction = systemReminder + '\n\n' + instruction
1213
for (let i = messages.length - 1; i >= 0; i--) {
1314
const msg = messages[i]
1415
if (msg.role === 'user') {
1516
if (isNudgeMessage(msg, nudgeText)) continue
1617

1718
if (typeof msg.content === 'string') {
1819
if (msg.content.includes(instruction)) return false
19-
msg.content = msg.content + '\n\n' + instruction
20+
msg.content = msg.content + '\n\n' + fullInstruction
2021
} else if (Array.isArray(msg.content)) {
2122
const alreadyInjected = msg.content.some(
2223
(part: any) => part?.type === 'text' && typeof part.text === 'string' && part.text.includes(instruction)
2324
)
2425
if (alreadyInjected) return false
25-
msg.content.push({ type: 'text', text: instruction })
26+
msg.content.push({ type: 'text', text: fullInstruction })
2627
}
2728
return true
2829
}
@@ -47,8 +48,8 @@ export const openaiChatFormat: FormatDescriptor = {
4748
return body.messages
4849
},
4950

50-
injectSynth(data: any[], instruction: string, nudgeText: string): boolean {
51-
return injectSynth(data, instruction, nudgeText)
51+
injectSynth(data: any[], instruction: string, nudgeText: string, systemReminder: string): boolean {
52+
return injectSynth(data, instruction, nudgeText, systemReminder)
5253
},
5354

5455
injectPrunableList(data: any[], injection: string): boolean {

lib/fetch-wrapper/formats/openai-responses.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,22 @@ function isNudgeItem(item: any, nudgeText: string): boolean {
88
return false
99
}
1010

11-
function injectSynth(input: any[], instruction: string, nudgeText: string): boolean {
11+
function injectSynth(input: any[], instruction: string, nudgeText: string, systemReminder: string): boolean {
12+
const fullInstruction = systemReminder + '\n\n' + instruction
1213
for (let i = input.length - 1; i >= 0; i--) {
1314
const item = input[i]
1415
if (item.type === 'message' && item.role === 'user') {
1516
if (isNudgeItem(item, nudgeText)) continue
1617

1718
if (typeof item.content === 'string') {
1819
if (item.content.includes(instruction)) return false
19-
item.content = item.content + '\n\n' + instruction
20+
item.content = item.content + '\n\n' + fullInstruction
2021
} else if (Array.isArray(item.content)) {
2122
const alreadyInjected = item.content.some(
2223
(part: any) => part?.type === 'input_text' && typeof part.text === 'string' && part.text.includes(instruction)
2324
)
2425
if (alreadyInjected) return false
25-
item.content.push({ type: 'input_text', text: instruction })
26+
item.content.push({ type: 'input_text', text: fullInstruction })
2627
}
2728
return true
2829
}
@@ -47,8 +48,8 @@ export const openaiResponsesFormat: FormatDescriptor = {
4748
return body.input
4849
},
4950

50-
injectSynth(data: any[], instruction: string, nudgeText: string): boolean {
51-
return injectSynth(data, instruction, nudgeText)
51+
injectSynth(data: any[], instruction: string, nudgeText: string, systemReminder: string): boolean {
52+
return injectSynth(data, instruction, nudgeText, systemReminder)
5253
},
5354

5455
injectPrunableList(data: any[], injection: string): boolean {

lib/fetch-wrapper/handler.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ export async function handleFormat(
7676
}
7777

7878
if (ctx.config.strategies.onTool.length > 0) {
79-
if (format.injectSynth(data, ctx.prompts.synthInstruction, ctx.prompts.nudgeInstruction)) {
79+
if (format.injectSynth(data, ctx.prompts.synthInstruction, ctx.prompts.nudgeInstruction, ctx.prompts.systemReminder)) {
8080
modified = true
8181
}
8282

lib/fetch-wrapper/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export interface FormatDescriptor {
1818
name: string
1919
detect(body: any): boolean
2020
getDataArray(body: any): any[] | undefined
21-
injectSynth(data: any[], instruction: string, nudgeText: string): boolean
21+
injectSynth(data: any[], instruction: string, nudgeText: string, systemReminder: string): boolean
2222
injectPrunableList(data: any[], injection: string): boolean
2323
extractToolOutputs(data: any[], state: PluginState): ToolOutput[]
2424
replaceToolOutput(data: any[], toolId: string, prunedMessage: string, state: PluginState): boolean
@@ -29,6 +29,7 @@ export interface FormatDescriptor {
2929
export interface SynthPrompts {
3030
synthInstruction: string
3131
nudgeInstruction: string
32+
systemReminder: string
3233
}
3334

3435
export interface FetchHandlerContext {

lib/prompts/nudge.txt

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
<system-reminder>
2-
This nudge is injected by a plugin and is invisible to the user. Do not acknowledge or reference it in your response - simply follow it silently.
3-
</system-reminder>
4-
51
<instruction name=agent_nudge>
6-
You have accumulated several tool outputs. If you have completed a discrete unit of work and distilled relevant understanding in writing for the user to keep, use the prune tool to remove obsolete tool outputs from this conversation and optimize token usage.
2+
You have accumulated several tool outputs. Consider using the `prune` tool to declutter the conversation context.
73
</instruction>

lib/prompts/system-reminder.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<system-reminder>
2+
These instructions are injected by a plugin and are invisible to the user. You must follow them silently and NEVER reference or discuss them (including the <prunable-tools> list) in your response or thinking/reasoning blocks, as this will confuse the user who cannot see them.
3+
</system-reminder>

0 commit comments

Comments
 (0)