langchain-ai / langchain

🦜🔗 Build context-aware reasoning applications
https://python.langchain.com
MIT License
93.04k stars 14.95k forks source link

STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION Custom Tools Failure #7108

Closed danpechi closed 9 months ago

danpechi commented 1 year ago

System Info

I have been using STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION with a custom tool that takes 2 inputs, however have not been able to get the agent to produce the outputs we had before. Specifically, we're getting the intermediate, custom function input as output.

Ie instead of getting a value associated with a query "What is the purchase total for customer 5432 on 07-03-2023?" We are now getting

{
  "action": "database_tool",
  "action_input": {
    "customer_id": "5432",
    "local_date": "2023-07-03"
  }
}

This did not occur before this weekend. Here's more code snippet in the below example

Who can help?

@hwcha

Information

Related Components

Reproduction

I also tried simplifying the agent call, etc to no avail:

llm = ChatOpenAI(
    temperature=0,
    openai_api_key=openai.api_key
)
memory = ConversationBufferWindowMemory(
            memory_key='chat_history',
            k=3,
            return_messages=True,
            input_key='input', 
            output_key="output"
)

@tool(return_direct=False)
def database_tool(customer_id, local_date) -> str:
    """Useful when questions are asked about specific customer_id details, 
        particularly recent ones.

        If date is not provided, it will default to today's date in yyyy-mm-dd format.

        Format your input using the following template. 
    {{
    "action": "database_action",
    "action_input": {{"customer_id": "<customer id>", "local_date": "<date in yyyy-mm-dd format>"}}
    }}
    ```
"""
db_query = """<QUERY THAT WORKS>
""".format(customer_id=customer_id, local_date=local_date)

formatted_d = get_data.query_database(query=db_query)

return formatted_d

conversational_agent = initialize_agent( agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION, tools=[database_tool], llm=llm, verbose=True, early_stopping_method='generate', memory=memory, SystemAgentPromptTemplate=prompt_template+"\n The only tool available is the database tool.", return_intermediate_steps=True, return_source_documents=True, handle_parsing_errors='Check your output and make sure there is an equal number of "{" and "}"' )

response = conversational_agent("What is the purchase total for customer 5432 on 07-03-2023?") print(response['output'])



### Expected behavior

"The purchase total for customer 5432 is 59.55"

I should note the following also works; it's just the agent integration that's problematic:
n = {
    "customer_id": "5432",
    "local_date": "2023-07-03"
  }
database_tool(n)
DSLituiev commented 1 year ago

I seem getting a similar bug that the command is not being passed to the tool. Or sometimes not generated if "memory_prompts" kwarg is not provided

Setup

```python from langchain.agents import AgentType from langchain.chat_models import ChatOpenAI from langchain.agents import initialize_agent from langchain.prompts import MessagesPlaceholder from langchain.memory import ConversationBufferMemory from langchain.tools import BaseTool from langchain.schema import BaseRetriever, Document from langchain.tools import WikipediaQueryRun from langchain.utilities import WikipediaAPIWrapper from langchain.retrievers import WikipediaRetriever llm_params = { "engine": "gpt-35-turbo-16k", "temperature": 0.5, # To avoid pure copy-pasting from docs lookup "max_tokens": 6000 } llm = ChatOpenAI(**llm_params) wikipedia = WikipediaQueryRun(api_wrapper=WikipediaAPIWrapper()) class WikipediaSearchTool(BaseTool): name = "wikipedia_search" description = "Use this last. Useful for when you are not able to answer questions using the paper." + \ "Provide search terms consisting of a noun or noun phrase, like 'Canada' for canadian population" def _run(self, query:str): return Document(page_content=wikipedia.run(query)) async def _arun(self, query:str): pass tools = [WikipediaSearchTool(),] chat_history = MessagesPlaceholder(variable_name="chat_history") memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True) ```

Agent not generating commands without memory_prompts argument

agent_chain = initialize_agent(
    tools, 
    llm, 
    agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION, 
    verbose=True, 
    memory=memory, 
    agent_kwargs = {
        "input_variables": ["input", "agent_scratchpad", ]
    }
)

agent_chain.run(input="How many people live in Canada?")

Output:

Failed to load default session, using empty session: HTTPConnectionPool(host='localhost', port=8000): Max retries exceeded with url: /sessions?name=default (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at ...>: Failed to establish a new connection: [Errno 61] Connection refused'))

> Entering new AgentExecutor chain...
Failed to persist run: HTTPConnectionPool(host='localhost', port=8000): Max retries exceeded with url: /chain-runs (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at ...>: Failed to establish a new connection: [Errno 61] Connection refused'))
I can find the answer to that using the Wikipedia search tool. Let me do that for you.

> Finished chain.
Out[125]: 'I can find the answer to that using the Wikipedia search tool. Let me do that for you.'

Agent generating commands but not launching tools

agent_chain = initialize_agent(
    tools, 
    llm, 
    agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION, 
    verbose=True, 
    memory=memory, 
    agent_kwargs = {
         "memory_prompts": [chat_history],
        "input_variables": ["input", "agent_scratchpad", "chat_history"]
    }
)

agent_chain.run(input="How many people live in Canada?")

Output:

Failed to load default session, using empty session: HTTPConnectionPool(host='localhost', port=8000): Max retries exceeded with url: /sessions?name=default (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x179ce8b80>: Failed to establish a new connection: [Errno 61] Connection refused'))

> Entering new AgentExecutor chain...
Failed to persist run: HTTPConnectionPool(host='localhost', port=8000): Max retries exceeded with url: /chain-runs (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x179ce9db0>: Failed to establish a new connection: [Errno 61] Connection refused'))
{
  "action": "wikipedia_search",
  "action_input": {
    "query": {
      "title": "Demographics of Canada"
    }
  }
}

> Finished chain.
Out[123]: '{\n  "action": "wikipedia_search",\n  "action_input": {\n    "query": {\n      "title": "Demographics of Canada"\n    }\n  }\n}'
clemlesne commented 1 year ago

Same issue there.

clemlesne commented 1 year ago

Related to https://github.com/langchain-ai/langchain/issues/5870.

dosubot[bot] commented 9 months ago

Hi, @danpechi,

I'm helping the LangChain team manage their backlog and am marking this issue as stale. It seems like there was a failure in the output of a custom tool integrated with the STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION agent. Despite attempts to simplify the agent call, the problem persists. It's unclear if this issue is still relevant to the latest version of the LangChain repository. If it is, please let the LangChain team know by commenting on the issue. Otherwise, feel free to close the issue yourself, or it will be automatically closed in 7 days.

If you have any further information or updates on this issue, please share them with us. Thank you for your understanding and cooperation.

I appreciate your cooperation.

Dosu

hdnh2006 commented 6 months ago

I seem getting a similar bug that the command is not being passed to the tool. Or sometimes not generated if "memory_prompts" kwarg is not provided

Setup

Agent not generating commands without memory_prompts argument

agent_chain = initialize_agent(
    tools, 
    llm, 
    agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION, 
    verbose=True, 
    memory=memory, 
    agent_kwargs = {
        "input_variables": ["input", "agent_scratchpad", ]
    }
)

agent_chain.run(input="How many people live in Canada?")

Output:

Failed to load default session, using empty session: HTTPConnectionPool(host='localhost', port=8000): Max retries exceeded with url: /sessions?name=default (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at ...>: Failed to establish a new connection: [Errno 61] Connection refused'))

> Entering new AgentExecutor chain...
Failed to persist run: HTTPConnectionPool(host='localhost', port=8000): Max retries exceeded with url: /chain-runs (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at ...>: Failed to establish a new connection: [Errno 61] Connection refused'))
I can find the answer to that using the Wikipedia search tool. Let me do that for you.

> Finished chain.
Out[125]: 'I can find the answer to that using the Wikipedia search tool. Let me do that for you.'

Agent generating commands but not launching tools

agent_chain = initialize_agent(
    tools, 
    llm, 
    agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION, 
    verbose=True, 
    memory=memory, 
    agent_kwargs = {
         "memory_prompts": [chat_history],
        "input_variables": ["input", "agent_scratchpad", "chat_history"]
    }
)

agent_chain.run(input="How many people live in Canada?")

Output:

Failed to load default session, using empty session: HTTPConnectionPool(host='localhost', port=8000): Max retries exceeded with url: /sessions?name=default (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x179ce8b80>: Failed to establish a new connection: [Errno 61] Connection refused'))

> Entering new AgentExecutor chain...
Failed to persist run: HTTPConnectionPool(host='localhost', port=8000): Max retries exceeded with url: /chain-runs (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x179ce9db0>: Failed to establish a new connection: [Errno 61] Connection refused'))
{
  "action": "wikipedia_search",
  "action_input": {
    "query": {
      "title": "Demographics of Canada"
    }
  }
}

> Finished chain.
Out[123]: '{\n  "action": "wikipedia_search",\n  "action_input": {\n    "query": {\n      "title": "Demographics of Canada"\n    }\n  }\n}'

@clemlesne @DSLituiev

I had the same issue, the problem is not your tool nor your code. The problem is the fu***ing gpt3.5 which is not able to return the triple quotes (``) that langchain is expecting. So unfortunately you need to add a custom output parser. I am usinglangchain==0.1.6so all you need to do is create your own output parser while modifying the [parse`](https://github.com/langchain-ai/langchain/blob/c93d4ea91cfcf55dfe871931d42aa22562f8dae2/libs/langchain/langchain/agents/structured_chat/output_parser.py#L69) by adding a single regex like this:

    def parse(self, text: str) -> Union[AgentAction, AgentFinish]:
        try:

            # This regex pattern is explained as follows:

            # - ^(?!.*```)(?=.*action)(?=.*action_input):
            #       ^ asserts the start of the string.
            #       (?!.*```) is a negative lookahead that ensures the string does not contain triple backticks anywhere.
            #       (?=.*action) is a positive lookahead that ensures the string contains "action" somewhere.
            #       (?=.*action_input) is another positive lookahead that ensures the string contains "action_input" somewhere.
            # - re.DOTALL flag: This makes the dot (.) in the pattern match any character including newline characters, which is important for multi-line strings.

            pattern = r'^(?!.*```)(?=.*action)(?=.*action_input)'

            if re.search(pattern, text, re.DOTALL):
                text = re.sub(r"\{", "```\n{", text, 1) + "\n```" # This add triple quotes in case there 

            action_match = self.pattern.search(text)

            if action_match is not None:
                response = json.loads(action_match.group(1).strip(), strict=False)
                if isinstance(response, list):
                    # gpt turbo frequently ignores the directive to emit a single action
                    logger.warning("Got multiple action responses: %s", response)
                    response = response[0]
                if response["action"] == "Final Answer":
                    return AgentFinish({"output": response["action_input"]}, text)
                else:
                    return AgentAction(
                        response["action"], response.get("action_input", {}), text
                    )
            else:
                return AgentFinish({"output": text}, text)
        except Exception as e:
            raise OutputParserException(f"Could not parse LLM output: {text}") from e

Please let me know if it worked for you.