Meeting Minutes Action Items
Extract action items and generate structured meeting minutes from transcripts
Meeting Minutes Action Items
Extract action items and generate structured meeting minutes from meeting transcripts.
Problem Statement
Who: Project managers, team leads, executive assistants
Why: Manual note-taking misses details. Automated extraction ensures nothing falls through the cracks.
What You’ll Build
A recipe that analyzes meeting transcripts and produces structured minutes with action items.
Input/Output Contract
| Input | Type | Required | Description |
|---|---|---|---|
transcript_text | string | Yes | Meeting transcript |
attendees | array | No | List of attendee names |
format | string | No | Output format: markdown or json |
| Output | Type | Description |
|---|---|---|
minutes | string | Formatted meeting minutes |
action_items | array | Extracted action items with assignees |
ok | boolean | Success indicator |
Prerequisites
export OPENAI_API_KEY=your_key_here
pip install praisonaiagentsStep-by-Step Build
- Create Recipe Directory
bash mkdir -p ~/.praisonai/templates/meeting-minutes-action-items cd ~/.praisonai/templates/meeting-minutes-action-items - Create TEMPLATE.yaml
yaml name: meeting-minutes-action-items version: "1.0.0" description: "Extract action items from meeting transcripts" author: "PraisonAI" license: "MIT" tags: - meetings - productivity - action-items requires: env: - OPENAI_API_KEY packages: - praisonaiagents inputs: transcript_text: type: string description: "Meeting transcript text" required: true attendees: type: array description: "List of attendee names" required: false default: [] format: type: string description: "Output format" required: false default: "markdown" enum: - markdown - json outputs: minutes: type: string description: "Formatted meeting minutes" action_items: type: array description: "List of action items" ok: type: boolean description: "Success indicator" cli: command: "praison recipes run meeting-minutes-action-items" examples: - 'praison recipes run meeting-minutes-action-items --input ''{"transcript_text": "..."}''' safety: dry_run_default: false requires_consent: false overwrites_files: false network_access: true pii_handling: true - Create recipe.py
python # recipe.py import json from praisonaiagents import Agent, Task, AgentTeam def run(input_data: dict, config: dict = None) -> dict: """Extract action items from meeting transcript.""" transcript_text = input_data.get("transcript_text") attendees = input_data.get("attendees", []) output_format = input_data.get("format", "markdown") if not transcript_text: return {"ok": False, "error": {"code": "MISSING_INPUT", "message": "transcript_text is required"}} try: attendees_str = ", ".join(attendees) if attendees else "Not specified" # Create analyzer agent analyzer = Agent( name="Meeting Analyst", role="Meeting Content Specialist", goal="Analyze meeting content and identify key points", instructions=""" You are a meeting analysis expert. - Identify main topics discussed - Note decisions made - Find commitments and deadlines - Track who said what when relevant """, ) # Create action extractor extractor = Agent( name="Action Extractor", role="Action Item Specialist", goal="Extract clear, actionable items", instructions=f""" You are an action item extraction expert. Known attendees: {attendees_str} For each action item, identify: - Task description - Assignee (if mentioned) - Due date (if mentioned) - Priority (if implied) Output as JSON array. """, ) # Create formatter formatter = Agent( name="Minutes Formatter", role="Documentation Specialist", goal=f"Format minutes in {output_format}", instructions=f""" You are a documentation specialist. Format meeting minutes in {output_format} format. Include: - Meeting summary - Key discussion points - Decisions made - Action items section """, ) # Define tasks analyze_task = Task( name="analyze_meeting", description=f"Analyze this meeting transcript:\n\n{transcript_text[:8000]}", expected_output="Meeting analysis with key points", agent=analyzer, ) extract_task = Task( name="extract_actions", description="Extract action items from the meeting analysis", expected_output='JSON array: [{"task": "...", "assignee": "...", "due": "...", "priority": "..."}]', agent=extractor, context=[analyze_task], ) format_task = Task( name="format_minutes", description=f"Format complete meeting minutes in {output_format}", expected_output=f"Formatted meeting minutes in {output_format}", agent=formatter, context=[analyze_task, extract_task], ) # Execute agents = AgentTeam( agents=[analyzer, extractor, formatter], tasks=[analyze_task, extract_task, format_task], ) result = agents.start() # Parse action items actions_text = result.get("extract_actions", "[]") action_items = parse_action_items(actions_text) return { "ok": True, "minutes": result.get("format_minutes", ""), "action_items": action_items, "artifacts": [], "warnings": [], } except Exception as e: return {"ok": False, "error": {"code": "PROCESSING_ERROR", "message": str(e)}} def parse_action_items(text: str) -> list: """Parse action items from agent output.""" import re try: match = re.search(r'\[.*\]', text, re.DOTALL) if match: return json.loads(match.group()) except json.JSONDecodeError: pass # Fallback: parse bullet points items = [] for line in text.split('\n'): line = line.strip() if line.startswith(('-', '*', '•')) and len(line) > 2: items.append({"task": line[1:].strip(), "assignee": None, "due": None}) return items - Create test_recipe.py
python # test_recipe.py import pytest from recipe import run, parse_action_items def test_missing_transcript(): result = run({}) assert result["ok"] is False assert result["error"]["code"] == "MISSING_INPUT" def test_parse_action_items_json(): text = '[{"task": "Review doc", "assignee": "John"}]' items = parse_action_items(text) assert len(items) == 1 assert items[0]["task"] == "Review doc" def test_parse_action_items_bullets(): text = "- Review the document\n- Send email to client" items = parse_action_items(text) assert len(items) == 2 @pytest.mark.integration def test_end_to_end(): transcript = """ John: Let's discuss the Q4 roadmap. Sarah: I think we should prioritize the mobile app. John: Agreed. Sarah, can you draft a proposal by Friday? Sarah: Sure, I'll have it ready. John: Great. Mike, please review the budget. Mike: Will do, I'll send it by Monday. """ result = run({ "transcript_text": transcript, "attendees": ["John", "Sarah", "Mike"], "format": "markdown" }) assert result["ok"] is True assert len(result["action_items"]) > 0
Run Locally
# Basic usage
praison recipes run meeting-minutes-action-items \
--input '{"transcript_text": "Meeting transcript here..."}'
# With attendees
praison recipes run meeting-minutes-action-items \
--input '{"transcript_text": "...", "attendees": ["John", "Sarah"], "format": "json"}'Deploy & Integrate: 6 Integration Models
Model 1: Embedded SDK
from praisonai import recipe
# Load or define transcript
transcript = "Meeting notes: John said we need to finish the project by Friday..."
result = recipe.run(
"meeting-minutes-action-items",
input={
"transcript_text": transcript,
"attendees": ["John", "Sarah", "Mike"]
}
)
if result.ok:
for item in result.output["action_items"]:
print(f"Task: {item['task']}, Assignee: {item['assignee']}")Model 2: CLI Invocation
cat meeting_transcript.txt | praison recipes run meeting-minutes-action-items \
--input '{"transcript_text": "'"$(cat)"'"}' --jsonModel 3: Plugin Mode
class MeetingPlugin:
def extract_actions(self, transcript, attendees=None):
from praisonai import recipe
return recipe.run(
"meeting-minutes-action-items",
input={"transcript_text": transcript, "attendees": attendees or []}
)Model 4: Local HTTP Sidecar
const response = await fetch('http://localhost:8765/recipes/meeting-minutes-action-items/run', {
method: 'POST',
body: JSON.stringify({
transcript_text: transcript,
format: 'json'
})
});Model 5: Remote Managed Runner
response = requests.post(
"https://api.meeting-tools.com/extract-actions",
headers={"Authorization": f"Bearer {api_key}"},
json={"transcript_text": transcript}
)Model 6: Event-Driven
def on_meeting_ended(event):
import queue as q
job_queue = q.Queue() # Replace with SQS/RabbitMQ in production
job_queue.put({
"recipe": "meeting-minutes-action-items",
"input": {
"transcript_text": event['transcript'],
"attendees": event['participants']
},
"callback_url": f"https://api.example.com/meetings/{event['meeting_id']}/minutes"
})Troubleshooting
Missing action items
Ensure the transcript clearly states commitments. Phrases like “I will” or “by Friday” help extraction.
Wrong assignees
Provide the attendees list to help the model match names correctly.
Next Steps
- Prefer starting from an audio file? See Meeting Notes Agent.
- Podcast Transcription Cleaner – Clean up meeting recordings
- Document Summarizer – Summarize meeting documents