From 7edabdf7c35ad3689846ec9d89e70d0de06ffe90 Mon Sep 17 00:00:00 2001 From: plind <59729252+plind-junior@users.noreply.github.com> Date: Tue, 19 May 2026 00:08:31 -0700 Subject: [PATCH] fix(retrieval): keep manual metadata filter reusable inside Iteration (#14849) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What problem does this PR solve? Closes #12582. When a Retrieval component sits inside an Iteration with a **manual** metadata filter that references the iteration variable (e.g. `{IterationItem:abc@item}`), every iteration reuses the value resolved on the **first** pass. Root cause: [`_resolve_manual_filter` in `agent/tools/retrieval.py`](https://github.com/infiniflow/ragflow/blob/main/agent/tools/retrieval.py#L144-L171) mutated `flt["value"]` in place. The `filters` list passed in is the live `self._param.meta_data_filter["manual"]` (see [`apply_meta_data_filter` in `common/metadata_utils.py:257-261`](https://github.com/infiniflow/ragflow/blob/main/common/metadata_utils.py#L257-L261)), so after the first iteration the param dict permanently held the resolved string instead of the original variable reference. ```text iter #1: flt["value"] = "{IterationItem:abc@item}" → resolved to "AI" after mutation: flt["value"] = "AI" ← written back into _param iter #2: flt["value"] = "AI" ← no {…} matches retrieval keeps filtering by "AI" forever ``` This PR returns a shallow copy with the resolved value instead, leaving the original filter (and its variable reference) intact for the next iteration. ## Type of change - [x] Bug fix (non-breaking change which fixes an issue) ## Test plan - [ ] Build an agent: `Agent (structured output → list of areas) → Iteration → Retrieval (manual filter: Area = {IterationItem/Item}) → Message`. Run with a multi-area query and confirm each iteration's Retrieval result matches its own item, not the first item. - [ ] Regression: Retrieval with a manual metadata filter outside an Iteration still resolves the variable correctly on each request. - [ ] Regression: Retrieval with no metadata filter and with `auto` / `semi_auto` filters behave unchanged. --- agent/tools/retrieval.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/agent/tools/retrieval.py b/agent/tools/retrieval.py index 0ccf056a29..02cb3e2ce6 100644 --- a/agent/tools/retrieval.py +++ b/agent/tools/retrieval.py @@ -142,6 +142,11 @@ class Retrieval(ToolBase, ABC): return DocMetadataService.get_flatted_meta_by_kbs(kb_ids) def _resolve_manual_filter(flt: dict) -> dict: + # Return a new dict instead of mutating `flt` in place. The + # caller passes filters straight out of self._param.meta_data_filter, + # so mutating them would replace the variable reference with its + # resolved value and every subsequent invocation (e.g. inside an + # Iteration component) would reuse that stale value. pat = re.compile(self.variable_ref_patt) s = flt.get("value", "") out_parts = [] @@ -167,8 +172,9 @@ class Retrieval(ToolBase, ABC): last = m.end() out_parts.append(s[last:]) - flt["value"] = "".join(out_parts) - return flt + resolved = dict(flt) + resolved["value"] = "".join(out_parts) + return resolved chat_mdl = None if self._param.meta_data_filter.get("method") in ["auto", "semi_auto"]: