diff --git a/agents/executor.go b/agents/executor.go index 5835ad00..45a36065 100644 --- a/agents/executor.go +++ b/agents/executor.go @@ -134,7 +134,7 @@ func (e *Executor) doAction( }), nil } - observation, err := tool.Call(ctx, action.ToolInput) + observation, err := tool.Call(ctx, strings.TrimSuffix(action.ToolInput, "\nObservation:")) if err != nil { return nil, err } diff --git a/agents/executor_test.go b/agents/executor_test.go index fecb8ded..f93ff38a 100644 --- a/agents/executor_test.go +++ b/agents/executor_test.go @@ -23,6 +23,7 @@ type testAgent struct { err error inputKeys []string outputKeys []string + tools []tools.Tool recordedIntermediateSteps []schema.AgentStep recordedInputs map[string]string @@ -50,7 +51,7 @@ func (a testAgent) GetOutputKeys() []string { } func (a *testAgent) GetTools() []tools.Tool { - return nil + return a.tools } func TestExecutorWithErrorHandler(t *testing.T) { @@ -171,3 +172,59 @@ func TestExecutorWithOpenAIFunctionAgent(t *testing.T) { require.True(t, strings.Contains(result, "2012") || strings.Contains(result, "March"), "correct answer 2012 or March not in response") } + +// mockTool implements the tools.Tool interface for testing +type mockTool struct { + name string + description string + receivedInputPtr *string +} + +func (m *mockTool) Name() string { + return m.name +} + +func (m *mockTool) Description() string { + return m.description +} + +func (m *mockTool) Call(_ context.Context, input string) (string, error) { + *m.receivedInputPtr = input + return "mock result", nil +} + +func TestExecutorTrimsObservationSuffix(t *testing.T) { + t.Parallel() + ctx := context.Background() + + // Create a mock tool that records what input it receives + var receivedInput string + mockToolInst := &mockTool{ + name: "mock_tool", + description: "A mock tool for testing", + receivedInputPtr: &receivedInput, + } + + // Create a test agent that returns an action with trailing "\nObservation:" + testAgent := &testAgent{ + actions: []schema.AgentAction{ + { + Tool: "mock_tool", + ToolInput: "test input\nObservation:", + Log: "Action: mock_tool\nAction Input: test input\nObservation:", + }, + }, + inputKeys: []string{"input"}, + outputKeys: []string{"output"}, + tools: []tools.Tool{mockToolInst}, + } + + executor := agents.NewExecutor(testAgent, agents.WithMaxIterations(1)) + + _, err := chains.Call(ctx, executor, map[string]any{"input": "test question"}) + // We expect ErrNotFinished since our test agent doesn't provide a finish action + require.ErrorIs(t, err, agents.ErrNotFinished) + + // Verify that the tool received the input with "\nObservation:" trimmed off + require.Equal(t, "test input", receivedInput, "Tool should receive input with \\nObservation: suffix trimmed") +} diff --git a/agents/markl_test.go b/agents/markl_test.go index 3112f77e..e33f7f75 100644 --- a/agents/markl_test.go +++ b/agents/markl_test.go @@ -36,6 +36,16 @@ func TestMRKLOutputParser(t *testing.T) { expectedFinish: nil, expectedErr: nil, }, + { + input: "Action: calculator\nAction Input: 5 + 3\nObservation:", + expectedActions: []schema.AgentAction{{ + Tool: "calculator", + ToolInput: "5 + 3\nObservation:", + Log: "Action: calculator\nAction Input: 5 + 3\nObservation:", + }}, + expectedFinish: nil, + expectedErr: nil, + }, } a := OneShotZeroAgent{}