Skip to content

Commit 25b93a5

Browse files
committed
Add results merge strategy
1 parent 5bf8b96 commit 25b93a5

File tree

8 files changed

+52
-1
lines changed

8 files changed

+52
-1
lines changed

app/backend/approaches/approach.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@ async def run_agentic_retrieval(
261261
filter_add_on: Optional[str] = None,
262262
minimum_reranker_score: Optional[float] = None,
263263
max_docs_for_reranker: Optional[int] = None,
264+
results_merge_strategy: Optional[str] = None,
264265
) -> tuple[KnowledgeAgentRetrievalResponse, list[Document]]:
265266
# STEP 1: Invoke agentic retrieval
266267
response = await agent_client.retrieve(
@@ -298,7 +299,12 @@ async def run_agentic_retrieval(
298299

299300
results = []
300301
if response and response.references:
301-
references = sorted(response.references, key=lambda reference: int(reference.id))
302+
if results_merge_strategy == "interleaved":
303+
# Use interleaved reference order
304+
references = sorted(response.references, key=lambda reference: int(reference.id))
305+
else:
306+
# Default to descending strategy
307+
references = response.references
302308
for reference in references:
303309
if isinstance(reference, KnowledgeAgentAzureSearchDocReference) and reference.source_data:
304310
results.append(

app/backend/approaches/chatreadretrieveread.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@ async def run_agentic_retrieval_approach(
235235
search_index_filter = self.build_filter(overrides, auth_claims)
236236
top = overrides.get("top", 3)
237237
max_subqueries = overrides.get("max_subqueries", 10)
238+
results_merge_strategy = overrides.get("results_merge_strategy", "interleaved")
238239
# 50 is the amount of documents that the reranker can process per query
239240
max_docs_for_reranker = max_subqueries * 50
240241

@@ -246,6 +247,7 @@ async def run_agentic_retrieval_approach(
246247
filter_add_on=search_index_filter,
247248
minimum_reranker_score=minimum_reranker_score,
248249
max_docs_for_reranker=max_docs_for_reranker,
250+
results_merge_strategy=results_merge_strategy,
249251
)
250252

251253
text_sources = self.get_sources_content(results, use_semantic_captions=False, use_image_citation=False)

app/backend/approaches/retrievethenread.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ async def run_agentic_retrieval_approach(
186186
search_index_filter = self.build_filter(overrides, auth_claims)
187187
top = overrides.get("top", 3)
188188
max_subqueries = overrides.get("max_subqueries", 10)
189+
results_merge_strategy = overrides.get("results_merge_strategy", "interleaved")
189190
# 50 is the amount of documents that the reranker can process per query
190191
max_docs_for_reranker = max_subqueries * 50
191192

@@ -197,6 +198,7 @@ async def run_agentic_retrieval_approach(
197198
filter_add_on=search_index_filter,
198199
minimum_reranker_score=minimum_reranker_score,
199200
max_docs_for_reranker=max_docs_for_reranker,
201+
results_merge_strategy=results_merge_strategy,
200202
)
201203

202204
text_sources = self.get_sources_content(results, use_semantic_captions=False, use_image_citation=False)

app/frontend/src/api/models.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export type ChatAppRequestOverrides = {
2727
seed?: number;
2828
top?: number;
2929
max_subqueries?: number;
30+
results_merge_strategy?: string;
3031
temperature?: number;
3132
minimum_search_score?: number;
3233
minimum_reranker_score?: number;

app/frontend/src/components/Settings/Settings.tsx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export interface SettingsProps {
1515
temperature: number;
1616
retrieveCount: number;
1717
maxSubqueryCount: number;
18+
resultsMergeStrategy: string;
1819
seed: number | null;
1920
minimumSearchScore: number;
2021
minimumRerankerScore: number;
@@ -55,6 +56,7 @@ export const Settings = ({
5556
temperature,
5657
retrieveCount,
5758
maxSubqueryCount,
59+
resultsMergeStrategy,
5860
seed,
5961
minimumSearchScore,
6062
minimumRerankerScore,
@@ -108,6 +110,7 @@ export const Settings = ({
108110
const retrieveCountFieldId = useId("retrieveCountField");
109111
const maxSubqueryCountId = useId("maxSubqueryCount");
110112
const maxSubqueryCountFieldId = useId("maxSubqueryCountField");
113+
const resultsMergeStrategyFieldId = useId("resultsMergeStrategy");
111114
const includeCategoryId = useId("includeCategory");
112115
const includeCategoryFieldId = useId("includeCategoryField");
113116
const excludeCategoryId = useId("excludeCategory");
@@ -227,6 +230,24 @@ export const Settings = ({
227230
/>
228231
)}
229232

233+
{showAgenticRetrievalOption && useAgenticRetrieval && (
234+
<Dropdown
235+
id={resultsMergeStrategyFieldId}
236+
className={styles.settingsSeparator}
237+
label={t("labels.resultsMergeStrategy")}
238+
selectedKey={resultsMergeStrategy}
239+
onChange={(_ev?: React.FormEvent<HTMLElement | HTMLInputElement>, option?: IDropdownOption) =>
240+
onChange("resultsMergeStrategy", option?.key)
241+
}
242+
aria-labelledby={includeCategoryId}
243+
options={[
244+
{ key: "interleaved", text: t("labels.resultsMergeStrategyOptions.interleaved") },
245+
{ key: "descending", text: t("labels.resultsMergeStrategyOptions.descending") }
246+
]}
247+
onRenderLabel={props => renderLabel(props, includeCategoryId, includeCategoryFieldId, t("helpTexts.resultsMergeStrategy"))}
248+
/>
249+
)}
250+
230251
<TextField
231252
id={retrieveCountFieldId}
232253
className={styles.settingsSeparator}

app/frontend/src/locales/en/translation.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,11 @@
8484
"minimumRerankerScore": "Minimum reranker score",
8585
"retrieveCount": "Retrieve this many search results:",
8686
"maxSubqueryCount": "Max subqueries",
87+
"resultsMergeStrategy": "Results merge strategy",
88+
"resultsMergeStrategyOptions": {
89+
"interleaved": "Interleaved",
90+
"descending": "Descending"
91+
},
8792
"includeCategory": "Include category",
8893
"includeCategoryOptions": {
8994
"all": "All"
@@ -144,6 +149,8 @@
144149
"Sets the number of search results to retrieve from Azure AI search. More results may increase the likelihood of finding the correct answer, but may lead to the model getting 'lost in the middle'.",
145150
"maxSubqueryCount":
146151
"Sets the maximum number of subqueries to use for the agentic retrieval query plan.",
152+
"resultsMergeStrategy":
153+
"Sets the strategy for merging results from multiple subqueries. Interleaved picks the top results from each subquery in round robin order, while descending sorts all results by reranker score.",
147154
"includeCategory":
148155
"Specifies a category to include in the search results. There are no categories used in the default data set.",
149156
"excludeCategory":

app/frontend/src/pages/ask/Ask.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export function Component(): JSX.Element {
3131
const [retrievalMode, setRetrievalMode] = useState<RetrievalMode>(RetrievalMode.Hybrid);
3232
const [retrieveCount, setRetrieveCount] = useState<number>(3);
3333
const [maxSubqueryCount, setMaxSubqueryCount] = useState<number>(10);
34+
const [resultsMergeStrategy, setResultsMergeStrategy] = useState<string>("interleaved");
3435
const [useSemanticRanker, setUseSemanticRanker] = useState<boolean>(true);
3536
const [useSemanticCaptions, setUseSemanticCaptions] = useState<boolean>(false);
3637
const [useQueryRewriting, setUseQueryRewriting] = useState<boolean>(false);
@@ -139,6 +140,7 @@ export function Component(): JSX.Element {
139140
exclude_category: excludeCategory.length === 0 ? undefined : excludeCategory,
140141
top: retrieveCount,
141142
max_subqueries: maxSubqueryCount,
143+
results_merge_strategy: resultsMergeStrategy,
142144
temperature: temperature,
143145
minimum_reranker_score: minimumRerankerScore,
144146
minimum_search_score: minimumSearchScore,
@@ -199,6 +201,9 @@ export function Component(): JSX.Element {
199201
case "maxSubqueryCount":
200202
setMaxSubqueryCount(value);
201203
break;
204+
case "resultsMergeStrategy":
205+
setResultsMergeStrategy(value);
206+
break;
202207
case "useSemanticRanker":
203208
setUseSemanticRanker(value);
204209
break;
@@ -350,6 +355,7 @@ export function Component(): JSX.Element {
350355
temperature={temperature}
351356
retrieveCount={retrieveCount}
352357
maxSubqueryCount={maxSubqueryCount}
358+
resultsMergeStrategy={resultsMergeStrategy}
353359
seed={seed}
354360
minimumSearchScore={minimumSearchScore}
355361
minimumRerankerScore={minimumRerankerScore}

app/frontend/src/pages/chat/Chat.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ const Chat = () => {
4747
const [minimumSearchScore, setMinimumSearchScore] = useState<number>(0);
4848
const [retrieveCount, setRetrieveCount] = useState<number>(3);
4949
const [maxSubqueryCount, setMaxSubqueryCount] = useState<number>(10);
50+
const [resultsMergeStrategy, setResultsMergeStrategy] = useState<string>("interleaved");
5051
const [retrievalMode, setRetrievalMode] = useState<RetrievalMode>(RetrievalMode.Hybrid);
5152
const [useSemanticRanker, setUseSemanticRanker] = useState<boolean>(true);
5253
const [useQueryRewriting, setUseQueryRewriting] = useState<boolean>(false);
@@ -219,6 +220,7 @@ const Chat = () => {
219220
exclude_category: excludeCategory.length === 0 ? undefined : excludeCategory,
220221
top: retrieveCount,
221222
max_subqueries: maxSubqueryCount,
223+
results_merge_strategy: resultsMergeStrategy,
222224
temperature: temperature,
223225
minimum_reranker_score: minimumRerankerScore,
224226
minimum_search_score: minimumSearchScore,
@@ -316,6 +318,9 @@ const Chat = () => {
316318
case "maxSubqueryCount":
317319
setMaxSubqueryCount(value);
318320
break;
321+
case "resultsMergeStrategy":
322+
setResultsMergeStrategy(value);
323+
break;
319324
case "useSemanticRanker":
320325
setUseSemanticRanker(value);
321326
break;
@@ -538,6 +543,7 @@ const Chat = () => {
538543
temperature={temperature}
539544
retrieveCount={retrieveCount}
540545
maxSubqueryCount={maxSubqueryCount}
546+
resultsMergeStrategy={resultsMergeStrategy}
541547
seed={seed}
542548
minimumSearchScore={minimumSearchScore}
543549
minimumRerankerScore={minimumRerankerScore}

0 commit comments

Comments
 (0)