| """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, |
| ) |