Skip to content

feat(adk): add support for early stop instructions#755

Open
crossProblems wants to merge 2 commits intocloudwego:mainfrom
crossProblems:main
Open

feat(adk): add support for early stop instructions#755
crossProblems wants to merge 2 commits intocloudwego:mainfrom
crossProblems:main

Conversation

@crossProblems
Copy link

What type of PR is this?

Check the PR title.

  • This PR title match the format: <type>(optional scope): <description>
  • The description of this PR title is user-oriented and clear enough for others to understand.
  • Attach the PR updating the user documentation if the current PR requires user awareness at the usage level. User docs repo

(Optional) Translate the PR title into Chinese.

(Optional) More detailed description for this PR(en: English/zh: Chinese).

en:
zh(optional):

(Optional) Which issue(s) this PR fixes:

(optional) The PR that updates user documentation:

@CLAassistant
Copy link

CLAassistant commented Feb 4, 2026

CLA assistant check
All committers have signed the CLA.

@crossProblems
Copy link
Author

This PR adds LangChain-compatible early stopping behavior to the ADK ReAct agent when MaxIterations is exceeded.

Motivation

Today, when the ReAct agent reaches MaxIterations, it returns ErrExceedMaxIterations immediately. In LangChain, users can choose early_stopping_method:

  • force: stop and raise error
  • generate: perform a final LLM pass to produce a best-effort final answer (no more tool calls)

This PR brings the same option to Eino’s ChatModelAgent.

API changes

Added to ChatModelAgentConfig:

  • EarlyStoppingMethod string: "force" or "generate"
  • EarlyStoppingInstruction string: appended as the final user message when EarlyStoppingMethod == "generate"

Internally, reactConfig is extended with the same two fields.

Behavior

  • force: when RemainingIterations <= 0, the agent behaves exactly as before and returns ErrExceedMaxIterations.

  • generate:

    1. When RemainingIterations <= 0, set State.EarlyStop = true
    2. Append EarlyStoppingInstruction as a final user message before the last model call
    3. Prevent further tool execution even if the model returns tool calls (graph ends after the model output)

This ensures a “final answer” attempt instead of looping into tools once the iteration budget is exhausted.

Implementation highlights

  • State includes EarlyStop bool to record early-stop mode.

  • modelPreHandle:

    • checks RemainingIterations
    • on generate-mode exceed: sets EarlyStop, appends instruction, then continues to call the model
  • toolCallCheck:

    • reads EarlyStop from state
    • if EarlyStop == true, tool calls are ignored and the graph terminates (compose.END)

Tests

adk/react_test.go adds cases to cover both modes:

  • Force mode: exceeds iterations and asserts ErrExceedMaxIterations
  • Generate mode: exceeds iterations and still returns a final response (with instruction applied)

中文补充

这个 PR 为 ADK 的 ReAct agent 增加了类似 LangChain 的 early_stopping_method

  • force:超过 MaxIterations 直接返回 ErrExceedMaxIterations(保持原行为)
  • generate:超过迭代次数后,追加一条最终的 user 指令(EarlyStoppingInstruction),允许再进行一次模型调用用于总结/生成最终回答,并且通过 State.EarlyStop 禁止后续进入 Tools 节点,避免再次 tool loop。

@shentongmartin
Copy link
Contributor

这种处理流程:抛出ErrExceedMaxIterations -> 应用层捕获这个 error -> 以预设的 query 再次发起 agent 调用。感觉跟你这里的配置是等价的。

@crossProblems
Copy link
Author

这种处理流程:抛出ErrExceedMaxIterations -> 应用层捕获这个 error -> 以预设的 query 再次发起 agent 调用。感觉跟你这里的配置是等价的。

我理解您说的“应用层捕获ErrExceedMaxIterations后用预设query再次调用agent”的方案,在直接调用单个agent的场景确实可以达到类似效果;但我这次主要想覆盖的是agent被adk.NewAgentTool包装成“子Agent工具”之后的链路:当子Agent触发ErrExceedMaxIterations时,这个error往往会作为tool调用失败向上冒泡,最终导致最外层Agent也直接失败。此时应用层通常只能重新对最外层Agent发起一次完整query,而无法在同一次执行里对作为工具的子Agent做“追加总结指令/再生成一次最终输出”的收敛处理,所以我希望在框架侧提供generate这种可配置的early stopping,让子Agent在触顶时仍能返回一个best-effort的final answer(且不再进入工具调用),从而避免错误扩散把整条链路打断。

@shentongmartin
Copy link
Contributor

AgentTool 的话,可以写一个 middleware 配置给外层的 ChatModelAgent,捕获这个特定 error,转换成 tool result 给模型。

@crossProblems
Copy link
Author

AgentTool 的话,可以写一个 middleware 配置给外层的 ChatModelAgent,捕获这个特定 error,转换成 tool result 给模型。

明白您的意思。不过这种做法下,外层通常只能拿到子Agent触顶的提示信息,而拿不到子Agent基于自身完整上下文生成的best-effort最终答案,我也注意到有OutputKey这个参数,但一旦报错它往往会为空,或者最多只能拿到最后一次的输出,同时还需要在每个使用AgentTool的场景里单独配置拦截与转换逻辑。相比之下,将force/generate作为子Agent自身(也适用于普通Agent)的可选行为更内聚,集成成本更低,对使用eino开发Agent的开发者来说更加便捷,您觉得呢。

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

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants