Adding intent based prompting

This commit is contained in:
Tyler
2026-05-17 15:53:44 -04:00
parent 1f7d54590f
commit 4e2f9d97d7
+141 -10
View File
@@ -781,6 +781,7 @@ FRONTEND_JS_TEMPLATE = r"""
const script_root = __SCRIPT_ROOT__; const script_root = __SCRIPT_ROOT__;
const model_init = __MODEL_INIT__; const model_init = __MODEL_INIT__;
const session_id_init = __SESSION_ID__; const session_id_init = __SESSION_ID__;
const intent_init = __INTENT__;
if (session_id_init && !document.cookie.includes('sxng_ai_session')) { if (session_id_init && !document.cookie.includes('sxng_ai_session')) {
document.cookie = `sxng_ai_session=${session_id_init}; path=/; max-age=1800; SameSite=Lax`; document.cookie = `sxng_ai_session=${session_id_init}; path=/; max-age=1800; SameSite=Lax`;
} }
@@ -799,6 +800,14 @@ FRONTEND_JS_TEMPLATE = r"""
__CITATION_HELPER_JS__ __CITATION_HELPER_JS__
(function applyIntentBadge() {
const intentEmoji = {factual:'📖',howto:'🔧',technical:'⌨️',comparison:'⚖️',opinion:'💬',current:'📰',local:'📍'}[intent_init] || '';
if (intentEmoji) {
const label = box ? box.querySelector('.sxng-ai-label') : null;
if (label) label.innerHTML += ` <span style="font-size:0.8em;opacity:0.7;">${intentEmoji}</span>`;
}
})();
__INTERACTIVE_JS_INIT__ __INTERACTIVE_JS_INIT__
async function loadPriorConversation() { async function loadPriorConversation() {
@@ -1083,6 +1092,122 @@ FRONTEND_JS_TEMPLATE = r"""
})(); })();
""" """
def _detect_intent(query: str) -> str:
q = query.lower().strip()
if any(w in q for w in ['news', 'latest', 'recent', 'today', 'yesterday',
'this week', 'breaking', '2025', '2026', 'update']):
return 'current'
if any(q.startswith(p) for p in ['how to', 'how do i', 'how can i',
'how do you', 'steps to', 'guide to',
'tutorial', 'how does']):
return 'howto'
if any(w in q for w in ['install', 'configure', 'setup', 'set up',
'enable', 'disable', 'fix', 'repair']):
return 'howto'
if any(w in q for w in ['error', 'exception', 'traceback', 'debug',
'code', 'function', 'script', 'api', 'command',
'terminal', 'bash', 'python', 'javascript',
'docker', 'linux', 'git', 'sql', 'regex']):
return 'technical'
if ' vs ' in q or ' versus ' in q or 'difference between' in q or \
'compare ' in q or (' or ' in q and len(q.split()) < 8):
return 'comparison'
if any(q.startswith(p) for p in ['best ', 'top ', 'worst ', 'should i',
'is it worth', 'recommend']):
return 'opinion'
if any(w in q for w in ['worth it', 'better than', 'best way',
'recommend', 'suggestion', 'advice']):
return 'opinion'
if any(w in q for w in ['near me', 'nearby', 'local', 'in my area',
'closest', 'directions to']):
return 'local'
if any(q.startswith(p) for p in ['what is', 'what are', 'who is',
'who was', 'when did', 'when was',
'where is', 'where was', 'why is',
'why does', 'define ', 'what does']):
return 'factual'
return 'general'
INTENT_CONFIGS = {
'factual': {
'system_suffix': (
"This is a factual question. Provide a direct, accurate definition "
"or explanation. Lead with the core fact. Cite your primary source. "
"2-3 sentences maximum."
),
'task': "DEFINE: State the fact or definition directly. No preamble.",
'format': "Plain prose. No lists. Cite the most authoritative source first."
},
'howto': {
'system_suffix': (
"This is a how-to question. Provide clear, actionable steps. "
"Be specific and practical. Number the key steps if there are more than 2."
),
'task': "INSTRUCT: Give the key steps or method directly. Be actionable.",
'format': "Numbered steps if 3+, otherwise prose. Cite sources for each step."
},
'technical': {
'system_suffix': (
"This is a technical question. Be precise and specific. "
"Include exact commands, syntax, or error explanations where relevant. "
"Prioritize official documentation and technical sources."
),
'task': "TECHNICAL: Provide the precise technical answer. Include specifics.",
'format': "Exact terminology. Commands in backticks if applicable. Cite docs."
},
'comparison': {
'system_suffix': (
"This is a comparison question. Objectively compare the options. "
"Highlight key differences. Avoid picking a winner unless sources clearly support it."
),
'task': "COMPARE: State the key differences between the options directly.",
'format': "Brief parallel structure. Cite a source for each side if available."
},
'opinion': {
'system_suffix': (
"This is a recommendation or opinion question. Synthesize what sources say. "
"Present the consensus view if one exists. Note disagreement if present. "
"Do not present personal opinions as fact."
),
'task': "SYNTHESIZE: State what sources recommend or what consensus says.",
'format': "Lead with the consensus. Note any caveats. Cite sources."
},
'current': {
'system_suffix': (
"This is a current events question. Prioritize the most recent sources. "
"Note the date of information if relevant. Be clear about what is known vs uncertain."
),
'task': "REPORT: State the latest known information directly. Note recency.",
'format': "Lead with most recent fact. Include dates where available. Cite news sources."
},
'local': {
'system_suffix': (
"This is a local or location-based question. "
"Provide relevant location-specific information from sources. "
"Note if information may vary by location."
),
'task': "LOCAL: Provide location-relevant information from sources.",
'format': "Be specific to the location context. Cite local sources."
},
'general': {
'system_suffix': (
"Provide a concise, accurate overview that directly answers the query."
),
'task': "ANSWER FIRST: Lead with the direct answer. No preamble.",
'format': "2-4 sentences. Cite most relevant sources."
},
}
import typing import typing
if typing.TYPE_CHECKING: if typing.TYPE_CHECKING:
from searx.search import SearchWithPlugins from searx.search import SearchWithPlugins
@@ -1396,17 +1521,21 @@ class SXNGPlugin(Plugin):
if not self.api_key: if not self.api_key:
return Response("Missing API key or query", status=400) return Response("Missing API key or query", status=400)
intent = _detect_intent(q)
intent_cfg = INTENT_CONFIGS.get(intent, INTENT_CONFIGS['general'])
logger.debug(f"{PLUGIN_NAME}: detected intent '{intent}' for query: {q[:50]}")
today = time.strftime("%Y-%m-%d") today = time.strftime("%Y-%m-%d")
lang_instruction = f" Respond in {lang}." if lang not in ('all', 'auto') else "" lang_instruction = f" Respond in {lang}." if lang not in ('all', 'auto') else ""
base_sys = self.system_prompt if self.system_prompt else "You are a direct, citation-accurate search synthesis engine." base_sys = self.system_prompt if self.system_prompt else \
SYSTEM = (f"{base_sys} Today is {today}.{lang_instruction} " "You are a direct, citation-accurate search synthesis engine."
SYSTEM = (
f"{base_sys} Today is {today}.{lang_instruction} "
"Output only your final answer. Do not output your thinking process, " "Output only your final answer. Do not output your thinking process, "
"reasoning steps, or internal monologue. Begin your response with the " "reasoning steps, or internal monologue. Begin your response with the "
"direct answer immediately. " f"direct answer immediately. {intent_cfg['system_suffix']}"
"Be concise. Give a 2-4 sentence overview that directly answers the query. " )
"The user can ask follow-up questions for more detail. "
"Do not enumerate or list everything from the sources.")
max_source_idx = 0 max_source_idx = 0
if context_text: if context_text:
indices = re.findall(r'\[(\d+)\]', context_text) indices = re.findall(r'\[(\d+)\]', context_text)
@@ -1417,9 +1546,7 @@ class SXNGPlugin(Plugin):
"Answer the question directly using the provided context.", "Answer the question directly using the provided context.",
"MUST CITE SOURCES by tailing a sentence with [n] or [n,n] etc. If citing general knowledge, use [*].", "MUST CITE SOURCES by tailing a sentence with [n] or [n,n] etc. If citing general knowledge, use [*].",
"Never explain your process. The user expects a direct response.", "Never explain your process. The user expects a direct response.",
"Response format must be plain text with no markdown. " intent_cfg['format'],
"Be brief: 2-4 sentences maximum. Lead with the direct answer. "
"Cite the most relevant source(s) only. Stop after the overview.",
"If sources and general knowledge are insufficient, respond with 'Insufficient information to answer.'" "If sources and general knowledge are insufficient, respond with 'Insufficient information to answer.'"
] ]
@@ -1428,7 +1555,7 @@ class SXNGPlugin(Plugin):
elif prev_answer: elif prev_answer:
task = "FOLLOW-UP: Address the new question using prior context. Prioritize the new query." task = "FOLLOW-UP: Address the new question using prior context. Prioritize the new query."
else: else:
task = "ANSWER FIRST: Lead with the direct answer. No preamble, no context-setting." task = intent_cfg['task']
grounding = "GROUNDING: KNOWLEDGE GRAPH > DEEP > SHALLOW." if context_text else "GROUNDING: No sources available. Use general knowledge and cite as [*] which means based on general knowledge." grounding = "GROUNDING: KNOWLEDGE GRAPH > DEEP > SHALLOW." if context_text else "GROUNDING: No sources available. Use general knowledge and cite as [*] which means based on general knowledge."
history_rule = "HISTORY: Refer to prior exchange for context. Ideally, do not repeat any claims." if prev_answer else None history_rule = "HISTORY: Refer to prior exchange for context. Ideally, do not repeat any claims." if prev_answer else None
@@ -1770,6 +1897,9 @@ class SXNGPlugin(Plugin):
).hexdigest()[:24] ).hexdigest()[:24]
js_session_id = safe_json(session_id) js_session_id = safe_json(session_id)
detected_intent = _detect_intent(q_clean)
js_intent = safe_json(detected_intent)
b64_context = base64.b64encode(context_str.encode('utf-8')).decode('utf-8') b64_context = base64.b64encode(context_str.encode('utf-8')).decode('utf-8')
total_context_count = self.context_deep_count + self.context_shallow_count total_context_count = self.context_deep_count + self.context_shallow_count
@@ -1803,6 +1933,7 @@ class SXNGPlugin(Plugin):
.replace("__SCRIPT_ROOT__", js_script_root) \ .replace("__SCRIPT_ROOT__", js_script_root) \
.replace("__MODEL_INIT__", js_model_init) \ .replace("__MODEL_INIT__", js_model_init) \
.replace("__SESSION_ID__", js_session_id) \ .replace("__SESSION_ID__", js_session_id) \
.replace("__INTENT__", js_intent) \
.replace("__CITATION_HELPER_JS__", CITATION_HELPER_JS) \ .replace("__CITATION_HELPER_JS__", CITATION_HELPER_JS) \
.replace("__INTERACTIVE_JS_INIT__", interactive_js_init) \ .replace("__INTERACTIVE_JS_INIT__", interactive_js_init) \
.replace("__STREAM_FN_SIG__", stream_fn_sig) \ .replace("__STREAM_FN_SIG__", stream_fn_sig) \