Skip to content

[!WARNING] Planex is no longer maintained and is not included on the main file/bootstrap.

Planex as an Approximation to $A_1$

Planex is a sophisticated 3-step planner agent designed to approximate the ECM problem through the utilization of LangChain. This approximation involves the collaborative efforts of three distinct agents, each focusing on a crucial aspect of the process: Planning, Reduction, and Translation, as delineated in the Theoretical Analysis.

To provide a comprehensive understanding, these three steps correspond to the following concepts: - Planning: The planning agent receives a natural language query from the user and devises a solution to approximate the problem. This process is independent of the specific context or tools available to the agents. - Reduction: The reduction agent takes the explanation of the problem provided by the planner and identifies a set of tools that correspond to each instruction. Consequently, the resulting plan includes the necessary keywords and tools for execution. - Translation: The translation agent receives the plan and generates an Exelent file, which can be executed to achieve the desired solution.

Usage

To begin, let us explore the implementation of these agents. All agents within Planex are located in the /agents directory. Each agent possesses an attribute chain, which contains the execution module of the chain. Additionally, each agent includes a function for direct execution with a query, as illustrated in the example below:

from cognition_layer.planex.agents.planner import Planner
from cognition_layer.planex.agents.reducer import Reducer
from cognition_layer.planex.agents.translator import Translator

planner = Planner()
reducer = Reducer()
translator = Translator()

# Execute an example action for each agent
planner_result = planner.plan("Open Spotify", verbose=True)
reducer_result = reducer.reduce(planner_result.content, verbose=True)
translator_result = translator.translate(translator.content, verbose=False)
print("Plan defined: ", translator_result.content)

It is important to note that each agent returns a BaseMessage from LangChain, ensuring both traceability and scalability. As a result, the output of the agents is contained in the .content attribute of the Messages.

Upon executing the above code, you may observe that the plan references functions not yet defined. This occurs because the agents have not been provided with the specific actions they can utilize to solve the plan. To define new actions, you can register your functions within the ItemRegistry, and they will be automatically integrated into the planners.

from ecm.tools.registry import ItemRegistry

@ItemRegistry.register_function
def click(place: str) -> None:
    """Clicks on the specified place"""
    print("Clicked on ", place)

# If you instantiate the tool after initializing the agents, notify the reducer to update its actions:
reducer.auto_bind_actions()

It is crucial that the docstring and typing are provided for the registered function, as this information is used by the agent to describe the function to the LLM.

After registering the new actions, you can re-execute your three agents, and the defined actions will be considered when formulating the Exelent plan.

Merging Chains

As mentioned earlier, each agent contains a LangChain chain. This allows for the combination of all agents into a single entity by pipelining the necessary messages for each agent:

planex = (
    planner.chain
    | (lambda output: {"input": output.content, "actions": reducer.actions})
    | reducer.chain
    | (lambda output: {"input": output.content})
    | translator.chain
)

You can now effortlessly execute Planex by invoking it as a chain:

result = planex.invoke({"input": query})
print("Result: ", result.content)

Planex Prompt Engineering

The instructions defined for each agent in Planex can be found in the /agents/prompts.py module. This module contains multiple prompts that are included in each invocation. Each prompt comprises specific instructions detailing the target, guidelines to be followed, and examples to illustrate proper usage. You are encouraged to modify these prompts to explore potential new behaviors and functionalities of Planex.

After defining the prompts in the prompts.py file, they are merged for each agent as follows:

sys_message = (
    "\nInstructions: \n"
    + PlanexPrompts.AGENT_INSTRUCTIONS
    + "\nGuidelines: \n"
    + PlanexPrompts.AGENT_GUIDELINES
    + "\nExample:\n"
    + PlanexPrompts.AGENT_EXAMPLE
)

# The agent here could be the planner, reducer, or translator 
agent.prompt = ChatPromptTemplate.from_messages(
    [("system", sys_message), ("user", "{input}")]
)

In this implementation, the sys_message variable is constructed by concatenating the instructions, guidelines, and example prompts defined in the PlanexPrompts class. This combined system message is then assigned to the prompt attribute of the respective agent (planner, reducer, or translator). The ChatPromptTemplate.from_messages method is used to create a prompt template from these system and user messages, facilitating effective communication and task execution by the agents.

Feel free to experiment with and refine these prompts to optimize the performance and adaptability of Planex to various scenarios and requirements.