DEV Community

Cover image for Designing a Meeting Assistant People Actually Want to Use
Rishita Verma
Rishita Verma

Posted on

Designing a Meeting Assistant People Actually Want to Use

The Problem

Most professionals walk into meetings under-prepared — not because they don't care, but because gathering context is genuinely tedious. You scan old email threads, dig through scattered notes across different apps, and mentally reconstruct what you promised someone two weeks ago. After the meeting, the same problem repeats in reverse: you scribble some notes, close the tab, and never look at them again. The next time you meet that person, you're starting from scratch.

MeetMind closes that loop. You get a structured AI briefing before walking in, and you can log what happened in under a minute before you forget it.


What MeetMind Does

MeetMind works in two phases — before the meeting and after it — connected by a memory layer that makes each meeting smarter than the last.

Before the meeting, you type a contact's name and click "Generate my briefing." The app checks its memory for any past notes about that person, sends them along with the contact name to LLaMA 3.3 70B running on Groq, and returns a structured three-part briefing:

  • Summary of Past Interactions — a concise recap of what's happened before
  • Key Reminders — commitments made, preferences noted, things to watch for
  • Suggested Conversation Openers — specific ways to start naturally based on context

If there's no history, a "🌙 No memory yet" badge tells you upfront — the output is a sensible first-meeting guide, not fabricated context.

After the meeting, you return to the same page, write freeform notes in a textarea — decisions made, budget agreed, follow-ups promised — and save them. Those notes become the context for the next briefing with that person. The loop:

Type a name → Generate briefing → Meet → Save notes → Richer briefing next time
Enter fullscreen mode Exit fullscreen mode

A client you've met five times gets a briefing that references their preferences, their constraints, and the follow-up you promised last Tuesday. That's the difference between a tool someone uses once and one they keep open before every call.

The stack is intentionally lightweight: Flask backend, vanilla HTML/CSS/JS frontend, Groq API for inference, and a local JSON file for storage. No database, no login, no setup beyond an API key.


My Role

I owned the entire frontend: landing page, briefing form, notes form, AI response rendering, and every interaction state in between. That meant designing the layout, writing HTML/CSS, wiring JavaScript fetch calls to the Flask API, and making sure loading, success, error, and empty states were all handled clearly. The backend was handled separately — my job was making sure users never had to think about what was happening underneath.


Designing The User Journey

The user journey is two actions: generate before, save after. Everything in the interface is designed to support those two moments and nothing else.

Before the meeting, friction had to be near zero. People check this right before a call, often rushing. The briefing form is one field and one button — no date pickers, no categories, no meeting type dropdowns. After the meeting, the notes form is equally minimal: a name field and a freeform textarea. The placeholder text — Budget discussed, follow-ups, preferences, key decisions... — tells users what to write without any instructional copy.

Both sections live on the same scrollable page, briefing form on top and notes form below. That vertical order is itself an instruction: top to bottom maps directly to before to after. The physical layout communicates the temporal workflow without a word of explanation.


Frontend Architecture

The frontend is a single Flask template (index.html) with no JavaScript framework. Two sections, each with its own form and result container. State is entirely local: on submit, the button is disabled and relabelled "⏳ Generating..." while the fetch fires, then the response is injected into a result div.

The /get_briefing response returns both the briefing text and a has_memory boolean. That flag drives the "🌙 No memory yet" badge — one value from the server, one conditional render on the client. Without that signal, users reading a generic first-meeting briefing would assume something was broken.


UI Decisions That Mattered

Descriptive button labels. "✨ Generate my briefing" and "🗂️ Save to memory" tell the user exactly what each action does. "Submit" technically works but requires a beat of interpretation. At the scale of a two-action app, that beat matters.

The memory status badge. "No memory yet" isn't just informational — it's trust-building. It confirms the system is working, explains why the output is generic, and implicitly tells the user how to improve it: save notes after this meeting.

Inline success confirmation. The save message — "Successfully remembered details for priya!" — renders below the form and stays there. Not a toast that disappears in two seconds. Users can read it and move on knowing the save worked.

Structured AI output. The briefing prompt instructs the model to always follow a numbered three-section format. This was as much a frontend decision as a prompt decision — without predictable structure, rendering the response consistently is impossible, and scanning it in 30 seconds (the real usage context) becomes harder.


Code Walkthrough

The briefing route returns both the text and has_memory in one response — the frontend never needs a second request:

@app.route('/get_briefing', methods=['POST'])
def get_briefing():
    contact = request.json.get('contact', '').strip()
    if not contact:
        return jsonify({"error": "Please enter a valid name."}), 400
    history = hindsight_db.recall(contact)
    briefing_text = generate_meeting_briefing(contact, history)
    return jsonify({"briefing": briefing_text, "has_memory": len(history) > 0})
Enter fullscreen mode Exit fullscreen mode

The AI prompt enforces a fixed output structure — this is the contract between the model and the UI:

user_prompt = f"""
Provide your output exactly in this structure:
1. SUMMARY OF PAST INTERACTION (Keep it short)
2. KEY REMINDERS (Promises made or things to look out for)
3. SUGGESTED CONVERSATION OPENERS
"""
Enter fullscreen mode Exit fullscreen mode

The save route includes the contact name in the confirmation — so users see "Successfully remembered details for priya!" not just "Saved." That specificity matters when logging multiple contacts in one session:

if success:
    return jsonify({"status": f"Successfully remembered details for {contact}!"})
Enter fullscreen mode Exit fullscreen mode

What Didn't Work Initially

My first layout placed both forms side by side in a two-column grid. Equal visual weight for two equally important actions seemed logical. In practice it was confusing — users couldn't tell which to use first. A horizontal layout implies parallel alternatives, not sequential steps. Switching to a vertical single-column stack fixed it without a word of copy. The layout became the instruction.


Challenges

Rendering AI output. The briefing returns as plain text with asterisk bullet points. Parsing it into DOM elements is fragile — the model doesn't always format identically. Using white-space: pre-line CSS to respect line breaks and keeping a fixed readable container width is simpler and far more resilient to output variation.

Loading state. Briefing generation takes 1–2 seconds. Without an indicator, the UI looks frozen. Disabling the button and relabelling it "⏳ Generating..." costs five lines of JavaScript and completely changes perceived responsiveness.


Lessons Learned

  1. Layout communicates sequence. Stack A above B if users do A then B. Horizontal grids imply choice, not order.
  2. Placeholder text is free UX. It sets expectations and removes hesitation without adding instructional copy.
  3. Surface the memory state. When AI output depends on stored data, always show what the system knows. Honest empty states build more trust than confident-sounding generic output.
  4. Every fetch needs three UI states. Loading, success, and error. Missing any one creates a moment users can't interpret — and those moments erode trust fast.
  5. Prompt structure is a frontend concern. A numbered output format in the prompt is part of the rendering contract. Variable AI output format means variable UI — constrain it at the source.

Future Improvements

The biggest gap is mobile — the forms need larger tap targets and better spacing on small screens. There's also no onboarding; first-time users don't immediately understand that notes saved today improve next week's briefing. One sentence above the notes form would close that gap. Longer term, parsing the briefing into section cards with per-section copy buttons would make it easier to reference specific parts mid-meeting.


Conclusion

The most valuable decision on MeetMind was keeping the user flow to two steps. Everything else — the layout order, the button labels, the loading states, the inline confirmations, the memory badge — exists to support those two moments without friction. Interface complexity compounds. Every ambiguous state and silent failure adds cognitive cost. Keep the flow obvious, handle every state, make the output readable.


Try It

Live app: https://meetmind-iatt.onrender.com

Source code: https://github.com/RishitaVerma570/MeetMind

Hindsight memory layer: https://hindsight.vectorize.io

Vectorize agent memory: https://vectorize.io/what-is-agent-memory

Top comments (0)