From aicp-feedback-ocr
Reads scanned AICP Workshop Feedback PDFs (physical forms photographed/scanned into PDF),
How this skill is triggered — by the user, by Claude, or both
Slash command
/aicp-feedback-ocr:aicp-feedback-ocrThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Reads scanned AICP Workshop Feedback PDFs (physical forms photographed/scanned into PDF),
Reads scanned AICP Workshop Feedback PDFs (physical forms photographed/scanned into PDF),
extracts all fields visually, and writes a formatted .xlsx file that matches the Medflix
tracking sheet layout (columns A–O).
Doctor handwriting is notoriously difficult to read. Names are the most error-prone field. Always ask the user for the attendee list from the event register/sign-in sheet before reading any forms.
Prompt the user:
"Before I start processing the feedback forms, please paste or type the list of doctors who attended this session (from your event register / attendance sheet). One name per line is fine. This ensures names are matched accurately and not misread from handwriting."
Store this as attendance_list — a list of cleaned name strings.
This list is used in Step 3 to fuzzy-match every extracted name against known attendees.
If user says "I don't have the list":
[VERIFY] prefix in the output (e.g.
[VERIFY] Dr. Mahi Chitree) so the user knows to manually check each one.Name matching rules (Step 3 detail):
attendance_list using token-sort ratio
(handles word-order differences like "Paresh Almani Dr." vs "Dr. Paresh Almani").[LOW CONF] flag.[VERIFY].scripts/match_names.py.3 interactions total. Do not proceed to Step 2 until all 6 values are confirmed. ⚠️ Lists can change. If a user's value isn't in the widget, accept whatever they type.
ask_user_input_v0(questions=[
{
"question": "Company",
"type": "single_select",
"options": ["Sun Pharma", "Lupin", "Cadila", "Alkem", "Other (I'll type below)"]
},
{
"question": "Specialty",
"type": "single_select",
"options": ["Cardiology", "Diabetology", "Urology", "Neurology",
"Dermatology", "ENT", "Endocrinology", "Orthopaedics", "Other (I'll type below)"]
}
])
If either selection is "Other (I'll type below)" — note which fields need typing and collect them in the next plain-text step.
Ask in one message:
"Please type: City, Date (DD-MON-YY format, e.g. 17-May-26) [and Company / Specialty if you selected Other above]"
User replies in one chat message, e.g.: Pune, 17-May-26
ask_user_input_v0(questions=[
{
"question": "Day",
"type": "single_select",
"options": ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
},
{
"question": "Faculty",
"type": "single_select",
"options": ["Amey", "Swathy", "Megh", "Nitya", "Rohan",
"Natasha", "Dipannita", "Vedika", "Vrushali", "Nisha", "Other (I'll type below)"]
}
])
If Faculty is "Other (I'll type below)" — ask for the name in plain text before proceeding.
Store all confirmed values as batch_meta:
batch_meta = {
"Company": <value>,
"Specialty": <value>,
"City": <value>,
"Date": <value>, ← DD-MON-YY format e.g. 17-May-26
"Day": <value>, ← e.g. Sun
"Faculty": <value>
}
The forms are scanned physical documents. Do NOT attempt pdftotext — use vision.
⚠️ ORDER GUARANTEE: The Excel row order must exactly match the PDF page order. Page 1 of the PDF → Row 1 (Sr. No. 1) in the sheet. Page 2 → Row 2. No exceptions. Process pages strictly in ascending order; never reorder.
Run the rasterization script (see scripts/rasterize.sh) or inline bash:
# Install deps if needed
pip install pypdf openpyxl rapidfuzz --break-system-packages -q
# Rasterize every page of every uploaded PDF at 300 DPI
for PDF in <uploaded_pdf_paths>; do
BASENAME=$(basename "$PDF" .pdf)
pdftoppm -jpeg -r 300 "$PDF" "/tmp/pages_${BASENAME}"
done
# List all generated page images in STRICT ASCENDING ORDER
ls /tmp/pages_*.jpg | sort
Then for each page image in order (page-01 first, then page-02, etc.):
view the image file (Claude reads it visually)Page limit note: AICP feedback forms are visually sparse (mostly white space, a few circled numbers, short handwritten lines), so they cost fewer tokens than dense documents. Practical limits:
| Batch size | Behaviour |
|---|---|
| 1–30 pages | Process fully — works reliably every time |
| 31–50 pages | Process fully — works most of the time; accuracy may dip slightly on later forms if doctors wrote a lot in Q4/Q5 |
| 50+ pages | Warn the user and split: process first 40, ask them to continue in a new chat thread with remaining pages |
If total pages exceed 50, inform the user:
"This batch has forms. I'll process the first 40 in this session. Start a new chat thread for the remaining forms — no need to upload the Excel again, just append manually or paste the new rows."
For each form page, extract these fields in page order:
| Column | Field | Extraction Notes |
|---|---|---|
| A | Sr. No. | Auto-increment (1, 2, 3… or continue from previous) |
| B | Company | From batch_meta |
| C | Specialty | From batch_meta |
| D | City | From batch_meta |
| E | Date | From batch_meta |
| F | Day | From batch_meta |
| G | Faculty | From batch_meta |
| H | Name | Read raw from form → fuzzy-match against attendance_list (see Step 0.5) |
| I | Overall Experience \nRating (1-10) | Circled/checked number |
| J | Speaker Rating\n (1-10) | Circled/checked number |
| K | Pace | One of: Too slow / Slow / Appropriate / Fast / Too fast |
| L | Difficulty | One of: Too simple / Simple / Perfect / Complex / Too complex |
| M | Relevance | One of: Very Irrelevant / Irrelevant / Okay / Relevant / Very Relevant |
| N | Interested in attending\nAdvanced Workshop? | Yes / No |
| O | What did you like \nabout the workshop? | Free text from Q4 box |
| P | How can we make the workshop \nbetter or more useful? | Free text from Q5 box |
Name reading strategy (for terrible doctor handwriting):
scripts/match_names.py to fuzzy-match against attendance_list.Other reading tips:
"") if empty.| Confidence | Situation | What to write |
|---|---|---|
| Clean | You can read it clearly, even if messy | Transcribe as-is, no flag |
[LOW CONF] | You can make out most words but 1–2 are genuinely uncertain | Write your best read + append [LOW CONF] |
[VERIFY] | More than half the text is unreadable even with vocabulary hints | Write whatever you can make out + append [VERIFY] |
Examples:
Hands on training, very informativeGood content, more time needed [LOW CONF]Prctcl appl... [VERIFY]Never leave Q4/Q5 completely blank if there is any ink at all — always write the best-effort read with the appropriate flag. A partial transcription is always more useful than nothing.
Use the script at scripts/build_excel.py. It:
.xlsx file/mnt/user-data/outputs/aicp_feedback_<timestamp>.xlsxRun it inline or call the script. See full code in scripts/build_excel.py.
After writing the Excel:
present_files with the output path."?" (illegible fields)| Situation | Action |
|---|---|
| Blank form page (no name, no marks) | Skip the row, log "blank page skipped" |
| Name only, ratings blank | Include row with blank rating cells |
| Multi-page form (unusual) | Treat as one respondent; merge fields |
| PDF is text-extractable (digital) | Still rasterize — form checkboxes need vision |
| Phone number written in name field | Redact — write "Dr. [name]" if recoverable, else "?" |
A 2025 benchmark study found that zero-shot LLMs vastly outperform classical OCR/HTR methods on handwritten text, especially for names. TrOCR and Tesseract returned no meaningful output on difficult handwriting without domain fine-tuning, while Claude correctly read names that other tools failed on entirely. For messy doctor handwriting, Claude's vision is the right primary tool. The attendance list fuzzy-match is the safety net on top of that.
scripts/build_excel.py — Full openpyxl script to write output xlsxscripts/match_names.py — Fuzzy-name matcher using rapidfuzzscripts/rasterize.sh — Converts PDF pages to JPEG imagesreferences/dropdowns.md — Full dropdown option lists (update here when lists change)Two situations — treat them differently:
Situation A — Genuine ambiguity (mark drawn between two circles, no clear winner): Apply the positive bias — pick the higher/more positive option. e.g. slash drawn between 8 and 9 → take 9. Mark between "Relevant" and "Very Relevant" → take "Very Relevant".
Situation B — Clear mark with pen trail (one circle is clearly filled/ticked, pen just happens to physically extend past it into the next circle): Do NOT apply the bias. Read the clearly marked circle, ignore the trail. e.g. circle on 9 with pen stroke trailing into 10 → read as 9.
How to tell the difference:
MCQ option examples (positive bias for genuine ambiguity only): Pace: ambiguous between "Appropriate" and "Fast" → pick "Appropriate". Difficulty: ambiguous between "Perfect" and "Complex" → pick "Perfect". Relevance: ambiguous between "Relevant" and "Very Relevant" → pick "Very Relevant".
When decoding difficult handwriting in the freetext boxes, use the vocabulary below as a disambiguation guide — built from 2,356 real AICP feedback responses. If a word is ambiguous between two readings and one of them matches something here, prefer the match.
Q4 — What did you like (most common words):
informative, relevant, useful, practical, interactive, comprehensive, excellent,
presentation, content, simple, clarity, simplicity, concepts, introduction, awareness,
explained, understanding, knowledge, learning, experience, hands-on, tools, workshop,
session, speaker, faculty, clinical, research, academic, social media, writing,
summarizing, diagnosis, treatment, everything, good, great, nice, perfect, crisp,
clear, lucid
Q4 — Common full phrases (written as-is by doctors):
very informative, very useful, very relevant, very practical, very interactive,
very good, very helpful, very well explained, very clear,
AI tools, AI apps, use of AI, about AI, AI in practice, AI in clinical,
how to use AI, how to write prompts, which AI to use, AI simplified,
hands on, hands on training, lot to learn, good content, good presentation,
good workshop, introduction to AI, eye opener, eye opening,
day to day practice, clinical practice, new things, new tools,
different AI, various AI tools, prompt engineering, prompts, prompt writing,
how to make prompts
Q5 — How to improve (most common words):
more, time, interactive, hands-on, practical, elaborate, detailed, specific,
sessions, workshops, demonstration, demo, slow, fast, pace, slides, recording,
online, zoom, access, materials, practice, clinical, topics, focus,
long, short, duration, break, interval, repeat, continue, keep up
Q5 — Common full phrases:
more time, more hands-on, more interactive, more practical, more sessions,
more elaborate, more time needed, more demonstrations, more demo,
more specific, more detailed, hands on training, keep it up,
too long, too fast, slow down, full day, share slides, soft copy,
step by step, case studies, real examples, topic wise, specialty wise,
clinically focused, daily practice, patient care,
online session, zoom recording, screen sharing, more workshops, repeat session
AI tool names doctors commonly write:
ChatGPT, NotebookLM, Perplexity, Gemini, Copilot, Microsoft Copilot,
Google Gemini, Claude, Grok, Midjourney, Canva AI, AI Khan, Medflix
When a scrawled word is close to anything in these lists, resolve towards it. Examples: "Compreehnsve" → "Comprehensive", "Nootbook lm" → "NotebookLM", "Prectcl appl... [LOW CONF]" → "Practical appl... [LOW CONF]", "Hends on" → "Hands on".
Searches MemPalace before answering questions about past work, people, projects, or prior decisions. Returns verbatim stored content instead of guessing from model memory.
Guides Payload CMS config (payload.config.ts), collections, fields, hooks, access control, APIs. Debugs validation errors, security, relationships, queries, transactions, hook behavior.
Implements vector databases with Pinecone, Weaviate, Qdrant, Milvus, pgvector for semantic search, RAG, recommendations, and similarity systems. Optimizes embeddings, indexing, and hybrid search.
npx claudepluginhub neelb28/aicp-marketplace --plugin aicp-feedback-ocr