mirror of
https://github.com/hansjone/netxops.git
synced 2026-10-09 03:53:15 +08:00
Fix reply language ignored when user writes Chinese.
Strengthen Host prompt injection with a per-step reply reminder, read live settings, and stop persona from forcing follow-user language. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
d52c05e774
commit
91f2e9a5c8
7 changed files with 108 additions and 19 deletions
38
src/index.ts
38
src/index.ts
|
|
@ -59,6 +59,7 @@ import {
|
|||
normalizeThinkingLanguage,
|
||||
readSystemLocalePreference,
|
||||
replyInstruction,
|
||||
replyReminder,
|
||||
resolveThinkingLanguage,
|
||||
thinkingInstruction,
|
||||
thinkingReminder,
|
||||
|
|
@ -412,14 +413,34 @@ export function apply(ctx: Context, config: Config = Config({})): void {
|
|||
context: (spec: { name: string, order: number, text: string | (() => string) }) => (() => void) | void
|
||||
}
|
||||
const systemPrompt = (promptCtx as { systemPrompt: PromptSurface }).systemPrompt
|
||||
/** Prefer live settings document over cordis base config (survives UI saves). */
|
||||
const liveConfig = (): Config => {
|
||||
const settings = promptCtx.get('settings') as {
|
||||
get?: (ns: string) => unknown
|
||||
} | undefined
|
||||
const section = typeof settings?.get === 'function'
|
||||
? (() => {
|
||||
try {
|
||||
return settings.get!(NETXOPS_SETTINGS_NAMESPACE)
|
||||
} catch {
|
||||
return undefined
|
||||
}
|
||||
})()
|
||||
: undefined
|
||||
if (section !== null && typeof section === 'object' && !Array.isArray(section)) {
|
||||
return { ...source(), ...(section as Partial<Config>) }
|
||||
}
|
||||
return source()
|
||||
}
|
||||
const effectiveThinking = (): ReturnType<typeof resolveThinkingLanguage> => {
|
||||
const settings = promptCtx.get('settings') as { get?: (ns: string) => unknown } | undefined
|
||||
const current = source()
|
||||
const current = liveConfig()
|
||||
return resolveThinkingLanguage(
|
||||
normalizeThinkingLanguage(current.thinkingLanguage),
|
||||
readSystemLocalePreference(settings),
|
||||
)
|
||||
}
|
||||
const replySetting = (): string => normalizeReplyLanguage(liveConfig().replyLanguage)
|
||||
promptCtx.effect(() => {
|
||||
const disposers: Array<() => void> = []
|
||||
const push = (dispose: (() => void) | void): void => {
|
||||
|
|
@ -435,11 +456,22 @@ export function apply(ctx: Context, config: Config = Config({})): void {
|
|||
order: 1000,
|
||||
text: () => thinkingReminder(effectiveThinking()),
|
||||
}))
|
||||
// Late section + per-step context so reply language beats persona / user language.
|
||||
push(systemPrompt.section({
|
||||
name: 'netxops:reply-language',
|
||||
order: 890,
|
||||
text: () => replyInstruction(normalizeReplyLanguage(source().replyLanguage)),
|
||||
order: 10150,
|
||||
text: () => replyInstruction(replySetting()),
|
||||
}))
|
||||
push(systemPrompt.context({
|
||||
name: 'netxops:reply-language-reminder',
|
||||
order: 1100,
|
||||
text: () => replyReminder(replySetting()),
|
||||
}))
|
||||
promptCtx.logger.info(
|
||||
'netxops: model language prompts registered (thinking=%s reply=%s)',
|
||||
normalizeThinkingLanguage(liveConfig().thinkingLanguage),
|
||||
replySetting(),
|
||||
)
|
||||
return () => {
|
||||
for (const dispose of disposers.splice(0)) dispose()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -135,10 +135,26 @@ export function replyInstruction(setting: string | undefined | null): string {
|
|||
if (normalized === REPLY_LANGUAGE_DEFAULT) return ''
|
||||
const meta = REPLY_META[normalized]
|
||||
return [
|
||||
'## Reply language requirement',
|
||||
`Regardless of the language the user writes in, you must always write the entire reply in ${meta.native} (${meta.name}) —`,
|
||||
'including explanations, summaries, body text, titles, and error descriptions.',
|
||||
'Keep code, commands, file paths, API names, host names, and proper nouns in their original form; do not translate them.',
|
||||
'This requirement outranks language hints in the user message or persona until the Netx Ops reply-language setting changes.',
|
||||
'## Reply language requirement (mandatory)',
|
||||
`The operator set Netx Ops reply language to ${meta.native} (${meta.name}).`,
|
||||
`You MUST write the entire final reply in ${meta.name} only.`,
|
||||
`Do not switch to the user's language even if they write in Chinese, English, or any other language.`,
|
||||
'This includes titles, bullet labels (Result / Evidence / Next may stay as English keywords), explanations, summaries, warnings, and error text.',
|
||||
'Keep code, commands, file paths, API names, host names, UUIDs, and proper nouns unchanged.',
|
||||
'This rule outranks persona wording such as "follow the user language" and outranks language hints in the user message.',
|
||||
].join(' ')
|
||||
}
|
||||
|
||||
/**
|
||||
* Per-step reminder for forced reply language.
|
||||
* Empty when `follow-user`. Appended after the user message each model call.
|
||||
*/
|
||||
export function replyReminder(setting: string | undefined | null): string {
|
||||
const normalized = normalizeReplyLanguage(setting)
|
||||
if (normalized === REPLY_LANGUAGE_DEFAULT) return ''
|
||||
const meta = REPLY_META[normalized]
|
||||
return [
|
||||
`[Reply language] Mandatory: write your entire final answer in ${meta.native} (${meta.name}).`,
|
||||
'Do not answer in the user\'s language. Code/paths/host names stay original.',
|
||||
].join(' ')
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue