-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
cc951a9
commit add25a2
Showing
1 changed file
with
85 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,86 @@ | ||
## Chapter 12: Creating Custom Tools | ||
LangChain allows you to create your own custom tools and incorporate them into your agents. | ||
|
||
``` | ||
from langchain.agents import Tool, AgentExecutor, LLMSingleActionAgent | ||
from langchain.prompts import StringPromptTemplate | ||
from langchain import OpenAI, SerpAPIWrapper, LLMChain | ||
from typing import List, Union | ||
from langchain.schema import AgentAction, AgentFinish | ||
class CustomPromptTemplate(StringPromptTemplate): | ||
template: str | ||
tools: List[Tool] | ||
def format(self, **kwargs) -> str: | ||
intermediate_steps = kwargs.pop("intermediate_steps") | ||
thoughts = "" | ||
for action, observation in intermediate_steps: | ||
thoughts += action.log | ||
thoughts += f"\nObservation: {observation}\nThought: " | ||
kwargs["agent_scratchpad"] = thoughts | ||
kwargs["tools"] = "\n".join([f"{tool.name}: {tool.description}" for tool in self.tools]) | ||
kwargs["tool_names"] = ", ".join([tool.name for tool in self.tools]) | ||
return self.template.format(**kwargs) | ||
def random_word(word: str) -> str: | ||
return f"A random word related to {word} is: example" | ||
search = SerpAPIWrapper() | ||
tools = [ | ||
Tool( | ||
name = "Search", | ||
func=search.run, | ||
description="useful for when you need to answer questions about current events" | ||
), | ||
Tool( | ||
name = "RandomWord", | ||
func=random_word, | ||
description="useful for when you need a random word related to a given word" | ||
) | ||
] | ||
prompt = CustomPromptTemplate( | ||
template="Answer the following questions as best you can. You have access to the following tools:\n\n{tools}\n\nUse the following format:\n\nQuestion: the input question you must answer\nThought: you should always think about what to do\nAction: the action to take, should be one of [{tool_names}]\nAction Input: the input to the action\nObservation: the result of the action\n... (this Thought/Action/Action Input/Observation can repeat N times)\nThought: I now know the final answer\nFinal Answer: the final answer to the original input question\n\nBegin!\n\nQuestion: {input}\nThought:{agent_scratchpad}", | ||
tools=tools, | ||
) | ||
class CustomOutputParser: | ||
def parse(self, llm_output: str) -> Union[AgentAction, AgentFinish]: | ||
if "Final Answer:" in llm_output: | ||
return AgentFinish( | ||
return_values={"output": llm_output.split("Final Answer:")[-1].strip()}, | ||
log=llm_output, | ||
) | ||
regex = r"Action: (.*?)\nAction Input: (.*)" | ||
match = re.search(regex, llm_output, re.DOTALL) | ||
if not match: | ||
raise ValueError(f"Could not parse LLM output: `{llm_output}`") | ||
action = match.group(1).strip() | ||
action_input = match.group(2).strip() | ||
return AgentAction(tool=action, tool_input=action_input, log=llm_output) | ||
output_parser = CustomOutputParser() | ||
# LLM | ||
llm = OpenAI(temperature=0) | ||
llm_chain = LLMChain(llm=llm, prompt=prompt) | ||
tool_names = [tool.name for tool in tools] | ||
agent = LLMSingleActionAgent( | ||
llm_chain=llm_chain, | ||
output_parser=output_parser, | ||
stop=["\nObservation:"], | ||
allowed_tools=tool_names | ||
) | ||
agent_executor = AgentExecutor.from_agent_and_tools(agent=agent, tools=tools, verbose=True) | ||
agent_executor.run("What's a random word related to 'programming'?") | ||
``` |