Skip to content

fix(agent): pass interceptor-set system message to model requests (issue #270) - #339

Merged
yuluo-yx merged 1 commit into
agentic-ai-java:mainfrom
konlue:fix/issue270-system-message
Nov 1, 2026
Merged

yuluo-yx merged 1 commit into
agentic-ai-java:mainfrom
konlue:fix/issue270-system-message

Conversation

@konlue

@konlue konlue commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

Describe what this PR does / why we need it

Fixes #270: the standalone ReAct agent's invokeModel built the ChatRequest from request.getMessages() only, silently dropping ModelRequest.getSystemMessage() — so the four framework interceptors that inject system prompts through that field (Skills, Filesystem, SubAgent, TodoList, all using the null-then-appendText merge pattern) lost their injection entirely on the standalone ReAct path ("system message sent=false" in the issue's repro).

Does this pull request fix one issue?

Fixes #270

Describe how you did it

A single composition point in invokeModel builds the outgoing message list: when an interceptor set a system message, it becomes the single SYSTEM entry at the head of the request and replaces any SYSTEM messages already present in the conversation list (two SYSTEM entries would be transmitted verbatim by the Spring/LangChain4j adapters and concatenated by the Google ADK adapter, so the explicit field wins); all non-SYSTEM messages keep their order. With no interceptor-set system message the incoming list is passed through unchanged.

The composition is a local copy for this call only — the run-level message list is never written back, so nothing accumulates across tool-call turns. Streaming and non-streaming share the same construction point, so both paths behave identically. No public API, event, or configuration changes; ModelRequest's existing SYSTEM role validation already guarantees plain messages are never promoted.

Describe how to verify it

ReActAgentTest (+6 cases, all offline via the existing ScriptedClient pattern): the issue's repro (interceptor sets "required-policy", the recorded request starts with that SYSTEM entry); an existing user-provided SYSTEM message is replaced with exactly one SYSTEM entry transmitted; without an interceptor system message the conversation is passed through byte-for-byte (user SYSTEM preserved); system message count stays exactly 1 across tool-call turns (no accumulation); the streaming path receives the same system message; non-SYSTEM messages keep their order around the injected entry.

Red before the fix: system-message assertions fail (sent.get(0) has no SYSTEM role). Green after: ReActAgentTest 22/22, argi-agent-core full module 133/133 (with upstream reactor), checkstyle clean, studio compiles.

Special notes for reviews

The replace rule (interceptor system message wins over conversation SYSTEM entries) follows the interceptors' own merge convention: they treat the systemMessage field as the single system-prompt carrier (null-then-appendText), so no second protocol is introduced. If a stricter merge (e.g. appending the interceptor prompt to the conversation's existing SYSTEM text) is preferred, the change is confined to the single composition method.

@yuluo-yx
yuluo-yx merged commit 9c77163 into agentic-ai-java:main Nov 1, 2026
7 checks passed
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 应将拦截器设置的 system message 传入模型请求

2 participants