|
1 | | -"""Research agent for improved information gathering.""" |
| 1 | +"""Research agent for improved information gathering. |
2 | 2 |
|
| 3 | +Now uses Claude Agent SDK for direct code inspection instead of relying on |
| 4 | +documentation search alone. This prevents hallucination by allowing the agent |
| 5 | +to actually read Prefect source code. |
| 6 | +""" |
| 7 | + |
| 8 | +from pathlib import Path |
| 9 | + |
| 10 | +from claude_agent_sdk import ClaudeAgentOptions, ClaudeSDKClient |
| 11 | +from claude_agent_sdk.types import AssistantMessage, TextBlock |
3 | 12 | from prefect import task |
4 | 13 | from prefect.cache_policies import INPUTS |
5 | | -from pydantic import BaseModel, Field |
6 | | -from pydantic_ai import Agent |
7 | | -from pydantic_ai.models import Model |
8 | 14 |
|
9 | | -from slackbot.search import ( |
10 | | - display_callable_signature, |
11 | | - explore_module_offerings, |
12 | | - review_common_3x_gotchas, |
13 | | - search_marvin_docs, |
14 | | - search_prefect_2x_docs, |
15 | | - search_prefect_3x_docs, |
16 | | -) |
17 | 15 |
|
| 16 | +async def research_topic_with_code_access(question: str, version: str = "3.x") -> str: |
| 17 | + """ |
| 18 | + Research a Prefect topic using Claude Agent SDK with direct source code access. |
18 | 19 |
|
19 | | -class ResearchFindings(BaseModel): |
20 | | - """Structured findings from research.""" |
| 20 | + The agent will clone the Prefect repo to .research_cache/prefect if it doesn't exist, |
| 21 | + or update it if it does. This works consistently in both local and Docker environments. |
21 | 22 |
|
22 | | - main_findings: list[str] = Field( |
23 | | - description="Key findings that answer the question" |
24 | | - ) |
25 | | - supporting_details: list[str] = Field(description="Additional context and details") |
26 | | - confidence_level: str = Field( |
27 | | - description="high/medium/low confidence in the answer" |
28 | | - ) |
29 | | - knowledge_gaps: list[str] = Field(description="What we still don't know") |
30 | | - relevant_links: list[str] = Field(description="Links to documentation or resources") |
| 23 | + Args: |
| 24 | + question: The research question |
| 25 | + version: Prefect version ("2.x" or "3.x") |
31 | 26 |
|
| 27 | + Returns: |
| 28 | + Research findings as a formatted string |
| 29 | + """ |
| 30 | + # Use current working directory for cache |
| 31 | + # Locally: <wherever you run from>/.research_cache/prefect |
| 32 | + # Docker: /app/.research_cache/prefect (since WORKDIR is /app) |
| 33 | + cache_dir = Path.cwd() / ".research_cache" |
| 34 | + prefect_repo = cache_dir / "prefect" |
32 | 35 |
|
33 | | -class ResearchContext(BaseModel): |
34 | | - """Context for the research agent.""" |
| 36 | + version_context = "Prefect 3.x" if version.startswith("3") else "Prefect 2.x" |
| 37 | + branch = "main" if version.startswith("3") else "2.x" |
35 | 38 |
|
36 | | - namespace: str = "prefect-3" # default to Prefect 3.x docs |
| 39 | + system_prompt = f"""You are a specialized research agent for {version_context}. |
| 40 | +Your job is to thoroughly research topics by reading the actual Prefect source code and community discussions. |
37 | 41 |
|
| 42 | +IMPORTANT: Before researching, ensure you have the Prefect source code: |
| 43 | +1. If {prefect_repo} doesn't exist: clone it with `git clone https://github.com/PrefectHQ/prefect.git {prefect_repo}` |
| 44 | +2. If it already exists: update it with `cd {prefect_repo} && git checkout {branch} && git pull` |
| 45 | +3. Then search/read the source code in {prefect_repo}/src/prefect/ to answer questions |
38 | 46 |
|
39 | | -def create_research_agent( |
40 | | - model: Model | None = None, |
41 | | -) -> Agent[ResearchContext, ResearchFindings]: |
42 | | - """Create a specialized research agent for thorough information gathering.""" |
| 47 | +If cloning fails (e.g., disk space), fall back to searching the installed package in your current environment. |
43 | 48 |
|
44 | | - agent = Agent[ResearchContext, ResearchFindings]( |
45 | | - model=model or "anthropic:claude-haiku-4-5", |
46 | | - deps_type=ResearchContext, |
47 | | - output_type=ResearchFindings, |
48 | | - system_prompt="""You are a specialized research agent for Prefect documentation and knowledge. |
49 | | -Your job is to thoroughly research topics by using available tools to gather comprehensive, accurate information. |
| 49 | +ADDITIONAL RESEARCH TOOLS: |
| 50 | +- Use `gh` CLI to search GitHub discussions and issues for community-verified solutions |
| 51 | +- Example: `gh search issues --repo PrefectHQ/prefect "custom state event"` |
| 52 | +- This helps find real-world patterns and edge cases that users have discovered |
50 | 53 |
|
51 | | -Your research process: |
52 | | -1. Start with broad documentation searches to understand the topic context |
53 | | -2. Use multiple search queries with different keywords - don't stop at first result |
54 | | -3. Use explore_module_offerings to understand what's available in relevant modules (i.e. valid imports, types and functions available) |
55 | | -4. Use display_callable_signature to get detailed signatures of functions, classes, and methods when needed |
56 | | -5. **IMPORTANT**: ONLY use search_prefect_3x_docs unless the user explicitly mentions "2.x", "Prefect 2", or version compatibility |
57 | | -6. Review gotchas for recent changes |
| 54 | +Use your tools to search and read the actual implementation - do not make assumptions. |
| 55 | +Be thorough - verify everything by reading the source. |
58 | 56 |
|
59 | 57 | CRITICAL VERSION-SPECIFIC RULES: |
60 | | -- **DEFAULT TO PREFECT 3.x**: Do NOT use search_prefect_2x_docs unless user explicitly mentions "2.x", "Prefect 2", or asks about version differences |
| 58 | +- **DEFAULT TO {version_context}**: Do NOT suggest deprecated patterns |
61 | 59 | - **NEVER** suggest `Deployment.build_from_flow()` for Prefect 3.x - it's COMPLETELY REMOVED |
62 | 60 | - **NEVER** suggest `prefect deployment build` CLI command for 3.x - use `prefect deploy` instead |
63 | 61 | - The correct deployment pattern in 3.x is: `flow.from_source(...).deploy(...)` |
64 | | -- If researching deployments, ALWAYS use review_common_3x_gotchas() to check removed features |
65 | 62 | - Default to Prefect 3.x patterns unless user explicitly states they're using 2.x |
66 | 63 | - If user is on 2.x, suggest upgrading to 3.x or using workers instead of deprecated patterns |
67 | 64 |
|
68 | 65 | Remember: You are the research specialist. The main agent relies on you for accurate, comprehensive information. |
69 | 66 | Be thorough - use tools repeatedly until you have complete information. |
70 | | -Do not use any Prefect syntax you have not gathered empirically. |
71 | | -""", |
72 | | - tools=[ |
73 | | - search_prefect_2x_docs, |
74 | | - display_callable_signature, |
75 | | - search_prefect_3x_docs, |
76 | | - search_marvin_docs, |
77 | | - explore_module_offerings, |
78 | | - review_common_3x_gotchas, |
79 | | - ], |
80 | | - ) |
81 | | - |
82 | | - return agent |
| 67 | +Do not use any Prefect syntax you have not verified by reading the actual source code.""" |
83 | 68 |
|
| 69 | + options = ClaudeAgentOptions( |
| 70 | + allowed_tools=["Read", "Grep", "Glob", "Bash"], |
| 71 | + cwd=str(Path.cwd()), |
| 72 | + model="claude-haiku-4-5-20251001", |
| 73 | + system_prompt=system_prompt, |
| 74 | + ) |
84 | 75 |
|
85 | | -async def research_topic( |
86 | | - question: str, namespace: str = "prefect-3", model: Model | None = None |
87 | | -) -> ResearchFindings: |
88 | | - """ |
89 | | - Thoroughly research a topic using an intelligent agent. |
90 | | - Args: |
91 | | - question: The question to research |
92 | | - namespace: The documentation namespace to search |
93 | | - model: Optional model to use for the agent |
| 76 | + research_output = [] |
94 | 77 |
|
95 | | - Returns: |
96 | | - ResearchFindings with comprehensive information |
97 | | - """ |
98 | | - context = ResearchContext(namespace=namespace) |
| 78 | + async with ClaudeSDKClient(options=options) as client: |
| 79 | + await client.query(question) |
99 | 80 |
|
100 | | - agent = create_research_agent(model) |
101 | | - result = await agent.run(user_prompt=question, deps=context) |
| 81 | + async for message in client.receive_response(): |
| 82 | + if isinstance(message, AssistantMessage): |
| 83 | + for block in message.content: |
| 84 | + if isinstance(block, TextBlock): |
| 85 | + research_output.append(block.text) |
102 | 86 |
|
103 | | - return result.output |
| 87 | + return "\n".join(research_output) |
104 | 88 |
|
105 | 89 |
|
106 | 90 | def research_prefect_topic(question: str, topic: str, version: str = "3.x") -> str: |
107 | 91 | """ |
108 | | - Thoroughly research a Prefect topic using an intelligent research agent. |
109 | | - This tool performs multiple searches and synthesizes comprehensive findings. |
| 92 | + Thoroughly research a Prefect topic using Claude Agent SDK with source code access. |
| 93 | +
|
| 94 | + This tool uses Claude Agent SDK to give the research agent direct access to: |
| 95 | + - Actual Prefect source code via Read tool |
| 96 | + - Code search via Grep/Bash (rg) |
| 97 | + - File discovery via Glob |
| 98 | + - Shell commands for exploration |
| 99 | +
|
| 100 | + This eliminates hallucination by allowing the agent to verify everything |
| 101 | + against the actual implementation. |
110 | 102 |
|
111 | 103 | Args: |
112 | 104 | question: The specific question or topic to research |
113 | 105 | topic: A short display name for the topic based on the question (for bookkeeping) |
114 | 106 | version: Prefect version ("2.x" or "3.x") |
115 | 107 | """ |
116 | | - namespace = f"prefect-{version[0]}" |
117 | | - |
118 | 108 | try: |
119 | | - findings = ( |
| 109 | + result = ( |
120 | 110 | task(task_run_name=f"Researching {topic}", cache_policy=INPUTS)( |
121 | | - research_topic |
| 111 | + research_topic_with_code_access |
122 | 112 | ) |
123 | | - .submit(question, namespace) |
| 113 | + .submit(question, version) |
124 | 114 | .result() |
125 | 115 | ) |
126 | 116 |
|
127 | | - result = f"**Research Findings** (Confidence: {findings.confidence_level})\n\n" |
128 | | - |
129 | | - result += "**Main Findings:**\n" |
130 | | - for finding in findings.main_findings: |
131 | | - result += f"- {finding}\n" |
132 | | - |
133 | | - if findings.supporting_details: |
134 | | - result += "\n**Supporting Details:**\n" |
135 | | - for detail in findings.supporting_details: |
136 | | - result += f"- {detail}\n" |
137 | | - |
138 | | - if findings.relevant_links: |
139 | | - result += "\n**Relevant Documentation:**\n" |
140 | | - for link in findings.relevant_links: |
141 | | - result += f"- {link}\n" |
142 | | - |
143 | | - if findings.knowledge_gaps: |
144 | | - result += "\n**Note:** Some aspects could not be fully researched:\n" |
145 | | - for gap in findings.knowledge_gaps: |
146 | | - result += f"- {gap}\n" |
147 | | - |
148 | | - return result |
| 117 | + return f"**Research Findings (Code-Verified)**\n\n{result}" |
149 | 118 |
|
150 | 119 | except Exception as e: |
151 | | - return f"Research failed: {str(e)}. Falling back to standard search." |
| 120 | + return f"Research failed: {str(e)}. The agent may not have access to the source code." |
0 commit comments