Introduces websearch.py (Tavily) + knowledge.py to ground chat responses in search results, tagging.py to tag user and assistant messages via LLM and pick related topics for the Discord useless-tips job, and word_memory.py to track recent conversation nouns (surfaced in the new WordPool.vue sidebar) used to pick tip topics close to a related tag.
57 lines
1.7 KiB
Python
57 lines
1.7 KiB
Python
import logging
|
|
|
|
import httpx
|
|
|
|
from app.core.config import TAVILY_API_KEY
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
TAVILY_SEARCH_URL = "https://api.tavily.com/search"
|
|
|
|
WEB_SEARCH_TOOL = {
|
|
"type": "function",
|
|
"function": {
|
|
"name": "web_search",
|
|
"description": "最新情報・事実確認・知らない固有名詞など、自分の知識だけでは答えられない可能性があるときにWebを検索する。",
|
|
"parameters": {
|
|
"type": "object",
|
|
"properties": {
|
|
"query": {"type": "string", "description": "検索したい内容を表す検索クエリ"},
|
|
},
|
|
"required": ["query"],
|
|
},
|
|
},
|
|
}
|
|
|
|
|
|
async def search_web(query: str, max_results: int = 5) -> list[dict]:
|
|
"""Tavily APIでWeb検索する。結果は [{title, url, content}, ...]。失敗時は空リスト。"""
|
|
if not TAVILY_API_KEY:
|
|
logger.warning("TAVILY_API_KEY is not set; skipping web search")
|
|
return []
|
|
|
|
try:
|
|
async with httpx.AsyncClient(timeout=15.0) as client:
|
|
response = await client.post(
|
|
TAVILY_SEARCH_URL,
|
|
json={
|
|
"api_key": TAVILY_API_KEY,
|
|
"query": query,
|
|
"max_results": max_results,
|
|
},
|
|
)
|
|
response.raise_for_status()
|
|
data = response.json()
|
|
except Exception:
|
|
logger.exception("Tavily search failed for query=%r", query)
|
|
return []
|
|
|
|
results = []
|
|
for item in data.get("results", []):
|
|
results.append({
|
|
"title": item.get("title") or "",
|
|
"url": item.get("url") or "",
|
|
"content": item.get("content") or "",
|
|
})
|
|
return results
|