# Agent Data Guard - Data Trust Score & Decoy Detector (`apricot_blackberry/agent-data-guard`) Actor

Insurance for your AI agent's inputs. Scores any Apify dataset 0-100 using transparent statistics - fill-rate, Shannon entropy, Benford's law, duplicate and decoy detection - then returns a verdict and per-signal flags. Catch blocked, empty, or manipulated scraper output before your agent trusts it.

- **URL**: https://apify.com/apricot\_blackberry/agent-data-guard.md
- **Developed by:** [Creator Fusion](https://apify.com/apricot_blackberry) (community)
- **Categories:** AI, Developer tools
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Agent Data Guard

**Before your AI agent acts on a scraped dataset, ask: is this data real?**

Agent Data Guard takes an Apify `datasetId`, runs a battery of generalizable
statistical integrity checks over the rows, and returns a single **authenticity
score (0-100)**, a **verdict**, and a list of **signal flags**. It is built for
agents: one call in, one machine-readable summary row out, plus per-field detail.

Scrapers fail silently all the time - a blocked IP returns a 200 with a decoy
body, a source serves a truncated mirror, a template emits the same canned row
thousands of times. The run status says `SUCCEEDED` and the item count looks
fine. This actor is the check that catches that *before* your agent trusts it.

***

### What it checks (textbook statistics only)

| # | Signal | What it catches |
|---|--------|-----------------|
| 1 | **Field fill-rate + null clustering** | Missing values, and nulls concentrated in a contiguous block (truncated / decoy tail) |
| 2 | **Value distribution + Shannon entropy** | Near-zero-entropy fields = constant / canned data |
| 3 | **Benford first-digit test** | Numeric fields (with real spread) whose leading-digit distribution is unnatural (chi-square vs Benford, p<0.01) |
| 4 | **Duplicate detection** | Exact-row duplicates + near-duplicates via k-shingling / Jaccard on text |
| 5 | **Low-cardinality / constant fields** | Columns that never vary |
| 6 | **Boilerplate / decoy detection** | A single string repeated across >=90% of rows |
| 7 | **Row-count sanity** | Empty datasets / zero-row decoys |

### Scoring (transparent, no black box)

Start at **100** and subtract additive penalties:

| Signal | Max penalty |
|--------|-------------|
| Field fill-rate | `(1 - avgFillRate) * 25` |
| Low-entropy fields | `fracLowEntropy * 20` |
| Exact duplicates | `exactDupeRate * 20` |
| Null clustering | `fracClusteredFields * 15` |
| Constant fields | `fracConstantFields * 15` |
| Boilerplate fields | `fracBoilerplateFields * 15` |
| Near duplicates | `nearDupeRate * 10` |
| Benford failures | `fracFailingBenford * 10` |

`authenticityScore = clamp(100 - totalPenalty, 0, 100)`. Signals deliberately
overlap (a constant field is penalized as both low-entropy and constant) because
each is a distinct, independently reported red flag.

**Verdict:** `>=70` -> `likely-authentic`, `40-69` -> `degraded`,
`<40` (or 0 rows) -> `probable-decoy-or-empty`.

***

### Input

```json
{
  "datasetId": "aBcDeFgHiJkLmNoPq",
  "fields": ["name", "price", "url"],
  "sampleLimit": 1000
}
```

- `datasetId` (**required**) - the dataset to inspect. Read with your own token.
- `fields` (optional) - subset of fields to check; omit to check all.
- `sampleLimit` (optional, default `1000`) - max rows to load.

### Output

One **summary** row followed by one **detail** row per field.

```json
{
  "rowType": "summary",
  "authenticityScore": 30,
  "verdict": "probable-decoy-or-empty",
  "signalFlags": [
    { "signal": "constant-field", "severity": "high", "detail": "5 field(s) hold a single constant value." },
    { "signal": "exact-duplicates", "severity": "high", "detail": "99.5% of rows are exact duplicates." }
  ],
  "rowsAnalyzed": 400,
  "fieldsAnalyzed": 5,
  "avgFillRate": 0.925,
  "exactDuplicateRate": 0.995,
  "nearDuplicateRate": 1.0
}
```

Per-field rows carry `fillRate`, `cardinality`, `entropyBits`, `normEntropy`,
`constant`, `boilerplate`, `nullClustered`, `benfordChiSq`, `benfordFail`,
`topValueShare`, and `emptyCount`.

***

### Calling it

#### MCP (Apify actor tool)

Call the actor `apricot_blackberry/agent-data-guard` with
`{ "datasetId": "<id>" }`, then read the default dataset's `/items`. The summary
row is the one with `rowType: "summary"`.

#### curl

```bash
curl -X POST "https://api.apify.com/v2/acts/apricot_blackberry~agent-data-guard/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"datasetId":"aBcDeFgHiJkLmNoPq","sampleLimit":1000}'
```

#### JavaScript (apify-client)

```js
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('apricot_blackberry/agent-data-guard')
  .call({ datasetId: 'aBcDeFgHiJkLmNoPq' });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
const summary = items.find((r) => r.rowType === 'summary');
if (summary.authenticityScore < 40) throw new Error(`Untrustworthy data: ${summary.verdict}`);
```

#### Python (apify-client)

```python
from apify_client import ApifyClient
client = ApifyClient(token=os.environ["APIFY_TOKEN"])
run = client.actor("apricot_blackberry/agent-data-guard").call(
    run_input={"datasetId": "aBcDeFgHiJkLmNoPq"})
items = client.dataset(run["defaultDatasetId"]).list_items().items
summary = next(r for r in items if r["rowType"] == "summary")
if summary["authenticityScore"] < 40:
    raise RuntimeError(f'Untrustworthy data: {summary["verdict"]}')
```

### Pricing

Pay-per-event: a small actor-start fee plus **one `report` charge per run**.
No proxy needed - the actor only reads the Apify API.

### Notes / limits

- Near-duplicate detection compares the first 300 sampled rows pairwise
  (O(n^2)); larger samples still get exact-dupe, entropy, and Benford over the
  full sample.
- Benford is only applied to numeric fields spanning at least one order of
  magnitude with >=30 positive values - it is skipped (not failed) otherwise.
- Statistical signals are heuristics: a low score means "inspect before
  trusting", not a proof of forgery.

# Actor input Schema

## `datasetId` (type: `string`):

The Apify dataset whose items should be checked for integrity. Its rows are read (clean=true) and scored. Uses a resource picker so this limited-permissions actor is granted READ access to the dataset you select.

## `fields` (type: `array`):

Optional subset of field names to analyze. Leave empty to analyze every field found across the sampled rows.

## `sampleLimit` (type: `integer`):

Maximum number of rows to load and analyze from the dataset (default 1000). Larger samples give more stable statistics but cost more compute.

## Actor input object example

```json
{
  "fields": [],
  "sampleLimit": 1000
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {};

// Run the Actor and wait for it to finish
const run = await client.actor("apricot_blackberry/agent-data-guard").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {}

# Run the Actor and wait for it to finish
run = client.actor("apricot_blackberry/agent-data-guard").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{}' |
apify call apricot_blackberry/agent-data-guard --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,apricot_blackberry/agent-data-guard"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/zDeOPAQkRwgwXdEV6/builds/ngR1Dhntw0JnGMeWK/openapi.json
