griptape icon indicating copy to clipboard operation
griptape copied to clipboard

Add `subtask_summarizer` to `ToolkitTask`

Open vasinov opened this issue 2 years ago • 2 comments

Toolkit task subtasks are not included in the conversation history after the task is completed. It's fine for single runs but often results in lost context in conversational workflows.

I propose adding anOptional parameter subtask_summarizer to ToolkitTask that, if not None summarizers the subtask and includes it in the conversation history.

vasinov avatar Jul 24 '23 19:07 vasinov

Just to add context to anyone else searching for a similar issue, I talked with @collindutter about an error I was experiencing where we think this would be a solution.

Essentially, if there isn't summaries of a subtask an agent is required to perform in the conversation memory then it can pick up on a response pattern (which doesn't include the tool call) and not actually perform it.

It's a confusing issue as effectively it just stops using the tools after a few conversation cycles.

In my situation I was trying a sort of supervisor architecture, where a top level agent used tools to call other agents, and after 2-4 conversation cycles, it stops using the tools due to the response pattern in the conversation history looking like:

"messages": [
  {
    "role": "system",
    "content": "<system_prompt>"
  },
  {
    "role": "user",
    "content": "hi"
  },
  {
    "role": "assistant",
    "content": "I've asked the user what comes after 1."
  },
  {
    "role": "user",
    "content": "2"
  },
  {
    "role": "assistant",
    "content": "I've asked the user what comes after 2."
  },
  {
    "role": "user",
    "content": "3"
  }
]

The expected behaviour would be something like this (which is the previous response cycle, where the tool call is correctly executed):

"messages": [
  {
    "role": "system",
    "content": "<system_prompt>"
  },
  {
    "role": "user",
    "content": "hi"
  },
  {
    "role": "assistant",
    "content": "I've asked the user what comes after 1."
  },
  {
    "role": "user",
    "content": "2"
  },
  {
    "role": "assistant",
    "content": "",
    "tool_calls": [
      {
        "type": "function",
        "id": "call_jsWeU80odjbXGmr6SqB13waG",
        "function": {
          "name": "InformationGatheringTool_ask_question",
          "arguments": "{\"values\": {\"section_key\": \"counting\",\"information_key\": \"num_2\"}}"
        }
      }
    ]
  },
  {
    "role": "tool",
    "content": "Asked the user the following information: counting.num_2",
    "tool_call_id": "call_jsWeU80odjbXGmr6SqB13waG"
  }
]

invke avatar Apr 02 '25 19:04 invke

Bit more context, here is a basic repro script. May take a few runs to trigger.

import logging

import schema
from griptape.artifacts import InfoArtifact
from griptape.configs import Defaults
from griptape.configs.logging import JsonFormatter
from griptape.drivers.prompt.openai import OpenAiChatPromptDriver
from griptape.rules import Rule, Ruleset
from griptape.structures import Agent
from griptape.tools import BaseTool
from griptape.utils.decorators import activity

logger = logging.getLogger(Defaults.logging_config.logger_name)
logger.setLevel(logging.DEBUG)
logger.handlers[0].setFormatter(JsonFormatter())


class InformationGatheringTool(BaseTool):
    @activity(
        {
            "description": "Asks the user a question to gather some information",
            "schema": schema.Schema(
                {
                    schema.Literal(
                        "section_key",
                        description="The section_key of the information to ask a question about",
                    ): str,
                    schema.Literal(
                        "information_key",
                        description="The information_key of the information to ask a question about",
                    ): str,
                }
            ),
        }
    )
    def ask_question(self, section_key: str, information_key: str) -> InfoArtifact:
        return InfoArtifact(
            f"Asked the user a question about {section_key} with value {information_key}"
        )


agent = Agent(
    tools=[InformationGatheringTool()],
    prompt_driver=OpenAiChatPromptDriver(model="gpt-4o"),
    rulesets=[
        Ruleset(
            name="Purpose",
            rules=[
                Rule(
                    "You are an agent that controls the flow of a conversation with the user."
                ),
                Rule(
                    "You will receive user input and use tools to decide the direction of the conversation."
                ),
                Rule(
                    "You are not responding directly to the user yourself, another agent does that based on what tools you call."
                ),
                Rule(
                    "You will ask the user questions using the InformationGatheringTool one by one."
                ),
            ],
        ),
        Ruleset(
            name="Response",
            rules=[
                Rule(
                    "ALWAYS respond with your thoughts and what tool you used to send a message to the user."
                ),
            ],
        ),
        Ruleset(
            name="Tools",
            rules=[
                Rule(
                    "NEVER stop without calling a tool that sends a message to the user"
                ),
                Rule(
                    "ONLY use the tools that send a message to the user ONCE PER RESPONSE"
                ),
                Rule(
                    "The following tools will send a message to the user:\n- InformationGatheringTool_ask_question"
                ),
            ],
        ),
        Ruleset(
            name="InformationGathering",
            rules=[
                Rule(
                    "Below is all the information you can gather via the InformationGatheringTool:"
                ),
                Rule(
                    "what comes after 1 → section_key: counting, information_key: num_1, description: A number"
                ),
                Rule(
                    "what comes after 2 → section_key: counting, information_key: num_2, description: A number"
                ),
                Rule(
                    "what comes after 3 → section_key: counting, information_key: num_3, description: A number"
                ),
                Rule(
                    "what comes after 4 → section_key: counting, information_key: num_4, description: A number"
                ),
                Rule(
                    "what comes after 5 → section_key: counting, information_key: num_5, description: A number"
                ),
                Rule(
                    "what comes after 6 → section_key: counting, information_key: num_6, description: A number"
                ),
                Rule(
                    "what comes after 7 → section_key: counting, information_key: num_7, description: A number"
                ),
                Rule(
                    "what comes after 8 → section_key: counting, information_key: num_8, description: A number"
                ),
                Rule(
                    "what comes after 9 → section_key: counting, information_key: num_9, description: A number"
                ),
                Rule(
                    "what comes after 10 → section_key: counting, information_key: num_10, description: A number"
                ),
            ],
        ),
    ],
)

agent.run("1")
agent.run("2")
agent.run("3")

collindutter avatar Apr 02 '25 20:04 collindutter