improve this:
Thought: To find out the current weather in Rostock, Germany, I need to search for up-to-date information.
Action: DuckDuckGo Search
Action Input: "Rostock weather
Observation: Rostock Weather Forecasts. Weather Underground provides local & long-range weather forecasts, weatherreports, maps & tropical weather conditions for the Rostock area. Rostock. Rostock Weather Forecast. Providing a local hourly Rostock weather forecast of rain, sun, wind, humidity and temperature. The Long-range 12 day forecast also includes detail for Rostock weather today. Live weather reports from Rostock weather stations and weather warnings that include risk of thunder, high UV index and forecast gales. Rostock Administration centre, Mecklenburg-Vorpommern (Germany), elevation 16 m Press to show information about this location. Forecast Forecast. Other conditions Other conditions. ... Weather forecast for the next 10 days. Night Morning Day Afternoon Evening. Temperature high/low Wind Precip. Rostock Administration centre, Mecklenburg-Vorpommern (Germany), elevation 16 m Press to show information about this location. Forecast Forecast. Other conditions Other conditions. ... Weather forecast for the next 10 days. Night Morning Day Afternoon Evening. Temperature high/low Wind Precip. Current condition and temperature - Rostock, Germany. At the moment, the weather is decidedly cloudy. The temperature is a mild 13°C (55.4°F), and the real-feel temperature is appraised at a fresh 11°C (51.8°F). The current temperature is almost at the minimum-predicted 12°C (53.6°F) for today.
Thought:Question: what is the current temperature in Rostock
Thought: Now that I have searched for up-to-date information on the weather in Rostock, I can determine the current temperature from the search result.
Action: DuckDuckGo Search
Action Input: "Rostock current temperature
Observation: Weather Underground provides local & long-range weather forecasts, weatherreports, maps & tropical weather conditions for the Rostock area. ... Current Weather for Popular Cities . Rostock Administration centre, Mecklenburg-Vorpommern (Germany), ... Current conditions. Temperature 7 ° 7 ° Feels like 4 ° Precipitation 0 mm. 4 m/s gentle breeze from south with gusts at 6 m/s 4 (6) m/s. Table Graph. Weather forecast for the next 10 days. Night Morning Day Afternoon Evening. Current condition and temperature - Rostock, Germany. At the moment, the weather is decidedly cloudy. The temperature is a mild 13°C (55.4°F), and the real-feel temperature is appraised at a fresh 11°C (51.8°F). The current temperature is almost at the minimum-predicted 12°C (53.6°F) for today. Rostock Administration centre, Mecklenburg-Vorpommern ... Current conditions. Temperature 17 ° 17 ° Feels like 17 ° Precipitation 0 mm. 4 m/s gentle breeze from north with gusts at 9 m/s 4 (9) m/s. Table Graph. Weather forecast for the next 10 days. Night Morning Day Afternoon Evening. Temperature high/low Wind Precip. Hour-by-Hour Forecast for Rostock, Germany. Weather. Weather Today Weather Hourly 14 Day Forecast Yesterday/Past Weather Climate (Averages) Currently: 72 °F. Partly sunny. (Weather station: Laage, Germany). See more current weather.
Thought:Question: what is the current temperature in Rostock
Thought: Now that I have searched for up-to-date information on the weather in Rostock, I can determine the current temperature from the search result.
Action: DuckDuckGo Search
Action Input: "Rostock current temperature
Observation: Weather Underground provides local & long-range weather forecasts, weatherreports, maps & tropical weather conditions for the Rostock area. ... Current Weather for Popular Cities . Rostock Administration centre, Mecklenburg-Vorpommern (Germany), ... Current conditions. Temperature 7 ° 7 ° Feels like 4 ° Precipitation 0 mm. 4 m/s gentle breeze from south with gusts at 6 m/s 4 (6) m/s. Table Graph. Weather forecast for the next 10 days. Night Morning Day Afternoon Evening. Current condition and temperature - Rostock, Germany. At the moment, the weather is decidedly cloudy. The temperature is a mild 13°C (55.4°F), and the real-feel temperature is appraised at a fresh 11°C (51.8°F). The current temperature is almost at the minimum-predicted 12°C (53.6°F) for today. Hour-by-Hour Forecast for Rostock, Germany. Weather. Weather Today Weather Hourly 14 Day Forecast Yesterday/Past Weather Climate (Averages) Currently: 72 °F. Partly sunny. (Weather station: Laage, Germany). See more current weather. Röbel/müritz 16° 7°. Schwerin 15° 6°. Rostock Weather Forecast. Access detailed hourly and 14 day forecasts, current conditions, maps, warnings, meteograms, historical data and more for Rostock.
Thought:Question: what is the current temperature in Rostock
Thought: Now that I have searched for up-to-date information on the weather in Rostock, I can determine the current temperature from the search result.
Action: DuckDuckGo Search
Action Input: "Rostock current temperature
Observation: Weather Underground provides local & long-range weather forecasts, weatherreports, maps & tropical weather conditions for the Rostock area. ... Current Weather for Popular Cities . Current condition and temperature - Rostock, Germany. At the moment, the weather is decidedly cloudy. The temperature is a mild 13°C (55.4°F), and the real-feel temperature is appraised at a fresh 11°C (51.8°F). The current temperature is almost at the minimum-predicted 12°C (53.6°F) for today. Rostock Administration centre, Mecklenburg-Vorpommern (Germany), ... Current conditions. Temperature 7 ° 7 ° Feels like 4 ° Precipitation 0 mm. 4 m/s gentle breeze from south with gusts at 6 m/s 4 (6) m/s. Table Graph. Weather forecast for the next 10 days. Night Morning Day Afternoon Evening. Rostock Administration centre, Mecklenburg-Vorpommern ... Current conditions. Temperature 17 ° 17 ° Feels like 17 ° Precipitation 0 mm. 4 m/s gentle breeze from north with gusts at 9 m/s 4 (9) m/s. Table Graph. Weather forecast for the next 10 days. Night Morning Day Afternoon Evening. Temperature high/low Wind Precip. Hour-by-Hour Forecast for Rostock, Germany. Weather. Weather Today Weather Hourly 14 Day Forecast Yesterday/Past Weather Climate (Averages) Currently: 72 °F. Partly sunny. (Weather station: Laage, Germany). See more current weather.
Thought:Question: what is the current temperature in Rostock
Thought: I have now searched for up-to-date information on the weather in Rostock and can see that the current temperature is 13°C (55.4°F).
Final Answer: The final answer is 13°C (55.4°F).



Code:
import os
import shutil
import gradio as gr
from langchain.embeddings import HuggingFaceEmbeddings
from langchain.document_loaders import DirectoryLoader
from langchain.text_splitter import CharacterTextSplitter
from langchain.vectorstores import Chroma
from langchain.tools import Tool
from langchain.utilities import WikipediaAPIWrapper, DuckDuckGoSearchAPIWrapper
from langchain.llms import Ollama
from langchain.chains import LLMChain
from langchain.agents import initialize_agent, AgentType
from langchain.prompts import PromptTemplate
from langsmith import traceable

# Update environment variables
os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_API_KEY"] = "<TRUFFLEHOG_REDACTED_LANGSMITH>"
os.environ["LANGCHAIN_PROJECT"] = "rag test"  # Optional: "default" is used if not set

# Initialize embeddings and vector store
embeddings = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2")
persist_directory = "./chroma_db"
if not os.path.exists(persist_directory):
    os.makedirs(persist_directory)
vectorstore = Chroma(persist_directory=persist_directory, embedding_function=embeddings)

# Initialize tools
duckduckgo_search = DuckDuckGoSearchAPIWrapper()
wikipedia = WikipediaAPIWrapper()

# Create Knowledge Base Tool
def create_knowledge_base_tool(vectorstore):
    def run_knowledge_base_tool(query: str) -> str:
        results = vectorstore.similarity_search(query, k=3)
        if results:
            return "\n\n".join([doc.page_content for doc in results])
        else:
            return "NO_RESULTS_FOUND"
    return Tool(
        name="Knowledge Base Tool",
        func=run_knowledge_base_tool,
        description="Use this tool to search and retrieve information from the internal knowledge base when answering questions."
    )

knowledge_base_tool = create_knowledge_base_tool(vectorstore)

# Declare global variables
documents_list = []
knowledge_content = ""

# Load knowledge documents to include in the task description
def load_knowledge_documents():
    global documents_list
    contents = []
    for doc in documents_list:
        contents.append(doc.page_content)
    return "\n\n".join(contents) if contents else "No knowledge documents available."

# Function to get the list of filenames in the knowledge base
def get_knowledge_base_files():
    files = os.listdir("uploads") if os.path.exists("uploads") else []
    return files

# Define Agents using LangChain
# Boss Agent - Uses LLM directly to decide which agent to delegate to
def create_boss_agent():
    boss_llm = Ollama(model="llama3.1")
    return boss_llm

# Internet Research Agent
def create_internet_research_agent():
    # Create the tools
    tools = [
        Tool(
            name="DuckDuckGo Search",
            func=duckduckgo_search.run,
            description="Use this tool to search the web for current events and information."
        ),
        Tool(
            name="Wikipedia",
            func=wikipedia.run,
            description="Use this tool to retrieve information from Wikipedia pages."
        ),
     ]

    # Create the LLM
    llm = Ollama(model="llama3.1")

    # Initialize the agent
    agent_executor = initialize_agent(
        tools,
        llm,
        agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
        verbose=True,
        handle_parsing_errors=True
    )

    return agent_executor

# Knowledge Base Agent
def create_knowledge_base_agent():
    # Create the tools (only the knowledge base tool)
    tools = [knowledge_base_tool]

    # Create the LLM
    llm = Ollama(model="llama3.1")

    # Initialize the agent
    agent_executor = initialize_agent(
        tools,
        llm,
        agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
        verbose=True,
        handle_parsing_errors=True
    )

    return agent_executor

# Response Writer Agent - Uses LLM to generate the final response
def create_response_writer_agent():
    response_llm = Ollama(model="llama3.1")
    return response_llm

# Functions for processing documents and clearing the database
def process_documents(files):
    global vectorstore
    global knowledge_content
    global documents_list

    if not files:
        return "No new documents to process.", get_processed_files()

    processed_files = []
    skipped_files = []

    for file in files:
        file_path = file.name
        destination_path = os.path.join("uploads", os.path.basename(file_path))

        if os.path.exists(destination_path):
            skipped_files.append(os.path.basename(file_path))
        else:
            try:
                shutil.move(file_path, destination_path)
                processed_files.append(os.path.basename(file_path))
            except Exception as e:
                return f"Error moving file: {str(e)}", get_processed_files()

    if processed_files:
        loader = DirectoryLoader("uploads", glob="**/*")
        documents = loader.load()
        documents_list.extend(documents)  # Store documents globally
        text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=200, length_function=len)
        docs = text_splitter.split_documents(documents)

        vectorstore.add_documents(docs)

    # Update the knowledge content after processing new documents
    knowledge_content = load_knowledge_documents()

    result = f"Processed {len(processed_files)} new document(s)."
    if skipped_files:
        result += f"\nSkipped {len(skipped_files)} existing file(s): {', '.join(skipped_files)}"
        gr.Warning(f"The following files already exist and were not processed: {', '.join(skipped_files)}")

    return result, get_processed_files()

def clear_database():
    global vectorstore
    global knowledge_content
    global documents_list

    try:
        vectorstore = None
        shutil.rmtree("uploads", ignore_errors=True)
        os.makedirs("uploads", exist_ok=True)
        shutil.rmtree(persist_directory, ignore_errors=True)
        os.makedirs(persist_directory, exist_ok=True)
    except Exception as e:
        print(f"Error clearing database: {e}")

    # Reinitialize the vectorstore and clear knowledge content
    vectorstore = Chroma(persist_directory=persist_directory, embedding_function=embeddings)
    knowledge_content = ""
    documents_list = []

    return "Database and uploaded files cleared.", get_processed_files()

def get_processed_files():
    files = os.listdir("uploads") if os.path.exists("uploads") else []
    return "Processed files:\n" + "\n".join(files) if files else "No files processed yet."

# Function to handle user input and process with the agents
@traceable
def handle_user_input(user_message, history):
    global knowledge_content

    # Initialize the boss LLM
    boss_llm = create_boss_agent()

    # Boss agent decides which agent to delegate to
    boss_prompt = (
        f"As the Boss Agent, decide which agent is best suited to answer the following user question:\n\n"
        f"User question: '{user_message}'\n\n"
        f"Available agents:\n"
        f"1. Internet Research Agent: Has access to internet tools.\n"
        f"2. Knowledge Base Agent: Has access to the knowledge base containing files: {', '.join(get_knowledge_base_files())}\n\n"
        f"Respond with the agent's number (1 or 2) that should handle this task."
    )

    # Use invoke() method
    boss_decision = boss_llm.invoke(boss_prompt).strip()

    # Determine which agent was selected
    if '1' in boss_decision:
        selected_agent = create_internet_research_agent()
        selected_agent_name = "Internet Research Agent"
        other_agent_option = '2'
    elif '2' in boss_decision:
        selected_agent = create_knowledge_base_agent()
        selected_agent_name = "Knowledge Base Agent"
        other_agent_option = '1'
    else:
        selected_agent = None
        selected_agent_name = "None"
        other_agent_option = None

    if selected_agent is not None:
        # Run the selected agent with the input
        gathered_info = selected_agent.run(user_message)
        
        # Check if the agent couldn't find good information
        if "NO_RESULTS_FOUND" in gathered_info or "couldn't find relevant information" in gathered_info:
            # Report back to the boss
            boss_follow_up_prompt = (
                f"The {selected_agent_name} reports that it couldn't find sufficient information to answer the user's question.\n\n"
                f"User question: '{user_message}'\n\n"
                f"As the Boss Agent, decide whether to assign another agent to handle this task.\n"
                f"Available option: Agent {other_agent_option}\n"
                f"Respond with 'yes' to assign the other agent, or 'no' to stop."
            )
            boss_follow_up_decision = boss_llm.invoke(boss_follow_up_prompt).strip().lower()

            if 'yes' in boss_follow_up_decision and other_agent_option is not None:
                # Assign the other agent
                if other_agent_option == '1':
                    selected_agent = create_internet_research_agent()
                    selected_agent_name = "Internet Research Agent"
                elif other_agent_option == '2':
                    selected_agent = create_knowledge_base_agent()
                    selected_agent_name = "Knowledge Base Agent"

                # Run the selected agent with the input
                gathered_info = selected_agent.run(user_message)
            else:
                gathered_info = "I'm sorry, neither agent can answer your question."
    else:
        gathered_info = "I'm sorry, neither agent can answer your question."

    # Response Writer Agent crafts the final answer
    response_llm = create_response_writer_agent()
    response_prompt = (
        f"As the Response Writer Agent, use the following information to answer the user's question.\n\n"
        f"User question: '{user_message}'\n\n"
        f"Information from {selected_agent_name}:\n{gathered_info}\n\n"
        f"Ensure that the answer is detailed, accurate, and helpful."
    )

    final_response = response_llm.invoke(response_prompt).strip()

    history.append([user_message, final_response])
    return "", history

# Gradio UI setup
with gr.Blocks() as demo:
    gr.Markdown("# Collaborative Agents Chatbot with Boss Agent and Response Writer Agent")

    with gr.Tabs():
        with gr.TabItem("Chat"):
            chatbot = gr.Chatbot()
            msg = gr.Textbox(placeholder="Enter your message...", label="Your Message")
            clear_chat = gr.Button("Clear Chat")

        with gr.TabItem("Knowledge Base"):
            with gr.Row():
                file_output = gr.File(file_count="multiple", label="Upload Documents")
                process_button = gr.Button("Process Documents")
                clear_button = gr.Button("Clear Database")

            process_output = gr.Textbox(label="Processing Output")
            processed_files = gr.Textbox(label="Processed Files", value=get_processed_files())

    # Define interactions
    msg.submit(handle_user_input, [msg, chatbot], [msg, chatbot], queue=False)
    process_button.click(process_documents, inputs=[file_output], outputs=[process_output, processed_files])
    clear_button.click(clear_database, outputs=[process_output, processed_files])
    clear_chat.click(lambda: [], outputs=chatbot, queue=False)

# Launch the Gradio interface
if __name__ == "__main__":
    if not os.path.exists("uploads"):
        os.makedirs("uploads")
    demo.queue().launch()