Skip to content

Commit f2e5280

Browse files
zzstoatzzclaude
andauthored
feat: integrate Claude Agent SDK for code-aware research (#1244)
* feat: integrate Claude Agent SDK for code-aware research Replace pydantic-ai research agent with Claude Agent SDK to enable direct source code inspection, eliminating hallucination by allowing the agent to read actual Prefect implementations. Key changes: - Add claude-agent-sdk dependency to slackbot - Rewrite research_agent.py to use ClaudeSDKClient with file access tools - Agent clones/updates Prefect source to .research_cache/prefect - Use Claude Haiku 4.5 for cost-effective research - Add gh CLI guidance for searching community discussions - Add .research_cache/ to .gitignore The agent now has Read, Grep, Glob, and Bash tools to verify everything against actual source code instead of guessing from documentation. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: use /app for research cache consistently Use /app/.research_cache for both local and Docker since Docker WORKDIR is /app. No more parent navigation. * fix: use Path.cwd() for cache location Works in both local and Docker: - Locally: uses current working directory - Docker: uses /app (the WORKDIR) --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent d5114c0 commit f2e5280

4 files changed

Lines changed: 101 additions & 104 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,4 @@ wheels/
2525
**/*.db
2626
**/*.sqlite
2727
sandbox/
28+
.research_cache/

examples/slackbot/pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ dependencies = [
4141
"numpy",
4242
"raggy[tpuf] @ git+https://github.com/zzstoatzz/raggy.git",
4343
"pretty-mod",
44+
"claude-agent-sdk",
4445
]
4546

4647

Lines changed: 73 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -1,151 +1,120 @@
1-
"""Research agent for improved information gathering."""
1+
"""Research agent for improved information gathering.
22
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
312
from prefect import task
413
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
814

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-
)
1715

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.
1819
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.
2122
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")
3126
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"
3235

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"
3538

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.
3741
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
3846
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.
4348
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
5053
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.
5856
5957
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
6159
- **NEVER** suggest `Deployment.build_from_flow()` for Prefect 3.x - it's COMPLETELY REMOVED
6260
- **NEVER** suggest `prefect deployment build` CLI command for 3.x - use `prefect deploy` instead
6361
- 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
6562
- Default to Prefect 3.x patterns unless user explicitly states they're using 2.x
6663
- If user is on 2.x, suggest upgrading to 3.x or using workers instead of deprecated patterns
6764
6865
Remember: You are the research specialist. The main agent relies on you for accurate, comprehensive information.
6966
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."""
8368

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+
)
8475

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 = []
9477

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)
9980

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)
10286

103-
return result.output
87+
return "\n".join(research_output)
10488

10589

10690
def research_prefect_topic(question: str, topic: str, version: str = "3.x") -> str:
10791
"""
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.
110102
111103
Args:
112104
question: The specific question or topic to research
113105
topic: A short display name for the topic based on the question (for bookkeeping)
114106
version: Prefect version ("2.x" or "3.x")
115107
"""
116-
namespace = f"prefect-{version[0]}"
117-
118108
try:
119-
findings = (
109+
result = (
120110
task(task_run_name=f"Researching {topic}", cache_policy=INPUTS)(
121-
research_topic
111+
research_topic_with_code_access
122112
)
123-
.submit(question, namespace)
113+
.submit(question, version)
124114
.result()
125115
)
126116

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}"
149118

150119
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

Comments
 (0)