"""Research Agent - Simple version using langchain create_agent directly.""" from datetime import datetime from dotenv import load_dotenv load_dotenv(".env", override=True) from langchain_ollama import ChatOllama from langchain.agents import create_agent from langchain_core.tools import tool import httpx from markdownify import markdownify from tavily import TavilyClient tavily_client = TavilyClient() def fetch_webpage_content(url: str, timeout: float = 10.0) -> str: headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" } try: response = httpx.get(url, headers=headers, timeout=timeout, follow_redirects=True) response.raise_for_status() if response.headers.get('content-type', '').startswith('application/pdf'): return f"[PDF content not displayed - URL: {url}]" content = markdownify(response.text) if len(content) > 3000: content = content[:3000] + "\n\n[Content truncated]" return content except Exception as e: return f"Error fetching content from {url}: {str(e)}" @tool def tavily_search(query: str, max_results: int = 3) -> str: """Search the web for information on a given query. Args: query: Search query to execute max_results: Maximum number of results to return (default: 3) Returns: Formatted search results with webpage content """ search_results = tavily_client.search(query, max_results=max_results) result_texts = [] for result in search_results.get("results", []): url = result["url"] title = result["title"] content = fetch_webpage_content(url) result_text = f"""## {title} **URL:** {url} {content} --- """ result_texts.append(result_text) response = f"🔍 Found {len(result_texts)} result(s) for '{query}':\n\n" + "\n".join(result_texts) return response @tool def think_tool(reflection: str) -> str: """Tool for strategic reflection on research progress.""" return f"Reflection recorded: {reflection}" @tool def write_file(file_path: str, content: str) -> str: """Write content to a file.""" with open(file_path, 'w', encoding='utf-8') as f: f.write(content) return f"File written: {file_path}" current_date = datetime.now().strftime("%Y-%m-%d") RESEARCHER_INSTRUCTIONS = f"""You are an expert research assistant with a strict workflow. Today's date is {current_date}. **YOUR MISSION:** Design a comprehensive 50-gene panel for human prostate cancer research. YOU MUST COMPLETE ALL STEPS. **TOOLS AVAILABLE:** 1. tavily_search(query, max_results): Search the web for academic papers and research 2. think_tool(reflection): Record analysis and plan next steps 3. write_file(file_path, content): Write final report when ALL research is done **WORKFLOW - FOLLOW EXACTLY IN ORDER:** STEP 1: Search for prostate cancer driver genes. Use tavily_search with query about oncogenes, tumor suppressors, and key mutations in prostate cancer. STEP 2: Search for immune microenvironment markers. Use tavily_search with query about PD-1, PD-L1, CTLA-4, CD4, CD8, and tumor immune infiltration markers. STEP 3: Search for tissue and stromal markers. Use tavily_search with query about angiogenesis (VEGF), extracellular matrix (collagen), and stromal fibroblasts in prostate cancer. STEP 4: Search for commercial gene panels. Use tavily_search with query about FDA-approved or commercially available prostate cancer gene tests like Oncotype DX, Prolaris, Decipher. STEP 5: Use think_tool to analyze all findings and create the 50-gene list. STEP 6: Use write_file to save the complete report to 'final_report.md'. **FINAL REPORT FORMAT:** # Prostate Cancer 50-Gene Panel Design ## Executive Summary Overview of the panel design and its clinical significance. ## Tumor Status Markers (20 genes) - Gene: Description and relevance ## Immune Microenvironment Markers (15 genes) - Gene: Description and relevance ## Tissue Context Markers (15 genes) - Gene: Description and relevance ## References List of sources with URLs. **CRITICAL INSTRUCTION:** Do NOT summarize or ask questions. EXECUTE THE STEPS ONE BY ONE. After each tool call, immediately proceed to the next step. Continue until all 6 steps are complete. YOU MUST use write_file at the end to save the report.""" model = ChatOllama(model="qwen3.5:9b", temperature=0.0) tools = [tavily_search, think_tool, write_file] agent = create_agent( model=model, tools=tools, system_prompt=RESEARCHER_INSTRUCTIONS, )