agentsclimarketplace

Ares

Skill AndreaBozzo/Ares-Claude-Skill/ares

Use when working with the Ares web scraper — an LLM-powered Rust tool that extracts structured data from websites using JSON Schemas. Covers library usage, CLI commands, REST API, schema creation, adding custom fetchers/cleaners/extractors, deployment, and contributing to the Ares codebase.From its SKILL.md

Install
npx -y skills add AndreaBozzo/Ares-Claude-Skill --skill ares

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

9.7 KB, ~2.5k tokens by cl100k_base, as published. Nobody here has run it

Ares — LLM-Powered Web Scraper

Ares is a Rust library, CLI, and HTTP server that extracts structured data from websites using LLMs and JSON Schemas.

Repository: https://github.com/AndreaBozzo/Ares License: Apache-2.0 | Rust edition: 2024 | MSRV: 1.88+

Pipeline

URL → [ContentCache?] → Fetcher (HTML) → Cleaner (Markdown) → [ExtractionCache?] → Extractor (LLM + JSON Schema) → Validate → Hash → Compare → Store

Each stage is a trait, so every component can be swapped or mocked independently. Optional in-memory caches (moka) skip fetch/extraction when content or results are already cached.

After extraction the result is validated against the JSON Schema (validate_extracted_output); on mismatch the pipeline returns AppError::ExtractionValidationError and nothing is persisted (toggle with .with_validation(false)). A heuristic groundedness check (ungrounded_fields) then warns — without failing — when short atomic values look absent from the source (a hallucination signal schema validation can't catch). Valid JSON is not necessarily grounded truth.

Crate Map

CratePurposeKey Exports
ares-coreBusiness logic, traits, pipelineScrapeService, WorkerService, CircuitBreaker, ThrottledFetcher, CrawlConfig, ContentCache, ExtractionCache, CacheConfig, ProxyConfig, StealthConfig, TlsBackend, validate_schema, validate_extracted_output, ungrounded_fields, traits
ares-clientHTTP/browser fetchers, cleaner, LLM clientsReqwestFetcher, BrowserFetcher, HtmdCleaner, OpenAiExtractor (+Factory), AnthropicExtractor (feature anthropic), CandleExtractor (feature local-llm), Provider, ProviderExtractor (+Factory), HtmlLinkDiscoverer, CachedRobotsChecker, UserAgentPool
ares-dbPostgreSQL persistenceDatabase, ExtractionRepository, ScrapeJobRepository
ares-apiAxum REST APIRoutes, DTOs, bearer auth, OpenAPI/Swagger, crawl endpoints
ares-cliCommand-line interfacescrape, history, job, worker, crawl, schema, model subcommands, output formats

Core Traits (ares-core::traits)

pub trait Fetcher: Send + Sync + Clone {
    fn fetch(&self, url: &str) -> impl Future<Output = Result<String, AppError>> + Send;
}

pub trait Cleaner: Send + Sync + Clone {
    fn clean(&self, html: &str) -> Result<String, AppError>;
}

pub trait Extractor: Send + Sync + Clone {
    fn extract(&self, content: &str, schema: &serde_json::Value)
        -> impl Future<Output = Result<serde_json::Value, AppError>> + Send;
}

pub trait ExtractorFactory: Send + Sync + Clone {
    type Extractor: Extractor;
    fn create(&self, model: &str, base_url: &str) -> Result<Self::Extractor, AppError>;
}

pub trait ExtractionStore: Send + Sync + Clone {
    fn save(&self, extraction: &NewExtraction) -> impl Future<Output = Result<Uuid, AppError>> + Send;
    fn get_latest(&self, url: &str, schema_name: &str) -> impl Future<Output = Result<Option<Extraction>, AppError>> + Send;
    fn get_history(&self, url: &str, schema_name: &str, limit: usize, offset: usize) -> impl Future<Output = Result<Vec<Extraction>, AppError>> + Send;
}

JobQueue trait: see ares-core::job_queue — persistent queue with atomic claiming (SELECT FOR UPDATE SKIP LOCKED).

pub trait LinkDiscoverer: Send + Sync + Clone {
    fn discover_links(&self, html: &str, base_url: &str) -> Result<Vec<String>, AppError>;
}

pub trait RobotsChecker: Send + Sync + Clone {
    // Returns `true` if the URL may be fetched. On fetch/parse errors it
    // defaults to allowing (graceful degradation) — hence plain `bool`, not Result.
    fn is_allowed(&self, url: &str) -> impl Future<Output = bool> + Send;
}

Key Types

TypeModulePurpose
Extractionares_core::modelsCompleted extraction (id, url, schema_name, extracted_data, hashes, model, created_at)
NewExtractionares_core::modelsInsert DTO (no id/timestamps)
ScrapeResultares_core::modelsPipeline output (extracted_data, hashes, changed flag, extraction_id)
ScrapeJobares_core::jobQueued job with status, retry info, LLM config
JobStatusares_core::jobEnum: Pending, Running, Completed, Failed, Cancelled
RetryConfigares_core::jobExponential backoff: 1min → 5min → 30min → 60min (capped)
WorkerConfigares_core::jobWorker settings: poll_interval, retry_config, skip_unchanged
AppErrorares_core::errorError enum with is_retryable() and should_trip_circuit()
SchemaResolverares_core::schemaCRUD for schemas: resolve, create, update, delete + registry management
CircuitBreakerares_core::circuit_breakerClosed → Open → HalfOpen state machine
ThrottledFetcher<F>ares_core::throttlePer-domain delay with jitter
CrawlConfigares_core::crawlCrawl settings: max_depth, max_pages, allowed_domains, respect_robots_txt
CacheConfigares_core::cacheCache TTL and capacity limits
ContentCacheares_core::cacheURL-keyed in-memory HTML cache (moka)
ExtractionCacheares_core::cacheContent+schema+model-keyed extraction result cache (moka)
OutputFormatares_cli::outputEnum: Json, Jsonl, Csv, Table, Jq
ProxyConfigares_core::proxyProxy pool with rotation (round-robin or random), thread-safe via AtomicUsize
ProxyEntryares_core::proxySingle proxy endpoint (url + optional auth credentials, percent-encoded)
RotationStrategyares_core::proxyEnum: RoundRobin, Random
TlsBackendares_core::proxyEnum: Rustls (default), Native, Random — for TLS fingerprint diversity
StealthConfigares_core::stealthBrowser anti-fingerprinting config (all opt-in, default disabled)
UserAgentPoolares_client::user_agent20 realistic browser UA strings, random selection per request

Quick Start (Library Usage)

use ares_client::{ReqwestFetcher, HtmdCleaner, OpenAiExtractor};
use ares_core::{ScrapeService, NullStore};

let fetcher = ReqwestFetcher::new()?;
let cleaner = HtmdCleaner::new();
let extractor = OpenAiExtractor::with_base_url(&api_key, "gpt-4o-mini", "https://api.openai.com/v1")?;

let service = ScrapeService::<_, _, _, NullStore>::new(fetcher, cleaner, extractor, "gpt-4o-mini".into());

let schema = serde_json::json!({
    "type": "object",
    "properties": {
        "title": {"type": "string"},
        "author": {"type": "string"}
    },
    "required": ["title", "author"]
});

let result = service.scrape("https://example.com/blog", &schema, "blog").await?;
println!("{}", serde_json::to_string_pretty(&result.extracted_data)?);

With persistence, use ScrapeService::with_store(fetcher, cleaner, extractor, store, model).

Providers & Backends

The Extractor trait is the seam for inference backends. Three ship in-tree, selected at runtime via --provider / ARES_PROVIDER (CLI), the provider field of POST /v1/scrape (API), or Provider + ProviderExtractor/ProviderExtractorFactory dispatch enums (library):

ProviderValueBackendBuildNotes
OpenAI-compatibleopenai (default)OpenAiExtractordefaultOpenAI, Gemini compat endpoint, local OpenAI-style servers (llama.cpp/Ollama/LM Studio) via --base-url
Anthropic (Claude)anthropicAnthropicExtractor--features anthropicNative Messages API via forced tool use (not OpenAI-compatible)
Local (native)localCandleExtractor--features local-llmNative CPU inference through Candle; manage weights with ares model pull/list/remove. No API key, no per-token cost

New backends implement Extractor + ExtractorFactory; nothing else in the pipeline changes.

Reference Guides

TopicFileWhen to Read
Architecture deep-divereferences/architecture.mdUnderstanding pipeline internals, crate dependencies, resilience patterns
JSON Schema systemreferences/schemas.mdCreating/managing schemas, registry, versioning
Extending Aresreferences/extending.mdImplementing custom Fetcher/Cleaner/Extractor/Store/JobQueue
CLI & REST APIreferences/cli-and-server.mdRunning CLI commands, calling API endpoints, deploying
Contributingreferences/contributing.mdDev setup, testing, CI, code style

Version Notes

  • Current version: 0.4.0
  • Until crates.io release, use git dependency: ares-core = { git = "https://github.com/AndreaBozzo/Ares" }
  • Works with any OpenAI-compatible API (OpenAI, Gemini, local servers) out of the box; Anthropic and native local inference are feature-gated.
  • Optional features: browser (headless Chrome), anthropic (native Claude), local-llm (native Candle CPU inference).
  • New in 0.3.0: Provider abstraction with runtime selection (--provider/ARES_PROVIDER), native Anthropic backend, output validation (validate_extracted_output, returned as 422 over HTTP) and groundedness checks (ungrounded_fields), --max-content cap, additional schemas (public_tenders, tender_list, job_board).
  • New in 0.4.0: Native local inference via Candle (local-llm feature, --provider local, ares model subcommand).
  • Earlier (0.2.0): Web crawling, in-memory caching, output formats (json/jsonl/csv/table/jq), proxy rotation, User-Agent rotation, browser stealth mode, TLS backend selection.

What ships with it: 5 files

45.7 KB alongside SKILL.md

Keep looking

Skills are one crate of 326,835. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.