mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-07-23 08:56:42 +08:00
fix(go-agent): route graph step errors through exception outputs (#17167)
## Summary - Route ReAct graph step-limit errors through the Agent `_ERROR` output. - Align the Go Agent default max rounds with Python by using five rounds. - Preserve cancellation, timeout, and configuration errors as real failures. ## Testing - `bash build.sh --test ./internal/agent/component/...` <img width="2050" height="1409" alt="image" src="https://github.com/user-attachments/assets/108e2d27-e115-4595-91a2-21f90c2531ae" />
This commit is contained in:
@@ -538,7 +538,8 @@ func buildAgentTools(p AgentParam) ([]einotool.BaseTool, error) {
|
||||
// NewAgentComponent builds an AgentComponent from raw params.
|
||||
func NewAgentComponent(p AgentParam) *AgentComponent {
|
||||
if p.MaxRounds <= 0 {
|
||||
p.MaxRounds = 3
|
||||
// Keep the Python Agent default (AgentParam.max_rounds = 5).
|
||||
p.MaxRounds = 5
|
||||
}
|
||||
return &AgentComponent{param: p}
|
||||
}
|
||||
@@ -627,7 +628,15 @@ func (c *AgentComponent) Invoke(ctx context.Context, inputs map[string]any) (map
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("component: Agent.Invoke: %w", err)
|
||||
// Python's LLM layer turns model/tool execution failures into an
|
||||
// ``**ERROR**`` response, which Agent exposes as the `_ERROR`
|
||||
// component output. Preserve cancellation as a real graph error,
|
||||
// but keep ordinary ReAct failures in the component data flow so
|
||||
// Canvas exception branches can handle them.
|
||||
if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) || !isAgentGraphRunError(err) {
|
||||
return nil, fmt.Errorf("component: Agent.Invoke: %w", err)
|
||||
}
|
||||
return map[string]any{"_ERROR": "**ERROR**: " + err.Error()}, nil
|
||||
}
|
||||
// Post-stream citation grounding. When Cite is enabled and
|
||||
// the canvas state has recorded retrieval chunks (populated
|
||||
@@ -686,6 +695,18 @@ func (c *AgentComponent) Invoke(ctx context.Context, inputs map[string]any) (map
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// isAgentGraphRunError identifies ReAct graph failures that Python exposes
|
||||
// through the Agent component's _ERROR output. Construction/configuration
|
||||
// errors must remain real component errors so invalid canvases still fail
|
||||
// fast; only the graph execution limit/error is routed to exception branches.
|
||||
func isAgentGraphRunError(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
msg := strings.ToLower(err.Error())
|
||||
return strings.Contains(msg, "[graphrunerror]") || strings.Contains(msg, "exceeds max steps")
|
||||
}
|
||||
|
||||
// Stream implements Component.Stream. Mirrors Invoke then pushes the
|
||||
// single payload through the channel.
|
||||
func (c *AgentComponent) Stream(ctx context.Context, inputs map[string]any) (<-chan map[string]any, error) {
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
// 2. ToolCallRound: the runner returns a message with ToolCalls →
|
||||
// component extracts them into the tool_calls output.
|
||||
// 3. ExhaustRoundsError: the runner returns an error → component
|
||||
// propagates it.
|
||||
// exposes it through the Python-compatible _ERROR output.
|
||||
// 4. MissingModelID: the component rejects before calling the runner.
|
||||
package component
|
||||
|
||||
@@ -324,15 +324,18 @@ func TestAgent_ToolCallRound(t *testing.T) {
|
||||
|
||||
func TestAgent_ExhaustRoundsError(t *testing.T) {
|
||||
withAgentRunner(t, func(_ context.Context, _ AgentParam) (*schema.Message, error) {
|
||||
return nil, errors.New("agent: exhausted rounds without final answer")
|
||||
return nil, errors.New("[GraphRunError] exceeds max steps")
|
||||
})
|
||||
|
||||
c := NewAgentComponent(AgentParam{ModelID: "stub", MaxRounds: 2})
|
||||
_, err := c.Invoke(context.Background(), map[string]any{
|
||||
out, err := c.Invoke(context.Background(), map[string]any{
|
||||
"user_prompt": "x",
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatal("expected error when loop exhausts without a final answer")
|
||||
if err != nil {
|
||||
t.Fatalf("Invoke: %v", err)
|
||||
}
|
||||
if got := out["_ERROR"]; got != "**ERROR**: [GraphRunError] exceeds max steps" {
|
||||
t.Fatalf("_ERROR = %v, want Python-compatible error envelope", got)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user