Spaces:
Sleeping
Sleeping
File size: 10,220 Bytes
0a78fb1 0e39d80 0a78fb1 0e39d80 0a78fb1 0e39d80 0a78fb1 0e39d80 0a78fb1 0e39d80 0a78fb1 0e39d80 0a78fb1 0e39d80 0a78fb1 0e39d80 0a78fb1 0e39d80 0a78fb1 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 | """Per-document-type Gemini prompts β hyper-granular, zero-prose JSON output.
Design:
- Each doc type has visual layout, genuine markers, and specific forgery tells
- Output is STRICT JSON only β no markdown, no prose, no explanations
- max_output_tokens=400 covers all fields + forgery + confidence scores
- Field keys are fixed β Gemini must not invent new keys
- Forgery scoring is calibrated with real-world examples
"""
# ββ Exact field keys per doc type βββββββββββββββββββββββββββββββββββββββββ
FIELD_SCHEMAS: dict[str, list[str]] = {
"aadhaar": [
"name", "date_of_birth", "gender",
"aadhaar_number", # output as XXXX XXXX last4 β never full 12
"address", "pincode", "state",
],
"pan": [
"name", "father_name", "date_of_birth",
"pan_number", # format: AAAAA9999A (5 letters, 4 digits, 1 letter)
],
"caste": [
"person_name", "category", # SC / ST / OBC / EWS / General
"caste_name", "certificate_number",
"issuing_authority", "issue_date",
"state", "district",
],
"experience": [
"employee_name", "company_name", "designation",
"joining_date", "relieving_date", "employment_duration",
"hr_email", # company email (used for verification)
],
"education": [
"institute_name", "student_name", "degree", "branch",
"roll_number", "passing_year", "percentage_or_grade",
],
"resume": [
"candidate_name", "email", "phone",
"skills", # top 5, comma-separated
"experience_years", "current_company", "highest_qualification",
],
"general": ["document_type", "key_info"],
}
# ββ Per-doc layout + forgery knowledge base βββββββββββββββββββββββββββββββ
_DOC_KNOWLEDGE: dict[str, str] = {
"aadhaar": (
"UIDAI Aadhaar Card issued by Government of India. "
"LAYOUT: Blue/white gradient card. Top: UIDAI logo (left) + 'ΰ€ΰ€Ύΰ€°ΰ€€ ΰ€Έΰ€°ΰ€ΰ€Ύΰ€° / Government of India' (center). "
"Body: holder photo (left), name, DOB, gender, address (right). Bottom: 12-digit Aadhaar number in groups of 4 (e.g. 1234 5678 9012). QR code bottom-right corner. "
"GENUINE MARKERS: UIDAI text is embossed/crisp, font is uniform Noto Sans across all fields, QR matches the data, holographic strip visible in physical photos. "
"FORGERY TELLS (be specific): (1) Aadhaar number region has different JPEG compression block boundaries than surrounding card β visible as slight blur or color banding around digits. "
"(2) Name or DOB text has slightly different font weight/spacing compared to other text on same card. "
"(3) Photo has different image quality/compression than the rest of the card. "
"(4) Address text is typed in different font or has inconsistent line spacing. "
"(5) Background gradient is interrupted or shows color seam near text fields. "
"(6) QR code is missing, partially obscured, or clearly copy-pasted. "
"IMPORTANT: Low image quality, watermarks, and lighting reflections are NOT forgery β score 5-15 for these."
),
"pan": (
"PAN Card issued by Income Tax Department, Government of India. "
"LAYOUT: Cream/white background, blue header strip. Ashoka Lion Emblem top-left. "
"'ΰ€ΰ€―ΰ€ΰ€° ΰ€΅ΰ€Ώΰ€ΰ€Ύΰ€ / Income Tax Department' and 'Govt. of India' in header. "
"Holder photo right side. Name, Father's Name, DOB in center. PAN number bottom-center (format: AAAAA9999A). "
"Signature strip at bottom. "
"GENUINE MARKERS: PAN format exactly AAAAA9999A (5 caps, 4 digits, 1 cap), Ashoka emblem clear, holographic strip. "
"FORGERY TELLS: (1) PAN number format wrong (e.g. all digits, wrong length). "
"(2) Holder photo has different resolution/compression than card background. "
"(3) Name/Father name area has cut-paste boundary (visible pixel seam). "
"(4) Ashoka emblem is blurry while text around it is sharp. "
"(5) Header text font differs from body text font. "
"IMPORTANT: Printed PAN cards scanned at low DPI look grainy β that is NOT forgery."
),
"caste": (
"Indian State Government Caste/Community Certificate. "
"LAYOUT: Official government letterhead with state emblem top-center. "
"Certificate number top-right. Body: applicant name, parent name, caste, sub-caste, category (SC/ST/OBC/EWS/General), village/district. "
"Bottom: Tehsildar/SDM/District Collector designation, official rubber stamp, handwritten signature, date. "
"GENUINE MARKERS: Government rubber stamp impression (slightly blurry by nature), handwritten signature, official letterhead, unique certificate number. "
"FORGERY TELLS: (1) Stamp is too perfect/sharp β real stamps are imperfect impressions. "
"(2) Certificate number appears to be typed over different background. "
"(3) Official title and date in different fonts. "
"(4) Category field (SC/ST/OBC) appears added on top of existing text. "
"IMPORTANT: Poor scan quality, skewed paper, and worn stamps on real documents score 0-15."
),
"experience": (
"Company Experience/Relieving Letter on official letterhead. "
"LAYOUT: Company logo + name top. Date top-right. 'To Whom It May Concern' or addressee. "
"Body: employee name, designation, joining date, relieving date, employment duration, sometimes CTC. "
"Footer: HR Manager name, designation, company seal (optional), signature. "
"GENUINE MARKERS: Consistent company letterhead throughout, professional language, company email/website footer. "
"FORGERY TELLS: (1) Company logo appears at different DPI/compression than letterhead text. "
"(2) Employee name or date appears in different font/size from surrounding text. "
"(3) Joining/relieving dates are inconsistent (relieving before joining, future dates). "
"(4) Signature or seal appears digitally inserted (uniform white box around it). "
"(5) Company name in header differs from company name in body text. "
"IMPORTANT: Digital PDFs with consistent fonts are likely genuine β score 0-15."
),
"education": (
"University/Board Degree Certificate or Marksheet. "
"LAYOUT: University/Board name + seal top-center. Student photo (for degrees) or absent (marksheets). "
"Enrollment/Roll number. Programme/Branch. Examination year/passing year. Marks/Grade/Percentage. "
"Registrar/Controller signature + official seal bottom. "
"GENUINE MARKERS: University seal is complex (hard to replicate cleanly), embossed or raised seal. "
"FORGERY TELLS: (1) Percentage/grade appears in different font or color from surrounding marks. "
"(2) University seal is blurry while surrounding text is sharp (opposite of genuine β real seals are slightly blurry). "
"(3) Roll number or year has different background color (text was inserted). "
"(4) Student name appears corrected or overwritten. "
"IMPORTANT: Old paper documents scanned in poor quality are not forgeries β score 0-20."
),
"resume": (
"Resume/CV β user-created document, no forgery scoring needed. "
"Extract key professional information only. "
"IMPORTANT: Score forgery=0 always for resumes."
),
"general": (
"Unknown document type. First identify what kind of document this is, then extract all visible key-value information. "
"Check if it appears to be an official government document or a private/company document."
),
}
def build_prompt(doc_type: str) -> str:
"""Build a hyper-granular, zero-prose prompt for this document type.
Target token budget for RESPONSE: ~300 tokens max.
The model must return ONLY valid JSON with no wrapping or prose.
"""
keys = FIELD_SCHEMAS.get(doc_type, FIELD_SCHEMAS["general"])
knowledge = _DOC_KNOWLEDGE.get(doc_type, _DOC_KNOWLEDGE["general"])
# Build field template β null by default, Gemini fills in what it sees
fields_template = "{" + ", ".join(f'"{k}": null' for k in keys) + "}"
# Forgery section differs for resume (never forged)
if doc_type == "resume":
forgery_template = '{"score": 0, "reason": "n/a"}'
forgery_rules = ""
else:
forgery_template = '{"score": 0-100, "reason": "max 15 words, specific evidence only"}'
forgery_rules = (
"FORGERY SCORE: 0-15=genuine document; 16-35=quality issues only (NOT forgery); "
"36-60=suspicious (specific evidence required); 61-100=likely forged (clear evidence only). "
"Only flag as forged if you see SPECIFIC visual evidence listed above. "
"Low image quality, compression artifacts, and photo angle are NOT forgery. "
)
prompt = (
f"You are a forensic HR document analyst. Analyze this image of an Indian {doc_type} document.\n"
f"\n"
f"DOCUMENT KNOWLEDGE:\n{knowledge}\n"
f"\n"
f"OUTPUT RULES (CRITICAL):\n"
f"1. Return ONLY valid JSON β zero markdown, zero prose, zero explanation\n"
f"2. Use EXACTLY these keys, no additions: {list(keys)}\n"
f"3. For missing/unclear fields use null\n"
f"4. aadhaar_number: output as 'XXXX XXXX <last4>' to protect privacy\n"
f"5. pan_number: output full value (needed for format validation)\n"
f"6. Dates: preserve exact format shown on document (e.g. '15/03/1990' or '15 Mar 1990')\n"
f"\n"
f"{forgery_rules}"
f"ai_confidence = your certainty that extracted fields are correct (0=nothing readable, 100=all fields clear and certain).\n"
f"\n"
f"RETURN THIS EXACT JSON STRUCTURE (fill in values):\n"
f'{{"fields": {fields_template}, '
f'"forgery": {forgery_template}, '
f'"ai_confidence": {{"score": 0-100, "reason": "max 10 words"}}}}'
)
return prompt
|