feat: add new examples from pocketflow-academy

This commit is contained in:
Alan ALves
2025-03-19 10:31:04 -03:00
parent 84720ceebd
commit 557a14f695
129 changed files with 13455 additions and 0 deletions
+71
View File
@@ -0,0 +1,71 @@
# Web Search with Analysis
A web search tool built with PocketFlow that performs searches using SerpAPI and analyzes results using LLM.
## Features
- Web search using Google via SerpAPI
- Extracts titles, snippets, and links
- Analyzes search results using GPT-4 to provide:
- Result summaries
- Key points/facts
- Suggested follow-up queries
- Clean command-line interface
## Installation
1. Clone the repository
2. Install dependencies:
```bash
pip install -r requirements.txt
```
3. Set required API keys:
```bash
export SERPAPI_API_KEY='your-serpapi-key'
export OPENAI_API_KEY='your-openai-key'
```
## Usage
Run the search tool:
```bash
python main.py
```
You will be prompted to:
1. Enter your search query
2. Specify number of results to fetch (default: 5)
The tool will then:
1. Perform the search using SerpAPI
2. Analyze results using GPT-4
3. Present a summary with key points and follow-up queries
## Project Structure
```
pocketflow-tool-search/
├── tools/
│ ├── search.py # SerpAPI search functionality
│ └── parser.py # Result analysis using LLM
├── utils/
│ └── call_llm.py # LLM API wrapper
├── nodes.py # PocketFlow nodes
├── flow.py # Flow configuration
├── main.py # Main script
└── requirements.txt # Dependencies
```
## Limitations
- Requires SerpAPI subscription
- Rate limited by both APIs
- Basic error handling
- Text results only
## Dependencies
- pocketflow: Flow-based processing
- google-search-results: SerpAPI client
- openai: GPT-4 API access
- pyyaml: YAML processing
+18
View File
@@ -0,0 +1,18 @@
from pocketflow import Flow
from nodes import SearchNode, AnalyzeResultsNode
def create_flow() -> Flow:
"""Create and configure the search flow
Returns:
Flow: Configured flow ready to run
"""
# Create nodes
search = SearchNode()
analyze = AnalyzeResultsNode()
# Connect nodes
search >> analyze
# Create flow starting with search
return Flow(start=search)
+26
View File
@@ -0,0 +1,26 @@
import os
from flow import create_flow
def main():
"""Run the web search flow"""
# Get search query from user
query = input("Enter search query: ")
if not query:
print("Error: Query is required")
return
# Initialize shared data
shared = {
"query": query,
"num_results": 5
}
# Create and run flow
flow = create_flow()
flow.run(shared)
# Results are in shared["analysis"]
if __name__ == "__main__":
main()
+56
View File
@@ -0,0 +1,56 @@
from pocketflow import Node
from tools.search import SearchTool
from tools.parser import analyze_results
from typing import List, Dict
class SearchNode(Node):
"""Node to perform web search using SerpAPI"""
def prep(self, shared):
return shared.get("query"), shared.get("num_results", 5)
def exec(self, inputs):
query, num_results = inputs
if not query:
return []
searcher = SearchTool()
return searcher.search(query, num_results)
def post(self, shared, prep_res, exec_res):
shared["search_results"] = exec_res
return "default"
class AnalyzeResultsNode(Node):
"""Node to analyze search results using LLM"""
def prep(self, shared):
return shared.get("query"), shared.get("search_results", [])
def exec(self, inputs):
query, results = inputs
if not results:
return {
"summary": "No search results to analyze",
"key_points": [],
"follow_up_queries": []
}
return analyze_results(query, results)
def post(self, shared, prep_res, exec_res):
shared["analysis"] = exec_res
# Print analysis
print("\nSearch Analysis:")
print("\nSummary:", exec_res["summary"])
print("\nKey Points:")
for point in exec_res["key_points"]:
print(f"- {point}")
print("\nSuggested Follow-up Queries:")
for query in exec_res["follow_up_queries"]:
print(f"- {query}")
return "default"
@@ -0,0 +1,4 @@
pocketflow>=0.1.0
google-search-results>=2.4.2 # SerpAPI client
openai>=1.0.0 # for search result analysis
pyyaml>=6.0.1 # for structured output
@@ -0,0 +1,70 @@
from typing import Dict, List
from utils.call_llm import call_llm
def analyze_results(query: str, results: List[Dict]) -> Dict:
"""Analyze search results using LLM
Args:
query (str): Original search query
results (List[Dict]): Search results to analyze
Returns:
Dict: Analysis including summary and key points
"""
# Format results for prompt
formatted_results = []
for i, result in enumerate(results, 1):
formatted_results.append(f"""
Result {i}:
Title: {result['title']}
Snippet: {result['snippet']}
URL: {result['link']}
""")
prompt = f"""
Analyze these search results for the query: "{query}"
{'\n'.join(formatted_results)}
Please provide:
1. A concise summary of the findings (2-3 sentences)
2. Key points or facts (up to 5 bullet points)
3. Suggested follow-up queries (2-3)
Output in YAML format:
```yaml
summary: >
brief summary here
key_points:
- point 1
- point 2
follow_up_queries:
- query 1
- query 2
```
"""
try:
response = call_llm(prompt)
# Extract YAML between code fences
yaml_str = response.split("```yaml")[1].split("```")[0].strip()
import yaml
analysis = yaml.safe_load(yaml_str)
# Validate required fields
assert "summary" in analysis
assert "key_points" in analysis
assert "follow_up_queries" in analysis
assert isinstance(analysis["key_points"], list)
assert isinstance(analysis["follow_up_queries"], list)
return analysis
except Exception as e:
print(f"Error analyzing results: {str(e)}")
return {
"summary": "Error analyzing results",
"key_points": [],
"follow_up_queries": []
}
@@ -0,0 +1,57 @@
import os
from serpapi import GoogleSearch
from typing import Dict, List, Optional
class SearchTool:
"""Tool for performing web searches using SerpAPI"""
def __init__(self, api_key: Optional[str] = None):
"""Initialize search tool with API key
Args:
api_key (str, optional): SerpAPI key. Defaults to env var SERPAPI_API_KEY.
"""
self.api_key = api_key or os.getenv("SERPAPI_API_KEY")
if not self.api_key:
raise ValueError("SerpAPI key not found. Set SERPAPI_API_KEY env var.")
def search(self, query: str, num_results: int = 5) -> List[Dict]:
"""Perform Google search via SerpAPI
Args:
query (str): Search query
num_results (int, optional): Number of results to return. Defaults to 5.
Returns:
List[Dict]: Search results with title, snippet, and link
"""
# Configure search parameters
params = {
"engine": "google",
"q": query,
"api_key": self.api_key,
"num": num_results
}
try:
# Execute search
search = GoogleSearch(params)
results = search.get_dict()
# Extract organic results
if "organic_results" not in results:
return []
processed_results = []
for result in results["organic_results"][:num_results]:
processed_results.append({
"title": result.get("title", ""),
"snippet": result.get("snippet", ""),
"link": result.get("link", "")
})
return processed_results
except Exception as e:
print(f"Search error: {str(e)}")
return []
@@ -0,0 +1,34 @@
import os
from openai import OpenAI
from pathlib import Path
# Get the project root directory (parent of utils directory)
ROOT_DIR = Path(__file__).parent.parent
# Initialize OpenAI client with API key from environment
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
def call_llm(prompt: str) -> str:
"""Call OpenAI API to analyze text
Args:
prompt (str): Input prompt for the model
Returns:
str: Model response
"""
try:
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": prompt}]
)
return response.choices[0].message.content
except Exception as e:
print(f"Error calling LLM API: {str(e)}")
return ""
if __name__ == "__main__":
# Test LLM call
response = call_llm("What is web search?")
print("Response:", response)