The challenge
Orchestrator-core is an open-source framework for managing product lifecycles and workflows for network infrastructure. It was built by and for the NREN community: National Research and Education Networks like SURF, which runs it to manage subscriptions, services, and configurations across more than 100 Dutch educational and research institutions, and GÉANT, the pan-European backbone that connects them all.
The problem was search. Orchestrator-core's domain models are user-defined: operators assemble products from reusable product blocks, which can nest arbitrarily. A single subscription can contain hundreds of attributes spread across deeply nested structures. The schema isn't fixed. It evolves whenever someone defines a new product type.
Traditional search doesn't work here. Elasticsearch needs a known schema. Static column indexes can't track a structure that doesn't exist until runtime. Simple LIKE queries don't understand meaning. For operators managing thousands of subscriptions, finding anything required clicking through nested pages by hand.
The collaboration
SURF initiated the project and secured funding through the GÉANT GN5-2 Incubator program. Tim Fröhlich designed and built the core search system. Virge contributed the AI agent layer: the type-safe query DSL, the PydanticAI integration, and the three-phase agent architecture that makes natural language search production-safe.
Small team. Six months. Shipped in orchestrator-core v5.0 under Apache 2.0.
The solution
Everything runs on PostgreSQL. No Elasticsearch cluster, no separate vector database, no extra infrastructure to operate or scale.
The foundation is Entity-Attribute-Value indexing with hierarchical paths. Instead of mapping dynamic schemas to fixed columns, each attribute gets its own index row: a path (stored with PostgreSQL's ltree extension), a typed value, and an optional embedding vector. A field like product.interface.speed becomes a traversable path. New product types are searchable the moment they're defined. No migrations, no reindexing.
Three retrievers run in parallel on top of that index:
- pgvector: semantic search. Finds results by meaning, not just matching text.
- pg_trgm: fuzzy text matching. Catches typos, partial matches, and exact ID searches that semantic search would miss.
- Structured filters: typed predicates on dates, enums, booleans, and UUIDs.
Reciprocal Rank Fusion merges all three into a single ranking. Each retriever contributes its own ordering; RRF combines them without score normalization. The result: a search that handles "amsterdam network" and "sub-550e8400" equally well.
Search architecture PostgreSQL-native · no external services
EAV index ltree · typed values · embeddings
🧠 pgvector semantic similarity
🔍 pg_trgm fuzzy text
🏷 filters typed predicates
RRF Reciprocal Rank Fusion
Ranked results cursor-paginated
Safe for AI agents
Raw SQL generation by an LLM is a bad idea. One hallucinated DROP TABLE and the day is ruined. We built a Pydantic-based query DSL that compiles to validated SQL. The agent never touches query strings directly.
The same Pydantic models that define the DSL serve as typed tool parameters for PydanticAI. The agent fills in a structured form; the system compiles and validates it before anything reaches the database. The agent can construct filters, sorting, aggregations, and pagination, all without generating a single line of SQL.
The agent workflow runs in three phases:
Agent WorkflowDiscover · Build · Execute
1
AgentDiscoverQueries valid indexed paths matching user intent. Surfaces missing fields. Handles ambiguity before building the query.
2
AgentBuildConstructs a typed query object: filters, sorting, aggregations. Structurally valid by construction. No SQL generated.
3
SystemExecuteCompiles the query to validated SQL and runs it. Returns a stable cursor for pagination, even if the data changes mid-session.
This means an operator can ask "show me all 10Gbps interfaces in Amsterdam provisioned last month" and get back accurate, paginated results. No raw query involved.
The outcome
Hybrid search shipped in orchestrator-core v5.0, Apache 2.0. Any organization running orchestrator-core gets it out of the box. No configuration, no extra infrastructure. SURF's operators can now search across thousands of subscriptions and workflows in seconds.
Incremental indexing uses SHA-256 content hashing to skip re-embedding unchanged fields, so the index stays current without full rebuilds. Keyset pagination embeds a (score, entity_id, query_id) cursor so result sets stay stable even when the underlying data changes mid-session. The EAV approach scales to millions of indexed attributes across deeply nested schemas.
For the full technical write-up (EAV implementation, retriever routing, keyset pagination, and agent architecture), see Tim Fröhlich's post: Building a Schema-Agnostic Hybrid Search System in PostgreSQL ↗
Since then
At the third Workflow Orchestrator user meeting (TNC26 Helsinki, June 2026), the community reported on the first year of this system in the wild. It has a name now: AI Search. Version 5.0 made it the default and deprecated the old Postgres full-text search. Every organization upgrading to 5.0 runs it, whether they use the AI features or not; connecting an LLM for semantic search stays optional and off by default.
The retriever set grew to five: structured filters, fuzzy trigram matching, semantic embeddings, and two hybrid combinations, all on the same EAV index described above.
The search engine also became the foundation for orchestrator-agent, a natural-language agent that answers plain-language questions about subscriptions, products, workflows, and processes. It reuses this search engine directly and exposes it over MCP, A2A, and AG-UI. The WFO community now counts "AI & agents" among the five core themes of its ecosystem.