Skip to content

fix(agent): honor interceptor tool selection and dynamic registries in standalone ReAct (issue #268) - #333

Merged
yuluo-yx merged 1 commit into
agentic-ai-java:mainfrom
konlue:feat/t268-interceptor-tool-filtering
Oct 30, 2026
Merged

yuluo-yx merged 1 commit into
agentic-ai-java:mainfrom
konlue:feat/t268-interceptor-tool-filtering

Conversation

@konlue

@konlue konlue commented Oct 28, 2026

Copy link
Copy Markdown
Contributor

Describe what this PR does / why we need it

The standalone ReAct agent's invokeModel disclosed the full static tool registry to the model regardless of ModelRequest's tool-selection semantics — an interceptor calling tools(List.of()) (explicit empty selection) still left every tool visible, and explicit subsets and invocation-local dynamic registries were ignored. Selection semantics now gate both disclosure and execution, so what the model sees and what can run always agree.

Does this pull request fix one issue?

Fixes #268

Describe how you did it

  • A single resolution point at the end of the interceptor chain computes the effective tool set: static bindings merged with the invocation-local dynamicToolRegistry (static shadows same-named dynamic disclosures, matching ModelRequest.getTools()'s existing distinct order), narrowed by the explicit selection when hasToolSelection() is set. An explicit empty selection disables disclosure and execution entirely; an absent selection uses every binding.
  • The effective set is handed back to the turn via a local reference (no API change) and the same-turn tool execution uses exactly that set: a filtered-out tool called by the model surfaces through the existing unknown-tool error path. Authorization checks and trusted-execution-context semantics are untouched.
  • resolveTools's static/dynamic merge previously threw on duplicate names, which made the duplicate-name policy untestable; it now applies the same shadowing rule.

Describe how to verify it

  • ReActAgentTest (+8 cases, all offline via the existing ScriptedClient pattern): unfiltered disclosure guard (including the no-copy fast path), explicit empty selection (no disclosure + unknown-tool error on a filtered call), subset selection (chosen tool disclosed and executed, filtered tool never leaks), dynamic tools participating in selection, static-shadowing-dynamic on name conflicts (execution counts 1:0), streaming and non-streaming disclosing the same filtered set, and no selection leakage across sequential runs.

  • Run:

    ./mvnw -pl argi-agent/argi-agent-core -am test -DskipTests=false
  • Full module: 69 tests green (with argi-api / runtime-api / agent-api upstream in the reactor); checkstyle clean; studio compilation unaffected.

Special notes for reviews

  • Tool non-disclosure is not business authorization: the existing authorization policy check in executeTool runs exactly as before.
  • The duplicate-name merge change in resolveTools is the one deliberate behavior widening: a static and a dynamic tool with the same name used to fail the whole merge; the static binding now shadows the dynamic one (consistent with getTools()), which the issue's acceptance criteria require to be testable.
  • Execution-binding consistency across retry/fallback attempts ([BUG][P1] 独立 ReAct 应执行成功模型轮次实际披露的工具绑定 #269) is the natural follow-up; this PR already gives it the effective-set handoff to build on.

@yuluo-yx yuluo-yx left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks for you

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG][P1] 独立 ReAct 应遵守拦截器的显式工具筛选与动态工具声明

2 participants