Skip to content

Feedback

Every intelligent-search request — streaming or JSON — is persisted, so that:

  • The frontend can submit a thumbs-up/down vote (and optional comment) against a previous search using its uuid.
  • Content editors can review questions, answers, and submitted feedback in the Sulu admin UI.

Failed requests are persisted too (with their upstream error). Failed prompts are often the most valuable signal for tuning ingestion and chunking.

Where the Rows Come From

Every search is persisted by the bundle itself, wherever it was performed: the public endpoint, the CLI command, and any code calling IntelligentSearchClientInterface — an agent tool, for instance. Failed searches are recorded too, with the error message and code.

The client assigns an id to every search it performs and puts it on the result:

$result = $client->search(IntelligentSearchQuery::create($question, $locale));

$result->uuid; // the persisted row and the feedback endpoint below

Running the same query twice produces two searches with two ids. When the id has to be known before the answer exists, as the streaming endpoint needs it for its metadata event, set it on the query instead:

$query = IntelligentSearchQuery::create($question, $locale)->withUuid($uuid);

Persistence happens in a listener on IntelligentSearchCompletedEvent and IntelligentSearchFailedEvent, so an application can listen to the same events for its own purposes. Both events carry the IntelligentSearchQuery they came from, next to the uuid.

Every search is announced. The two switches below only decide whether the bundle writes its feedback row, so a listener of your own keeps seeing searches either way:

# config/packages/sulu_ai_platform.yaml — for the whole application
sulu_ai_platform:
    intelligent_search:
        feedback:
            persist: false
// or per search
$query = IntelligentSearchQuery::create($question, $locale)->withoutPersistence();

The CLI command takes --no-persist for the same reason.

Writing the row cannot fail a search: if the database is unreachable, not migrated, or already holds that id, the failure is logged and the answer is still returned.

Submission Endpoint

Route: POST /api/intelligent-search/feedback

Request body (JSON):

Field Required Description
uuid yes UUID of the search, from the metadata SSE event or the JSON uuid field
feedback yes up or down
comment no Free-text comment, up to max_comment_length characters (2000 by default). Omit it to keep a comment that was submitted earlier; send an empty string to clear it.

Success response (200):

{
  "success": true,
  "message": "Feedback submitted successfully"
}

Errors:

  • 400 — invalid JSON body, missing/empty uuid, missing/invalid feedback, non-string comment, or a comment longer than max_comment_length
  • 404 — no persisted search matches the given uuid
  • 429 — the per-IP rate limit is exceeded (see Searching → Rate Limiting); feedback has its own budget, separate from search
{
  "success": false,
  "error": {
    "message": "Field \"feedback\" must be \"up\" or \"down\"",
    "code": 400
  }
}

Example

await fetch('/api/intelligent-search/feedback', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
        uuid: searchUuid,        // captured from the `metadata` SSE event
        feedback: 'up',          // or 'down'
        comment: 'Exactly what I needed.',
    }),
});

Comment handling

A comment longer than max_comment_length is rejected with 400; the limit defaults to 2000 characters and is configurable:

# config/packages/sulu_ai_platform.yaml
sulu_ai_platform:
    intelligent_search:
        feedback:
            max_comment_length: 2000

Sending a vote without a comment leaves a previously submitted one in place — switching from thumbs-down to thumbs-up does not discard the explanation that came with the down-vote. To remove a comment, send comment: '' explicitly.

Admin UI

A navigation entry appears under the Sulu AI menu, listing every persisted search with its question, answer, sources, and submitted feedback. Records can be reviewed and deleted from the detail view.

Access is gated by the sulu.ai.intelligent_search_feedback security context with VIEW and DELETE permission types — grant these to the roles that should review search interactions.