xiaohongshu
xiaohongshu
Comprehensive guide to creating, managing, and maintaining ground truth datasets for AI evaluation including annotation, quality control, and versioning
Full skill instructions
Definition: Correct answers for evaluation - human-verified data that serves as the gold standard for measuring AI performance.
Question: "What is the capital of France?"
Ground Truth: "Paris"
AI Answer: "Paris" → Correct ✓
AI Answer: "Lyon" → Incorrect ✗
Without ground truth: "This answer seems good" (subjective)
With ground truth: "Accuracy: 85%" (objective)
Training: Learn from ground truth examples
Validation: Measure performance on ground truth test set
Before change: Accuracy 90%
After change: Accuracy 85%
→ Regression detected!
Model A: 90% accuracy on ground truth
Model B: 85% accuracy on ground truth
→ Model A is better
{
"question": "What is 2+2?",
"answer": "4"
}
{
"question": "What is the capital of France?",
"acceptable_answers": ["Paris", "paris", "PARIS", "The capital is Paris"]
}
{
"question": "Summarize this article",
"rubric": {
"1": "Poor summary, missing key points",
"3": "Adequate summary, covers main points",
"5": "Excellent summary, concise and comprehensive"
}
}
{
"question": "Which answer is better?",
"answer_a": "Paris is the capital of France.",
"answer_b": "The capital of France is Paris, a city of 2.1 million people.",
"preference": "B",
"reasoning": "More informative"
}
Process:
1. Collect examples (questions, documents, images)
2. Human annotators label each
3. Quality control (review annotations)
4. Store in dataset
Medical: Doctors annotate
Legal: Lawyers annotate
Technical: Engineers annotate
Higher quality but more expensive
Pros:
- Fast (many workers)
- Cheap ($0.10-1.00 per annotation)
Cons:
- Variable quality
- Need quality control
LLM-generated questions + answers
Careful validation needed
Good for scale, risky for quality
Use for augmentation, not sole source
{
"input": {
"type": "question",
"text": "What is the capital of France?"
}
}
{
"expected_output": {
"type": "answer",
"text": "Paris",
"acceptable_variants": ["paris", "PARIS"]
}
}
{
"metadata": {
"difficulty": "easy",
"category": "geography",
"source": "wikipedia",
"language": "en"
}
}
{
"annotation": {
"annotator_id": "annotator_123",
"timestamp": "2024-01-15T10:00:00Z",
"confidence": 0.95,
"time_spent_seconds": 30
}
}
Complete Example:
{
"id": "example_001",
"input": {
"type": "question",
"text": "What is the capital of France?"
},
"expected_output": {
"type": "answer",
"text": "Paris",
"acceptable_variants": ["paris", "PARIS", "The capital is Paris"]
},
"metadata": {
"difficulty": "easy",
"category": "geography",
"source": "wikipedia",
"language": "en"
},
"annotation": {
"annotator_id": "annotator_123",
"timestamp": "2024-01-15T10:00:00Z",
"confidence": 0.95
}
}
# Annotation Guidelines
## Task
Label whether the answer is correct.
## Instructions
1. Read the question carefully
2. Read the answer
3. Determine if answer is factually correct
4. Mark as "Correct" or "Incorrect"
## Examples
Question: "What is 2+2?"
Answer: "4"
Label: Correct
Question: "What is 2+2?"
Answer: "5"
Label: Incorrect
## Good Example
Question: "What is the capital of France?"
Answer: "Paris"
Label: Correct
Reasoning: Factually accurate and directly answers question
## Bad Example
Question: "What is the capital of France?"
Answer: "France is a country in Europe"
Label: Incorrect
Reasoning: Doesn't answer the question
## Edge Cases
### Partially Correct
Question: "What are the capitals of France and Germany?"
Answer: "Paris"
Label: Partially Correct (missing Germany)
### Ambiguous Question
Question: "What is the best programming language?"
Label: N/A - Subjective question, no single correct answer
### No Answer in Context
Question: "What is the population of Paris?"
Context: "Paris is the capital of France."
Label: "Cannot be determined from context"
## Consistency Rules
1. Same question → Same answer
2. Synonyms are acceptable ("car" = "automobile")
3. Case-insensitive ("Paris" = "paris")
4. Extra details are OK ("Paris" vs "Paris, France")
Each example labeled by 3 annotators
Majority vote determines final label
Catches individual annotator errors
Measure: Do annotators agree?
Metric: Cohen's Kappa (κ)
Target: κ > 0.7 (good agreement)
10% of examples have known correct labels
Mix into annotation tasks
Measure annotator accuracy on gold standard
Remove low-quality annotators
Expert reviews 10% of annotations
Validates quality
Identifies systematic errors
from sklearn.metrics import cohen_kappa_score
annotator1 = [1, 0, 1, 1, 0] # Labels from annotator 1
annotator2 = [1, 0, 1, 0, 0] # Labels from annotator 2
kappa = cohen_kappa_score(annotator1, annotator2)
print(f"Kappa: {kappa:.2f}")
# Interpretation:
# κ < 0.4: Poor agreement
# κ 0.4-0.6: Moderate agreement
# κ 0.6-0.8: Good agreement
# κ > 0.8: Excellent agreement
from statsmodels.stats.inter_rater import fleiss_kappa
# 3 annotators, 5 examples
# Each row: [count_label_0, count_label_1]
data = [
[0, 3], # Example 1: All 3 annotators chose label 1
[1, 2], # Example 2: 1 chose 0, 2 chose 1
[3, 0], # Example 3: All 3 chose label 0
[2, 1], # Example 4: 2 chose 0, 1 chose 1
[0, 3], # Example 5: All 3 chose label 1
]
kappa = fleiss_kappa(data)
print(f"Fleiss' Kappa: {kappa:.2f}")
def percentage_agreement(annotator1, annotator2):
agreements = sum(a == b for a, b in zip(annotator1, annotator2))
total = len(annotator1)
return agreements / total
agreement = percentage_agreement(annotator1, annotator2)
print(f"Agreement: {agreement:.1%}")
If κ < 0.7:
1. Review annotation guidelines (unclear?)
2. Provide more examples
3. Train annotators
4. Simplify task
def majority_vote(labels):
from collections import Counter
counts = Counter(labels)
majority_label = counts.most_common(1)[0][0]
return majority_label
# 3 annotators
labels = [1, 1, 0] # Two say 1, one says 0
final_label = majority_vote(labels) # 1
If no majority (e.g., 1, 0, 2):
→ Expert reviews and decides
Annotators discuss disagreement
Reach consensus
Update guidelines if needed
If systematic disagreements:
→ Guidelines unclear
→ Update and re-annotate
{
"text": "This product is amazing!",
"label": "positive"
}
{
"question": "What is the capital of France?",
"answer": "Paris",
"acceptable_variants": ["paris", "PARIS", "The capital is Paris"]
}
{
"document": "Long article text...",
"reference_summary": "Concise summary of key points"
}
{
"question": "What is the capital of France?",
"context": "Paris is the capital and largest city of France.",
"answer": "Paris",
"relevant_chunks": ["Paris is the capital and largest city of France."]
}
{
"prompt": "Write a haiku about spring",
"acceptable_outputs": [
"Cherry blossoms bloom\nGentle breeze carries petals\nSpring has arrived now",
"Flowers start to bloom\nBirds sing in the morning light\nSpring is here at last"
]
}
Purpose: Quick evaluation during development
Size: 100-1000 examples
Quality: High (manually curated)
Coverage: Representative of production
Purpose: Final evaluation before deployment
Size: 500-5000 examples
Quality: High (gold standard)
Coverage: Comprehensive (all categories, edge cases)
Better: 100 high-quality examples
Worse: 1000 low-quality examples
Include:
- Common cases (80%)
- Edge cases (15%)
- Adversarial cases (5%)
# Git for dataset versioning
git init
git add dataset.jsonl
git commit -m "Initial dataset v1.0"
# Tag versions
git tag v1.0
# Update dataset
git add dataset.jsonl
git commit -m "Added 100 new examples"
git tag v1.1
Monthly: Add 50-100 new examples
Quarterly: Major update (500+ examples)
Examples that are:
- No longer relevant
- Incorrect (facts changed)
- Duplicates
# Dataset Changelog
## v1.2 (2024-02-01)
- Added 100 new examples (geography category)
- Removed 20 outdated examples
- Fixed 5 incorrect labels
## v1.1 (2024-01-01)
- Added 50 new examples (science category)
- Updated annotation guidelines
## v1.0 (2023-12-01)
- Initial release (500 examples)
Easy: 40%
Medium: 40%
Hard: 20%
Geography: 25%
Science: 25%
History: 25%
Math: 25%
Common cases: 80%
Edge cases: 15%
Adversarial: 5%
Sample from actual production queries
Ensures dataset matches real usage
def generate_synthetic_qa(document):
prompt = f"""
Document: {document}
Generate 5 question-answer pairs based on this document.
Format:
Q1: [question]
A1: [answer]
...
"""
response = llm.generate(prompt)
qa_pairs = parse_qa_pairs(response)
return qa_pairs
LLM-generated data can have:
- Hallucinations
- Incorrect facts
- Biased questions
→ Always validate with humans
Pros: Can generate 1000s quickly
Cons: Quality varies, needs validation
Strategy:
- 80% human-annotated (high quality)
- 20% synthetic (validated)
Annotators: Licensed doctors
Cost: $50-100 per hour
Quality: Very high
Use case: Medical diagnosis, treatment recommendations
Annotators: Licensed lawyers
Cost: $100-300 per hour
Quality: Very high
Use case: Legal document analysis, case law
Annotators: Senior engineers
Cost: $50-150 per hour
Quality: High
Use case: Code review, technical Q&A
{"id": "1", "question": "What is 2+2?", "answer": "4"}
{"id": "2", "question": "Capital of France?", "answer": "Paris"}
CREATE TABLE ground_truth (
id UUID PRIMARY KEY,
question TEXT NOT NULL,
answer TEXT NOT NULL,
category VARCHAR(50),
difficulty VARCHAR(20),
created_at TIMESTAMP DEFAULT NOW()
);
git add dataset/
git commit -m "Update ground truth dataset"
git push
# Upload to S3 with versioning
aws s3 cp dataset.jsonl s3://my-bucket/ground-truth/v1.0/dataset.jsonl
aws s3api put-bucket-versioning --bucket my-bucket --versioning-configuration Status=Enabled
Structure:
{
"question": "What is the capital of France?",
"expected_answer": "Paris",
"relevant_document_chunks": [
"Paris is the capital and largest city of France."
],
"evaluation_criteria": {
"faithfulness": "Answer must be grounded in context",
"relevance": "Answer must directly address question",
"completeness": "Answer should mention Paris"
}
}
def exact_match(predicted, ground_truth):
return predicted.strip().lower() == ground_truth.strip().lower()
accuracy = sum(exact_match(p, gt) for p, gt in zip(predicted, ground_truth)) / len(predicted)
def f1_score(predicted, ground_truth):
pred_tokens = set(predicted.lower().split())
gt_tokens = set(ground_truth.lower().split())
common = pred_tokens & gt_tokens
if len(pred_tokens) == 0 or len(gt_tokens) == 0:
return 0
precision = len(common) / len(pred_tokens)
recall = len(common) / len(gt_tokens)
if precision + recall == 0:
return 0
f1 = 2 * (precision * recall) / (precision + recall)
return f1
from nltk.translate.bleu_score import sentence_bleu
reference = [["Paris", "is", "the", "capital"]]
candidate = ["Paris", "is", "the", "capital"]
bleu = sentence_bleu(reference, candidate)
from sentence_transformers import SentenceTransformer
from sklearn.metrics.pairwise import cosine_similarity
model = SentenceTransformer('all-MiniLM-L6-v2')
emb1 = model.encode("Paris is the capital of France")
emb2 = model.encode("The capital of France is Paris")
similarity = cosine_similarity([emb1], [emb2])[0][0]
# Log user feedback
feedback = {
"question": "What is the capital of France?",
"answer": "Paris",
"user_feedback": "thumbs_up",
"timestamp": "2024-01-15T10:00:00Z"
}
# Add to ground truth if positive
if feedback["user_feedback"] == "thumbs_up":
add_to_ground_truth(feedback["question"], feedback["answer"])
User flags answer as incorrect
→ Human reviews
→ If incorrect, add correct answer to ground truth
→ If correct, keep as is
Monthly: Review 100 flagged examples
Add 50 to ground truth
Update dataset version
Label Studio:
pip install label-studio
label-studio start
# Open http://localhost:8080
Prodigy:
pip install prodigy
prodigy textcat.manual dataset_name source.jsonl --label positive,negative
pip install dvc
dvc init
dvc add dataset.jsonl
git add dataset.jsonl.dvc .gitignore
git commit -m "Add dataset"
dvc push
See "Ground Truth Storage" section
Ground Truth: Correct answers for evaluation
Why:
Types:
Creating:
Quality Control:
Dataset Size:
Maintenance:
Tools:
xiaohongshu
technical spec
product ux expert
database patterns
Conduct multi-agent task orchestration and workflow coordination.
Initialize project with Conductor artifacts (product definition,
Expert in web animations, transitions, and motion design using Framer Motion and CSS
Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...
PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.
Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...
Display project status, active tracks, and next actions
Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgänger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience