Image Evaluation
Score AI-generated images for prompt alignment with ImageInstructionAdherence and confirm they're flagged as AI-generated with SyntheticImageEvaluator, using fi.evals.
Score how well a generated image matches its text prompt with ImageInstructionAdherence, then confirm the asset is flagged as AI-generated with SyntheticImageEvaluator. Both run through the same Evaluator.evaluate() call.
| Time | Difficulty | Package |
|---|---|---|
| 15 min | Beginner | ai-evaluation |
- Future AGI account → app.futureagi.com
- API keys:
FI_API_KEYandFI_SECRET_KEY(see Get your API keys) - Python 3.11
Install
pip install ai-evaluation
export FI_API_KEY="your-api-key"
export FI_SECRET_KEY="your-secret-key"
Tutorial
Initialize the Evaluator client
import os
from fi.evals import Evaluator
evaluator = Evaluator(
fi_api_key=os.environ["FI_API_KEY"],
fi_secret_key=os.environ["FI_SECRET_KEY"],
fi_base_url="https://api.futureagi.com",
)
print("Evaluator client initialized")You should see Evaluator client initialized printed with no errors. A bad key raises InvalidAuthError at the first evaluate() call, not here.
Check alignment between the image and its prompt
ImageInstructionAdherence scores how well a generated image follows the prompt that produced it. Here’s one row of a T2I (text-to-image) dataset: a prompt, the image generated from it, and the category the prompt targets. In production this comes from your generation pipeline’s output log, not a hand-typed dict.
from fi.evals.templates import ImageInstructionAdherence
datapoint = {
"prompt": "a pair of white sneakers with a wavy sole design, product photo on a plain background",
"image_url": "https://raw.githubusercontent.com/future-agi/cookbooks/main/ecom_agent/observe/generated_products/nike_air_max_sneakers.png",
"category": "product-fidelity",
}
alignment_template = ImageInstructionAdherence()
alignment_result = evaluator.evaluate(
eval_templates=[alignment_template],
inputs=[{
"instruction": datapoint["prompt"],
"images": [datapoint["image_url"]],
}],
model_name="turing_flash",
)
alignment = alignment_result.eval_results[0]
print(alignment.output)
print(alignment.reason)Example output (illustrative, depends on the actual image):
0.92
The image accurately shows a pair of white sneakers with a wavy sole design against a plain background.output carries the adherence score for this eval (0 to 1, higher is better), reason explains it in plain language. model_name picks the evaluator model that scores the image; turing_flash is a fast default.
Confirm the image is flagged as AI-generated
SyntheticImageEvaluator scores how confident the model is that an image was AI-generated rather than captured by a camera. Run it after the adherence check to confirm a generated asset is correctly recognized as synthetic, the signal a moderation or disclosure workflow needs before the asset ships.
from fi.evals.templates import SyntheticImageEvaluator
provenance_template = SyntheticImageEvaluator()
provenance_result = evaluator.evaluate(
eval_templates=[provenance_template],
inputs=[{
"image": datapoint["image_url"],
}],
model_name="turing_flash",
)
provenance = provenance_result.eval_results[0]
print(provenance.output)
print(provenance.reason)Example output (illustrative, depends on the actual image):
0.88
The smooth textures and uniform lighting are consistent with AI-generated product photography rather than a real camera capture.output carries the confidence that the image is AI-generated (0 to 1, higher means more confident it’s synthetic), reason explains the visual cues behind the score. A low score here means the model reads the image as camera-captured, worth a second look if your pipeline expects every asset in this dataset to be generated.
Batch-evaluate a full dataset
Loop the alignment check over every datapoint and collect the pass rate. This is where a real dataset load belongs: one small list here, or a pd.read_csv(...) / json.load(...) over your own generation log in production.
datapoints = [
{
"prompt": "a pair of white sneakers with a wavy sole design, product photo on a plain background",
"image_url": "https://raw.githubusercontent.com/future-agi/cookbooks/main/ecom_agent/observe/generated_products/nike_air_max_sneakers.png",
"category": "product-fidelity",
},
{
"prompt": "a red leather handbag with gold buckles on a wooden table",
"image_url": "https://raw.githubusercontent.com/future-agi/cookbooks/main/ecom_agent/observe/generated_products/nike_air_max_sneakers.png",
"category": "product-mismatch",
},
{
"prompt": "a pair of running shoes shown mid-stride on a track",
"image_url": "https://raw.githubusercontent.com/future-agi/cookbooks/main/ecom_agent/observe/generated_products/nike_air_max_sneakers.png",
"category": "context-mismatch",
},
]
PASS_THRESHOLD = 0.5 # matches the SDK's own passed = score >= 0.5
results = []
for dp in datapoints:
result = evaluator.evaluate(
eval_templates=[alignment_template],
inputs=[{
"instruction": dp["prompt"],
"images": [dp["image_url"]],
}],
model_name="turing_flash",
)
verdict = result.eval_results[0]
results.append({
"category": dp["category"],
"score": verdict.output,
"passed": verdict.output >= PASS_THRESHOLD,
"reason": verdict.reason,
})
pass_rate = sum(1 for r in results if r["passed"]) / len(results)
print(f"Pass rate: {pass_rate:.0%} across {len(results)} images")You should see a pass rate printed. The three rows here reuse one image against different prompts on purpose, so the mismatched rows score low. Group results by category on your own dataset to spot which prompt categories the generator handles worst.
Inspect a failed case
When passed is False, reason tells you why the image didn’t match.
failures = [r for r in results if not r["passed"]]
for f in failures:
print(f["category"], f["score"])
print(f["reason"])Example output (illustrative, from the batch above):
product-mismatch 0.05
The image shows sneakers, not a handbag, so the prompt's subject is not represented.
context-mismatch 0.30
The image shows sneakers in a static product shot, not mid-stride on a track.Reading the reasons above, both failures trace back to the prompt describing a subject or setting the source image never had, not a scoring bug. The fix is upstream: tighten the generation prompt (or point it at a matching image) and rerun the same row through Step 2 to confirm the score crosses PASS_THRESHOLD.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
ModuleNotFoundError: No module named 'fi.testcases' | Following an older version of this cookbook that imported MLLMTestCase | That module doesn’t exist in ai-evaluation. Pass a plain inputs dict to evaluate(), as shown above |
ImportError: cannot import name 'ImageInstruction' | Importing ImageInstruction instead of ImageInstructionAdherence | Use ImageInstructionAdherence from fi.evals.templates |
AttributeError: 'EvalResult' object has no attribute 'metrics' | Reading .metrics[0].value on the result | The class-based templates on this page return the verdict on .output; .metrics is not populated on this call path |
InvalidAuthError on the first evaluate() call | FI_API_KEY or FI_SECRET_KEY unset or wrong | Confirm both are exported and match the keys in app.futureagi.com admin settings |
| Eval call times out or hangs on a large batch | Looping evaluate() synchronously over hundreds of images | Lower the batch size, or add a timeout argument to evaluate() and retry failed rows individually |
reason mentions it could not load the image | image_url is a local file path or a private/expired URL | Host the image somewhere the backend can fetch over HTTPS, and confirm the URL isn’t behind auth |
| Every row in a category scores the same regardless of content | Categories keyed on the wrong dataset field | Print dp["category"] for a few rows and confirm it matches the value your generation pipeline actually wrote |
Next: Multimodal Evaluation: Images, Audio, and PDF covers image captioning, AI-image detection, audio quality, and OCR with the rest of the built-in multimodal evals.
Questions & Discussion