Elizon Docs
Workflows

Knowledge Search

Find documents in a knowledge base by metadata, name or a query, and attach them.

Add a Knowledge Search node (knowledge_search) to find documents in one of your knowledge bases as an ordinary workflow step — no AI Agent in between. Each document comes back with its source, its metadata and, if you ask for it, a copy of the original file that Send Email or AI Prompt can attach.

This is the node for "a customer asked for a copy of invoice INV-2026-0142": look the invoice up by its number, attach it to the reply.

Configuration

FieldTypeDefaultDescription
knowledgeBaseIdstring''The knowledge base to search, from this workflow's project.
knowledgeBaseNamestring''Its name, kept for display on the node card.
querystring''Optional. Text or an expression. Empty means an exact lookup.
metadataFilterslist of { field, value, valueMode }[]Each row compares one of the knowledge base's declared metadata fields with a value, Fixed or an expression. All rows must match (AND).
documentName{ operator: equals | contains, value }noneOptional. Matches the document's filename, case-insensitive.
maxResultsnumber10How many documents to return, 1–50.
includeFilesbooleanfalseAdds each document's original as a file you can attach. See Files.

At least one of query, a metadata filter or a document name must be set; a node with none of them refuses to run and says so. The same goes for a run in which every one of them resolved to empty text — searching by nothing would list the whole knowledge base, which is never what was meant.

The field dropdown lists the fields the knowledge base declares (under Knowledge → Advanced → Metadata fields), so you never type a field name. A value is compared as the field's own type, the same way it was stored when the document was uploaded: 42 and "42" both find a document whose int field is 42.

Without a query, the node is an exact database lookup. It returns the documents whose metadata equals every filter and whose name matches, ordered by filename. There is no embedding call and no score, so the same input always returns the same documents in the same order — the right tool for "find INV-2026-0142".

With a query, the knowledge base's own search runs (vector, keyword or hybrid, whatever the knowledge base is set to), with the metadata filters applied. The best-matching passages are grouped by document, so maxResults counts documents rather than passages, and each document keeps its best score and the passage that earned it.

Only documents that have finished indexing are returned. A query search can also return web pages the knowledge base crawled; they have no metadata of their own and no original file, and their source is their URL.

Output

PathValue
documentsThe documents found, in order.
countHow many there are — 0 when nothing matched. Branch on it with an If/Else.
documents.0.documentNameThe first document's filename (or a crawled page's title).
documents.0.documentIdIts id in the knowledge base.
documents.0.sourceThe citation: { kind: 'file', name }, or { kind: 'url', name, url } for a crawled page.
documents.0.metadataIts metadata, e.g. documents.0.metadata.invoice_number.
documents.0.scoreIts best score, 0–1. Empty on an exact lookup.
documents.0.excerptThe passage that scored best. Empty on an exact lookup.
documents.0.fileIts original as an attachable file when Include files is on, otherwise empty.

Files

With Include files on, each document's original is copied into the workflow's own files, so it is a file like any other on the canvas: Send Email's Attachments and AI Prompt's attachments take {{ upstream.<id>.output.documents.0.file }} unchanged. A retried step reuses the copy it already made rather than making another.

A file is skipped rather than failing the step — file is empty and fileSkippedReason says why:

  • too_large — over 20 MB, the most Send Email can attach to one message;
  • unavailable — the original is no longer in storage.

A crawled web page has no original, so its file is always empty, with no reason.

Worked example: send an invoice copy on request

A customer writes "please send me a copy of INV-2026-0142". The workflow reads the invoice number out of the subject, looks it up in the Invoices knowledge base by its invoice_number metadata field, and replies with the invoice attached.

  1. An Email Trigger on your accounts-receivable mailbox, filtered to subjects containing copy.
  2. A Code node that pulls the invoice number out of the subject.
  3. Knowledge Search on Invoices, with one filter — invoice_number = {{ upstream.invoice-number.output.result.invoice }} — no query, and Include files on.
  4. Send Email back to the sender, attaching {{ upstream.find-invoice.output.documents.0.file }}.

Put an If/Else on {{ upstream.find-invoice.output.count }} before step 4 if you want a different reply when the invoice is not found.

workflows/invoice-copy.yaml
name: Invoice copy on request
description: Replies to "copy of INV-…" emails with the invoice attached.
graph:
  nodes:
    - id: request
      type: trigger_email
      position: { x: 0, y: 0 }
      data:
        filterUnseen: true
        filterSubjectContains: copy
        downloadAttachments: false
        postAction: markRead
        pollMinutes: 5
    - id: invoice-number
      type: code
      position: { x: 280, y: 0 }
      data:
        language: python
        inputExpression: '{{ trigger.subject }}'
        fileExpressions: []
        timeoutSeconds: 10
        code: |
          import re

          def main(input, files):
              match = re.search(r"INV-\d{4}-\d+", input or "")
              return {"invoice": match.group(0) if match else ""}
    - id: find-invoice
      type: knowledge_search
      position: { x: 560, y: 0 }
      data:
        knowledgeBase: knowledgeBases.invoices
        knowledgeBaseName: Invoices
        query: ''
        metadataFilters:
          - id: by-number
            field: invoice_number
            value: '{{ upstream.invoice-number.output.result.invoice }}'
            valueMode: expression
        maxResults: 1
        includeFiles: true
    - id: reply
      type: send_email
      position: { x: 840, y: 0 }
      data:
        to: '{{ trigger.from.address }}'
        subject: 'Re: {{ trigger.subject }}'
        body: 'Please find {{ upstream.find-invoice.output.documents.0.documentName }} attached.'
        bodyFormat: text
        attachments: ['{{ upstream.find-invoice.output.documents.0.file }}']
  edges:
    - { id: request-to-number, source: request, target: invoice-number }
    - { id: number-to-find, source: invoice-number, target: find-invoice }
    - { id: find-to-reply, source: find-invoice, target: reply }

Choosing the knowledge base

The picker lists the knowledge bases of this workflow's project, and the search runs in that project: a knowledge base of another project or organization is simply not found. A workflow that belongs to no project cannot use this node, because knowledge bases are always project-scoped. A knowledge base that is still being set up, or failed to, refuses with its status.

In a Package

The knowledge base reference travels, exactly as the Knowledge Base document trigger's does: it is converted to the knowledge base the Package itself declares (knowledgeBases.<name>), and the installed node searches the Package's own knowledge base. A node pointing at a knowledge base the Package does not declare, or at none, is refused at build time. Because a knowledge base lives in a project, a Package with this node installs into a project.

On the node:

  • Travels: knowledgeBaseName, query, metadataFilters, documentName, maxResults, includeFiles
  • Converted: knowledgeBaseId → knowledgeBase (a knowledgeBases.<name> reference)
  • Never carried: none