Vertesia Documentation

Search & agentic planning

A View's search section chooses how users search. There are two modes: deterministic (the default — keyword search plus structured key terms) and agentic (natural language, translated into a validated Elasticsearch query by a model). Both compose with the View's fixed scope, active navigation, project boundary, and content-security filters.

Numeric ranges, date facets, and typed sorts depend on how the project indexes properties — see Typed property indexing at the end of this page.

Deterministic search is the default. It combines the text query with the View's fixed scope, active navigation selections, structured key terms, configured sort, project boundary, and content-security filters.

The default text query searches content name, description, full text, and properties. Key terms add predictable domain controls, each declaring a concrete field:

  • match performs analyzed text matching;
  • term performs an exact value filter; and
  • range accepts a from..to value, with either bound allowed to be empty.

Use exact property terms for fields such as properties.customer and properties.status, while leaving the main query available for descriptions or order text. Omit the search section entirely for a browse-only experience; the runtime then rejects query and key-term input instead of silently ignoring it.

Agentic query and presentation planning

Set search.mode to agentic when users should express domain intent in natural language. The default sys:ContentSearchAgent interaction converts the request into a supported Elasticsearch query. The server limits the model to built-in fields and the fields disclosed by the View configuration, validates the returned query, then adds the immutable scope and content-security filters.

Agentic Views support two planning modes:

  • query generates only the Elasticsearch query and uses the configured View presentation.
  • query_and_view generates the query plus a safe, ephemeral presentation suited to that question.

A View has one model execution configuration. Set it under search.agentic.config, or omit it to inherit the system interaction and project defaults:

{
  "mode": "agentic",
  "placeholder": "Describe the sales orders you need",
  "fields": [
    { "field": "text", "description": "Full ingested and OCR sales-order text.", "type": "text", "mode": "full_text" },
    { "field": "properties.instructions", "description": "Customer instructions associated with the order.", "type": "text", "mode": "full_text", "boost": 2 },
    { "field": "properties.order_total", "description": "Numeric order total.", "type": "number", "mode": "exact" }
  ],
  "agentic": {
    "interaction": "sys:ContentSearchAgent",
    "config": { "environment": "<ENVIRONMENT_ID>", "model": "<MODEL_ID>" },
    "mode": "query_and_view",
    "instructions": "Treat customer and sold-to state as exact filters. Prefer open orders.",
    "timeout_ms": 12000,
    "minimum_confidence": 0.6,
    "rerank": {
      "max_candidates": 30,
      "include_why_match": true,
      "timeout_ms": 15000
    }
  }
}

query_and_view uses one model call and the same model configuration as query-only planning. Alongside the validated query, the model may choose a title, description, browse/worklist layout, up to six property facets, one built-in list/table/cards/gallery/board display, and up to three sort clauses. For example, “compare orders over $10,000” can produce a numeric comparison table, while “browse product photos by brand” can produce a gallery with a brand facet.

The generated presentation is returned as the execution response's definition; it is not persisted and the existing ViewExperience component renders it without a second API or component. The model cannot change the View's scope, search configuration, actions, selection, upload/drop behavior, or app-contributed renderers. Folder and collection navigation, active selections, and those configured behaviors remain server-owned and are merged into the ephemeral definition.

Agentic result reranking

Add search.agentic.rerank when Elasticsearch should retrieve the candidates and the model should make a bounded second pass over the results. Reranking is independent of mode: it works with both query and query_and_view and uses the same search.agentic.config model. By default it calls sys:ContentSearchReranker; set interaction inside rerank only when the project provides a purpose-built reranking interaction.

The server sends at most max_candidates authorized hits from the current result page (default 30, maximum 50), with only fields from the View's validated field catalog. The interaction must return every candidate ID exactly once. Zeno rejects invented, omitted, or duplicate IDs, so reranking can change order but cannot change the candidate set, scope, total count, facets, or canonical content. Hits beyond the bounded prefix retain their Elasticsearch order.

When include_why_match is true, the response may include a grounded explanation under hit.annotation.why_match; the built-in result layouts display it automatically. The execution response also reports search.rerank.status as applied, fallback, or skipped and includes the candidate count. Any applied configured sort—including an explicit user sort or a generated query_and_view sort—skips reranking. Query-planning fallback also skips it. A timeout, interaction failure, or invalid permutation returns the original Elasticsearch order with an agentic_rerank_failed warning.

Reranking applies to each requested page independently. It is intended to improve findability inside a strong, bounded Elasticsearch candidate set, not to produce one global model-ranked order across every result page.

search.fields is the semantic field catalog the model plans against. The active Elasticsearch mapping remains authoritative: a field configured as full_text participates in deterministic fallback only when it is actually mapped as text. An explicit field list replaces the default lexical fields and fails closed when none is actively mapped as text.

When a natural-language plan contains scoring text clauses, results use Elasticsearch relevance unless the request explicitly selects a configured sort. Browse and structured-only plans continue to use results.default_sort.

If query planning times out, fails validation, or does not meet minimum_confidence, the runtime falls back to deterministic search over the View's mapped text fields and returns an explicit warning. If only the generated presentation is invalid, the validated agent query still runs with the configured presentation and an agentic_presentation_failed warning. The fallback never applies fuzzy queries outside the validated field catalog. When no explicit search.fields list is configured, the broad lexical default searches name, description, full document text, and properties.*.

Typed property indexing

By default, indexed property strings are keyword fields — exact-match and aggregatable, which is what property facets and term key terms need. But a range facet, a date facet, or a numeric sort needs the field to be mapped with a typed Elasticsearch type. This is the most common reason a range or date control appears to do nothing.

Two rules govern how properties are typed:

  • JSON numbers and booleans are inferred as native numeric/boolean types automatically, so store numbers as JSON numbers (not strings) if you want ranges and numeric sorts to work.
  • Strings map to keyword. Date-looking strings stay keywords too — JSON has no date scalar — so a date facet or date sort requires an explicit date mapping for that path.

Declare explicit types in the project's indexing.property_mappings, keyed by dotted property path:

{
  "indexing": {
    "property_mappings": {
      "order_total": { "type": "double", "ignore_malformed": true },
      "delivery_date": { "type": "date", "format": "strict_date_optional_time||epoch_millis", "ignore_malformed": true },
      "customer_code": { "type": "keyword", "ignore_above": 256 }
    }
  }
}

Supported type values are keyword, text, boolean, long, double, and date. ignore_malformed lets a single bad value be skipped instead of rejecting the whole document; date accepts a format; keyword accepts ignore_above.

New indexes store property leaves with subobjects: false, so a scalar path such as properties.bill_to can coexist with a more specific path such as properties.bill_to.name, even when source documents use nested JSON objects. The same leaf should still have a consistent scalar type across documents.

Was this page helpful?