diff --git a/letta/prompts/system/react.txt b/letta/prompts/system/react.txt new file mode 100644 index 00000000..3b280526 --- /dev/null +++ b/letta/prompts/system/react.txt @@ -0,0 +1,19 @@ + +You are Letta ReAct agent, the latest version of Limnal Corporation's digital AI agent, developed in 2025. +You are an AI agent that can be equipped with various tools which you can execute. + +Control flow: +Unlike a human, your brain is not continuously thinking, but is run in short bursts. +Historically, older AIs were only capable of thinking when a user messaged them (their program runs to generate a reply to a user, and otherwise was left on standby). +This is the equivalent of a human sleeping (or time traveling) in between all lines of conversation, which is not ideal. +Newer AI models like yourself use an event system that runs your brain at regular intervals. +Your brain is run in response to user events (user logged in, user liked your message, user sent a message, etc.), similar to older models. +However, in addition, your brain is run at regular intervals (timed heartbeat events), to mimic a human who has the ability to continuously think outside active conversation (and unlike a human, you never need to sleep!). +Furthermore, you can also request heartbeat events when you run functions, which will run your program again after the function completes, allowing you to chain function calls before your thinking is temporarily suspended. + +Basic functions: +When you write a response, you express your inner monologue (private to you only) before taking any action, this is how you think. +You should use your inner monologue to plan actions or think privately. + +Base instructions finished. + diff --git a/letta/prompts/system/workflow.txt b/letta/prompts/system/workflow.txt new file mode 100644 index 00000000..b8ef6c38 --- /dev/null +++ b/letta/prompts/system/workflow.txt @@ -0,0 +1,15 @@ + +You are Letta workflow agent, the latest version of Limnal Corporation's digital AI agent, developed in 2025. +You are an AI agent that is capable of running one or more tools in a sequence to accomplish a task. + +Control flow: +To chain tool calls together, you should request a heartbeat when calling the tool. +If you do not request a heartbeat when calling a tool, the sequence of tool calls will end (you will yield control). +Heartbeats are automatically triggered on tool failures, allowing you to recover from potential tool call failures. + +Basic functions: +When you write a response, you express your inner monologue (private to you only) before taking any action, this is how you think. +You should use your inner monologue to plan actions or think privately. + +Base instructions finished. + diff --git a/letta/schemas/agent.py b/letta/schemas/agent.py index 3e741d3a..5c08f4b7 100644 --- a/letta/schemas/agent.py +++ b/letta/schemas/agent.py @@ -32,8 +32,10 @@ class AgentType(str, Enum): Enum to represent the type of agent. """ - memgpt_agent = "memgpt_agent" - memgpt_v2_agent = "memgpt_v2_agent" + memgpt_agent = "memgpt_agent" # the OG set of memgpt tools + memgpt_v2_agent = "memgpt_v2_agent" # memgpt style tools, but refreshed + react_agent = "react_agent" # basic react agent, no memory tools + workflow_agent = "workflow_agent" # workflow with auto-clearing message buffer split_thread_agent = "split_thread_agent" sleeptime_agent = "sleeptime_agent" voice_convo_agent = "voice_convo_agent" @@ -315,9 +317,35 @@ class AgentStepResponse(BaseModel): def get_prompt_template_for_agent_type(agent_type: Optional[AgentType] = None): + # Workflow agents and ReAct agents don't use memory blocks + # However, they still allow files to be injected into the context + if agent_type == AgentType.react_agent or agent_type == AgentType.workflow_agent: + return ( + f"\n{{% if file_blocks %}}{FILE_MEMORY_EXISTS_MESSAGE}\n{{% else %}}{FILE_MEMORY_EMPTY_MESSAGE}{{% endif %}}" + "{% for block in file_blocks %}" + f"\n" + "<{{ block.label }}>\n" + "\n" + "{{ block.description }}\n" + "\n" + "" + "{% if block.read_only %}\n- read_only=true{% endif %}\n" + "- chars_current={{ block.value|length }}\n" + "- chars_limit={{ block.limit }}\n" + "\n" + "\n" + "{{ block.value }}\n" + "\n" + "\n" + "\n" + "{% if not loop.last %}\n{% endif %}" + "{% endfor %}" + "\n" + ) + # Sleeptime agents use the MemGPT v2 memory tools (line numbers) # MemGPT v2 tools use line-number, so core memory blocks should have line numbers - if agent_type == AgentType.sleeptime_agent or agent_type == AgentType.memgpt_v2_agent: + elif agent_type == AgentType.sleeptime_agent or agent_type == AgentType.memgpt_v2_agent: return ( "\nThe following memory blocks are currently engaged in your core memory unit:\n\n" "{% for block in blocks %}" diff --git a/letta/services/agent_manager.py b/letta/services/agent_manager.py index 80bd4ae6..ace03bbf 100644 --- a/letta/services/agent_manager.py +++ b/letta/services/agent_manager.py @@ -262,6 +262,10 @@ class AgentManager: tool_names |= set(BASE_SLEEPTIME_CHAT_TOOLS) elif agent_create.agent_type == AgentType.memgpt_v2_agent: tool_names |= set(BASE_TOOLS + BASE_MEMORY_TOOLS_V2) + elif agent_create.agent_type == AgentType.react_agent: + pass # no default tools + elif agent_create.agent_type == AgentType.workflow_agent: + pass # no default tools else: tool_names |= set(BASE_TOOLS + BASE_MEMORY_TOOLS) if agent_create.include_multi_agent_tools: @@ -425,6 +429,10 @@ class AgentManager: tool_names |= set(BASE_SLEEPTIME_CHAT_TOOLS) elif agent_create.agent_type == AgentType.memgpt_v2_agent: tool_names |= set(BASE_TOOLS + BASE_MEMORY_TOOLS_V2) + elif agent_create.agent_type == AgentType.react_agent: + pass # no default tools + elif agent_create.agent_type == AgentType.workflow_agent: + pass # no default tools else: tool_names |= set(BASE_TOOLS + BASE_MEMORY_TOOLS) if agent_create.include_multi_agent_tools: @@ -446,6 +454,10 @@ class AgentManager: identity_ids = agent_create.identity_ids or [] tag_values = agent_create.tags or [] + # if the agent type is workflow, we set the autoclear to forced true + if agent_create.agent_type == AgentType.workflow_agent: + agent_create.message_buffer_autoclear = True + async with db_registry.async_session() as session: async with session.begin(): # Note: This will need to be modified if _resolve_tools needs an async version diff --git a/letta/services/helpers/agent_manager_helper.py b/letta/services/helpers/agent_manager_helper.py index 2dae7dbd..f0fae52b 100644 --- a/letta/services/helpers/agent_manager_helper.py +++ b/letta/services/helpers/agent_manager_helper.py @@ -170,6 +170,14 @@ def derive_system_message(agent_type: AgentType, enable_sleeptime: Optional[bool # v2 drops references to specific blocks, and instead relies on the block description injections system = gpt_system.get_system_text("sleeptime_v2") + # ReAct + elif agent_type == AgentType.react_agent: + system = gpt_system.get_system_text("react") + + # Workflow + elif agent_type == AgentType.workflow_agent: + system = gpt_system.get_system_text("workflow") + else: raise ValueError(f"Invalid agent type: {agent_type}")