Fade0510's picture
Fix logging and enable semantic cache, stock qoute cache
357c48c
Raw
History Blame Contribute Delete
6.59 kB
import logging
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from src.core.AgentCommand import AgentCommand
from src.core.FinanceState import FinanceState
from src.core.errors import add_error
from src.core.settings import get_settings
from src.rag.KnowledgeBase import KnowledgeBase
from src.rag.TavilySearchRag import TavilySearchRag
from src.data.SemanticCache import SemanticCache
logger = logging.getLogger(__name__)
class TaxAgent(AgentCommand):
def __init__(self, state: FinanceState):
load_dotenv()
self.state = state
self.tavily = TavilySearchRag()
self.kb = KnowledgeBase()
self.semantic_cache = SemanticCache()
settings = get_settings()
self.client = ChatOpenAI(model=settings.models.agent_model)
def process(self):
query = self.state.get("user_query", "") or ""
trace_id = str(self.state.get("trace_id") or "")
logger.info("[trace=%s] TaxAgent.start query_len=%d", trace_id, len(query))
profile = self.state.get("user_profile") or {}
risk = str(profile.get("risk") or "").strip().lower()
experience = str(profile.get("experience") or "").strip().lower()
cache_query = (
"agent=tax\n"
f"risk={risk}; experience={experience}\n"
f"query={query.strip().lower()}"
).strip()
# Agent-level semantic cache (prevents cross-intent collisions).
try:
cached = self.semantic_cache.check_cache(cache_query, threshold=0.91)
except Exception as e:
cached = None
add_error(
self.state,
code="semantic_cache_error",
message=str(e),
agent="tax_agent",
)
if isinstance(cached, str) and cached.strip():
logger.info("[trace=%s] TaxAgent.semantic_cache_hit", trace_id)
self.state["response"] = cached.strip()
return self.state
logger.info("[trace=%s] TaxAgent.semantic_cache_miss", trace_id)
# KB-first for tax topics.
try:
sources = self.kb.retrieve(query, k=4, categories=["tax"])
except Exception as e:
logger.info("[trace=%s] KB retrieval unavailable: %s", trace_id, e)
sources = []
if sources:
logger.info("[trace=%s] TaxAgent.source=kb hits=%d", trace_id, len(sources))
snippets = "\n\n".join(
[
f"TITLE: {s.title}\nCATEGORY: {s.category}\nEXCERPT:\n{s.excerpt}"
for s in sources
]
)
citations = "\n".join([f"- {s.title} ({s.source_path})" for s in sources])
system = (
"You are a financial education assistant focusing on taxes. "
"You must avoid personalized tax advice; provide general education and encourage consulting a professional. "
"Use only the provided KB excerpts as truth. Always include: 'Educational only, not tax or financial advice.'"
)
prompt = f"""
User question:
{query}
KB excerpts:
{snippets}
Write a clear, general educational answer. End with a short 'Sources' list.
"""
try:
msg = self.client.invoke(
[
{"role": "system", "content": system},
{"role": "user", "content": prompt},
],
temperature=0.2,
)
text = msg.content.strip()
text += "\n\nSources:\n" + citations
self.state["response"] = text
self.state["retrieved_sources"] = [
{
"title": s.title,
"category": s.category,
"source_path": s.source_path,
}
for s in sources
]
try:
self.semantic_cache.save_to_cache(cache_query, self.state["response"])
except Exception:
pass
return self.state
except Exception as e:
logger.info("[trace=%s] LLM synthesis failed, returning excerpts: %s", trace_id, e)
self.state["response"] = (
"Educational only, not tax or financial advice.\n\n"
"I found these relevant KB excerpts:\n\n"
+ snippets
+ "\n\nSources:\n"
+ citations
)
self.state["retrieved_sources"] = [
{
"title": s.title,
"category": s.category,
"source_path": s.source_path,
}
for s in sources
]
try:
self.semantic_cache.save_to_cache(cache_query, self.state["response"])
except Exception:
pass
return self.state
# Fallback: web search
tavily_results = self.tavily.search(query)
logger.info(
"[trace=%s] TaxAgent.source=tavily results=%d",
trace_id,
len(tavily_results or []),
)
if (
isinstance(tavily_results, list)
and tavily_results
and tavily_results[0].get("error")
):
add_error(
self.state,
code="tavily_error",
message=str(tavily_results[0].get("error")),
agent="tax_agent",
)
self.state["response"] = (
"Educational only, not tax or financial advice.\n\n"
f"Error retrieving tax information: {tavily_results[0].get('error')}"
)
return self.state
response_text = "Tax information (web search):\n"
for result in tavily_results or []:
if result.get("title"):
response_text += f"Title: {result['title']}\n"
if result.get("url"):
response_text += f"URL: {result['url']}\n"
if result.get("content"):
response_text += f"Content: {result['content']}...\n"
response_text += "\n"
self.state["response"] = response_text.strip()
try:
self.semantic_cache.save_to_cache(cache_query, self.state["response"])
except Exception:
pass
return self.state