Skip to content

Commit bc83bd1

Browse files
committed
sample: E-commerce Product Scraper Agent
1 parent dd572e4 commit bc83bd1

File tree

12 files changed

+837
-0
lines changed

12 files changed

+837
-0
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
flowchart TB
2+
__start__(__start__)
3+
coordinator(coordinator)
4+
scraper(scraper)
5+
finalize(finalize)
6+
__end__(__end__)
7+
__start__ --> coordinator
8+
coordinator --> finalize
9+
coordinator --> scraper
10+
scraper --> coordinator
11+
finalize --> __end__
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"version": "2.0",
3+
"resources": []
4+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
{
2+
"version": "1.0",
3+
"id": "edge-cases",
4+
"name": "Edge Cases and Error Scenarios",
5+
"description": "Tests for unusual inputs, single pages, and boundary conditions",
6+
"evaluatorRefs": ["JsonSimilarityEvaluator", "LLMJudgeOutputEvaluator", "TrajectoryEvaluator"],
7+
"evaluations": [
8+
{
9+
"id": "test-1-single-product-page",
10+
"name": "Starting from a single product page extracts that product",
11+
"inputs": {
12+
"start_url": "https://sandbox.oxylabs.io/products/1"
13+
},
14+
"evaluationCriterias": {
15+
"LLMJudgeOutputEvaluator": {
16+
"expectedOutput": {
17+
"products": "Should contain at least one product with name, price, currency, and url fields. The product URL should be https://sandbox.oxylabs.io/products/1 or related products discovered from that page.",
18+
"total_products": "At least 1 product should be found"
19+
}
20+
},
21+
"TrajectoryEvaluator": {
22+
"expectedAgentBehavior": "The agent should: 1) Start with the single product URL. 2) Fetch the page and classify it as a 'product' page via LLM analysis. 3) Extract product data fields (name, price, description, etc.) using CSS selectors from the strategy. 4) Discover any related product links or navigation links on the product page. 5) Optionally follow discovered links to scrape more products. 6) Finalize with at least one product."
23+
}
24+
}
25+
},
26+
{
27+
"id": "test-2-last-pagination-page",
28+
"name": "Scraping the last page of pagination works correctly",
29+
"inputs": {
30+
"start_url": "https://sandbox.oxylabs.io/products?page=94"
31+
},
32+
"evaluationCriterias": {
33+
"LLMJudgeOutputEvaluator": {
34+
"expectedOutput": {
35+
"products": "Should contain products from the last page. Each product should have valid name, price, and url fields.",
36+
"total_products": "Should be a positive number, likely around 20-32 products from the last page and any discovered linked pages"
37+
}
38+
}
39+
}
40+
},
41+
{
42+
"id": "test-3-nonexistent-page",
43+
"name": "Handling a page that returns no products",
44+
"inputs": {
45+
"start_url": "https://sandbox.oxylabs.io/products?page=9999"
46+
},
47+
"evaluationCriterias": {
48+
"JsonSimilarityEvaluator": {
49+
"expectedOutput": {
50+
"total_products": 0,
51+
"urls_scraped": 0
52+
}
53+
}
54+
}
55+
}
56+
]
57+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{
2+
"version": "1.0",
3+
"id": "happy-path",
4+
"name": "Happy Path Scenarios",
5+
"description": "Tests for normal scraping operations with the oxylabs sandbox site",
6+
"evaluatorRefs": ["JsonSimilarityEvaluator", "TrajectoryEvaluator"],
7+
"evaluations": [
8+
{
9+
"id": "test-1-default-url-products-found",
10+
"name": "Scrape default URL returns products with expected fields",
11+
"inputs": {
12+
"start_url": "https://sandbox.oxylabs.io/products"
13+
},
14+
"evaluationCriterias": {
15+
"JsonSimilarityEvaluator": {
16+
"expectedOutput": {
17+
"total_products": 2993,
18+
"urls_scraped": 3301
19+
}
20+
},
21+
"TrajectoryEvaluator": {
22+
"expectedAgentBehavior": "The agent should: 1) Start at the coordinator node which seeds the start URL. 2) Dispatch URLs to parallel scraper sub-agents. 3) Scrapers fetch pages, call the LLM to analyze page type (listing vs product), and extract product links from listing pages and product data from product pages. 4) Return discovered URLs to coordinator for further rounds. 5) Continue until no new URLs remain. 6) Finalize by deduplicating products and resolving currency symbols. The agent should scrape approximately 3000 products across multiple rounds of coordinator-scraper cycles."
23+
}
24+
}
25+
},
26+
{
27+
"id": "test-2-category-page",
28+
"name": "Scrape a specific category page discovers products",
29+
"inputs": {
30+
"start_url": "https://sandbox.oxylabs.io/products/category/nintendo"
31+
},
32+
"evaluationCriterias": {
33+
"TrajectoryEvaluator": {
34+
"expectedAgentBehavior": "The agent should: 1) Start at the coordinator with the Nintendo category URL. 2) Dispatch to scrapers which classify the page as a listing page. 3) Extract product links and pagination links from the category listing. 4) Visit individual product pages to extract product data (name, price, description, etc.). 5) Follow pagination to discover all products in the category. 6) Finalize with deduplicated products, all having currency resolved to ISO codes. The total products should be fewer than the full site (~1000 or less for a single category)."
35+
}
36+
}
37+
}
38+
]
39+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
{
2+
"version": "1.0",
3+
"id": "output-structure",
4+
"name": "Output Structure Validation",
5+
"description": "Validates that scraped products have the expected fields and data quality",
6+
"evaluatorRefs": ["LLMJudgeOutputEvaluator"],
7+
"evaluations": [
8+
{
9+
"id": "test-1-product-fields-present",
10+
"name": "Products contain required fields (name, price, url, currency)",
11+
"inputs": {
12+
"start_url": "https://sandbox.oxylabs.io/products?page=1"
13+
},
14+
"evaluationCriterias": {
15+
"LLMJudgeOutputEvaluator": {
16+
"expectedOutput": {
17+
"products": "A list of product objects where each product has at minimum: 'url' (a valid URL to the product page), 'name' (non-empty product name), 'price' (a numeric price value), and 'currency' (ISO 4217 code like EUR). Products may also have description, availability, developer, platform, and type fields.",
18+
"total_products": "A positive integer greater than 0 representing the number of unique products scraped",
19+
"urls_scraped": "A positive integer representing the total number of URLs visited during scraping"
20+
}
21+
}
22+
}
23+
},
24+
{
25+
"id": "test-2-currency-resolved",
26+
"name": "Currency symbols are resolved to ISO 4217 codes",
27+
"inputs": {
28+
"start_url": "https://sandbox.oxylabs.io/products?page=2"
29+
},
30+
"evaluationCriterias": {
31+
"LLMJudgeOutputEvaluator": {
32+
"expectedOutput": {
33+
"products": "Products should have a 'currency' field containing a valid ISO 4217 three-letter code (e.g. 'EUR', 'USD', 'GBP') rather than a raw currency symbol (e.g. not '€' or '$'). The price field should be a numeric string without currency symbols."
34+
}
35+
}
36+
}
37+
},
38+
{
39+
"id": "test-3-no-duplicate-products",
40+
"name": "No duplicate products in output",
41+
"inputs": {
42+
"start_url": "https://sandbox.oxylabs.io/products?page=3"
43+
},
44+
"evaluationCriterias": {
45+
"LLMJudgeOutputEvaluator": {
46+
"expectedOutput": {
47+
"products": "Each product in the list should have a unique URL. There should be no two products with the same 'url' field. The total_products count should match the length of the products list.",
48+
"total_products": "Should exactly match the number of items in the products array"
49+
}
50+
}
51+
}
52+
}
53+
]
54+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"version": "1.0",
3+
"id": "JsonSimilarityEvaluator",
4+
"evaluatorTypeId": "uipath-json-similarity",
5+
"evaluatorConfig": {
6+
"name": "JsonSimilarityEvaluator",
7+
"targetOutputKey": "*"
8+
}
9+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"version": "1.0",
3+
"id": "LLMJudgeOutputEvaluator",
4+
"evaluatorTypeId": "uipath-llm-judge-output-semantic-similarity",
5+
"evaluatorConfig": {
6+
"name": "LLMJudgeOutputEvaluator",
7+
"model": "gpt-4o-2024-11-20",
8+
"temperature": 0.0
9+
}
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"version": "1.0",
3+
"id": "TrajectoryEvaluator",
4+
"evaluatorTypeId": "uipath-llm-judge-trajectory-similarity",
5+
"evaluatorConfig": {
6+
"name": "LLMJudgeTrajectoryEvaluator",
7+
"model": "gpt-4o-2024-11-20",
8+
"temperature": 0.0
9+
}
10+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"graphs": {
3+
"agent": "./main.py:graph"
4+
}
5+
}

0 commit comments

Comments
 (0)