Files
task-6a1864f78a94f887e50d46da/agent.py
T
2026-06-03 10:25:13 +00:00

128 lines
4.7 KiB
Python

"""Agent construction for RAG with local KB and web search.
The agent uses LangChain's `create_openai_functions_agent` style with a custom
`AgentExecutor` that routes queries to either the local knowledge base or the
web search based on a simple heuristic: if the query contains words that
suggest recent events ("news", "today", "now", "latest"), we use the web
search; otherwise we use the local KB.
The agent returns the answer along with the source used.
"""
from typing import Dict, Any
from langchain_core.prompts import ChatPromptTemplate
from langchain_ollama import ChatOllama
from langchain.agents import create_openai_functions_agent, AgentExecutor
from rag_tools import search_local_kb, web_search
from vectorstore import create_vectorstore
# ---------------------------------------------------------------------------
# Helper: decide whether to use web search
# ---------------------------------------------------------------------------
WEB_KEYWORDS = {"news", "today", "now", "latest", "recent", "current"}
def should_use_web(query: str) -> bool:
words = set(query.lower().split())
return bool(words & WEB_KEYWORDS)
# ---------------------------------------------------------------------------
# System prompt
# ---------------------------------------------------------------------------
SYSTEM_PROMPT = "You are an assistant that answers questions. Use the local knowledge base when the question is about stored documents; otherwise use web search. Respond with the answer and indicate the source (chromadb or tavily)."
# ---------------------------------------------------------------------------
# Agent construction
# ---------------------------------------------------------------------------
def create_agent(vectorstore) -> AgentExecutor:
"""Create an AgentExecutor that routes queries to the appropriate tool.
Parameters
----------
vectorstore: Chroma
The vector store used by the local KB search tool.
Returns
-------
AgentExecutor
Configured agent.
"""
# Attach the vectorstore to the local search tool via closure
def search_local_kb_wrapper(query: str, top_k: int = 3) -> str:
return search_local_kb(query, top_k=top_k, vectorstore=vectorstore)
# Wrap the web search tool (no extra params needed)
def web_search_wrapper(query: str, top_k: int = 3) -> str:
return web_search(query, top_k=top_k)
# Define tools list with updated references
tools = [search_local_kb_wrapper, web_search_wrapper]
# LLM
llm = ChatOllama(model="llama3")
# Prompt template
prompt = ChatPromptTemplate.from_messages([
("system", SYSTEM_PROMPT),
("human", "{input}"),
])
# Create agent
agent = create_openai_functions_agent(llm=llm, tools=tools, prompt=prompt)
# Wrap with AgentExecutor
return AgentExecutor(agent=agent, tools=tools, verbose=True)
# ---------------------------------------------------------------------------
# Simple executor for tests
# ---------------------------------------------------------------------------
def create_agent_executor(vectorstore) -> Any:
"""Return a simple callable that mimics the agent for testing.
The returned callable takes a dictionary with keys ``input`` and optionally
``tool_choice``. It selects the appropriate tool based on the query and
returns a dictionary with an ``output`` key containing the result.
"""
agent = create_agent(vectorstore)
def executor(inputs: Dict[str, Any]) -> Dict[str, Any]:
query = inputs.get("input", "")
tool_choice = inputs.get("tool_choice")
if tool_choice == "web_search":
result = web_search(query)
elif tool_choice == "search_local_kb":
result = search_local_kb(query, vectorstore=vectorstore)
else:
# Default routing
if should_use_web(query):
result = web_search(query)
else:
result = search_local_kb(query, vectorstore=vectorstore)
return {"output": result}
return executor
# ---------------------------------------------------------------------------
# Example usage
# ---------------------------------------------------------------------------
if __name__ == "__main__":
store = create_vectorstore()
agent = create_agent(store)
while True:
q = input("Query> ")
if q.lower() in {"exit", "quit", "q"}:
break
# Simple routing: if query contains web keywords, use web tool
if should_use_web(q):
result = agent.invoke({"input": q, "tool_choice": "web_search"})
else:
result = agent.invoke({"input": q, "tool_choice": "search_local_kb"})
print("Answer:", result.get("output", ""))
""