Files
task-6a1d75c5fd30e81cf3126ae7/main.py
T
2026-06-04 17:16:55 +00:00

125 lines
4.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import os
import asyncio
from pathlib import Path
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_chroma import Chroma
from langchain_core.documents import Document
from langchain.tools import tool
from deepagents import create_deep_agent
from deepagents.backends import FilesystemBackend, LocalShellBackend, CompositeBackend
# ----------------- Configuration -----------------
# Load API key from .env or environment variable
os.environ.setdefault("OPENAI_API_KEY", os.getenv("OPENAI_API_KEY", ""))
# LLM via OpenRouter
llm = ChatOpenAI(
model="openai/gpt-oss-20b:free",
base_url="https://openrouter.ai/api/v1",
api_key=os.getenv("OPENAI_API_KEY"),
temperature=0.0,
)
# Embeddings for Chroma
embeddings = OpenAIEmbeddings(
model="text-embedding-3-small",
base_url="https://openrouter.ai/api/v1",
api_key=os.getenv("OPENAI_API_KEY"),
)
# ----------------- Chroma DB -----------------
CHROMA_PATH = Path("./chroma_faq")
CHROMA_COLLECTION = "faq_collection"
vector_store = Chroma(
collection_name=CHROMA_COLLECTION,
embedding_function=embeddings,
persist_directory=str(CHROMA_PATH),
)
# Load markdown files into Chroma (idempotent)
DATA_DIR = Path("./data")
if not CHROMA_PATH.exists() or not list(CHROMA_PATH.iterdir()):
docs = []
for md_file in DATA_DIR.glob("*.md"):
text = md_file.read_text(encoding="utf-8")
docs.append(Document(page_content=text, metadata={"source": md_file.name}))
vector_store.add_documents(docs)
vector_store.persist()
# ----------------- Tools -----------------
@tool
def search_course_docs(query: str) -> str:
"""Search the local FAQ collection for relevant passages."""
results = vector_store.similarity_search(query, k=3)
if not results:
return "No relevant information found in the course materials."
return "\n---\n".join(f"{doc.metadata.get('source', 'unknown')}\n{doc.page_content}" for doc in results)
# Mock MCP tool static JSON data
COURSE_META = {
"schedule": "Monday 10:00-12:00, Wednesday 14:00-16:00",
"instructor": "Dr. Ivanov",
"location": "Room 101",
}
@tool
def fetch_course_meta(query: str) -> str:
"""Return course metadata matching the query keyword.
For example, query="schedule" returns the schedule string.
"""
key = query.lower().strip()
return COURSE_META.get(key, f"No metadata found for '{query}'.")
# ----------------- Backend -----------------
backend = CompositeBackend([
LocalShellBackend(workspace_dir="./workspace"),
FilesystemBackend(),
])
# ----------------- Agent -----------------
agent = create_deep_agent(
model=llm,
tools=[search_course_docs, fetch_course_meta],
backend=backend,
system_prompt=(
"You are a helpful FAQ assistant for the course.\n"
"When answering a question, first decide whether the answer comes from the course materials (use search_course_docs)\n"
"or from course metadata (use fetch_course_meta).\n"
"Do not call both tools unless absolutely necessary.\n"
"In your final answer, prepend 'source: chroma' or 'source: mcp_meta' to indicate the origin."
),
)
# ----------------- CLI -----------------
PRESET_QUESTIONS = [
"What topics are covered in the first lecture?", # should hit chroma
"Who is the instructor for this course?", # should hit mcp_meta
"When is the next class?", # should hit chroma (or meta if schedule)
]
async def run_question(question: str):
result = await agent.ainvoke(
{"messages": ["HumanMessage(content=\"{}\")".format(question)]},
{"configurable": {"thread_id": "session-1"}},
)
# The agent returns a dict with 'messages'; extract last content
content = result["messages"][-1].content
print(f"\nQ: {question}\nA: {content}\n")
async def interactive():
print("Enter a question (or 'exit' to quit):")
while True:
q = input("> ")
if q.lower() in {"exit", "quit"}:
break
await run_question(q)
async def main():
print("Running preset questions...")
for q in PRESET_QUESTIONS:
await run_question(q)
await interactive()
if __name__ == "__main__":
asyncio.run(main())