Skip to content

Customer feedback analysis

The problem

The quarterly survey closes with 4,000 free-text responses. The NPS number goes in a slide. The comments — the part that actually says why — get skimmed by one product manager on a Thursday and then never opened again.

What it costs today:

  • The expensive part goes unused. You paid for the qualitative data and shipped only the quantitative summary.
  • Whoever skims decides. Themes are whatever the reader happened to notice in the first 200 comments.
  • No trend. Because last quarter was analysed differently, nothing is comparable.

The same problem shows up as app store reviews, support satisfaction comments, churn-reason free text and sales-lost notes.

What good looks like

Every comment is categorised the same way, every quarter. The output is a count per theme with sentiment — and the representative quotes, because a product team will act on "shipping delays, 312 mentions, here are eleven of them" and will not act on a bar chart.

What you build

Task  (the export as an attachment)


 [Parse Responses]  Node.js → array of { id, score, comment, segment }


 [Themes]  define-variable  themes = []      ← accumulator, before the loop


 [For Each response]

      ├──▶ [Classify]  Smart Agent → { theme, sentiment, severity, quote }

      ├──▶ [Track Event]  feedback_categorised   dims: theme, sentiment, segment

      └──▶ [Add Item to Array]  themes ← the classified record


 [Synthesise]  Smart Agent over the whole set
      │           → headline themes, what changed, recommended actions

 [Build Report]  markdown → PDF


 [Publish File]

Pieces used: a Task trigger, Execute Node.js, Define Variable, Each, a Smart Agent, Track Event, Add Item to Array, Markdown to PDF, Publish File.

Implementation

1. Fix the taxonomy before you write a step

This is the whole build. If the categories change every quarter, nothing is comparable and you have automated a one-off.

Agree a closed list — eight to fifteen themes — and put it somewhere the flow reads rather than somewhere a prompt hard-codes it. Two good options:

  • a Skill, if you also want the definitions and edge cases written down, or
  • Flow Memory at project scope, so the list is editable without touching the flow.

Include an explicit other theme. Without one, the model forces everything into the nearest category and you never discover the theme you did not anticipate.

2. Parse the export

An Execute Node.js step turns the attached CSV or JSON into _context: { responses: [...] }, dropping blanks and normalising the score field.

Filter out empty comments here. Classifying 1,200 blank strings costs real money and adds nothing.

3. Create the accumulator before the loop

Define Variable: themes, type array, empty.

The rule that makes this necessary: inside a loop, a body step's own output holds only the last iteration. Anything you want after the loop has to be a variable created before it.

4. Classify one comment at a time

Inside an Each over responses — array variable written bare, as responses, not as a template.

A Smart Agent with the theme list in its prompt or skill:

text
Classify this customer comment against the theme list.

Score:   {{loop.item.score}}
Segment: {{loop.item.segment}}
Comment: {{loop.item.comment}}

Pick exactly one theme. If nothing fits, use "other" — do not stretch a
theme to fit. Extract the single most representative sentence as the quote,
verbatim, without editing it.

Structured output:

json
{
  "type": "object",
  "properties": {
    "theme":     { "type": "string" },
    "sentiment": { "type": "string", "enum": ["positive", "neutral", "negative"] },
    "severity":  { "type": "string", "enum": ["low", "medium", "high"] },
    "quote":     { "type": "string" },
    "actionable":{ "type": "boolean" }
  },
  "required": ["theme", "sentiment"]
}

"Verbatim, without editing it" matters. A paraphrased quote is worthless as evidence, and models paraphrase by default.

Then two more steps in the loop body:

  • Track Eventfeedback_categorised, with theme, sentiment and segment as dimensions and count = 1 as the measure. This is what makes the quarter-over-quarter trend free.
  • Add Item to Array — push the classified record onto themes.

If a Classifier step's fixed categories fit your taxonomy and you do not need quotes, it is cheaper than an agent. Use the agent when you want the quote and the severity too.

5. Synthesise over the whole set

After the loop, one more Smart Agent — this time reading the accumulated array, not one comment:

text
Here is every categorised response: {{themes}}

Write the quarterly summary:
- The three themes that matter most, by volume AND severity, not volume alone
- For each, two verbatim quotes
- What changed versus the previous summary
- Three recommended actions, each naming which theme it addresses

Do not invent counts. Use only what is in the data.

Then Markdown to PDF and Publish File.

What happens at run time

The task runs through the set. Because each comment is its own step iteration, the trace shows exactly how any individual response was classified — which is what you open when someone disputes a theme count.

The finished task carries the PDF under Output files, and the categorised records are in the run context.

What you get

  • A quantified picture with evidence. Counts per theme, with the quotes that justify them.
  • Comparability. The same taxonomy every quarter, so the trend is real rather than an artefact of who read the comments.
  • A live breakdown in the Reports tab — theme by segment, sentiment over time — without building a dashboard.
  • A discovery channel. A rising other count is the signal that your taxonomy has stopped describing reality.

Extensions

Run it continuously instead of quarterly. Swap the Task trigger for an API trigger and classify each response as it arrives. The Reports tab becomes a live view, and a spike in high-severity negatives becomes something you notice in a week rather than a quarter.

Route the urgent ones. Add a Condition on severity === 'high' and create a follow-up task for the account owner — see ticket triage for the approval pattern.

Compare against last time. Store the previous summary in Flow Memory so the synthesis step can say what actually changed instead of describing this quarter in isolation.

Cover more sources. App store reviews, support CSAT comments and churn reasons all fit the same loop — only the parse step changes.

Pitfalls

A taxonomy that drifts. Change the theme list and you have broken every comparison. Version it deliberately, and say so in the report when you do.

No other bucket. Forces false precision and hides emerging problems.

Paraphrased quotes. They read fine and prove nothing. Insist on verbatim.

Volume as the only ranking. Eleven customers describing data loss matter more than 300 describing a colour scheme. That is why the schema carries severity.

One giant prompt. Pasting 4,000 comments into a single call produces a fluent summary with invented counts. Classify individually, synthesise over the results.

Studio · Pulse — Cognipeer product documentation