Add SearXNG web search for current information

- Add searxng.py service for web queries via SearXNG API
- Integrate search into ai_chat.py with AI-driven search decisions
- AI determines if query needs current info, then searches automatically
- Add SEARXNG_URL, SEARXNG_ENABLED, SEARXNG_MAX_RESULTS config options
- Update documentation in README.md, CLAUDE.md, and .env.example
This commit is contained in:
2026-01-11 20:49:20 +01:00
parent c5c42c8701
commit 6a9b6fdda2
7 changed files with 225 additions and 3 deletions

View File

@@ -7,7 +7,7 @@ import discord
from discord.ext import commands
from daemon_boyfriend.config import settings
from daemon_boyfriend.services import AIService, ConversationManager, Message
from daemon_boyfriend.services import AIService, ConversationManager, Message, SearXNGService
logger = logging.getLogger(__name__)
@@ -71,6 +71,9 @@ class AIChatCog(commands.Cog):
self.bot = bot
self.ai_service = AIService()
self.conversations = ConversationManager()
self.search_service: SearXNGService | None = None
if settings.searxng_enabled and settings.searxng_url:
self.search_service = SearXNGService(settings.searxng_url)
@commands.Cog.listener()
async def on_message(self, message: discord.Message) -> None:
@@ -138,10 +141,23 @@ class AIChatCog(commands.Cog):
# Add current message to history for the API call
messages = history + [Message(role="user", content=user_message)]
# Check if we should search the web
search_context = await self._maybe_search(user_message)
# Build system prompt with search context if available
system_prompt = self.ai_service.get_system_prompt()
if search_context:
system_prompt += (
"\n\n--- Web Search Results ---\n"
"Use the following current information from the web to help answer the user's question. "
"Cite sources when relevant.\n\n"
f"{search_context}"
)
# Generate response
response = await self.ai_service.chat(
messages=messages,
system_prompt=self.ai_service.get_system_prompt(),
system_prompt=system_prompt,
)
# Save the exchange to history
@@ -154,6 +170,64 @@ class AIChatCog(commands.Cog):
return response.content
async def _maybe_search(self, query: str) -> str | None:
"""Determine if a search is needed and perform it.
Args:
query: The user's message
Returns:
Formatted search results or None if search not needed/available
"""
if not self.search_service:
return None
# Ask the AI if this query needs current information
decision_prompt = (
"You are a search decision assistant. Your ONLY job is to decide if the user's "
"question requires current/real-time information from the internet.\n\n"
"Respond with ONLY 'SEARCH: <query>' if a web search would help answer the question "
"(replace <query> with optimal search terms), or 'NO_SEARCH' if the question can be "
"answered with general knowledge.\n\n"
"Examples that NEED search:\n"
"- Current events, news, recent happenings\n"
"- Current weather, stock prices, sports scores\n"
"- Latest version of software, current documentation\n"
"- Information about specific people, companies, or products that may have changed\n"
"- 'What time is it in Tokyo?' or any real-time data\n\n"
"Examples that DON'T need search:\n"
"- General knowledge, science, math, history\n"
"- Coding help, programming concepts\n"
"- Personal advice, opinions, creative writing\n"
"- Explanations of concepts or 'how does X work'"
)
try:
decision = await self.ai_service.chat(
messages=[Message(role="user", content=query)],
system_prompt=decision_prompt,
)
response_text = decision.content.strip()
if response_text.startswith("SEARCH:"):
search_query = response_text[7:].strip()
logger.info(f"AI decided to search for: {search_query}")
results = await self.search_service.search(
query=search_query,
max_results=settings.searxng_max_results,
)
if results:
return self.search_service.format_results_for_context(results)
return None
except Exception as e:
logger.warning(f"Search decision/execution failed: {e}")
return None
async def setup(bot: commands.Bot) -> None:
"""Load the AI Chat cog."""