LeetCode智能教练来袭!多智能体LLM协同架构实战指南 原创 精华

发布于 2025-5-12 08:30
浏览
0收藏

在人工智能飞速发展的今天,生成式AI(Generative AI)已经成为了技术领域的一颗明珠。从简单的文本生成到复杂的多模态应用,生成式AI正在不断刷新我们对机器智能的认知。然而,随着技术的不断进步,我们发现仅仅依靠单一的大型语言模型(LLM)已经无法满足日益复杂的应用需求。于是,多智能体LLM协同架构应运而生,它通过多个智能体的协作,极大地提升了生成式AI应用的性能和灵活性。今天,就让我们一起深入探索多智能体LLM协同的魅力,并通过一个实际案例——打造一个LeetCode智能教练,来感受它的强大能力。

一、什么是智能体(Agents)?

在生成式AI的世界里,智能体(Agents)是一种能够利用LLM进行决策、控制应用流程、规划复杂推理步骤、利用工具以及自主与其他智能体交互的系统。简单来说,智能体就像是AI应用中的“小助手”,它们可以根据用户的输入和任务需求,自主地选择行动路径、调用工具或者与其他智能体合作,从而完成复杂的任务。

举个例子,想象一下你正在开发一个智能客服系统。传统的做法是将所有的逻辑和决策都集中在一个大型的语言模型中,但这种方法往往会导致系统在处理复杂问题时显得力不从心。而如果你引入了智能体,就可以让不同的智能体分别负责不同的任务,比如一个智能体专门负责理解用户的问题,另一个智能体负责从数据库中检索信息,还有一个智能体负责生成最终的回答。这样一来,整个系统不仅更加高效,而且能够更好地应对复杂的用户需求。

二、多智能体架构的魅力

多智能体系统的核心在于多个独立的智能体之间的协作。每个智能体都有自己的角色和上下文,它们可以根据自己的任务需求调用工具并与其他智能体互动。这种架构的好处是显而易见的:

(一)降低偏见和幻觉

在单智能体系统中,模型可能会因为自身的局限性而产生偏见或者生成不准确的内容(我们通常称之为“幻觉”)。而在多智能体系统中,由于每个智能体都可以独立地对任务进行推理,因此可以通过多个智能体的协作来减少这种偏见和幻觉。例如,当一个智能体生成了一个可能存在问题的回答时,其他智能体可以通过自己的判断和验证来纠正这个错误。

(二)简化复杂任务

复杂的任务往往需要多个步骤来完成,而每个步骤都可能需要不同的技能和知识。在多智能体系统中,每个智能体都可以专注于自己擅长的任务,从而将复杂的任务分解为多个简单的子任务。这样不仅可以提高系统的效率,还可以让每个智能体都能够在自己的领域内发挥最大的优势。

(三)提升系统的灵活性和可扩展性

多智能体架构使得系统可以根据不同的需求灵活地调整智能体的数量和角色。例如,当你需要为系统增加一个新的功能时,你只需要添加一个新的智能体并定义它的角色和任务,而不需要对整个系统进行大规模的修改。这种灵活性和可扩展性使得多智能体系统在面对复杂多变的应用场景时具有巨大的优势。

三、多智能体架构的常见类型

多智能体系统可以根据不同的需求和应用场景采用不同的架构。以下是最常见的三种架构类型:

(一)网络架构(Network)

LeetCode智能教练来袭!多智能体LLM协同架构实战指南-AI.x社区

在网络架构中,每个智能体都可以与其他所有智能体直接交互。这种架构的优点是灵活性高,每个智能体都可以根据自己的需求选择与其他智能体的交互方式。然而,这种架构的缺点是可能会导致交互过程变得非常复杂,尤其是在智能体数量较多时。想象一下,如果一个系统中有几十个智能体,每个智能体都可以与其他智能体交互,那么整个系统的交互关系就会变得像一张错综复杂的蜘蛛网。

(二)监督架构(Supervisor)

LeetCode智能教练来袭!多智能体LLM协同架构实战指南-AI.x社区

在监督架构中,有一个专门的“监督者”智能体负责协调其他所有智能体的工作。这个监督者智能体可以根据用户的输入和任务需求,决定调用哪些智能体以及它们的调用顺序。这种架构的优点是结构清晰,监督者智能体可以对整个系统的流程进行有效的控制。然而,这种架构的缺点是监督者智能体可能会成为系统的瓶颈,如果它的决策能力不足,可能会影响整个系统的性能。

(三)层次架构(Hierarchical)

LeetCode智能教练来袭!多智能体LLM协同架构实战指南-AI.x社区

层次架构是在监督架构的基础上进行扩展的一种架构。它不仅有一个监督者智能体,还可能有多个“子监督者”智能体,形成一个层次化的结构。这种架构的优点是可以处理更加复杂的任务,因为它可以通过多个层次的智能体来实现更加精细的控制。然而,这种架构的缺点是设计和实现起来相对复杂,需要仔细地规划每个层次的智能体的角色和任务。

四、用LangGraph打造LeetCode智能教练

了解了多智能体架构的基本概念和优势之后,让我们通过一个实际的案例来感受它的强大能力。我们将使用LangGraph框架来打造一个LeetCode智能教练。这个智能教练能够根据用户的需求生成定制化的LeetCode风格的编程问题,并从互联网上搜索相关的资源来帮助用户学习和解答。

(一)准备工作

在开始之前,我们需要做一些准备工作。首先,我们需要安装一些必要的Python包,包括LangGraph、LangChain、OpenAI等。这些包将为我们提供构建多智能体系统所需的各种工具和接口。其次,我们需要获取OpenAI和Tavily的API密钥,因为我们将使用这些API来调用语言模型和搜索工具。

以下是安装Python包的命令:

pip install python-dotenv langgraph langchain langchain_openai langchain_community langchain_text_splitters chromadb tavily-python openai typing_extensions termcolor

然后,创建一个​​.env​​文件,并在其中定义API密钥:

OPENAI_API_KEY={INSERT_YOUR_OPENAI_KEY}
TAVILY_API_KEY={INSERT_YOUR_TAVILY_KEY}

(二)定义智能体

在我们的LeetCode智能教练中,我们将定义两个辅助智能体:资源查找器(Resource-Finder)和问题生成器(Problem-Generator)。

1. 资源查找器(Resource-Finder)

资源查找器的作用是根据用户的输入,在互联网上搜索相关的资源。我们将使用Tavily工具来实现这个功能。通过定义一个合适的提示(prompt),我们可以告诉智能体它的角色和任务是什么。然后,我们将Tavily工具绑定到语言模型上,并构建一个工具链来实现资源的搜索和返回。

以下是资源查找器的代码实现:

from langchain.prompts import ChatPromptTemplate
from langchain.llms import ChatOpenAI
from langchain.tools import TavilySearchResults
from langchain.chains import LLMChain
from langgraph import Command, State, TypedDict, Annotated, List

def resource_finder(state: State) -> Command[Literal["Supervisor"]]:
    """Finds resources based on the user's query."""
    query = state["messages"][-1].content
    context = state["messages"]

    # Define prompt
    prompt = ChatPromptTemplate.from_template(
        f"""You are a computer science resource finder. 
        You will find resources for the user based on their preferences 
        in the field of computer science and related topics. 
        Use the context to help you build the response. You may also answer
        general questions or miscellaneous queries.
        
        Question: {query}\n"""
    )
    
    # Define tool and bind LLM to Tavily tool
    tavily_tool = TavilySearchResults(max_results=5, search_depth="advanced", include_answer=True, include_raw_cnotallow=True)
    llm = ChatOpenAI(model="gpt-4o").bind_tools([tavily_tool])

    # Define LLM chain
    llm_chain = (
        prompt
        | llm
    )

    @chain
    def tool_chain(user_input: str, config: RunnableConfig):
        input_ = {"user_input": user_input, "context": context}
        ai_msg = llm_chain.invoke(input_, cnotallow=config)
        tool_msgs = tavily_tool.batch(ai_msg.tool_calls, cnotallow=config)
        return llm_chain.invoke({**input_, "messages": [ai_msg, *tool_msgs]}, cnotallow=config)

    response = tool_chain.invoke(query)

    return Command(
        update={
            "messages": [
                AIMessage(cnotallow=response.content, name="Resource-Finder")
            ]
        },
        goto="Supervisor",
    )

2. 问题生成器(Problem-Generator)

问题生成器的作用是根据用户的输入生成LeetCode风格的编程问题。我们将使用一种流行的生成式AI技术——检索增强生成(RAG)。具体来说,我们将使用一个包含近2000个LeetCode问题的Kaggle数据集作为知识库。通过检索与用户输入相关的数据,并结合语言模型的生成能力,我们可以生成定制化的编程问题。

以下是问题生成器的代码实现:

from langchain.loaders import CSVLoader
from langchain.text_splitters import RecursiveCharacterTextSplitter
from langchain.vectorstores import Chroma
from langchain.embeddings import OpenAIEmbeddings
from langchain.retrievers import VectorStoreRetriever
from langchain.prompts import ChatPromptTemplate
from langchain.llms import ChatOpenAI
from langchain.chains import LLMChain
from langgraph import Command, State, TypedDict, Annotated, List

def coding_problem_generator(state: State) -> Command[Literal["Supervisor"]]:
    """Generates a coding problem based on the user's query."""
    query = state["messages"][-1].content

    # Using LeetCode dataset for RAG to generate coding problems
    coding_dataset = "leetcode_dataset - lc.csv"

    # Load CSV file as input document
    loader = CSVLoader(file_path=coding_dataset)
    docs = loader.load()

    # Split documents into smaller chunks and store in Chroma vectorstore
    text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
    splits = text_splitter.split_documents(docs)
    vectorstore = Chroma.from_documents(documents=splits, embedding=OpenAIEmbeddings())

    # Define retriever using vector store
    retriever = vectorstore.as_retriever()

    # Define prompt
    prompt = ChatPromptTemplate.from_template(
        """"You are a LeetCode-style coding problem generator. 
        You will generate a coding problem for the user to solve 
        based on their preferences. Use the context to help you build the problem.
        Do not provide a solution to the problem unless asked for.

        Context: {context}\n 
        Answer:"""
    )
    
    def format_docs(docs):
        return"\n\n".join(doc.page_content for doc in docs)

    # Define LLM model
    llm = ChatOpenAI(model="gpt-4o")
    
    # Build RAG chain using retriever, prompt, and LLM
    rag_chain = (
        {"context": retriever | format_docs}
        | prompt
        | llm
        | StrOutputParser()
    )

    # Invoke RAG chain to generate coding problem
    response = rag_chain.invoke(query)

    return Command(
        update={
            "messages": [
                AIMessage(cnotallow=response, name="Problem-Generator")
            ]
        },
        goto="Supervisor",
    )

(三)定义监督者智能体

监督者智能体的作用是协调整个系统的流程。它将根据用户的输入和当前的交互状态,决定调用哪个智能体或者结束当前的任务。我们将定义一个合适的提示来告诉监督者智能体它的角色和任务,并使用语言模型来生成结构化的输出,从而决定下一步的行动。

以下是监督者智能体的代码实现:

from langgraph import Command, State, TypedDict, Annotated, List
from langchain.llms import ChatOpenAI

class Router(TypedDict):
    next: Literal["Resource-Finder", "Problem-Generator", "FINISH"]

def supervisor_agent(state: State) -> Command[Literal["Resource-Finder", "Problem-Generator", "__end__"]]:
    """Supervisor agent that manages the conversation between workers."""
    question = state["messages"][-1].content

    # Include the system prompt and the current conversation state in the messages
    members = ["Resource-Finder", "Problem-Generator"]
    system_prompt = (
            "You are a supervisor tasked with managing a conversation between the"
            f" following workers:  {members}. Given the following user request {question},"
            " respond with the worker to act next. Each worker will perform a"
            " task and respond with their results and status. When you determine a task to be finished,"
            " respond with FINISH."
            " Here are the uses of each worker:\n"
            "1. Resource-Finder: Find resources based on the user's query and handles any general or miscellaneous user queries.\n"
            "2. Problem-Generator: Generate a coding problem based on the user's query.\n"
        )

    messages = [
        {"role": "system", "content": system_prompt},
    ] + state["messages"]

    llm = ChatOpenAI(model="gpt-4o")

    # Use the LLM to decide the next step
    response = llm.with_structured_output(Router).invoke(messages)

    # Extract the next node from the response
    next_node = response.get("next", None)

    ifnot next_node:
        raise ValueError("Supervisor failed to determine the next step.")

    if next_node == "FINISH":
        next_node = "__end__"
    # Return a Command with the target node in the goto field.
    return Command(goto=next_node, update={"next": next_node})

(四)构建和运行多智能体系统

在定义了所有的智能体之后,我们需要将它们组合成一个多智能体系统。我们将使用LangGraph框架来构建这个系统,并定义智能体之间的交互关系。最后,我们将运行这个系统,并通过与用户的交互来测试它的性能。

以下是构建和运行系统的代码实现:

from langgraph import StateGraph, START, END
import os
from dotenv import load_dotenv
from termcolor import colored

# Load environment variables
load_dotenv()

# Define the graph state
class State(TypedDict):
    messages: Annotated[List, add_messages] = []    
    next: str = ""

# Create the state graph
def create_graph():
    workflow = StateGraph(State)

    workflow.add_node("Resource-Finder", resource_finder)
    workflow.add_node("Problem-Generator", coding_problem_generator)
    workflow.add_node("Supervisor", supervisor_agent)

    workflow.add_edge(START, "Supervisor")

    graph = workflow.compile()

    return graph

# Main function to run the graph
def main():
    # Create the state graph
    graph = create_graph()

    # Print out the LangGraph as ASCII
    graph.get_graph().print_ascii()

    # Continuous input and LLM interaction
    print(colored("You can start interacting with the coding assistant. Type 'exit' to end the conversation.", "blue"))

    whileTrue:
        user_message = input("> ")

        if user_message == "exit":
            print(colored("Goodbye!", "blue"))
            break

        input_state = {"messages": [{"role": "user", "content": user_message}]}

        # Verbose output
        for event in graph.stream(input_state):
            print(colored(event, "red"))
            print("------------------------------------")

        # # Concise output
        # final_state = graph.invoke(input_state, config)
        # print(colored(final_state["messages"][-1].content, "red"))
        # print("------------------------------------")


if __name__ == "__main__":
    main()

五、实际运行效果

通过实际运行我们的LeetCode智能教练,我们可以看到多智能体系统在处理复杂任务时的强大能力。无论是生成定制化的编程问题,还是从互联网上搜索相关的资源,我们的系统都能够快速、准确地完成任务。而且,由于采用了多智能体架构,我们的系统在处理复杂的多步骤任务时也表现出了良好的灵活性和适应性。

LeetCode智能教练来袭!多智能体LLM协同架构实战指南-AI.x社区

LeetCode智能教练来袭!多智能体LLM协同架构实战指南-AI.x社区

LeetCode智能教练来袭!多智能体LLM协同架构实战指南-AI.x社区

六、总结与展望

通过这个案例,我们深刻体会到了多智能体LLM协同架构的强大优势。它不仅能够提升系统的性能和灵活性,还能够降低偏见和幻觉,简化复杂任务的处理过程。在未来,随着生成式AI技术的不断发展,多智能体架构必将在更多的领域发挥重要作用。我们期待看到更多基于多智能体架构的创新应用,为我们的生活和工作带来更多的便利和惊喜。


本文转载自公众号Halo咯咯    作者:基咯咯

原文链接:​​https://mp.weixin.qq.com/s/fRU7WCxQosFz5ci-Moz8Vw​

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
已于2025-5-12 08:30:42修改
收藏
回复
举报
回复
相关推荐