mirror of
https://github.com/langchain-ai/langchain.git
synced 2026-10-07 18:35:22 +03:00
- Remove dynamic model creation in the `args()` property. _Only infer for the decorator (and add an argument to NOT infer if someone wishes to only pass as a string)_ - Update the validation example to make it less likely to be misinterpreted as a "safe" way to run a repl There is one example of "Multi-argument tools" in the custom_tools.ipynb from yesterday, but we could add more. The output parsing for the base MRKL agent hasn't been adapted to handle structured args at this point in time --------- Co-authored-by: Harrison Chase <hw.chase.17@gmail.com>
13 KiB
13 KiB
In [1]:
from typing import Any, Dict
from langchain.agents import AgentType, initialize_agent
from langchain.llms import OpenAI
from langchain.tools.requests.tool import RequestsGetTool, TextRequestsWrapper
from pydantic import BaseModel, Field, root_validator
In [2]:
llm = OpenAI(temperature=0)In [3]:
!pip install tldextract > /dev/null[1m[[0m[34;49mnotice[0m[1;39;49m][0m[39;49m A new release of pip is available: [0m[31;49m23.0.1[0m[39;49m -> [0m[32;49m23.1[0m [1m[[0m[34;49mnotice[0m[1;39;49m][0m[39;49m To update, run: [0m[32;49mpip install --upgrade pip[0m
In [4]:
import tldextract
_APPROVED_DOMAINS = {
"langchain",
"wikipedia",
}
class ToolInputSchema(BaseModel):
url: str = Field(...)
@root_validator
def validate_query(cls, values: Dict[str, Any]) -> Dict:
url = values["url"]
domain = tldextract.extract(url).domain
if domain not in _APPROVED_DOMAINS:
raise ValueError(f"Domain {domain} is not on the approved list:"
f" {sorted(_APPROVED_DOMAINS)}")
return values
tool = RequestsGetTool(args_schema=ToolInputSchema, requests_wrapper=TextRequestsWrapper())In [5]:
agent = initialize_agent([tool], llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=False)In [6]:
# This will succeed, since there aren't any arguments that will be triggered during validation
answer = agent.run("What's the main title on langchain.com?")
print(answer)The main title of langchain.com is "LANG CHAIN 🦜️🔗 Official Home Page"
In [7]:
agent.run("What's the main title on google.com?")[0;31m---------------------------------------------------------------------------[0m
[0;31mValidationError[0m Traceback (most recent call last)
Cell [0;32mIn[7], line 1[0m
[0;32m----> 1[0m agent[39m.[39;49mrun([39m"[39;49m[39mWhat[39;49m[39m'[39;49m[39ms the main title on google.com?[39;49m[39m"[39;49m)
File [0;32m~/code/lc/lckg/langchain/chains/base.py:213[0m, in [0;36mChain.run[0;34m(self, *args, **kwargs)[0m
[1;32m 211[0m [39mif[39;00m [39mlen[39m(args) [39m!=[39m [39m1[39m:
[1;32m 212[0m [39mraise[39;00m [39mValueError[39;00m([39m"[39m[39m`run` supports only one positional argument.[39m[39m"[39m)
[0;32m--> 213[0m [39mreturn[39;00m [39mself[39;49m(args[[39m0[39;49m])[[39mself[39m[39m.[39moutput_keys[[39m0[39m]]
[1;32m 215[0m [39mif[39;00m kwargs [39mand[39;00m [39mnot[39;00m args:
[1;32m 216[0m [39mreturn[39;00m [39mself[39m(kwargs)[[39mself[39m[39m.[39moutput_keys[[39m0[39m]]
File [0;32m~/code/lc/lckg/langchain/chains/base.py:116[0m, in [0;36mChain.__call__[0;34m(self, inputs, return_only_outputs)[0m
[1;32m 114[0m [39mexcept[39;00m ([39mKeyboardInterrupt[39;00m, [39mException[39;00m) [39mas[39;00m e:
[1;32m 115[0m [39mself[39m[39m.[39mcallback_manager[39m.[39mon_chain_error(e, verbose[39m=[39m[39mself[39m[39m.[39mverbose)
[0;32m--> 116[0m [39mraise[39;00m e
[1;32m 117[0m [39mself[39m[39m.[39mcallback_manager[39m.[39mon_chain_end(outputs, verbose[39m=[39m[39mself[39m[39m.[39mverbose)
[1;32m 118[0m [39mreturn[39;00m [39mself[39m[39m.[39mprep_outputs(inputs, outputs, return_only_outputs)
File [0;32m~/code/lc/lckg/langchain/chains/base.py:113[0m, in [0;36mChain.__call__[0;34m(self, inputs, return_only_outputs)[0m
[1;32m 107[0m [39mself[39m[39m.[39mcallback_manager[39m.[39mon_chain_start(
[1;32m 108[0m {[39m"[39m[39mname[39m[39m"[39m: [39mself[39m[39m.[39m[39m__class__[39m[39m.[39m[39m__name__[39m},
[1;32m 109[0m inputs,
[1;32m 110[0m verbose[39m=[39m[39mself[39m[39m.[39mverbose,
[1;32m 111[0m )
[1;32m 112[0m [39mtry[39;00m:
[0;32m--> 113[0m outputs [39m=[39m [39mself[39;49m[39m.[39;49m_call(inputs)
[1;32m 114[0m [39mexcept[39;00m ([39mKeyboardInterrupt[39;00m, [39mException[39;00m) [39mas[39;00m e:
[1;32m 115[0m [39mself[39m[39m.[39mcallback_manager[39m.[39mon_chain_error(e, verbose[39m=[39m[39mself[39m[39m.[39mverbose)
File [0;32m~/code/lc/lckg/langchain/agents/agent.py:792[0m, in [0;36mAgentExecutor._call[0;34m(self, inputs)[0m
[1;32m 790[0m [39m# We now enter the agent loop (until it returns something).[39;00m
[1;32m 791[0m [39mwhile[39;00m [39mself[39m[39m.[39m_should_continue(iterations, time_elapsed):
[0;32m--> 792[0m next_step_output [39m=[39m [39mself[39;49m[39m.[39;49m_take_next_step(
[1;32m 793[0m name_to_tool_map, color_mapping, inputs, intermediate_steps
[1;32m 794[0m )
[1;32m 795[0m [39mif[39;00m [39misinstance[39m(next_step_output, AgentFinish):
[1;32m 796[0m [39mreturn[39;00m [39mself[39m[39m.[39m_return(next_step_output, intermediate_steps)
File [0;32m~/code/lc/lckg/langchain/agents/agent.py:695[0m, in [0;36mAgentExecutor._take_next_step[0;34m(self, name_to_tool_map, color_mapping, inputs, intermediate_steps)[0m
[1;32m 693[0m tool_run_kwargs[[39m"[39m[39mllm_prefix[39m[39m"[39m] [39m=[39m [39m"[39m[39m"[39m
[1;32m 694[0m [39m# We then call the tool on the tool input to get an observation[39;00m
[0;32m--> 695[0m observation [39m=[39m tool[39m.[39;49mrun(
[1;32m 696[0m agent_action[39m.[39;49mtool_input,
[1;32m 697[0m verbose[39m=[39;49m[39mself[39;49m[39m.[39;49mverbose,
[1;32m 698[0m color[39m=[39;49mcolor,
[1;32m 699[0m [39m*[39;49m[39m*[39;49mtool_run_kwargs,
[1;32m 700[0m )
[1;32m 701[0m [39melse[39;00m:
[1;32m 702[0m tool_run_kwargs [39m=[39m [39mself[39m[39m.[39magent[39m.[39mtool_run_logging_kwargs()
File [0;32m~/code/lc/lckg/langchain/tools/base.py:110[0m, in [0;36mBaseTool.run[0;34m(self, tool_input, verbose, start_color, color, **kwargs)[0m
[1;32m 101[0m [39mdef[39;00m [39mrun[39m(
[1;32m 102[0m [39mself[39m,
[1;32m 103[0m tool_input: Union[[39mstr[39m, Dict],
[0;32m (...)[0m
[1;32m 107[0m [39m*[39m[39m*[39mkwargs: Any,
[1;32m 108[0m ) [39m-[39m[39m>[39m [39mstr[39m:
[1;32m 109[0m [39m [39m[39m"""Run the tool."""[39;00m
[0;32m--> 110[0m run_input [39m=[39m [39mself[39;49m[39m.[39;49m_parse_input(tool_input)
[1;32m 111[0m [39mif[39;00m [39mnot[39;00m [39mself[39m[39m.[39mverbose [39mand[39;00m verbose [39mis[39;00m [39mnot[39;00m [39mNone[39;00m:
[1;32m 112[0m verbose_ [39m=[39m verbose
File [0;32m~/code/lc/lckg/langchain/tools/base.py:71[0m, in [0;36mBaseTool._parse_input[0;34m(self, tool_input)[0m
[1;32m 69[0m [39mif[39;00m [39missubclass[39m(input_args, BaseModel):
[1;32m 70[0m key_ [39m=[39m [39mnext[39m([39miter[39m(input_args[39m.[39m__fields__[39m.[39mkeys()))
[0;32m---> 71[0m input_args[39m.[39;49mparse_obj({key_: tool_input})
[1;32m 72[0m [39m# Passing as a positional argument is more straightforward for[39;00m
[1;32m 73[0m [39m# backwards compatability[39;00m
[1;32m 74[0m [39mreturn[39;00m tool_input
File [0;32m~/code/lc/lckg/.venv/lib/python3.11/site-packages/pydantic/main.py:526[0m, in [0;36mpydantic.main.BaseModel.parse_obj[0;34m()[0m
File [0;32m~/code/lc/lckg/.venv/lib/python3.11/site-packages/pydantic/main.py:341[0m, in [0;36mpydantic.main.BaseModel.__init__[0;34m()[0m
[0;31mValidationError[0m: 1 validation error for ToolInputSchema
__root__
Domain google is not on the approved list: ['langchain', 'wikipedia'] (type=value_error)In [ ]: