{"id":2014,"date":"2026-02-18T14:47:54","date_gmt":"2026-02-18T09:17:54","guid":{"rendered":"https:\/\/sharanamshah.com\/mymusings\/?p=2014"},"modified":"2026-02-18T14:48:16","modified_gmt":"2026-02-18T09:18:16","slug":"how-to-extract-structured-meeting-insights-with-python-and-llms","status":"publish","type":"post","link":"https:\/\/sharanamshah.com\/mymusings\/blog\/2026\/02\/18\/how-to-extract-structured-meeting-insights-with-python-and-llms\/","title":{"rendered":"How to Extract Structured Meeting Insights with Python and LLMs"},"content":{"rendered":"<h1>How to extract structured data from transcripts with Python<\/h1>\n<h2>The real-world scenario<\/h2>\n<p>Imagine you are a **Technical Project Manager** or **DevOps Lead** juggling six hours of recorded meetings every day. You have the transcripts, but your project tracker requires structured data: who is doing what, by when, and what was decided. Manually reading through a **5,000-word transcript** to find three action items is like looking for a needle in a haystack while the haystack is growing. This script acts as a digital filter, instantly converting messy human conversation into a **clean JSON object** that you can pipe directly into **Jira**, **Trello**, or a **SQL database**.<\/p>\n<h2>The solution<\/h2>\n<p>We leverage the **OpenAI API** combined with **Pydantic**, a data validation library. By using **Structured Outputs**, we force the LLM to adhere to a specific schema. This eliminates the common problem of the AI returning conversational fluff or incorrectly formatted text. We use **pathlib** for robust file handling to ensure the script works across **Windows**, **macOS**, and **Linux** without path errors.<\/p>\n<h2>Prerequisites<\/h2>\n<ul>\n<li>Python 3.10 or higher installed<\/li>\n<li>An **OpenAI API Key**<\/li>\n<li>Install the required libraries: <code>pip install openai pydantic python-dotenv<\/code><\/li>\n<\/ul>\n<h2>The code<\/h2>\n<pre><code>\n\"\"\"\n-----------------------------------------------------------------------\nAuthors: Sharanam &amp; Vaishali Shah\nRecipe: AI Transcript Structurer\nIntent: Extract validated JSON action items from raw meeting text.\n-----------------------------------------------------------------------\n\"\"\"\nimport json\nfrom pathlib import Path\nfrom typing import List\nfrom pydantic import BaseModel\nfrom openai import OpenAI\n\n# Define the data structure for validation\nclass ActionItem(BaseModel):\n    task: str\n    assignee: str\n    priority: str\n\nclass MeetingSummary(BaseModel):\n    decisions: List[str]\n    action_items: List[ActionItem]\n    sentiment: str\n\ndef extract_insights(file_path: str):\n    # Initialize the client (ensure OPENAI_API_KEY is in your environment)\n    client = OpenAI()\n    \n    # Read the transcript using pathlib\n    target_file = Path(file_path)\n    if not target_file.exists():\n        print(f\"Error: {target_file} not found.\")\n        return\n\n    raw_text = target_file.read_text(encoding='utf-8')\n\n    # Call the LLM with structured output response format\n    completion = client.beta.chat.completions.parse(\n        model='gpt-4o-2024-08-06',\n        messages=[\n            {'role': 'system', 'content': 'Extract structured insights from the meeting transcript.'},\n            {'role': 'user', 'content': raw_text},\n        ],\n        response_format=MeetingSummary,\n    )\n\n    # Parse the validated response\n    insights = completion.choices[0].message.parsed\n    \n    # Save the output as JSON\n    output_file = target_file.with_suffix('.json')\n    with open(output_file, 'w') as f:\n        json.dump(insights.model_dump(), f, indent=4)\n    \n    print(f\"Success! Insights saved to {output_file}\")\n    return insights\n\nif __name__ == '__main__':\n    # Create a dummy transcript for demonstration\n    temp_file = Path('transcript.txt')\n    temp_file.write_text('Dave: We need to fix the login bug by Friday. Sarah, can you handle that? Sarah: Sure. We also decided to switch to Postgres for the database.')\n    \n    # Run the extraction\n    extract_insights('transcript.txt')\n<\/code><\/pre>\n<h2>Code walkthrough<\/h2>\n<p>The script begins by defining a **Pydantic schema**. The **ActionItem** and **MeetingSummary** classes tell Python exactly what fields to expect from the AI. This is the **source of truth** for your data. We then use **Path** from the **pathlib** module to handle the file input, which is more reliable than the older **os.path** approach. <\/p>\n<p>The core logic happens in the **client.beta.chat.completions.parse** method. By passing our **MeetingSummary** class into the **response_format** parameter, we tell the **OpenAI API** to validate the response against our model before it even reaches our script. If the AI tries to skip a field, the request fails, ensuring you never receive **broken JSON**. Finally, we use **model_dump** to convert the Python object back into a standard dictionary and save it as a **formatted JSON file**.<\/p>\n<h2>Sample output<\/h2>\n<p>When you run the script with the sample transcript, the terminal will display a success message, and a new file named **transcript.json** will appear in your directory with this content:<\/p>\n<pre><code>\n{\n    \"decisions\": [\n        \"Switch to Postgres for the database\"\n    ],\n    \"action_items\": [\n        {\n            \"task\": \"Fix the login bug\",\n            \"assignee\": \"Sarah\",\n            \"priority\": \"High\"\n        }\n    ],\n    \"sentiment\": \"Productive\"\n}\n<\/code><\/pre>\n<h2>Conclusion<\/h2>\n<p>Automating meeting summaries is no longer about simple keyword matching. By combining **Python types** with **LLM intelligence**, you create a robust pipeline that understands context and enforces data integrity. This script provides a solid foundation for building larger automation tools, such as auto-filling **Jira tickets** or sending **Slack alerts** based on meeting outcomes. You can now process hundreds of transcripts in seconds, ensuring that no critical decision ever falls through the cracks again.<\/p>\n<hr style=\"margin: 50px 0;border: 0;border-top: 1px solid #eaeaea\">\n<div style=\"background-color: #fcfcfc;padding: 35px;border-left: 5px solid #d63638;border-radius: 6px\">\n<h3 style=\"margin-top: 0;color: #23282d;font-size: 24px;font-weight: 700\">\ud83d\ude80 Don&#8217;t Just Learn AI &amp; LLMs \u2014 Master It.<\/h3>\n<p style=\"font-size: 17px;line-height: 1.6;color: #444;margin-bottom: 20px\">\n            This tutorial was just the tip of the iceberg. To truly advance your career and build <strong>professional-grade systems<\/strong>, you need the full architectural blueprint.\n        <\/p>\n<p style=\"font-size: 17px;line-height: 1.6;color: #444;margin-bottom: 25px\">\n            My book, <strong><a href=\"https:\/\/sharanamshah.com\/mymusings\/home\/books-ive-authored\/large-language-models-crash-course\/\" target=\"_blank\" style=\"color: #d63638;text-decoration: underline\">Large Language Models Crash Course<\/a><\/strong>, takes you from &#8220;making it work&#8221; to &#8220;making it scale.&#8221; I cover advanced patterns, real-world case studies, and the industry best practices that senior engineers use daily.\n        <\/p>\n<p style=\"margin-bottom: 0\">\n            <a href=\"https:\/\/sharanamshah.com\/mymusings\/home\/books-ive-authored\/large-language-models-crash-course\/\" target=\"_blank\" style=\"background-color: #d63638;color: white;padding: 15px 30px;text-decoration: none;border-radius: 5px;font-weight: bold;font-size: 18px;display: inline-block\"><br \/>\n                \ud83d\udcd6 Grab Your Copy Now &rarr;<br \/>\n            <\/a>\n        <\/p>\n<\/p><\/div>\n","protected":false},"excerpt":{"rendered":"<p>Automate meeting transcript analysis using Python and OpenAI. Learn to extract action items and decisions into validated JSON format with Pydantic.<\/p>\n","protected":false},"author":1,"featured_media":1989,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_themeisle_gutenberg_block_has_review":false,"footnotes":""},"categories":[94],"tags":[],"class_list":["post-2014","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-everyday-automation-hacks"],"_links":{"self":[{"href":"https:\/\/sharanamshah.com\/mymusings\/wp-json\/wp\/v2\/posts\/2014","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/sharanamshah.com\/mymusings\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/sharanamshah.com\/mymusings\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/sharanamshah.com\/mymusings\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/sharanamshah.com\/mymusings\/wp-json\/wp\/v2\/comments?post=2014"}],"version-history":[{"count":0,"href":"https:\/\/sharanamshah.com\/mymusings\/wp-json\/wp\/v2\/posts\/2014\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/sharanamshah.com\/mymusings\/wp-json\/wp\/v2\/media\/1989"}],"wp:attachment":[{"href":"https:\/\/sharanamshah.com\/mymusings\/wp-json\/wp\/v2\/media?parent=2014"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/sharanamshah.com\/mymusings\/wp-json\/wp\/v2\/categories?post=2014"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/sharanamshah.com\/mymusings\/wp-json\/wp\/v2\/tags?post=2014"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}