NEWVectors or files. Pick a path.Start →
    Media
    Template v1.0 · updated 2026-09-13

    UX session analysis

    Moderated sessions become a corpus you can question. The transcript is joined to what was on screen at that second, so one query reaches every session at once and returns the moment rather than the recording.

    Ask one question across every session and get every participant's answer, attributed. The query that defines this template is the roll-up: one task, every participant, with each result opening at the second it happened.

    UX research teams running moderated usability sessions

    Studio will also guide you to Three to five sessions from ONE study for zero-setup evaluation, then to a continuous data connection for production.

    7,257 / 319
    word-timed words joined to screen states
    Measured on the reference corpus of six sessions. The join is the product: a word without the screen it was spoken over is a transcript, and a screen state without the word is a screenshot.
    5s
    median scene, cut at screen transitions
    Segmenting on the display changing rather than on a fixed interval is what makes a result land on a behaviour instead of in the middle of one.
    112 / 22 / 9
    claim moments, setting changes, task boundaries
    From the same six sessions: 112 moments where a participant made a claim, 22 setting changes with durations read off the display rather than from a knob sensor, and 9 task boundaries taken from the moderator narrating the protocol.
    2 of 6
    sessions where hand-off-wheel timing works
    61.5% of frames on one rig and 15.9% on another, agreeing with 7 of 9 hand-labelled frames. It fails on the rest because the camera is framed on the display with the wheel out of shot. This is a rig property, not a model property.
    What goes in
    • sessions
      One object per recording, with study_id, participant_id, rig_id and task_id as fields. Those four are what every roll-up groups and filters on, so they are the schema decision that matters.
    What comes out
    • session-scenes
      Segment documents with a 1408-dimension multimodal embedding, timing, transcription, and the on-screen text read off that segment.
    • session-transcripts
      The spoken side as a 1024-dimension text embedding, carrying the study, participant and task through so a filtered question stays filtered.

    How the namespace is wired

    1 buckets, 2 collections, 0 clean views, 2 retrievers. The diagram generates the manifest below; they cannot drift apart.

    SourceBucketCollectionClean viewRetrieverClusterClick a node to inspect it
    syncsearchsearchpipeline inpipeline in

    Reward signals

    How reviewer decisions move the thresholds

    Thresholds at ingest drift as the corpus changes. The reviewers working the queue are the ones who see where a threshold is wrong first, so this template routes their decisions back into the model that set it.

    Nothing in this template writes signals back. A researcher marking a moment as the right one would be the obvious loop and it is not wired.
    Explicit signals

    None. No saves, no flags, no observer marks are recorded by the template as shipped.

    Implicit signals

    None from the application. Mixpeek records retriever executions server-side, which is telemetry about queries rather than feedback about results.

    Where they land
    mxp_retriever_executions

    System collections in your namespace, on the same vector store as the rest of the template. They are yours to query.

    How the loop closes

    The observers' notes are the ground truth this use case is missing: task start and stop, errors, and confidence per participant. That is an input to evaluation rather than a signal the template collects.

    One file spins up the namespace. Generated from the diagram above. Also served at /templates/ux-session-analysis.namespace.yaml.

    # ux-session-analysis: one manifest spins up the namespace.
    # Platform manifest schema (GET /v1/discovery/schema). Validate with POST /v1/manifest/validate,
    # apply with POST /v1/manifest/apply or the Deploy button. Wiring comes from the flow diagram:
    # edges are bucket -> collection sources, collection -> retriever scope, retriever -> view.
    version: '1.0'
    metadata:
      name: ux-session-analysis
      description: "Namespace template ux-session-analysis. Generated from the flow diagram on mixpeek.com/templates/ux-session-analysis."
    namespaces:
      - name: ux-session-analysis
        description: "Everything below lives in this namespace."
        feature_extractors:
          - name: multimodal_extractor
            version: v1
          - name: text_extractor
            version: v1
    
    # Data sources. A storage connection carries credentials, so it is created in Studio
    # (or POST /v1/organizations/storage-connections) and synced into the bucket named here.
    #   clinic-recordings: s3, continuous, moderated session recordings from your rig, in your account -> bucket sessions
    buckets:
      - name: sessions
        namespace: ux-session-analysis
        description: "Fed by clinic-recordings (s3, continuous)."
        schema:
          properties:
            content:
              type: video
    
    # Connect your own storage. Create the two secrets, uncomment, and apply again;
    # apply live-tests the connection, so it must have real credentials to succeed.
    # Until then the buckets above accept direct uploads.
    # storage_connections:
    #   - name: clinic-recordings-connection
    #     provider: s3
    #     description: "Read-only access to the s3 location holding moderated session recordings from your rig, in your account."
    #     config:
    #       region: us-east-1
    #       credentials:
    #         type: access_key
    #         access_key_id: ${{ secrets.AWS_ACCESS_KEY_ID }}
    #         secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
    
    # bucket_syncs:
    #   - name: clinic-recordings-sync
    #     bucket: sessions
    #     connection: clinic-recordings-connection
    #     source_path: "sessions/"
    #     sync_mode: continuous
    #     polling_interval_seconds: 300
    #     skip_duplicates: true
    #     file_filters:
    #       include_patterns: ["*.mp4", "*.mov"]
    #     schema_mapping:
    #       mappings:
    #         content:
    #           target_type: blob
    #           source: {type: file}
    #           blob_type: video
    
    collections:
      - name: session-scenes
        namespace: ux-session-analysis
        description: "multimodal_extractor@v1 over bucket sessions. Feeds session-transcripts, find-the-moment, task-roll-up."
        source:
          type: bucket
          bucket: sessions
        feature_extractor:
          name: multimodal_extractor
          version: v1
          input_mappings:
            video: content
          field_passthrough:
            - source_path: study_id
            - source_path: participant_id
            - source_path: rig_id
            - source_path: task_id
            - source_path: file_location
            - source_path: segment_id
            - source_path: start_time
            - source_path: end_time
        enabled: true
      - name: session-transcripts
        namespace: ux-session-analysis
        description: "text_extractor@v1 over collection session-scenes. Feeds find-the-moment, task-roll-up."
        source:
          type: collection
          collection: session-scenes
        feature_extractor:
          name: text_extractor
          version: v1
          input_mappings:
            text: transcription
          field_passthrough:
            - source_path: study_id
            - source_path: participant_id
            - source_path: task_id
            - source_path: segment_id
            - source_path: video_segment_url
            - source_path: start_time
            - source_path: end_time
        enabled: true
    retrievers:
      - name: find-the-moment
        namespace: ux-session-analysis
        description: "Searches session-scenes, session-transcripts across 2 feature indexes with rrf fusion."
        collections:
          - session-scenes
          - session-transcripts
        input_schema:
          query:
            type: text
            required: true
            description: "What to look for; searched across every index below"
        stages:
          - stage_name: search
            stage_id: feature_search
            parameters:
              searches:
                - feature_uri: "mixpeek://text_extractor@v1/multilingual_e5_large_instruct_v1"
                  query:
                    input_mode: text
                    value: "{{INPUT.query}}"
                  top_k: 25
                - feature_uri: "mixpeek://multimodal_extractor@v1/vertex_multimodal_embedding"
                  query:
                    input_mode: text
                    value: "{{INPUT.query}}"
                  top_k: 25
              fusion: rrf
              final_top_k: 25
        tags:
          - template:ux-session-analysis
      - name: task-roll-up
        namespace: ux-session-analysis
        description: "Searches session-transcripts, session-scenes across 1 feature index. Groups documents by participant_id."
        collections:
          - session-transcripts
          - session-scenes
        input_schema:
          query:
            type: text
            required: true
            description: "What to look for; searched across every index below"
        stages:
          - stage_name: search
            stage_id: feature_search
            parameters:
              searches:
                - feature_uri: "mixpeek://text_extractor@v1/multilingual_e5_large_instruct_v1"
                  query:
                    input_mode: text
                    value: "{{INPUT.query}}"
                  top_k: 200
              fusion: rrf
              final_top_k: 200
          - stage_name: group
            stage_id: group_by
            parameters:
              group_by_field: participant_id
        tags:
          - template:ux-session-analysis