Repository: tmc/langchaingo
Pull request #38 (2023-04-21 03:18:48)

exp/agents: flesh out agents implementation
#37 Hi this is my approach on agent and Zero shot agent, Already have the _test but i need my tools PR to be merged to work


Patch:
diff --git a/.github /PULL_REQUEST_TEMPLATE.md b/.github /PULL_REQUEST_TEMPLATE.md
index 2e80c444..84fda679 100644
--- a/.github /PULL_REQUEST_TEMPLATE.md
+++ b/.github /PULL_REQUEST_TEMPLATE.md
@@ -1,9 +1,20 @@
+<!-- Thank you for contributing! -->
+
+### Description
+
+<!-- Please insert your description here and provide especially info about the "what" this PR is solving -->
+
+### Additional context
+
+<!-- e.g. is there anything you'd like reviewers to focus on? -->
+
+---
 
 ### PR Checklist
 
 - [ ] Read the [Contributing documentation](https://github.com/tmc/langchaingo/blob/main/CONTRIBUTING.md).
 - [ ] Read the [Code of conduct documentation](https://github.com/tmc/langchaingo/blob/main/CODE_OF_CONDUCT.md).
-- [ ] Name your Pull Request title clearly, concisely, and prefixed with the name of primarily affected package you changed according to [Good commit messages](https://go.dev/doc/contribute#commit_messages) (such as `memory: add interfaces for X, Y` or `util: add whizzbang helpers`).
+- [ ] Name your Pull Request title clearly, concisely, and prefixed with the name of primarily affected package you changed according to [Go Contribute](https://go.dev/doc/contribute#commit_messages) (such as `memory: added interfaces` or `util: added helpers`).
 - [ ] Check that there isn't already a PR that solves the problem the same way to avoid creating a duplicate.
 - [ ] Provide a description in this PR that addresses **what** the PR is solving, or reference the issue that it solves (e.g. `fixes #123`).
 - [ ] Describes source of new concepts.
diff --git a/Makefile b/Makefile
index 869c1ba3..9f698c65 100644
--- a/Makefile
+++ b/Makefile
@@ -18,6 +18,3 @@ test-race:
 test-cover:
 	go run test -cover ./...
 
-.PHONY: run-pkgsite
-run-pkgsite:
-	go run golang.org/x/pkgsite/cmd/pkgsite@latest
diff --git a/exp/agent/agents.go b/exp/agent/agents.go
new file mode 100644
index 00000000..e94f4789
--- /dev/null
+++ b/exp/agent/agents.go
@@ -0,0 +1,60 @@
+// Package agents defines the types for langchaingo Agent.
+package agent
+
+import (
+	"errors"
+
+	"github.com/tmc/langchaingo/exp/agent/executor"
+	"github.com/tmc/langchaingo/exp/agent/mrkl"
+	"github.com/tmc/langchaingo/exp/tools"
+	"github.com/tmc/langchaingo/llms"
+)
+
+// AgentType is a string type representing the type of agent to create.
+type AgentType string
+
+const (
+	// ZeroShotReactDescription is an AgentType constant that represents the "zeroShotReactDescription" agent type.
+	ZeroShotReactDescription AgentType = "zeroShotReactDescription"
+)
+
+// AgentOption is a type alias for a map of string keys to any value, representing the options for the agent.
+type AgentOption map[string]any
+
+// Options is a function type that can be used to modify the AgentOption.
+type Options func(p *AgentOption)
+
+func defaultOptions() AgentOption {
+	return AgentOption{
+		"verbose": false,
+	}
+}
+
+// WithVerbosity is a function that sets the verbosity option for the agent.
+func WithVerbosity() Options {
+	return func(p *AgentOption) {
+		(*p)["verbose"] = true
+	}
+}
+
+// New is a function that creates a new agent with the specified LLM model, tools, agent type, and options.
+// It returns an AgentExecutor interface or an error if there is any issue during the creation process.
+func New(
+	llm llms.LLM,
+	tools []tools.Tool,
+	agentType AgentType,
+	opts ...Options,
+
+) (executor.AgentExecutor, error) {
+
+	options := defaultOptions()
+	for _, opt := range opts {
+		opt(&options)
+	}
+	switch agentType {
+	case ZeroShotReactDescription:
+		return mrkl.NewOneShotAgent(llm, tools, options)
+	default:
+		return nil, errors.New("Unknown agent type")
+	}
+}
diff --git a/exp/agent/doc.go b/exp/agent/doc.go
new file mode 100644
index 00000000..d5e52f47
--- /dev/null
+++ b/exp/agent/doc.go
@@ -0,0 +1,16 @@
+// Package agent provides an implementation of the AgentExecutor interface for
+// langchaingo agents. The package includes the necessary types, structures,
+// and functions to create and configure agents.
+//
+// The agent package provides the New function to create a new agent, and
+// supports different agent types and options to customize the agent's behavior.
+//
+// AgentType is a string type representing the type of agent to create.
+// The package currently supports the "zeroShotReactDescription" agent type.
+//
+// AgentOption is a type alias for a map of string keys to any value,
+// representing the options for the agent.
+//
+// Options is a function type that can be used to modify the AgentOption,
+// such as the WithVerbosity function, which sets the verbosity option for the agent.
+package agent
diff --git a/exp/agent/executor/agent_executor.go b/exp/agent/executor/agent_executor.go
new file mode 100644
index 00000000..496867da
--- /dev/null
+++ b/exp/agent/executor/agent_executor.go
@@ -0,0 +1,11 @@
+package executor
+
+import "github.com/tmc/langchaingo/schema"
+
+// AgentExecutor is an interface that defines methods for executing agent
+// queries. Implementations of this interface are responsible for executing
+// a query and returning the result in the form of an AgentFinish object
+// or an error if the execution fails.
+type AgentExecutor interface {
+	Run(query string) (*schema.AgentFinish, error)
+}
diff --git a/exp/agent/executor/doc.go b/exp/agent/executor/doc.go
new file mode 100644
index 00000000..7604a166
--- /dev/null
+++ b/exp/agent/executor/doc.go
@@ -0,0 +1,19 @@
+// Package executor provides interfaces and implementations for executing
+// agent queries.
+//
+// An agent query is a string that describes a task to be performed by an agent,
+// which is a software program that acts on behalf of a user or another program.
+// The query can specify various parameters, such as input data, processing
+// options, and output format.
+//
+// To execute an agent query, an application needs to create an object that
+// implements the AgentExecutor interface, which defines a method for running
+// a query and returning the result. The AgentFinish type from the langchaingo
+// schema package is used to represent the result of a successful execution.
+// If the execution fails, an error is returned.
+//
+// The executor package provides a sample implementation of the AgentExecutor
+// interface, called BasicExecutor, which executes queries using a simple
+// command-line interface. This implementation is intended for testing and
+// demonstration purposes only and should not be used in production environments.
+package executor
diff --git a/exp/agent/mrkl/doc.go b/exp/agent/mrkl/doc.go
new file mode 100644
index 00000000..2008a76a
--- /dev/null
+++ b/exp/agent/mrkl/doc.go
@@ -0,0 +1,11 @@
+// Package mrkl provides an implementation of the OneShotZeroAgent,
+// which is responsible for executing queries and returning results using
+// the LLM model, tools, and an internal chain. The package also includes
+// related structures and methods to support the agent's functionality.
+//
+// The OneShotZeroAgent struct implements the AgentExecutor interface
+// and provides methods to run queries and manage the agent's internal state.
+//
+// The package also includes utility functions for checking options,
+// planning, and running tools to perform various operations during query execution.
+package mrkl
diff --git a/exp/agent/mrkl/mrkl.go b/exp/agent/mrkl/mrkl.go
new file mode 100644
index 00000000..76828ee6
--- /dev/null
+++ b/exp/agent/mrkl/mrkl.go
@@ -0,0 +1,183 @@
+package mrkl
+
+import (
+	"fmt"
+	"strings"
+
+	"github.com/tmc/langchaingo/exp/agent/executor"
+	"github.com/tmc/langchaingo/exp/chains"
+	"github.com/tmc/langchaingo/exp/tools"
+	"github.com/tmc/langchaingo/llms"
+	"github.com/tmc/langchaingo/schema"
+)
+
+// OneShotZeroAgent is a struct that represents an agent responsible for executing a query
+// and returning the result using the LLM model, tools, and an internal chain.
+type OneShotZeroAgent struct {
+	llm        llms.LLM
+	query      string
+	chain      chains.Chain
+	tools      []tools.Tool
+	verbose    bool
+	maxRetries int
+}
+
+// OneShotZeroAgentOptions is a type alias for a map of string keys to any value,
+// representing the options for the OneShotZeroAgent.
+type OneShotZeroAgentOptions map[string]any
+
+var _ executor.AgentExecutor = (*OneShotZeroAgent)(nil)
+
+const FinalAnswerAction = "Final Answer:"
+
+func checkOptions(opts OneShotZeroAgentOptions) OneShotZeroAgentOptions {
+	if _, ok := opts["verbose"].(bool); !ok {
+		opts["verbose"] = false
+	}
+	if _, ok := opts["maxRetries"].(int); !ok {
+		opts["maxRetries"] = 3
+	}
+	return opts
+}
+
+// NewOneShotAgent creates a new OneShotZeroAgent with the given LLM model, tools,
+// and options. It returns a pointer to the created agent and an error if there is any
+// issue during the creation process.
+func NewOneShotAgent(llm llms.LLM, tools []tools.Tool, opts map[string]any) (*OneShotZeroAgent, error) {
+	firtPrompt, err := createPrompt(tools)
+	if err != nil {
+		return nil, err
+	}
+	opts = checkOptions(opts)
+	return &OneShotZeroAgent{
+		llm:        llm,
+		query:      "",
+		chain:      chains.NewLLMChain(llm, firtPrompt),
+		tools:      tools,
+		verbose:    opts["verbose"].(bool),
+		maxRetries: opts["maxRetries"].(int),
+	}, nil
+
+}
+
+// Run is an implementation of the AgentExecutor interface. It takes a query as input
+// and executes it, returning an AgentFinish object containing the result, or an error
+// if the execution fails.
+func (a *OneShotZeroAgent) Run(query string) (*schema.AgentFinish, error) {
+	var attempts int
+	a.query = query
+	resp, _ := a.chain.Call(map[string]interface{}{
+		"input":            a.query,
+		"agent_scratchpad": "",
+		"stop":             []string{"\nObservation:", "\n\tObservation:"},
+	})
+
+	output := resp["text"].(string)
+	for output != "" || attempts < a.maxRetries {
+		var err error
+		action, finish := a.plan(output)
+		if finish != nil {
+			return finish, nil
+		}
+		output, err = a.nextStep(*action)
+		if err != nil {
+			return nil, err
+		}
+
+		attempts++
+	}
+	return nil, fmt.Errorf("Agent did not finish after %d attempts", attempts)
+}
+
+func (a *OneShotZeroAgent) nextStep(action schema.AgentAction) (string, error) {
+	var scratchpad []string
+	// Perform your desired operation with the text value
+	observation, err := runTool(action.Tool, action.ToolInput.(string), &a.tools)
+	if err != nil {
+		return "", err
+	}
+	scratchpad = append(scratchpad, action.Log+observation)
+	if a.verbose {
+		fmt.Println(getCurrentThought(scratchpad))
+	}
+
+	// Update resp using a.chain.Call()
+	newResp, err := a.chain.Call(map[string]interface{}{
+		"input":            a.query,
+		"agent_scratchpad": strings.Join(scratchpad, "\n"),
+		"stop":             []string{"\nObservation:", "\n\tObservation:"},
+	})
+	if err != nil {
+		return "", err
+	}
+
+	// Use the updated resp in the next iteration
+	return newResp["text"].(string), nil
+
+}
+func (a *OneShotZeroAgent) plan(info string) (*schema.AgentAction, *schema.AgentFinish) {
+	action := getAgentAction(info)
+	if aswer := getFinalAnswer(action.Log); aswer != "" {
+		return nil, &schema.AgentFinish{
+			ReturnValues: map[string]any{
+				"answer": aswer,
+			},
+			Log: action.Log,
+		}
+	}
+	return &action, nil
+}
+
+func getCurrentThought(scratchpad []string) string {
+	if len(scratchpad) == 0 {
+		return ""
+	}
+	lastThought := scratchpad[len(scratchpad)-1]
+	if len(scratchpad) == 1 {
+		lastThought = "Thought:" + lastThought
+	}
+
+	return lastThought
+}
+
+func getFinalAnswer(text string) string {
+	finalAnswerPrefix := "Final Answer:"
+	startIndex := strings.Index(text, finalAnswerPrefix)
+
+	if startIndex == -1 {
+		return ""
+	}
+	startIndex += len(finalAnswerPrefix)
+	text = text[startIndex:]
+	trimmed := strings.Join(strings.Fields(text), " ")
+	return trimmed
+}
+
+func getAgentAction(input string) schema.AgentAction {
+	var agentAction schema.AgentAction
+	agentAction.Log = input
+	fields := strings.Split(input, "\n")
+	for _, field := range fields {
+		if strings.HasPrefix(field, "Action: ") {
+			agentAction.Tool = strings.TrimPrefix(field, "Action: ")
+		} else if strings.HasPrefix(field, "Action Input: ") {
+			agentAction.ToolInput = strings.TrimPrefix(field, "Action Input: ")
+		}
+	}
+	return agentAction
+}
+
+func runTool(action string, actionInput string, tools *[]tools.Tool) (string, error) {
+	var observation string
+	for _, tool := range *tools {
+		toolOutput, err := tool.Run(actionInput)
+		if err != nil {
+			return "", err
+		}
+		if tool.Name == strings.Trim(action, " ") {
+			observation = "\nObservation: " + toolOutput + "\n"
+			break
+		}
+	}
+	return observation, nil
+}
diff --git a/exp/agent/mrkl/prompt.go b/exp/agent/mrkl/prompt.go
new file mode 100644
index 00000000..111ca4e4
--- /dev/null
+++ b/exp/agent/mrkl/prompt.go
@@ -0,0 +1,110 @@
+package mrkl
+
+import (
+	"fmt"
+	"strings"
+
+	"github.com/tmc/langchaingo/exp/prompts"
+	"github.com/tmc/langchaingo/exp/tools"
+)
+
+const Prefix = `Answer the following questions as best you can. You have access to the following Tools:`
+const FormatInstructions = `Use the following format:
+
+Question: the input question you must answer
+Thought: you should always think about what to do
+Action: the action to take, should be one of [ {tool_names} ]
+Action Input: the input to the action
+Observation: the result of the action
+... (this Thought/Action/Action Input/Observation can repeat N times)
+Thought: I now know the final answer
+Final Answer: the final answer to the original input question`
+const Suffix = `Begin!
+
+Question: {input}
+Thought:{agent_scratchpad}`
+
+// CreatePromptOptions is a struct that holds options for creating a prompt template.
+type CreatePromptOptions struct {
+	Prefix             string
+	Suffix             string
+	FormatInstructions string
+	InputVariables     []string
+}
+
+// PromptTemplateOption is a function type that can be used to modify the CreatePromptOptions.
+type PromptTemplateOption func(p *CreatePromptOptions)
+
+// WithPrefix is a function that sets a custom prefix for the prompt template.
+func WithPrefix(prefix string) PromptTemplateOption {
+	return func(p *CreatePromptOptions) {
+		p.Prefix = prefix
+	}
+}
+
+// WithSuffix is a function that sets a custom suffix for the prompt template.
+func WithSuffix(suffix string) PromptTemplateOption {
+	return func(p *CreatePromptOptions) {
+		p.Suffix = suffix
+	}
+}
+
+// WithFormatInstructions is a function that sets custom format instructions for the prompt template.
+func WithFormatInstructions(formatInstructions string) PromptTemplateOption {
+	return func(p *CreatePromptOptions) {
+		p.FormatInstructions = formatInstructions
+	}
+}
+
+// WithInputVariables is a function that sets custom input variables for the prompt template.
+func WithInputVariables(inputVariables []string) PromptTemplateOption {
+	return func(p *CreatePromptOptions) {
+		p.InputVariables = inputVariables
+	}
+}
+
+func (options *CreatePromptOptions) handleDefaultValues() {
+	if options.Prefix == "" {
+		options.Prefix = Prefix
+	}
+
+	if options.Suffix == "" {
+		options.Suffix = Suffix
+	}
+
+	if options.FormatInstructions == "" {
+		options.FormatInstructions = FormatInstructions
+	}
+}
+
+// createPrompt is a function that takes a slice of tools and a variadic list of prompt
+// template options, and returns a prompt template with the specified options.
+// It returns an error if there is any issue during the creation process.
+func createPrompt(
+	tools []tools.Tool,
+	options ...PromptTemplateOption,
+) (prompts.PromptTemplate, error) {
+	opts := &CreatePromptOptions{}
+	for _, option := range options {
+		option(opts)
+	}
+	opts.handleDefaultValues()
+
+	var toolsStrings strings.Builder
+	for _, tool := range tools {
+		toolsStrings.WriteString(fmt.Sprintf("%s: %s\n", tool.Name, tool.Description))
+	}
+
+	var toolsNames strings.Builder
+	for i, tool := range tools {
+		if i > 0 {
+			toolsNames.WriteString(", ")
+		}
+		toolsNames.WriteString(tool.Name)
+	}
+	formatInstructions := strings.Replace(FormatInstructions, "{tool_names}", toolsNames.String(), -1)
+
+	template := strings.Join([]string{opts.Prefix, toolsStrings.String(), formatInstructions, opts.Suffix}, "\n\n")
+
+	return prompts.NewPromptTemplate(template, []string{"input", "tool_names", "agent_scratchpad"})
+}
diff --git a/exp/doc.go b/exp/doc.go
deleted file mode 100644
index f06dd638..00000000
--- a/exp/doc.go
+++ /dev/null
@@ -1,3 +0,0 @@
-// Package exp constains experiemntal code that is subject to change or removal.
-// There are no compatibility guarantees for this package and its subpackages.
-package exp
diff --git a/exp/prompts/chat_template.go b/exp/prompts/chatTemplate.go
similarity index 97%
rename from exp/prompts/chat_template.go
rename to exp/prompts/chatTemplate.go
index 595765e2..00e6df36 100644
--- a/exp/prompts/chat_template.go
+++ b/exp/prompts/chatTemplate.go
@@ -4,7 +4,7 @@ import (
 	"fmt"
 	"strings"
 
-	"github.com/tmc/langchaingo/schema"
+	"github.com/tmc/langchaingo/exp/schema"
 )
 
 /*
@@ -20,7 +20,7 @@ type Message struct {
 func (m Message) format(values map[string]any) (schema.ChatMessage, error) {
 	formatted, err := m.prompt.Format(values)
 	if err != nil {
-		return schema.AIChatMessage{}, err
+		return schema.AiChatMessage{}, err
 	}
 
 	return m.toChatMessage(formatted), nil
@@ -45,7 +45,7 @@ func NewHumanMessage(systemPrompt Template) Message {
 func NewAiMessage(systemPrompt Template) Message {
 	return Message{
 		prompt:         systemPrompt,
-		toChatMessage:  func(message string) schema.ChatMessage { return schema.AIChatMessage{Text: message} },
+		toChatMessage:  func(message string) schema.ChatMessage { return schema.AiChatMessage{Text: message} },
 		inputVariables: systemPrompt.GetInputVariables(),
 	}
 }
diff --git a/exp/prompts/doc.go b/exp/prompts/doc.go
deleted file mode 100644
index 81c39cb1..00000000
--- a/exp/prompts/doc.go
+++ /dev/null
@@ -1,3 +0,0 @@
-// Package prompts contains utilities to manage prompt templates, output parsers, example
-// selectors, and other utilities for working with LLM prompts.
-package prompts
diff --git a/exp/prompts/prompt_selector.go b/exp/prompts/promptSelector.go
similarity index 100%
rename from exp/prompts/prompt_selector.go
rename to exp/prompts/promptSelector.go
diff --git a/exp/prompts/prompt_template.go b/exp/prompts/promptTemplate.go
similarity index 100%
rename from exp/prompts/prompt_template.go
rename to exp/prompts/promptTemplate.go
diff --git a/exp/prompts/prompts.go b/exp/prompts/prompts.go
index 354d7bf4..53f36990 100644
--- a/exp/prompts/prompts.go
+++ b/exp/prompts/prompts.go
@@ -1,7 +1,7 @@
 package prompts
 
 import (
-	"github.com/tmc/langchaingo/schema"
+	"github.com/tmc/langchaingo/exp/schema"
 )
 
 type Template interface {
diff --git a/exp/tools/serpapi/internal/serpapi_client.go b/exp/tools/serpapi/internal/serpapi_client.go
new file mode 100644
index 00000000..146bd784
--- /dev/null
+++ b/exp/tools/serpapi/internal/serpapi_client.go
@@ -0,0 +1,109 @@
+package internal
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"io"
+	"log"
+	"net/http"
+	"net/url"
+	"os"
+	"strings"
+)
+
+var ErrMissingToken = errors.New("missing the OpenAI API key, set it in the SERPAPI_API_KEY environment variable")
+var NoGoodResult = "No good search result found"
+
+type SerpapiClient struct {
+	apiKey  string
+	baseURL string
+}
+
+func New() (*SerpapiClient, error) {
+	apiKey := os.Getenv("SERPAPI_API_KEY")
+	if apiKey == "" {
+		return nil, ErrMissingToken
+	}
+
+	return &SerpapiClient{
+			apiKey:  apiKey,
+			baseURL: "https://serpapi.com/search",
+		},
+		nil
+}
+
+func (s *SerpapiClient) Search(query string) (string, error) {
+	params := make(url.Values)
+	query = strings.ReplaceAll(query, " ", "+")
+	params.Add("q", query)
+	params.Add("google_domain", "google.com")
+	params.Add("gl", "us")
+	params.Add("hl", "en")
+	params.Add("api_key", s.apiKey)
+
+	reqURL := fmt.Sprintf("%s?%s", s.baseURL, params.Encode())
+	resp, err := http.Get(reqURL)
+	if err != nil {
+		log.Printf("Error: %s\n", err)
+		return "", err
+	}
+	defer resp.Body.Close()
+
+	body, err := io.ReadAll(resp.Body)
+	if err != nil {
+		log.Printf("Error: %s\n", err)
+		return "", err
+	}
+	var result map[string]interface{}
+	err = json.Unmarshal([]byte(body), &result)
+	if err != nil {
+		log.Printf("Error: %s\n", err)
+		return "", err
+	}
+
+	return processResponse(result)
+}
+
+func processResponse(res map[string]interface{}) (string, error) {
+	if errorValue, ok := res["error"]; ok {
+		return "", fmt.Errorf("Got error from SerpAPI: %v", errorValue)
+	}
+
+	answerBox, answerBoxExists := res["answer_box"].(map[string]interface{})
+	sportsResults, sportsResultsExists := res["sports_results"].(map[string]interface{})
+	knowledgeGraph, knowledgeGraphExists := res["knowledge_graph"].(map[string]interface{})
+	organicResults, organicResultsExists := res["organic_results"].([]interface{})
+
+	if answerBoxExists {
+		if answer, ok := answerBox["answer"].(string); ok {
+			return answer, nil
+		}
+		if snippet, ok := answerBox["snippet"].(string); ok {
+			return snippet, nil
+		}
+		if snippetHighlightedWords, ok := answerBox["snippet_highlighted_words"].([]interface{}); ok && len(snippetHighlightedWords) > 0 {
+			return snippetHighlightedWords[0].(string), nil
+		}
+	}
+	if sportsResultsExists {
+		if gameSpotlight, ok := sportsResults["game_spotlight"].(string); ok {
+			return gameSpotlight, nil
+		}
+	}
+	if knowledgeGraphExists {
+		if description, ok := knowledgeGraph["description"].(string); ok {
+			return description, nil
+		}
+	}
+	if organicResultsExists && len(organicResults) > 0 {
+		organicResult, ok := organicResults[0].(map[string]interface{})
+		if ok {
+			if snippet, ok := organicResult["snippet"].(string); ok {
+				return snippet, nil
+			}
+		}
+	}
+
+	return NoGoodResult, nil
+}
diff --git a/exp/tools/serpapi/serpapi_tool.go b/exp/tools/serpapi/serpapi_tool.go
new file mode 100644
index 00000000..9637f29e
--- /dev/null
+++ b/exp/tools/serpapi/serpapi_tool.go
@@ -0,0 +1,34 @@
+package serpapi
+
+import (
+	"strings"
+
+	"github.com/tmc/langchaingo/exp/tools"
+	"github.com/tmc/langchaingo/exp/tools/serpapi/internal"
+)
+
+//Create a new tool for serpapi to search on internet
+func New() (*tools.Tool, error) {
+	client, err := internal.New()
+	if err != nil {
+		return nil, err
+	}
+	return &tools.Tool{
+		Name: "Google Search",
+		Description: `"A wrapper around Google Search. "
+        "Useful for when you need to answer questions about current events. "
+		"Always one of the first options when you need to find information on internet"
+        "Input should be a search query."`,
+		Run: func(input string) (string, error) {
+			result, err := client.Search(input)
+			if err != nil {
+				return "", err
+			}
+			if len(result) == 0 {
+				return "No good Google Search Result was found", nil
+			}
+			return strings.Join(strings.Fields(result), " "), nil
+		},
+	}, nil
+
+}
diff --git a/exp/tools/tool.go b/exp/tools/tool.go
new file mode 100644
index 00000000..1fdcb84d
--- /dev/null
+++ b/exp/tools/tool.go
@@ -0,0 +1,17 @@
+// Package tools defines the types for tools to be used by the llms agents.
+package tools
+
+// Tool is a tool for the llm agent to interact with diferent application.
+type Tool struct {
+	Name        string
+	Description string
+	Run         func(string) (string, error)
+}
+
+func NewTool(name, description string, run func(string) (string, error)) *Tool {
+	return &Tool{
+		Name:        name,
+		Description: description,
+		Run:         run,
+	}
+}
diff --git a/go.mod b/go.mod
index 1b9c09d9..8e600490 100644
--- a/go.mod
+++ b/go.mod
@@ -2,7 +2,4 @@ module github.com/tmc/langchaingo
 
 go 1.20
 
-require (
-	github.com/google/go-cmp v0.5.9
-	github.com/google/uuid v1.3.0
-)
+require github.com/google/uuid v1.3.0
diff --git a/go.sum b/go.sum
index c1f0f156..3dfe1c9f 100644
--- a/go.sum
+++ b/go.sum
@@ -1,4 +1,2 @@
-github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
-github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
 github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
diff --git a/llms/huggingface/huggingfacellm.go b/llms/huggingface/huggingfacellm.go
index 419076ab..cecccae6 100644
--- a/llms/huggingface/huggingfacellm.go
+++ b/llms/huggingface/huggingfacellm.go
@@ -22,8 +22,8 @@ type LLM struct {
 
 var _ llms.LLM = (*LLM)(nil)
 
-func (o *LLM) Call(prompt string, stopWords []string) (string, error) {
-	r, err := o.Generate([]string{prompt}, stopWords)
+func (o *LLM) Call(prompt string) (string, error) {
+	r, err := o.Generate([]string{prompt})
 	if err != nil {
 		return "", err
 	}
@@ -33,7 +33,7 @@ func (o *LLM) Call(prompt string, stopWords []string) (string, error) {
 	return r[0].Text, nil
 }
 
-func (o *LLM) Generate(prompts []string, stopWords []string) ([]*llms.Generation, error) {
+func (o *LLM) Generate(prompts []string) ([]*llms.Generation, error) {
 	result, err := o.client.RunInference(context.TODO(), &huggingfaceclient.InferenceRequest{
 		RepoID: "google/flan-t5-xl",
 		Prompt: prompts[0],
diff --git a/llms/local/localllm.go b/llms/local/localllm.go
index 21e4e1dd..c2d1012f 100644
--- a/llms/local/localllm.go
+++ b/llms/local/localllm.go
@@ -33,8 +33,8 @@ type LLM struct {
 var _ llms.LLM = (*LLM)(nil)
 
 // Call calls the local LLM binary with the given prompt.
-func (o *LLM) Call(prompt string, stopWords []string) (string, error) {
-	r, err := o.Generate([]string{prompt}, stopWords)
+func (o *LLM) Call(prompt string) (string, error) {
+	r, err := o.Generate([]string{prompt})
 	if err != nil {
 		return "", err
 	}
@@ -45,7 +45,7 @@ func (o *LLM) Call(prompt string, stopWords []string) (string, error) {
 }
 
 // Generate generates completions using the local LLM binary.
-func (o *LLM) Generate(prompts []string, stopWords []string) ([]*llms.Generation, error) {
+func (o *LLM) Generate(prompts []string) ([]*llms.Generation, error) {
 	result, err := o.client.CreateCompletion(context.TODO(), &localclient.CompletionRequest{
 		Prompt: prompts[0],
 	})
diff --git a/schema/language_model.go b/schema/language_model.go
deleted file mode 100644
index f7f28837..00000000
--- a/schema/language_model.go
+++ /dev/null
@@ -1,11 +0,0 @@
-package schema
-
-// LanguageModel is the base class for all language models.
-type LanguageModel interface {
-	// Take in a list of prompt values and return an LLMResult.
-	GeneratePrompt(prompts []PromptValue, stop []string) LLMResult
-	// Get the number of tokens present in the text.
-	GetNumTokens(text string) int
-	// Get the number of tokens in the message.
-	GetNumTokensFromMessages(messages []ChatMessage) int
-}
diff --git a/schema/memory.go b/schema/memory.go
deleted file mode 100644
index 1ae2bf0c..00000000
--- a/schema/memory.go
+++ /dev/null
@@ -1,14 +0,0 @@
-package schema
-
-// Memory is the interface for memory in chains.
-type Memory interface {
-	// Input keys this memory class will load dynamically.
-	MemoryVariables() []string
-	// Return key-value pairs given the text input to the chain.
-	// If None, return all memories
-	LoadMemoryVariables(inputs map[string]any) map[string]any
-	// Save the context of this model run to memory.
-	SaveContext(inputs map[string]any, outputs map[string]string) error
-	// Clear memory contents.
-	Clear() error
-}
diff --git a/schema/prompts.go b/schema/prompts.go
deleted file mode 100644
index 8bf3ab24..00000000
--- a/schema/prompts.go
+++ /dev/null
@@ -1,7 +0,0 @@
-package schema
-
-// PromptValue is the interface that all prompt values must implement.
-type PromptValue interface {
-	String() string
-	Messages() []ChatMessage
-}


Test patch:
diff --git a/exp/prompts/chat_test.go b/exp/prompts/chat_test.go
index ca397751..041bf3a8 100644
--- a/exp/prompts/chat_test.go
+++ b/exp/prompts/chat_test.go
@@ -1,26 +1,26 @@
 package prompts
 
 import (
+	"reflect"
 	"testing"
 
-	"github.com/google/go-cmp/cmp"
-	"github.com/tmc/langchaingo/schema"
+	"github.com/tmc/langchaingo/exp/schema"
 )
 
 func TestChatTemplate(t *testing.T) {
 	systemPrompt, err := NewPromptTemplate("Here's some context: {context}", []string{"context"})
 	if err != nil {
-		t.Fatal(err)
+		t.Errorf("Unexpected error: %s", err.Error())
 	}
 
 	userPrompt, err := NewPromptTemplate("Hello AI. Give me a long response. {question}", []string{"question"})
 	if err != nil {
-		t.Fatal(err)
+		t.Errorf("Unexpected error: %s", err.Error())
 	}
 
 	aiPrompt, err := NewPromptTemplate("Very good question. My answer to {question} is {answer}", []string{"answer", "question"})
 	if err != nil {
-		t.Fatal(err)
+		t.Errorf("Unexpected error: %s", err.Error())
 	}
 
 	messages := []Message{
@@ -41,24 +41,19 @@ func TestChatTemplate(t *testing.T) {
 
 	chatTemplate, err := NewChatTemplate(messages, []string{"answer", "context", "question"})
 	if err != nil {
-		t.Fatal(err)
+		t.Errorf("Unexpected error: %s", err.Error())
 	}
 
 	chatMessages, err := chatTemplate.FormatPromptValue(map[string]any{"context": "foo", "question": "bar", "answer": "foobar"})
-	if err != nil {
-		t.Fatal(err)
-	}
 	expectedChatMessages := []schema.ChatMessage{
 		schema.SystemChatMessage{Text: "Here's some context: foo"},
 		schema.HumanChatMessage{Text: "Hello AI. Give me a long response. bar"},
-		schema.AIChatMessage{Text: "Very good question. My answer to bar is foobar"},
+		schema.AiChatMessage{Text: "Very good question. My answer to bar is foobar"},
 	}
 	expectedString := `[{"text":"Here's some context: foo"},{"text":"Hello AI. Give me a long response. bar"},{"text":"Very good question. My answer to bar is foobar"}]`
 
-	// use cmp to compare:
-	if !cmp.Equal(chatMessages.ToChatMessages(), expectedChatMessages) {
-		t.Errorf("Chat template format prompt value chat messages not equal to expected. Diff: %s", cmp.Diff(chatMessages.ToChatMessages(), expectedChatMessages))
-
+	if !reflect.DeepEqual(chatMessages.ToChatMessages(), expectedChatMessages) {
+		t.Errorf("Chat template format prompt value chat messages not equal to expected. Got: %v. Expect: %v", chatMessages.ToChatMessages(), expectedChatMessages)
 	}
 
 	if !(chatMessages.String() == expectedString) {