Skip to content

Latest commit

 

History

History
373 lines (286 loc) · 12.7 KB

multi-agent.md

File metadata and controls

373 lines (286 loc) · 12.7 KB

Multi Agent

LangGraph: Multi-Agent Workflows에서 설명하고 있는 3가지 multi agent에 대해 정리합니다.

Multi agent 방식으로 구현

아래에서는 research와 chart_generator agent들이 tool을 이용하여 순차적인 agent 동작을 수행하는것을 설명합니다. 상세한 코드는 multi-agent-collaboration.ipynblambda_function.py을 참조합니다.

  1. research agent는 사용자의 질문으로 적절한 정보를 tavily search를 통해 얻어옵니다. 검색한 정보가 충분하지 않다면 결과를 얻을때까지 반복합니다.
  2. 웹검색을 통해 얻언 정보를 기반으로 chart_generator agent가 chart에 필요한 정보를 생성합니다. 생성된 chart정보가 충분치 않을때에는 반복하여 chart_generator agent가 반복하여 동작을 수행합니다.

image

tool을 정의합니다.

tavily_tool = TavilySearchResults(max_results=5)
repl = PythonREPL()

@tool
def python_repl(
    code: Annotated[str, "The python code to execute to generate your chart."],
):
    """Use this to execute python code. If you want to see the output of a value,
    you should print it out with `print(...)`. This is visible to the user."""
    try:
        result = repl.run(code)
    except BaseException as e:
        return f"Failed to execute. Error: {repr(e)}"
    result_str = f"Successfully executed:\n```python\n{code}\n```\nStdout: {result}"
    return (
        result_str + "\n\nIf you have completed all tasks, respond with FINAL ANSWER."
    )

Graph State를 정의합니다.

class AgentState(TypedDict):
    messages: Annotated[Sequence[BaseMessage], operator.add]
    sender: str

agent_node()와 create_agent()을 정의합니다.

import functools
def agent_node(state, agent, name):
    result = agent.invoke(state)
    if isinstance(result, ToolMessage):
        pass
    else:
        result = AIMessage(**result.dict(exclude={"type", "name"}), name=name)
    return {
        "messages": [result],
        "sender": name,
    }

def create_agent(llm, tools, system_message: str):
    tool_names = ", ".join([tool.name for tool in tools])
    print("tool_names: ", tool_names)
                           
    system = (
        "You are a helpful AI assistant, collaborating with other assistants."
        "Use the provided tools to progress towards answering the question."
        "If you are unable to fully answer, that's OK, another assistant with different tools "
        "will help where you left off. Execute what you can to make progress."
        "If you or any of the other assistants have the final answer or deliverable,"
        "prefix your response with FINAL ANSWER so the team knows to stop."
        "You have access to the following tools: {tool_names}."
        "{system_message}"
    )
    
    prompt = ChatPromptTemplate.from_messages(
        [
            ("system",system),
            MessagesPlaceholder(variable_name="messages"),
        ]
    )
    
    prompt = prompt.partial(system_message=system_message)
    prompt = prompt.partial(tool_names=tool_names)
    
    return prompt | llm.bind_tools(tools)

Research와 Chart를 위한 agent node를 정의합니다.

# Research agent and node
research_agent = create_agent(
    chat,
    [tavily_tool],
    system_message="You should provide accurate data for the chart_generator to use.",
)
research_node = functools.partial(agent_node, agent=research_agent, name="Researcher")

# chart_generator
chart_agent = create_agent(
    chat,
    [python_repl],
    system_message="Any charts you display will be visible by the user.",
)
chart_node = functools.partial(agent_node, agent=chart_agent, name="chart_generator")

tool node를 정의합니다.

tools = [tavily_tool, python_repl]
tool_node = ToolNode(tools)

conditional edge를 위해 route와 route3를 정의합니다.

def router(state) -> Literal["call_tool", "end", "continue"]:
    # This is the router
    messages = state["messages"]
    last_message = messages[-1]
    if last_message.tool_calls:
        # The previous agent is invoking a tool
        return "call_tool"
    if "FINAL ANSWER" in last_message.content:
        # Any agent decided the work is done
        return "end"
    return "continue"

def router3(state):
    sender = state["sender"]
        
    return sender

Workflow를 위한 Graph를 준비합니다.

workflow = StateGraph(AgentState)

workflow.add_node("Researcher", research_node)
workflow.add_node("chart_generator", chart_node)
workflow.add_node("call_tool", tool_node)

workflow.add_conditional_edges(
    "Researcher",
    router,
    {
        "continue": "chart_generator", 
        "call_tool": "call_tool", 
        "end": END
    },
)

workflow.add_conditional_edges(
    "chart_generator",
    router,
    {
        "continue": "Researcher", 
        "call_tool": "call_tool", 
        "end": END
    },
)

workflow.add_conditional_edges(
    "call_tool",
    router3,
    {
        "Researcher": "Researcher",
        "chart_generator": "chart_generator",
    },
)

workflow.add_edge(START, "Researcher")
graph = workflow.compile()

Graph를 그려봅니다.

from IPython.display import Image, display

try:
    display(Image(graph.get_graph(xray=True).draw_mermaid_png()))
except Exception:
    # This requires some extra dependencies and is optional
    pass

image

아래와 같이 실행해봅니다.

qurry = "대한민국의 지난 10년간의 GDP를 찾으세요. 다음에 이 데이터를 가지고 line graph를 그리세요. 준비가 다 되면 마칩니다."
config = {"recursion_limit": 150}

events = graph.stream({"messages": [HumanMessage(content=qurry)]}, config)

for s in events:
    print(s)
    print("----")

이때의 최종 결과는 아래와 같습니다.

{'Researcher': {'messages': [AIMessage(content="The search results provide data on South Korea's GDP growth rate and GDP values over the past 10 years. Here are the key GDP data points from the last 10 years:\n\n2022: GDP $1,673.92 billion, growth rate 2.61%\n2021: GDP $1,818.43 billion, growth rate 4.30% \n2020: GDP $1,644.31 billion, growth rate -0.71%\n2019: GDP $1,651.42 billion, growth rate 2.24%\n2018: GDP $1,619.42 billion, growth rate 2.90%\n2017: GDP $1,530.75 billion, growth rate 3.16%\n2016: GDP $1,416.95 billion, growth rate 2.94% \n2015: GDP $1,382.83 billion, growth rate 2.79%\n2014: GDP $1,368.81 billion, growth rate 3.16%\n2013: GDP $1,305.61 billion, growth rate 2.90%\n\nTo visualize this data as a line graph:\n\nFINAL ANSWER:\n\nYear   GDP ($ billion)\n2013     1305.61\n2014     1368.81  \n2015     1382.83\n2016     1416.95\n2017     1530.75\n2018     1619.42\n2019     1651.42\n2020     1644.31\n2021     1818.43\n2022     1673.92\n\n[A line graph showing South Korea's GDP rising overall from around $1.3 trillion in 2013 to over $1.8 trillion in 2021, with a dip in 2020 likely due to the COVID-19 pandemic.]", additional_kwargs={'usage': {'prompt_tokens': 1381, 'completion_tokens': 417, 'total_tokens': 1798}, 'stop_reason': 'end_turn', 'model_id': 'anthropic.claude-3-sonnet-20240229-v1:0'}, response_metadata={'usage': {'prompt_tokens': 1381, 'completion_tokens': 417, 'total_tokens': 1798}, 'stop_reason': 'end_turn', 'model_id': 'anthropic.claude-3-sonnet-20240229-v1:0'}, name='Researcher', id='run-ec931a81-26a6-4af4-a6d9-1bf492ac6ec9-0', usage_metadata={'input_tokens': 1381, 'output_tokens': 417, 'total_tokens': 1798})], 'sender': 'Researcher'}}

Multi-agent Collaboration

multi-agent-collaboration.ipynb에서는 여러 agent들이 서로 협력하는 방법을 설명하고 있습니다.

AutoGen: Enabling Next-Gen LLM Applications via Multi-Agent Conversation을 참조하였습니다.

image

이때의 구조는 아래와 같습니다.

image

구현 코드는 아래와 같습니다.

workflow = StateGraph(AgentState)

workflow.add_node("Researcher", research_node)
workflow.add_node("chart_generator", chart_node)
workflow.add_node("call_tool", tool_node)

workflow.add_conditional_edges(
    "Researcher",
    router,
    {"continue": "chart_generator", "call_tool": "call_tool", "__end__": END},
)
workflow.add_conditional_edges(
    "chart_generator",
    router,
    {"continue": "Researcher", "call_tool": "call_tool", "__end__": END},
)

workflow.add_conditional_edges(
    "call_tool",
    lambda x: x["sender"],
    {
        "Researcher": "Researcher",
        "chart_generator": "chart_generator",
    },
)
workflow.set_entry_point("Researcher")
graph = workflow.compile()

Agent Supervisor

다른 여러개의 Agent를 orchestration하는 방법에 대해 설명합니다.

image

workflow = StateGraph(AgentState)
workflow.add_node("Researcher", research_node)
workflow.add_node("Coder", code_node)
workflow.add_node("supervisor", supervisor_chain)

for member in members:
    workflow.add_edge(member, "supervisor")
conditional_map = {k: k for k in members}
conditional_map["FINISH"] = END
workflow.add_conditional_edges("supervisor", lambda x: x["next"], conditional_map)

workflow.set_entry_point("supervisor")

graph = workflow.compile()

Hierarchical Agent Teams

hierarchical_agent_teams.ipynb에서는 Agent Superviser가 여러개 있을때의 supervisor node를 설명하고 있습니다.

image

Research Team

이때의 구현된 Graph는 아래와 같습니다.

image

이를 구현한 코드는 아래와 같습니다.

research_graph = StateGraph(ResearchTeamState)
research_graph.add_node("Search", search_node)
research_graph.add_node("WebScraper", research_node)
research_graph.add_node("supervisor", supervisor_agent)

research_graph.add_edge("Search", "supervisor")
research_graph.add_edge("WebScraper", "supervisor")
research_graph.add_conditional_edges(
    "supervisor",
    lambda x: x["next"],
    {"Search": "Search", "WebScraper": "WebScraper", "FINISH": END},
)


research_graph.set_entry_point("supervisor")
chain = research_graph.compile()

Document Writing Team

authoring_graph = StateGraph(DocWritingState)
authoring_graph.add_node("DocWriter", doc_writing_node)
authoring_graph.add_node("NoteTaker", note_taking_node)
authoring_graph.add_node("ChartGenerator", chart_generating_node)
authoring_graph.add_node("supervisor", doc_writing_supervisor)

# Add the edges that always occur
authoring_graph.add_edge("DocWriter", "supervisor")
authoring_graph.add_edge("NoteTaker", "supervisor")
authoring_graph.add_edge("ChartGenerator", "supervisor")

# Add the edges where routing applies
authoring_graph.add_conditional_edges(
    "supervisor",
    lambda x: x["next"],
    {
        "DocWriter": "DocWriter",
        "NoteTaker": "NoteTaker",
        "ChartGenerator": "ChartGenerator",
        "FINISH": END,
    },
)

authoring_graph.set_entry_point("supervisor")
chain = authoring_graph.compile()

image

Add Layers

super_graph = StateGraph(State)
super_graph.add_node("ResearchTeam", get_last_message | research_chain | join_graph)
super_graph.add_node(
    "PaperWritingTeam", get_last_message | authoring_chain | join_graph
)
super_graph.add_node("supervisor", supervisor_node)

super_graph.add_edge("ResearchTeam", "supervisor")
super_graph.add_edge("PaperWritingTeam", "supervisor")
super_graph.add_conditional_edges(
    "supervisor",
    lambda x: x["next"],
    {
        "PaperWritingTeam": "PaperWritingTeam",
        "ResearchTeam": "ResearchTeam",
        "FINISH": END,
    },
)
super_graph.set_entry_point("supervisor")
super_graph = super_graph.compile()

이때의 Graph는 아래와 같습니다.

image