microsoft / autogen

A programming framework for agentic AI 🤖
https://microsoft.github.io/autogen/
Creative Commons Attribution 4.0 International
33.05k stars 4.81k forks source link

[Issue]: Application creates multiple docker containers while running. #3542

Closed b4codify closed 1 month ago

b4codify commented 1 month ago

Describe the issue

I have setup 3 autogen agents as part of group chat along with tools function call:

Agent Config:

However, as soon as user proxy agent starts the group chat with planner, Autogen launches 2 container right away and the moment planner and coder start conversation, docker container count reaches 6 containers.

Issues:

  1. How to limit application to have only one docker container running?

    • I could see the same code gets copied inside all 6 container which is generated by Coder agent and executed by user proxy agent.
  2. Multiple LLM Api calls for a very basic conversation

    • When I stop the program execution after one round of communication in this order: User Proxy starts chat -> Planner -> coder -> user proxy execution -> Planner. I observed more than 12 LLM API calls for just 4-5 group chat interactions.

Need to know how to limit multiple containers and multiple LLM api calls for a very simple workflow?

Steps to reproduce

No response

Screenshots and logs

Screenshot 2024-09-17 at 2 56 11 PM

Additional Information

No response

jackgerrits commented 1 month ago

can you provide the code used so we can repro?

b4codify commented 1 month ago

Here is the test code for your reference. You need to update llm config based on your env. I have also attached a recording of this code to show the behavior at my end. If you see the recording, agent work flow was: user_proxy -> coder -> user_proxy

Here is the code.

import os
from autogen import (
    ConversableAgent,
    GroupChat,
    GroupChatManager,
)
from autogen.cache import Cache
from autogen.coding import DockerCommandLineCodeExecutor
import streamlit as st

from dotenv import load_dotenv
from model_config import ModelConfig

load_dotenv()
llm_env_config = ModelConfig.from_env()
os.makedirs("agent_code", exist_ok=True)

# Litellm config
litellm_config_list = [
    {
        "model": llm_env_config.model_name,
        "api_key": llm_env_config.api_key,
        "base_url": llm_env_config.api_url,  # LiteLLM proxy url http://0.0.0.0:4000
        "temperature": llm_env_config.temperature,
        "cache_seed": None,
        "price": [0, 0],
    },
]
config_list = litellm_config_list

# Create a docker command line code executor.
code_cmd_executor = DockerCommandLineCodeExecutor(
    image="python:3.11-slim",
    work_dir="agent_code",
)

planner = ConversableAgent(
    name="planner",
    system_message="You are a helpful AI Planner assistant."
    "You suggest a feasible plan for finishing a complex task by decomposing it into 3-5 sub-tasks. "
    "Do not suggest concrete code. coder agent will implement your plan by writing code."
    "Do not ask 'user_proxy' to carry out or modify any task."
    "Make sure that your suggested plan can only be implemented in code by coder."
    "Finally, inspect and review the feedback or execution results from user_proxy."
    "If the plan is not good, suggest a better plan. "
    "If the execution is wrong, analyze the error and suggest a fix."

    "IMPORTANT:"
    "Return 'TERMINATE' when all the tasks are completed."
    "DO NOT REPEAT YOURSELF."
    "DO NOT hallucinate.",
    llm_config={"config_list": config_list, "cache_seed": None},
    max_consecutive_auto_reply=3,
    code_execution_config=False,
    human_input_mode="NEVER",
    description="Plan the task by decomposing it into 3-5 sub-tasks.",
)

coder = ConversableAgent(
    name="coder",
    llm_config={"config_list": config_list, "cache_seed": None},
    max_consecutive_auto_reply=3,
    system_message="You are an expert in python programming and python ecosystem, who writes code based on planner suggestons and the user_proxy executes it."
    "You will solve tasks using your coding skills."
    "You follow the planner recommendations and use the available tools to you to complete the task."
    "Do not assume anything else and generate code only based on the given information by planner."
    "Make sure to install the libraries and dependencies you need using pip and bash commands before using them in the code."
    "Solve the tasks step by step if you need to."
    "The user can't modify your code. So do not suggest incomplete code which requires users to modify."
    "Check the execution result returned by the user."
    "If the result indicates there is an error, fix the error and output the code again."

    "IMPORTANT: "
    "Do not show appreciation in your responses, say only what is necessary."
    "'Thank you' or 'You're welcome' are said in the conversation, then say TERMINATE to indicate the conversation is finished and this is your last message."
    "Wait for the user to execute your code and then you can reply with the word 'TERMINATE'."
    "DO NOT OUTPUT 'TERMINATE' after your code block."
    "DO NOT repeat yourself."
    "DO NOT hallucinate.",
    description="Write the python code or bash commands to be executed by the user_proxy to complete a given task.",
    code_execution_config=False,
    human_input_mode="NEVER",
)

user_proxy = ConversableAgent(
    name="user_proxy",
    description="Execute the code or bash commands provided by the coder and reports the results back to coder.",
    human_input_mode="ALWAYS",
    max_consecutive_auto_reply=3,
    is_termination_msg=lambda msg: msg.get("content") is not None and "TERMINATE" in msg["content"],
    code_execution_config={
        "last_n_messages": "auto",
        "executor": code_cmd_executor,
    },
)

group_chat = GroupChat(
    agents=[user_proxy, planner, coder],
    speaker_transitions_type="allowed",
    messages=[],
    max_round=10,
    send_introductions=True,
    speaker_selection_method="auto",
)

group_chat_manager = GroupChatManager(
    groupchat=group_chat, llm_config={"config_list": config_list, "cache_seed": None}
)

def initiatize_agents(url, username, password):

    task = f"""
        Authenticate the user at this website: {url} using the provided credentials (username: {username} and password: {password}).
        """

    # Use Cache.disk to cache LLM responses. Change cache_seed for different responses.
    with Cache.disk(cache_seed=40) as cache:
        chat_results = user_proxy.initiate_chat(
            group_chat_manager,
            message=task,
            cache=cache,
            summary_method="last_msg",
        )
        # return  chat_results.chat_summary
        return chat_results

# Streamlit app
def main():
    st.title("Authentication Tool")

    url = st.text_input("Enter the site URL:")
    username = st.text_input("Enter your username:")
    password = st.text_input("Enter your password:", type="password")

    if st.button("Run Automation"):
        if url and username and password:
            with st.spinner("Running automation..."):
                res = initiatize_agents(url, username, password)
                if res:
                    st.write(res)
                print(f"####### Automation exited ########\n\n")
        else:
            st.warning("Please fill in all the fields.")

if __name__ == "__main__":
    main()

autogen_multiple_containers

SuperMalinge commented 1 month ago

you can try out this code for your current setup and check if its working better:


from autogen import (
    ConversableAgent,
    GroupChat,
    GroupChatManager,
)
from autogen.cache import Cache
from autogen.coding import DockerCommandLineCodeExecutor
import streamlit as st

from dotenv import load_dotenv
from model_config import ModelConfig

load_dotenv()
llm_env_config = ModelConfig.from_env()
os.makedirs("agent_code", exist_ok=True)

# ... (previous code remains the same)

# Modify the DockerCommandLineCodeExecutor configuration
code_cmd_executor = DockerCommandLineCodeExecutor(
    image="python:3.11-slim",
    work_dir="agent_code",
    timeout=60,
    use_docker=True,
    container_name="autogen_container",  # Specify a fixed container name
    auto_remove=False,  # Don't remove the container after execution
)

# ... (rest of the code remains the same)

# In the main() function, add cleanup code
def main():
    # ... (previous code remains the same)

    if st.button("Run Automation"):
        if url and username and password:
            with st.spinner("Running automation..."):
                res = initiatize_agents(url, username, password)
                if res:
                    st.write(res)
                print(f"####### Automation exited ########\n\n")
        else:
            st.warning("Please fill in all the fields.")

    # Add cleanup code
    if st.button("Cleanup"):
        cleanup_docker_container()

def cleanup_docker_container():
    import docker
    client = docker.from_env()
    try:
        container = client.containers.get("autogen_container")
        container.stop()
        container.remove()
        st.success("Docker container cleaned up successfully.")
    except docker.errors.NotFound:
        st.info("No container to clean up.")
    except Exception as e:
        st.error(f"Error cleaning up container: {str(e)}")

if __name__ == "__main__":
    main()```
b4codify commented 1 month ago

Thanks @SuperMalinge for the suggestion. There were couple of errors based on your suggestion such as use docker can't be used inside DockerCommandLineCodeExecutor and docker name conflict with already running docker container with the same name.

However, I could figure out as why my code was spawning multiple containers. It seems DockerCommandLineCodeExecutor was declared in global scope and once, I moved it inside a function scope initiatize_agents() multiple container issues got resolved. I can now reuse existing single named container. :)

I will mark this issue resolved. Thanks!