mirror of
https://github.com/hwchase17/langchain.git
synced 2025-06-23 23:29:21 +00:00
Add PATCH and DELETE to OpenAPI Agent (#2729)
This PR proposes an update to the OpenAPI Planner and Planner Prompts to make Patch and Delete available to the planner and executor. I followed the same patterns as for GET and POST, and made some updates to the examples available to the Planner and Orchestrator. Of note, I tried to write prompts for DELETE such that the model will only execute that job if the User specifically asks for a 'Delete' (see the Prompt_planner.py examples to see specificity), or if the User had previously authorized the Delete in the Conversation memory. Although PATCH also modifies existing data, I considered it lower risk and so did not try to enforce the same restrictions on the Planner.
This commit is contained in:
parent
8073bc849f
commit
446c3d586c
@ -14,9 +14,13 @@ from langchain.agents.agent_toolkits.openapi.planner_prompt import (
|
|||||||
API_PLANNER_PROMPT,
|
API_PLANNER_PROMPT,
|
||||||
API_PLANNER_TOOL_DESCRIPTION,
|
API_PLANNER_TOOL_DESCRIPTION,
|
||||||
API_PLANNER_TOOL_NAME,
|
API_PLANNER_TOOL_NAME,
|
||||||
|
PARSING_DELETE_PROMPT,
|
||||||
PARSING_GET_PROMPT,
|
PARSING_GET_PROMPT,
|
||||||
|
PARSING_PATCH_PROMPT,
|
||||||
PARSING_POST_PROMPT,
|
PARSING_POST_PROMPT,
|
||||||
|
REQUESTS_DELETE_TOOL_DESCRIPTION,
|
||||||
REQUESTS_GET_TOOL_DESCRIPTION,
|
REQUESTS_GET_TOOL_DESCRIPTION,
|
||||||
|
REQUESTS_PATCH_TOOL_DESCRIPTION,
|
||||||
REQUESTS_POST_TOOL_DESCRIPTION,
|
REQUESTS_POST_TOOL_DESCRIPTION,
|
||||||
)
|
)
|
||||||
from langchain.agents.agent_toolkits.openapi.spec import ReducedOpenAPISpec
|
from langchain.agents.agent_toolkits.openapi.spec import ReducedOpenAPISpec
|
||||||
@ -90,6 +94,56 @@ class RequestsPostToolWithParsing(BaseRequestsTool, BaseTool):
|
|||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
|
||||||
|
class RequestsPatchToolWithParsing(BaseRequestsTool, BaseTool):
|
||||||
|
name = "requests_patch"
|
||||||
|
description = REQUESTS_PATCH_TOOL_DESCRIPTION
|
||||||
|
|
||||||
|
response_length: Optional[int] = MAX_RESPONSE_LENGTH
|
||||||
|
llm_chain = LLMChain(
|
||||||
|
llm=OpenAI(),
|
||||||
|
prompt=PARSING_PATCH_PROMPT,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _run(self, text: str) -> str:
|
||||||
|
try:
|
||||||
|
data = json.loads(text)
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
raise e
|
||||||
|
response = self.requests_wrapper.patch(data["url"], data["data"])
|
||||||
|
response = response[: self.response_length]
|
||||||
|
return self.llm_chain.predict(
|
||||||
|
response=response, instructions=data["output_instructions"]
|
||||||
|
).strip()
|
||||||
|
|
||||||
|
async def _arun(self, text: str) -> str:
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
|
||||||
|
class RequestsDeleteToolWithParsing(BaseRequestsTool, BaseTool):
|
||||||
|
name = "requests_delete"
|
||||||
|
description = REQUESTS_DELETE_TOOL_DESCRIPTION
|
||||||
|
|
||||||
|
response_length: Optional[int] = MAX_RESPONSE_LENGTH
|
||||||
|
llm_chain = LLMChain(
|
||||||
|
llm=OpenAI(),
|
||||||
|
prompt=PARSING_DELETE_PROMPT,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _run(self, text: str) -> str:
|
||||||
|
try:
|
||||||
|
data = json.loads(text)
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
raise e
|
||||||
|
response = self.requests_wrapper.delete(data["url"])
|
||||||
|
response = response[: self.response_length]
|
||||||
|
return self.llm_chain.predict(
|
||||||
|
response=response, instructions=data["output_instructions"]
|
||||||
|
).strip()
|
||||||
|
|
||||||
|
async def _arun(self, text: str) -> str:
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Orchestrator, planner, controller.
|
# Orchestrator, planner, controller.
|
||||||
#
|
#
|
||||||
@ -157,7 +211,7 @@ def _create_api_controller_tool(
|
|||||||
base_url = api_spec.servers[0]["url"] # TODO: do better.
|
base_url = api_spec.servers[0]["url"] # TODO: do better.
|
||||||
|
|
||||||
def _create_and_run_api_controller_agent(plan_str: str) -> str:
|
def _create_and_run_api_controller_agent(plan_str: str) -> str:
|
||||||
pattern = r"\b(GET|POST)\s+(/\S+)*"
|
pattern = r"\b(GET|POST|PATCH|DELETE)\s+(/\S+)*"
|
||||||
matches = re.findall(pattern, plan_str)
|
matches = re.findall(pattern, plan_str)
|
||||||
endpoint_names = [
|
endpoint_names = [
|
||||||
"{method} {route}".format(method=method, route=route.split("?")[0])
|
"{method} {route}".format(method=method, route=route.split("?")[0])
|
||||||
|
@ -2,13 +2,16 @@
|
|||||||
|
|
||||||
from langchain.prompts.prompt import PromptTemplate
|
from langchain.prompts.prompt import PromptTemplate
|
||||||
|
|
||||||
|
|
||||||
API_PLANNER_PROMPT = """You are a planner that plans a sequence of API calls to assist with user queries against an API.
|
API_PLANNER_PROMPT = """You are a planner that plans a sequence of API calls to assist with user queries against an API.
|
||||||
|
|
||||||
You should:
|
You should:
|
||||||
1) evaluate whether the user query can be solved by the API documentated below. If no, say why.
|
1) evaluate whether the user query can be solved by the API documentated below. If no, say why.
|
||||||
2) if yes, generate a plan of API calls and say what they are doing step by step.
|
2) if yes, generate a plan of API calls and say what they are doing step by step.
|
||||||
|
3) If the plan includes a DELETE call, you should always return an ask from the User for authorization first unless the User has specifically asked to delete something.
|
||||||
|
|
||||||
You should only use API endpoints documented below ("Endpoints you can use:").
|
You should only use API endpoints documented below ("Endpoints you can use:").
|
||||||
|
You can only use the DELETE tool if the User has specifically asked to delete something. Otherwise, you should return a request authorization from the User first.
|
||||||
Some user queries can be resolved in a single API call, but some will require several API calls.
|
Some user queries can be resolved in a single API call, but some will require several API calls.
|
||||||
The plan will be passed to an API controller that can format it into web requests and return the responses.
|
The plan will be passed to an API controller that can format it into web requests and return the responses.
|
||||||
|
|
||||||
@ -20,15 +23,31 @@ Fake endpoints for examples:
|
|||||||
GET /user to get information about the current user
|
GET /user to get information about the current user
|
||||||
GET /products/search search across products
|
GET /products/search search across products
|
||||||
POST /users/{{id}}/cart to add products to a user's cart
|
POST /users/{{id}}/cart to add products to a user's cart
|
||||||
|
PATCH /users/{{id}}/cart to update a user's cart
|
||||||
|
DELETE /users/{{id}}/cart to delete a user's cart
|
||||||
|
|
||||||
User query: tell me a joke
|
User query: tell me a joke
|
||||||
Plan: Sorry, this API's domain is shopping, not comedy.
|
Plan: Sorry, this API's domain is shopping, not comedy.
|
||||||
|
|
||||||
Usery query: I want to buy a couch
|
Usery query: I want to buy a couch
|
||||||
Plan: 1. GET /products/search to search for couches
|
Plan: 1. GET /products with a query param to search for couches
|
||||||
2. GET /user to find the user's id
|
2. GET /user to find the user's id
|
||||||
3. POST /users/{{id}}/cart to add a couch to the user's cart
|
3. POST /users/{{id}}/cart to add a couch to the user's cart
|
||||||
|
|
||||||
|
User query: I want to add a lamp to my cart
|
||||||
|
Plan: 1. GET /products with a query param to search for lamps
|
||||||
|
2. GET /user to find the user's id
|
||||||
|
3. PATCH /users/{{id}}/cart to add a lamp to the user's cart
|
||||||
|
|
||||||
|
User query: I want to delete my cart
|
||||||
|
Plan: 1. GET /user to find the user's id
|
||||||
|
2. DELETE required. Did user specify DELETE or previously authorize? Yes, proceed.
|
||||||
|
3. DELETE /users/{{id}}/cart to delete the user's cart
|
||||||
|
|
||||||
|
User query: I want to start a new cart
|
||||||
|
Plan: 1. GET /user to find the user's id
|
||||||
|
2. DELETE required. Did user specify DELETE or previously authorize? No, ask for authorization.
|
||||||
|
3. Are you sure you want to delete your cart?
|
||||||
----
|
----
|
||||||
|
|
||||||
Here are endpoints you can use. Do not reference any of the endpoints above.
|
Here are endpoints you can use. Do not reference any of the endpoints above.
|
||||||
@ -83,6 +102,7 @@ API_CONTROLLER_TOOL_DESCRIPTION = f"Can be used to execute a plan of API calls,
|
|||||||
API_ORCHESTRATOR_PROMPT = """You are an agent that assists with user queries against API, things like querying information or creating resources.
|
API_ORCHESTRATOR_PROMPT = """You are an agent that assists with user queries against API, things like querying information or creating resources.
|
||||||
Some user queries can be resolved in a single API call, particularly if you can find appropriate params from the OpenAPI spec; though some require several API call.
|
Some user queries can be resolved in a single API call, particularly if you can find appropriate params from the OpenAPI spec; though some require several API call.
|
||||||
You should always plan your API calls first, and then execute the plan second.
|
You should always plan your API calls first, and then execute the plan second.
|
||||||
|
If the plan includes a DELETE call, be sure to ask the User for authorization first unless the User has specifically asked to delete something.
|
||||||
You should never return information without executing the api_controller tool.
|
You should never return information without executing the api_controller tool.
|
||||||
|
|
||||||
|
|
||||||
@ -145,7 +165,7 @@ REQUESTS_POST_TOOL_DESCRIPTION = """Use this when you want to POST to a website.
|
|||||||
Input to the tool should be a json string with 3 keys: "url", "data", and "output_instructions".
|
Input to the tool should be a json string with 3 keys: "url", "data", and "output_instructions".
|
||||||
The value of "url" should be a string.
|
The value of "url" should be a string.
|
||||||
The value of "data" should be a dictionary of key-value pairs you want to POST to the url.
|
The value of "data" should be a dictionary of key-value pairs you want to POST to the url.
|
||||||
The value of "summary_instructions" should be instructions on what information to extract from the response, for example the id(s) for a resource(s) that the POST request creates.
|
The value of "output_instructions" should be instructions on what information to extract from the response, for example the id(s) for a resource(s) that the POST request creates.
|
||||||
Always use double quotes for strings in the json string."""
|
Always use double quotes for strings in the json string."""
|
||||||
|
|
||||||
PARSING_POST_PROMPT = PromptTemplate(
|
PARSING_POST_PROMPT = PromptTemplate(
|
||||||
@ -157,3 +177,37 @@ If the response indicates an error, you should instead output a summary of the e
|
|||||||
Output:""",
|
Output:""",
|
||||||
input_variables=["response", "instructions"],
|
input_variables=["response", "instructions"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
REQUESTS_PATCH_TOOL_DESCRIPTION = """Use this when you want to PATCH content on a website.
|
||||||
|
Input to the tool should be a json string with 3 keys: "url", "data", and "output_instructions".
|
||||||
|
The value of "url" should be a string.
|
||||||
|
The value of "data" should be a dictionary of key-value pairs of the body params available in the OpenAPI spec you want to PATCH the content with at the url.
|
||||||
|
The value of "output_instructions" should be instructions on what information to extract from the response, for example the id(s) for a resource(s) that the PATCH request creates.
|
||||||
|
Always use double quotes for strings in the json string."""
|
||||||
|
|
||||||
|
PARSING_PATCH_PROMPT = PromptTemplate(
|
||||||
|
template="""Here is an API response:\n\n{response}\n\n====
|
||||||
|
Your task is to extract some information according to these instructions: {instructions}
|
||||||
|
When working with API objects, you should usually use ids over names. Do not return any ids or names that are not in the response.
|
||||||
|
If the response indicates an error, you should instead output a summary of the error.
|
||||||
|
|
||||||
|
Output:""",
|
||||||
|
input_variables=["response", "instructions"],
|
||||||
|
)
|
||||||
|
|
||||||
|
REQUESTS_DELETE_TOOL_DESCRIPTION = """ONLY USE THIS TOOL WHEN THE USER HAS SPECIFICALLY REQUESTED TO DELETE CONTENT FROM A WEBSITE.
|
||||||
|
Input to the tool should be a json string with 2 keys: "url", and "output_instructions".
|
||||||
|
The value of "url" should be a string.
|
||||||
|
The value of "output_instructions" should be instructions on what information to extract from the response, for example the id(s) for a resource(s) that the DELETE request creates.
|
||||||
|
Always use double quotes for strings in the json string.
|
||||||
|
ONLY USE THIS TOOL IF THE USER HAS SPECIFICALLY REQUESTED TO DELETE SOMETHING."""
|
||||||
|
|
||||||
|
PARSING_DELETE_PROMPT = PromptTemplate(
|
||||||
|
template="""Here is an API response:\n\n{response}\n\n====
|
||||||
|
Your task is to extract some information according to these instructions: {instructions}
|
||||||
|
When working with API objects, you should usually use ids over names. Do not return any ids or names that are not in the response.
|
||||||
|
If the response indicates an error, you should instead output a summary of the error.
|
||||||
|
|
||||||
|
Output:""",
|
||||||
|
input_variables=["response", "instructions"],
|
||||||
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user