Skip to content

Commit 6fdcc4b

Browse files
committed
improvement(search): rank across sources by query terms only on the Search page
The Search page's result list keeps the cross-source relevance order (#9134). MCP and assistant searches return to fusion order, which interleaves sources: an end-to-end client benchmark found no gain from relevance order there, and the client searched less.
1 parent ae6ac39 commit 6fdcc4b

4 files changed

Lines changed: 21 additions & 13 deletions

File tree

‎apps/sim/app/api/knowledge/search/route.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export const POST = defineInternalJsonRoute({
1717
'Provider limits apply independently to each user grant; requests have bounded fanout and deadlines',
1818
}),
1919
errorPolicy: internalKnowledgeErrorPolicies.search,
20-
mapInput: ({ body }, { request }) => ({ ...body, signal: request.signal }),
20+
mapInput: ({ body }, { request }) => ({ ...body, rankByRelevance: true, signal: request.signal }),
2121
useCase: searchLiveKnowledge,
2222
present: (data) => ({ success: true as const, data }),
2323
})

‎apps/sim/lib/sim-search/live/README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ The Search assistant exposes the dedicated search/read tools. It cannot discover
1616

1717
1. Authorize the acting member against the canonical organization/workspace and resolve that member's current provider grant.
1818
2. Load the integration's current mode and availability. In service mode, independently load and resolve the configured source credential within that organization.
19-
3. Search the member's provider API or personal Coda MCP. Push resource restrictions into the provider query where supported to improve recall. Native queries may narrow these restrictions, but cannot override the independent checks. One call may send up to four native queries per account (`MAX_NATIVE_QUERIES_PER_ACCOUNT`), such as alternative phrasings (Slack keyword search does not honor OR) or several GitHub/GitLab kinds. The account opens one session whose request budget is one search's budget per query, shared by its queries, and each query reports its own status and cursor under its `queryIndex`. Results from every query and account merge by reciprocal rank fusion, so a document several queries return ranks above one found once. Fusion scores only compare ranks within one account, so a plain query orders the merged results by the query terms each result's title and preview hold, then the terms its title holds, then how much of its title the terms make up; fusion breaks the remaining ties. Date-sorted, queryless and native-query searches keep fusion order.
19+
3. Search the member's provider API or personal Coda MCP. Push resource restrictions into the provider query where supported to improve recall. Native queries may narrow these restrictions, but cannot override the independent checks. One call may send up to four native queries per account (`MAX_NATIVE_QUERIES_PER_ACCOUNT`), such as alternative phrasings (Slack keyword search does not honor OR) or several GitHub/GitLab kinds. The account opens one session whose request budget is one search's budget per query, shared by its queries, and each query reports its own status and cursor under its `queryIndex`. Results from every query and account merge by reciprocal rank fusion, so a document several queries return ranks above one found once. Fusion scores only compare ranks within one account, so the Search page's result list orders a plain query's merged results by the query terms each result's title and preview hold, then the terms its title holds, then how much of its title the terms make up; fusion breaks the remaining ties. MCP and assistant searches keep fusion order, which interleaves sources: an end-to-end client benchmark found no gain from relevance order there, and the client searched less. Date-sorted, queryless and native-query searches keep fusion order everywhere.
2020
4. In service mode, use the source credential to verify each candidate against the source's current permissions and resource settings. Content still comes from the member's connection. Drop candidates that cannot be verified before projecting titles, snippets, or citations to the assistant.
2121
5. Bind document references to the member, owner scope, provider, and account. On a read, resolve access again, verify the source before reading, and check current source settings again before returning content. GitLab additionally validates ACLs against fresh content metadata.
2222

‎apps/sim/lib/sim-search/live/application.test.ts‎

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -557,7 +557,7 @@ describe('authorized live retrieval', () => {
557557
expect(statusOf('mail')?.nextCursor).toBeUndefined()
558558
expect(statusOf('account')).toMatchObject({ nextCursor: 'next' })
559559
})
560-
it('ranks results across accounts by the query terms they hold, not by account order', async () => {
560+
it('ranks across accounts by query terms only for a reader; agents keep fusion order', async () => {
561561
const gmail = { ...account, id: 'mail', provider: 'gmail', displayName: 'Mail' }
562562
mocks.accounts.mockResolvedValue([account, gmail])
563563
const result = (id: string, title: string, content: string) => ({
@@ -576,15 +576,15 @@ describe('authorized live retrieval', () => {
576576
]
577577
: [result('sheet', 'Question bank', 'Questions')],
578578
}))
579-
const { results } = await searchLiveKnowledge.execute({
580-
principal,
581-
input: { ...input, query: 'merge train' },
582-
})
583-
expect(results.map((row) => decodeLiveReference(row.documentId).id)).toEqual([
584-
'named',
585-
'notes',
586-
'sheet',
587-
])
579+
const order = async (rankByRelevance?: boolean) =>
580+
(
581+
await searchLiveKnowledge.execute({
582+
principal,
583+
input: { ...input, query: 'merge train', rankByRelevance },
584+
})
585+
).results.map((row) => decodeLiveReference(row.documentId).id)
586+
expect(await order(true)).toEqual(['named', 'notes', 'sheet'])
587+
expect(await order()).toEqual(['sheet', 'notes', 'named'])
588588
})
589589
it('applies date filters before spending provider verification', async () => {
590590
const verify = vi.fn(async () => true)

‎apps/sim/lib/sim-search/live/application.ts‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,12 @@ interface LiveSearchOptions {
167167
topK: number
168168
filters?: WorkspaceSearchFilters
169169
nativeQueries?: NativeSearchQuery[]
170+
/**
171+
* Orders a plain query's results across sources by the query terms they hold, for a person
172+
* reading the list. Agent clients keep fusion order, which interleaves sources: an end-to-end
173+
* client benchmark found no gain from relevance order, and the agent searched less.
174+
*/
175+
rankByRelevance?: boolean
170176
signal?: AbortSignal
171177
resultSecretRegistry?: ResolvedSecretTraceRegistry
172178
}
@@ -736,7 +742,9 @@ export const searchLiveKnowledge = defineAuthorizedKnowledgeUseCase({
736742
* Native queries carry provider operators and keep fusion order.
737743
*/
738744
const relevanceOf =
739-
dateSorted || queries || !input.query.trim() ? undefined : queryRelevance(input.query)
745+
!input.rankByRelevance || dateSorted || queries || !input.query.trim()
746+
? undefined
747+
: queryRelevance(input.query)
740748
const ranked = [...fused.values()]
741749
.map((entry) => ({
742750
...entry,

0 commit comments

Comments
 (0)