Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Convert voice recordings to structured construction reports. Field workers speak, AI transcribes and formats. Supports daily reports, safety observations, progress updates.
.claude/skills/datadrivenconstruction-voice-to-report/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 186% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 215% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 309% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 154% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 14% | 0% |
Field workers prefer talking over typing. This skill converts voice recordings into structured construction reports using speech-to-text and LLM processing.
| Typing | Voice | |--------|-------| | Slow on mobile | 3x faster | | Requires attention | Hands-free | | Limited in cold/rain | Works anywhere | | Formal language | Natural expression | | Short messages | Detailed descriptions |
┌─────────────────────────────────────────────────────────────────┐
│ VOICE TO REPORT PIPELINE │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 🎤 Voice → 📝 Transcribe → 🤖 Structure → 📊 Report │
│ Recording Whisper API GPT-4o Formatted │
│ │
│ "We finished "We finished { Daily Report │
│ the foundation the foundation "activity": ──────────── │
│ pour today, pour today, "foundation", Foundation │
│ about 500 about 500 "quantity": 500, pour: 500m³ │
│ cubic meters" cubic meters" "unit": "m³" Complete ✓ │
│ } │
└─────────────────────────────────────────────────────────────────┘pythonfrom openai import OpenAI import json client = OpenAI() def voice_to_report(audio_path: str, report_type: str = "daily") -> dict: """Convert voice recording to structured report""" # Step 1: Transcribe audio with open(audio_path, "rb") as audio_file: transcript = client.audio.transcriptions.create( model="whisper-1", file=audio_file, language="en" ) # Step 2: Structure with LLM schema = get_report_schema(report_type) response = client.chat.completions.create( model="gpt-4o", messages=[ { "role": "system", "content": f"""You are a construction report assistant. Convert the voice transcript into a structured report. Extract all relevant information and format as JSON. Report type: {report_type} Schema: {json.dumps(schema, indent=2)} Rules: - Extract quantities with units - Identify activities and locations - Note any issues or concerns - Capture weather if mentioned - List workers/trades if mentioned """ }, { "role": "user", "content": f"Transcript:\n{transcript.text}" } ], response_format={"type": "json_object"} ) return { "transcript": transcript.text, "structured_report": json.loads(response.choices[0].message.content) }
pythondaily_report_schema = { "date": "YYYY-MM-DD", "project": "string", "weather": { "conditions": "string", "temperature": "number", "impact": "none|minor|major" }, "workforce": [ { "trade": "string", "count": "number", "hours": "number" } ], "activities": [ { "description": "string", "location": "string", "quantity": "number", "unit": "string", "status": "in_progress|completed|delayed" } ], "equipment": [ { "type": "string", "hours": "number" } ], "issues": [ { "description": "string", "severity": "low|medium|high", "action_taken": "string" } ], "notes": "string" }
pythonsafety_schema = { "date": "YYYY-MM-DD", "time": "HH:MM", "location": "string", "observer": "string", "observation_type": "positive|concern|incident", "description": "string", "people_involved": ["list of names/roles"], "immediate_action": "string", "follow_up_required": "boolean", "photos_attached": "boolean" }
pythonprogress_schema = { "date": "YYYY-MM-DD", "area": "string", "activity": "string", "planned_quantity": "number", "actual_quantity": "number", "unit": "string", "percent_complete": "number", "on_schedule": "boolean", "variance_reason": "string or null", "next_steps": "string" }
json{ "workflow": "Voice to Report", "nodes": [ { "name": "Telegram Trigger", "type": "Telegram", "event": "voice_message" }, { "name": "Download Voice", "type": "Telegram", "action": "getFile" }, { "name": "Transcribe", "type": "OpenAI", "operation": "transcribe", "model": "whisper-1" }, { "name": "Detect Report Type", "type": "OpenAI", "prompt": "Classify: daily_report, safety, progress, issue" }, { "name": "Structure Report", "type": "OpenAI", "operation": "chat", "model": "gpt-4o" }, { "name": "Save to Database", "type": "PostgreSQL" }, { "name": "Confirm to User", "type": "Telegram", "action": "sendMessage" }, { "name": "Generate PDF", "type": "HTTP Request", "url": "pdf-service/generate" } ] }
pythondef transcribe_multilingual(audio_path: str) -> dict: """Transcribe in any language, output in English""" with open(audio_path, "rb") as audio_file: # Detect language automatically transcript = client.audio.transcriptions.create( model="whisper-1", file=audio_file # language parameter omitted for auto-detection ) # Translate to English if needed if not is_english(transcript.text): translation = client.chat.completions.create( model="gpt-4o", messages=[ {"role": "system", "content": "Translate to English, preserve construction terminology."}, {"role": "user", "content": transcript.text} ] ) english_text = translation.choices[0].message.content else: english_text = transcript.text return { "original": transcript.text, "english": english_text }
python# Example: Flutter/React Native integration # Send voice to API async def upload_voice_report(audio_bytes, project_id): response = await api.post( "/voice-report", files={"audio": audio_bytes}, data={ "project_id": project_id, "report_type": "daily" } ) return response.json() # Response includes: # - transcript # - structured_report # - report_id # - pdf_url (if generated)
python# Use local Whisper for high volume import whisper model = whisper.load_model("base") # or "small", "medium", "large" def transcribe_local(audio_path: str) -> str: """Transcribe locally to save API costs""" result = model.transcribe(audio_path) return result["text"] # Cost comparison (per hour of audio): # - OpenAI Whisper API: $0.36 # - Local Whisper (base): $0 (compute only) # - Local Whisper (large): $0 (compute only, slower)
bashpip install openai whisper python-telegram-bot
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 5,461 | 5,988 | +10% | 1 | 1 | 0% | 1,199 | 3,428 | +186% | 0 | 0 | — |
case-02 | fail→pass | 4,827 | 4,176 | -13% | 1 | 1 | 0% | 962 | 3,030 | +215% | 0 | 0 | — |
case-03 | fail→pass | 3,743 | 5,709 | +53% | 1 | 1 | 0% | 817 | 3,345 | +309% | 0 | 0 | — |
case-04 | pass→pass | 6,535 | 7,985 | +22% | 1 | 1 | 0% | 1,374 | 3,833 | +179% | 0 | 0 | — |
case-10 | fail→pass | 21,164 | 2,426 | -89% | 1 | 1 | 0% | 1,010 | 2,567 | +154% | 0 | 0 | — |
case-05 | pass→pass | 9,746 | 7,653 | -21% | 1 | 1 | 0% | 1,800 | 3,475 | +93% | 0 | 0 | — |
case-06 | pass→pass | 5,842 | 8,534 | +46% | 1 | 1 | 0% | 1,075 | 3,603 | +235% | 0 | 0 | — |
case-07 | pass→pass | 16,579 | 23,221 | +40% | 1 | 1 | 0% | 2,604 | 6,108 | +135% | 0 | 0 | — |
case-08 | fail→fail | 16,154 | 9,714 | -40% | 1 | 1 | 0% | 3,256 | 4,226 | +30% | 0 | 0 | — |
case-09 | fail→pass | 12,022 | 2,661 | -78% | 1 | 1 | 0% | 2,246 | 2,564 | +14% | 0 | 0 | — |
case-11 | fail→pass | 12,843 | 1,626 | -87% | 1 | 1 | 0% | 2,228 | 2,356 | +6% | 0 | 0 | — |
case-12 | fail→pass | 11,983 | 2,689 | -78% | 1 | 1 | 0% | 2,199 | 2,572 | +17% | 0 | 0 | — |
case-13 | pass→pass | 10,499 | 9,970 | -5% | 1 | 1 | 0% | 1,945 | 4,089 | +110% | 0 | 0 | — |
case-14 | fail→pass | 10,056 | 3,694 | -63% | 1 | 1 | 0% | 1,517 | 2,685 | +77% | 0 | 0 | — |
case-15 | pass→pass | 17,033 | 1,816 | -89% | 1 | 1 | 0% | 3,151 | 2,388 | -24% | 0 | 0 | — |
case-16 | pass→pass | 21,633 | 6,277 | -71% | 1 | 1 | 0% | 2,981 | 3,392 | +14% | 0 | 0 | — |
case-17 | fail→fail | 14,759 | 14,242 | -4% | 1 | 1 | 0% | 2,742 | 4,709 | +72% | 0 | 0 | — |
case-18 | fail→pass | 11,078 | 2,089 | -81% | 1 | 1 | 0% | 2,037 | 2,481 | +22% | 0 | 0 | — |
case-19 | pass→pass | 17,468 | 4,910 | -72% | 1 | 1 | 0% | 3,222 | 2,893 | -10% | 0 | 0 | — |
case-20 | fail→fail | 23,708 | 22,756 | -4% | 1 | 1 | 0% | 4,349 | 6,575 | +51% | 0 | 0 | — |
case-21 | fail→fail | 22,562 | 22,956 | +2% | 1 | 1 | 0% | 4,544 | 6,865 | +51% | 0 | 0 | — |
case-22 | fail→fail | 17,497 | 17,835 | +2% | 1 | 1 | 0% | 3,480 | 5,691 | +64% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted, and 21 counted toward the lift figure. The other 1 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +41 percentage points is the difference between those two pass rates over the 21 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.