Introducing Feedback and Feedback Analytics

Capture human feedback for every agent response and query them by agent, source, or time.

Paul ScanlonPaul Scanlon·

Aug 4, 2026

·

4 min read

You can now capture human feedback — thumbs, ratings, comments, or corrections — on any agent response. Tag each record by source (user, SME, or qa) so you can visualize, filter and compare later.

Every feedback record can be anchored to a threadId, traceId, or spanId. Reviewers can see agent responses in the context of a conversation and grade each one accordingly.

Feedback records can be queried per agent — average rating, thumb split, or comment volume. Compare user ratings versus QA ratings, and open the trace in Studio to inspect the model calls, tool calls, and outputs behind any feedback record.

Before feedback, ratings and reviewer comments lived in external storage with no link back to the trace that produced them. Now they sit alongside spans, metrics, and logs in the same observability store, and can be forwarded to PostHog, Braintrust, Arize, and other supported exporters.

Get started

Install Mastra observability and and storage backends for memory and observability:

GNU BashTerminal
npm install @mastra/core @mastra/observability @mastra/libsql @mastra/duckdb
note

Requires @mastra/core@1.18.0 or later, added in PR #14842.

Feedback lives in the observability domain. The example below routes the observability domain to DuckDB with default storage on LibSQL using a MastraCompositeStore:

TypeScriptsrc/mastra/index.ts
import { Mastra } from "@mastra/core/mastra";
import { MastraCompositeStore } from "@mastra/core/storage";
import { LibSQLStore } from "@mastra/libsql";
import { DuckDBStore } from "@mastra/duckdb";
import { Observability, MastraStorageExporter } from "@mastra/observability";
 
export const mastra = new Mastra({
  agents: {
    /* ... */
  },
  storage: new MastraCompositeStore({
    id: "composite-storage",
    default: new LibSQLStore({ url: "file:./mastra.db" }),
    domains: {
      observability: await new DuckDBStore().getStore("observability")
    }
  }),
  observability: new Observability({
    configs: {
      default: {
        serviceName: "mastra-feedback",
        exporters: [new MastraStorageExporter()]
      }
    }
  })
});

Capturing feedback

Feedback records are available via mastra.observability, which publishes events through Mastra's observability pipeline: your span processors run, the record is enriched with context pulled from the trace, and every exporter you've configured receives the record.

import { mastra } from "../src/mastra";
 
if (!mastra.observability.addFeedback) {
  throw new Error("Feedback not supported.");
}
 
await mastra.observability.addFeedback({
  traceId,
  feedback: {
    feedbackType: "rating",
    value: 4,
    feedbackSource: "qa",
    comment: "Solid answer, would ship as-is.",
    metadata: { messageId }
  }
});

List feedback

Read raw feedback records back from the store. Filter by any anchor or context field — threadId, traceId, spanId, feedbackType, feedbackSource, entityName — and paginate.

const { feedback } = await observability!.listFeedback({
  filters: { threadId },
  pagination: { perPage: 100 }
});

Query analytics

Numeric feedback (rating, or thumbs coded as 1/-1) queries through the same OLAP surface as metrics. Aggregate with an optional period-over-period comparison:

const avg = await observability!.getFeedbackAggregate({
  feedbackType: "rating",
  aggregation: "avg",
  comparePeriod: "previous_week"
});
// → { value: 4.2, previousValue: 3.8, changePercent: 10.5 }

Break down by any dimension — feedbackSource, entityName, environment:

const bySource = await observability!.getFeedbackBreakdown({
  feedbackType: "rating",
  groupBy: ["feedbackSource"],
  aggregation: "avg"
});
// → { groups: [{ dimensions: { feedbackSource: "admin" }, value: 4.1 }, ...] }

Bucket by interval for time series:

const overTime = await observability!.getFeedbackTimeSeries({
  feedbackType: "rating",
  interval: "1h",
  aggregation: "avg"
});
// → { series: [{ name: "rating", points: [{ timestamp, value }, ...] }] }

Same shape for getFeedbackPercentiles({ percentiles: [0.5, 0.95], interval: "1d" }).

From the browser

Every method above (excluding addFeedback) is also available using the Mastra client SDK:

import { MastraClient } from "@mastra/client-js";
 
export const client = new MastraClient({ baseUrl: "http://localhost:4111" });
 
const { feedback } = await client.listFeedback({
  filters: { threadId },
  pagination: { perPage: 100 }
});

For more information and full configuration options, see:

Share:
Paul Scanlon
Paul ScanlonTechnical Product Marketing Manager

Paul Scanlon sits between Developer Education and Product Marketing at Mastra. Previously, he was a Technical Product Marketing Manager at Neon and worked in Developer Relations at Gatsby, where he created educational content and developer experiences.

All articles by Paul Scanlon