Improve OCR checkbox detection prompts to reduce false positives
Strengthened system prompts and Zod schema descriptions in both ai-ocr.ts and ollama.ts with explicit checkbox rules: only mark as checked when a visible X/checkmark/fill is inside the box, treat empty boxes as unchecked, and err on the side of unchecked when uncertain. Clarified the ambiguous nextStep field to distinguish the two checkbox options from the section heading. Made-with: Cursor
This commit is contained in:
parent
8c84b2a0ac
commit
cf418bd197
2 changed files with 47 additions and 15 deletions
|
|
@ -59,19 +59,21 @@ const responseCardSchema = z.object({
|
||||||
|
|
||||||
const surveySchema = z.object({
|
const surveySchema = z.object({
|
||||||
messageTopics: z.array(z.string()).describe(
|
messageTopics: z.array(z.string()).describe(
|
||||||
"Checked topics from: Stress, Marriage, Revival, Addiction, Parenting, Miracles, Forgiveness, Finances, My Identity, Conflict Resolution, The Holy Spirit, Understanding The Bible, Spiritual Warfare, Sharing My Faith, Anxiety, Heaven, Spiritual Gifts"
|
"ONLY items whose checkbox is physically marked (X, checkmark, or filled). Options: Stress, Marriage, Revival, Addiction, Parenting, Miracles, Forgiveness, Finances, My Identity, Conflict Resolution, The Holy Spirit, Understanding The Bible, Spiritual Warfare, Sharing My Faith, Anxiety, Heaven, Spiritual Gifts. Return empty array if none are marked."
|
||||||
),
|
),
|
||||||
messageTopicsOther: z.string().nullable().describe("Value if Other is filled in"),
|
messageTopicsOther: z.string().nullable().describe("Value if Other is filled in"),
|
||||||
nextStep: z.array(z.string()).describe("Checked items from: Baptism, Next Steps"),
|
nextStep: z.array(z.string()).describe(
|
||||||
|
"This question has exactly 2 checkboxes. Include ONLY the ones with a visible mark (X, checkmark, filled). 'Baptism' = the checkbox for 'Expressing my faith in Jesus / baptized'. 'Next Steps' = the checkbox for 'Learning more about becoming a partner / attend Next Steps'. Return empty array if neither checkbox is marked."
|
||||||
|
),
|
||||||
attendanceDuration: z.string().nullable().describe(
|
attendanceDuration: z.string().nullable().describe(
|
||||||
"Less than 6 months, 6 Months - 1 Year, 1-3 Years, 4-6 Years, or 7+ Years"
|
"The single checked radio option. One of: Less than 6 months, 6 Months - 1 Year, 1-3 Years, 4-6 Years, or 7+ Years. null if none is marked."
|
||||||
),
|
),
|
||||||
campusPreference: z.array(z.string()).describe(
|
campusPreference: z.array(z.string()).describe(
|
||||||
"Checked locations from: Beulah, Pace/Milton, Gulf Breeze, Warrington"
|
"ONLY locations whose checkbox is physically marked. Options: Beulah, Pace/Milton, Gulf Breeze, Warrington. Return empty array if none are marked."
|
||||||
),
|
),
|
||||||
campusPreferenceOther: z.string().nullable().describe("Value if Other is filled in"),
|
campusPreferenceOther: z.string().nullable().describe("Value if Other is filled in"),
|
||||||
howHeard: z.array(z.string()).describe(
|
howHeard: z.array(z.string()).describe(
|
||||||
"Checked items from: This is my church home, Regular Attender, Drove by, Social Media, Google, Personal Invite"
|
"ONLY items whose checkbox is physically marked. Options: This is my church home, Regular Attender, Drove by, Social Media, Google, Personal Invite. Return empty array if none are marked."
|
||||||
),
|
),
|
||||||
howHeardOther: z.string().nullable().describe("Value if Other is filled in"),
|
howHeardOther: z.string().nullable().describe("Value if Other is filled in"),
|
||||||
serviceAttended: z.string().nullable().describe("Service letter: A, B, C, or D"),
|
serviceAttended: z.string().nullable().describe("Service letter: A, B, C, or D"),
|
||||||
|
|
@ -80,15 +82,26 @@ const surveySchema = z.object({
|
||||||
|
|
||||||
const RESPONSE_SYSTEM_PROMPT = `You are analyzing a scanned church response card. This is the PERSONAL INFORMATION side.
|
const RESPONSE_SYSTEM_PROMPT = `You are analyzing a scanned church response card. This is the PERSONAL INFORMATION side.
|
||||||
|
|
||||||
Extract ALL of the following fields from the image. For checkboxes, determine if they are checked or unchecked.
|
Extract ALL of the following fields from the image.
|
||||||
For handwritten text, read it as accurately as possible.
|
|
||||||
|
|
||||||
|
CHECKBOX RULES - be very strict:
|
||||||
|
- A checkbox is CHECKED only if it has a visible mark inside it: an X, a checkmark, a filled square, or pen/pencil marks inside the box.
|
||||||
|
- An EMPTY box (no marks inside) means UNCHECKED, even if text appears next to it.
|
||||||
|
- When in doubt, treat a checkbox as UNCHECKED.
|
||||||
|
|
||||||
|
For handwritten text, read it as accurately as possible.
|
||||||
Be precise: return null for fields you cannot read. Set prayerForTeam and prayerConfidential to false if the checkboxes are not clearly marked.`;
|
Be precise: return null for fields you cannot read. Set prayerForTeam and prayerConfidential to false if the checkboxes are not clearly marked.`;
|
||||||
|
|
||||||
const SURVEY_SYSTEM_PROMPT = `You are analyzing a scanned church Easter survey form. This is the SURVEY side.
|
const SURVEY_SYSTEM_PROMPT = `You are analyzing a scanned church Easter survey form. This is the SURVEY side.
|
||||||
|
|
||||||
Extract ALL of the following fields. For checkboxes, determine if they are checked (filled/marked) or unchecked (empty).
|
Extract ALL of the following fields from the image.
|
||||||
Return empty arrays for checkbox groups where nothing is checked.
|
|
||||||
|
CHECKBOX RULES - be very strict:
|
||||||
|
- A checkbox is CHECKED only if it has a visible mark inside it: an X, a checkmark, a filled square, or pen/pencil marks inside the box.
|
||||||
|
- An EMPTY box (no marks inside) means UNCHECKED, even if text appears next to it.
|
||||||
|
- When in doubt, treat a checkbox as UNCHECKED.
|
||||||
|
- For array fields (messageTopics, nextStep, campusPreference, howHeard): ONLY include items whose checkbox is physically marked. Return empty arrays when no checkboxes in that group are marked.
|
||||||
|
- Do NOT confuse section headings or question titles with checked answers.
|
||||||
|
|
||||||
Be precise: return null for fields you cannot read.`;
|
Be precise: return null for fields you cannot read.`;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,15 @@ async function getModel() {
|
||||||
|
|
||||||
const RESPONSE_CARD_PROMPT = `You are analyzing a scanned church response card. This is the PERSONAL INFORMATION side.
|
const RESPONSE_CARD_PROMPT = `You are analyzing a scanned church response card. This is the PERSONAL INFORMATION side.
|
||||||
|
|
||||||
Extract ALL of the following fields from the image. For checkboxes, determine if they are checked or unchecked.
|
Extract ALL of the following fields from the image.
|
||||||
|
|
||||||
|
CHECKBOX RULES - be very strict:
|
||||||
|
- A checkbox is CHECKED only if it has a visible mark inside it: an X, a checkmark, a filled square, or pen/pencil marks inside the box.
|
||||||
|
- An EMPTY box (no marks inside) means UNCHECKED, even if text appears next to it.
|
||||||
|
- When in doubt, treat a checkbox as UNCHECKED.
|
||||||
|
|
||||||
For handwritten text, read it as accurately as possible.
|
For handwritten text, read it as accurately as possible.
|
||||||
|
Set prayerForTeam and prayerConfidential to false if the checkboxes are not clearly marked.
|
||||||
|
|
||||||
Return ONLY valid JSON with this exact structure (no markdown, no code fences):
|
Return ONLY valid JSON with this exact structure (no markdown, no code fences):
|
||||||
{
|
{
|
||||||
|
|
@ -48,17 +55,29 @@ Return ONLY valid JSON with this exact structure (no markdown, no code fences):
|
||||||
|
|
||||||
const SURVEY_PROMPT = `You are analyzing a scanned church Easter survey form. This is the SURVEY side.
|
const SURVEY_PROMPT = `You are analyzing a scanned church Easter survey form. This is the SURVEY side.
|
||||||
|
|
||||||
Extract ALL of the following fields. For checkboxes, determine if they are checked (filled/marked) or unchecked (empty).
|
Extract ALL of the following fields.
|
||||||
|
|
||||||
|
CHECKBOX RULES - be very strict:
|
||||||
|
- A checkbox is CHECKED only if it has a visible mark inside it: an X, a checkmark, a filled square, or pen/pencil marks inside the box.
|
||||||
|
- An EMPTY box (no marks inside) means UNCHECKED, even if text appears next to it.
|
||||||
|
- When in doubt, treat a checkbox as UNCHECKED.
|
||||||
|
- For array fields: ONLY include items whose checkbox is physically marked. Return empty arrays when no checkboxes in that group are marked.
|
||||||
|
- Do NOT confuse section headings or question titles with checked answers.
|
||||||
|
|
||||||
|
IMPORTANT for nextStep: There are exactly 2 checkboxes.
|
||||||
|
- "Baptism" = the checkbox for "Expressing my faith in Jesus / I want to be baptized..."
|
||||||
|
- "Next Steps" = the checkbox for "Learning more about becoming a partner / attend Next Steps..."
|
||||||
|
Only include the one(s) whose box is physically marked.
|
||||||
|
|
||||||
Return ONLY valid JSON with this exact structure (no markdown, no code fences):
|
Return ONLY valid JSON with this exact structure (no markdown, no code fences):
|
||||||
{
|
{
|
||||||
"messageTopics": ["array of checked topics from: Stress, Marriage, Revival, Addiction, Parenting, Miracles, Forgiveness, Finances, My Identity, Conflict Resolution, The Holy Spirit, Understanding The Bible, Spiritual Warfare, Sharing My Faith, Anxiety, Heaven, Spiritual Gifts"],
|
"messageTopics": ["ONLY topics whose checkbox is marked, from: Stress, Marriage, Revival, Addiction, Parenting, Miracles, Forgiveness, Finances, My Identity, Conflict Resolution, The Holy Spirit, Understanding The Bible, Spiritual Warfare, Sharing My Faith, Anxiety, Heaven, Spiritual Gifts"],
|
||||||
"messageTopicsOther": "string if Other is filled in, else null",
|
"messageTopicsOther": "string if Other is filled in, else null",
|
||||||
"nextStep": ["array of checked items from: Baptism, Next Steps"],
|
"nextStep": ["ONLY items whose checkbox is marked, from: Baptism, Next Steps"],
|
||||||
"attendanceDuration": "Less than 6 months" or "6 Months - 1 Year" or "1-3 Years" or "4-6 Years" or "7+ Years" or null,
|
"attendanceDuration": "Less than 6 months" or "6 Months - 1 Year" or "1-3 Years" or "4-6 Years" or "7+ Years" or null,
|
||||||
"campusPreference": ["array of checked locations from: Beulah, Pace/Milton, Gulf Breeze, Warrington"],
|
"campusPreference": ["ONLY locations whose checkbox is marked, from: Beulah, Pace/Milton, Gulf Breeze, Warrington"],
|
||||||
"campusPreferenceOther": "string if Other is filled in, else null",
|
"campusPreferenceOther": "string if Other is filled in, else null",
|
||||||
"howHeard": ["array of checked items from: This is my church home, Regular Attender, Drove by, Social Media, Google, Personal Invite"],
|
"howHeard": ["ONLY items whose checkbox is marked, from: This is my church home, Regular Attender, Drove by, Social Media, Google, Personal Invite"],
|
||||||
"howHeardOther": "string if Other is filled in, else null",
|
"howHeardOther": "string if Other is filled in, else null",
|
||||||
"serviceAttended": "A" or "B" or "C" or "D" or null,
|
"serviceAttended": "A" or "B" or "C" or "D" or null,
|
||||||
"confidence": 0-100
|
"confidence": 0-100
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue