fix(retrieval): keep manual metadata filter reusable inside Iteration (#14849)

## 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.
This commit is contained in:
plind
2026-05-19 00:08:31 -07:00
committed by GitHub
parent f169ab4b39
commit 7edabdf7c3

View File

@@ -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"]: