콘텐츠로 이동

순차적 에이전트

ADK에서 지원Python v0.1.0Go v0.1.0Java v0.2.0

SequentialAgent는 하위 에이전트를 목록에 명시된 순서대로 실행하는 워크플로우 에이전트입니다. 실행이 고정된, 엄격한 순서로 이루어져야 할 때 SequentialAgent를 사용하세요.

예시

  • 두 가지 도구, 즉 페이지 콘텐츠 가져오기(Get Page Contents)페이지 요약하기(Summarize Page)를 사용하여 모든 웹페이지를 요약할 수 있는 에이전트를 구축한다고 가정해 봅시다. 에이전트는 항상 페이지 요약하기를 호출하기 전에 페이지 콘텐츠 가져오기를 호출해야 하므로(아무것도 없는 상태에서 요약할 수는 없으므로!), SequentialAgent를 사용하여 에이전트를 구축해야 합니다.

다른 워크플로우 에이전트와 마찬가지로 SequentialAgent는 LLM으로 구동되지 않으므로 실행 방식이 결정적(deterministic)입니다. 하지만 워크플로우 에이전트는 내부 로직이 아닌 실행(즉, 순차 실행)에만 관여합니다. 워크플로우 에이전트의 도구나 하위 에이전트는 LLM을 활용할 수도 있고, 그렇지 않을 수도 있습니다.

작동 방식

SequentialAgentRun Async 메서드가 호출되면 다음 작업을 수행합니다.

  1. 반복(Iteration): 제공된 순서대로 하위 에이전트 목록을 순회합니다.
  2. 하위 에이전트 실행(Sub-Agent Execution): 목록의 각 하위 에이전트에 대해 해당 하위 에이전트의 Run Async 메서드를 호출합니다.

Sequential Agent

전체 예시: 코드 개발 파이프라인

단순화된 코드 개발 파이프라인을 생각해 봅시다.

  • 코드 작성 에이전트(Code Writer Agent): 명세를 기반으로 초기 코드를 생성하는 LLM 에이전트입니다.
  • 코드 검토 에이전트(Code Reviewer Agent): 생성된 코드의 오류, 스타일 문제, 모범 사례 준수 여부를 검토하는 LLM 에이전트입니다. 이 에이전트는 코드 작성 에이전트의 출력을 입력으로 받습니다.
  • 코드 리팩터링 에이전트(Code Refactorer Agent): 검토된 코드(와 검토자의 의견)를 받아 품질을 개선하고 문제를 해결하기 위해 리팩터링하는 LLM 에이전트입니다.

이런 경우 SequentialAgent가 완벽하게 들어맞습니다.

SequentialAgent(sub_agents=[CodeWriterAgent, CodeReviewerAgent, CodeRefactorerAgent])

이를 통해 코드가 작성되고, 그 다음 검토되고, 마지막으로 리팩터링되는 엄격하고 신뢰할 수 있는 순서를 보장합니다. 각 하위 에이전트의 출력은 출력 키(Output Key)를 통해 상태(state)에 저장되어 다음 에이전트로 전달됩니다.

공유된 Invocation Context

SequentialAgent는 각 하위 에이전트에 동일한 InvocationContext를 전달합니다. 이는 모든 하위 에이전트가 임시 (temp:) 네임스페이스를 포함한 동일한 세션 상태를 공유한다는 것을 의미하며, 단일 턴 내에서 단계 간 데이터 전달을 용이하게 합니다.

코드
# Part of agent.py --> Follow https://google.github.io/adk-docs/get-started/quickstart/ to learn the setup

# --- 1. Define Sub-Agents for Each Pipeline Stage ---

# Code Writer Agent
# Takes the initial specification (from user query) and writes code.
code_writer_agent = LlmAgent(
    name="CodeWriterAgent",
    model=GEMINI_MODEL,
    # Change 3: Improved instruction
    instruction="""You are a Python Code Generator.
Based *only* on the user's request, write Python code that fulfills the requirement.
Output *only* the complete Python code block, enclosed in triple backticks (```python ... ```). 
Do not add any other text before or after the code block.
""",
    description="Writes initial Python code based on a specification.",
    output_key="generated_code" # Stores output in state['generated_code']
)

# Code Reviewer Agent
# Takes the code generated by the previous agent (read from state) and provides feedback.
code_reviewer_agent = LlmAgent(
    name="CodeReviewerAgent",
    model=GEMINI_MODEL,
    # Change 3: Improved instruction, correctly using state key injection
    instruction="""You are an expert Python Code Reviewer. 
    Your task is to provide constructive feedback on the provided code.

    **Code to Review:**
    ```python
    {generated_code}
    ```

**Review Criteria:**
1.  **Correctness:** Does the code work as intended? Are there logic errors?
2.  **Readability:** Is the code clear and easy to understand? Follows PEP 8 style guidelines?
3.  **Efficiency:** Is the code reasonably efficient? Any obvious performance bottlenecks?
4.  **Edge Cases:** Does the code handle potential edge cases or invalid inputs gracefully?
5.  **Best Practices:** Does the code follow common Python best practices?

**Output:**
Provide your feedback as a concise, bulleted list. Focus on the most important points for improvement.
If the code is excellent and requires no changes, simply state: "No major issues found."
Output *only* the review comments or the "No major issues" statement.
""",
    description="Reviews code and provides feedback.",
    output_key="review_comments", # Stores output in state['review_comments']
)


# Code Refactorer Agent
# Takes the original code and the review comments (read from state) and refactors the code.
code_refactorer_agent = LlmAgent(
    name="CodeRefactorerAgent",
    model=GEMINI_MODEL,
    # Change 3: Improved instruction, correctly using state key injection
    instruction="""You are a Python Code Refactoring AI.
Your goal is to improve the given Python code based on the provided review comments.

  **Original Code:**
  ```python
  {generated_code}
  ```

  **Review Comments:**
  {review_comments}

**Task:**
Carefully apply the suggestions from the review comments to refactor the original code.
If the review comments state "No major issues found," return the original code unchanged.
Ensure the final code is complete, functional, and includes necessary imports and docstrings.

**Output:**
Output *only* the final, refactored Python code block, enclosed in triple backticks (```python ... ```). 
Do not add any other text before or after the code block.
""",
    description="Refactors code based on review comments.",
    output_key="refactored_code", # Stores output in state['refactored_code']
)


# --- 2. Create the SequentialAgent ---
# This agent orchestrates the pipeline by running the sub_agents in order.
code_pipeline_agent = SequentialAgent(
    name="CodePipelineAgent",
    sub_agents=[code_writer_agent, code_reviewer_agent, code_refactorer_agent],
    description="Executes a sequence of code writing, reviewing, and refactoring.",
    # The agents will run in the order provided: Writer -> Reviewer -> Refactorer
)

# For ADK tools compatibility, the root agent must be named `root_agent`
root_agent = code_pipeline_agent
    model, err := gemini.NewModel(ctx, modelName, &genai.ClientConfig{})
    if err != nil {
        return fmt.Errorf("failed to create model: %v", err)
    }

    codeWriterAgent, err := llmagent.New(llmagent.Config{
        Name:        "CodeWriterAgent",
        Model:       model,
        Description: "Writes initial Go code based on a specification.",
        Instruction: `You are a Go Code Generator.
Based *only* on the user's request, write Go code that fulfills the requirement.
Output *only* the complete Go code block, enclosed in triple backticks ('''go ... ''').
Do not add any other text before or after the code block.`,
        OutputKey: "generated_code",
    })
    if err != nil {
        return fmt.Errorf("failed to create code writer agent: %v", err)
    }

    codeReviewerAgent, err := llmagent.New(llmagent.Config{
        Name:        "CodeReviewerAgent",
        Model:       model,
        Description: "Reviews code and provides feedback.",
        Instruction: `You are an expert Go Code Reviewer.
Your task is to provide constructive feedback on the provided code.

**Code to Review:**
'''go
{generated_code}
'''

**Review Criteria:**
1.  **Correctness:** Does the code work as intended? Are there logic errors?
2.  **Readability:** Is the code clear and easy to understand? Follows Go style guidelines?
3.  **Idiomatic Go:** Does the code use Go's features in a natural and standard way?
4.  **Edge Cases:** Does the code handle potential edge cases or invalid inputs gracefully?
5.  **Best Practices:** Does the code follow common Go best practices?

**Output:**
Provide your feedback as a concise, bulleted list. Focus on the most important points for improvement.
If the code is excellent and requires no changes, simply state: "No major issues found."
Output *only* the review comments or the "No major issues" statement.`,
        OutputKey: "review_comments",
    })
    if err != nil {
        return fmt.Errorf("failed to create code reviewer agent: %v", err)
    }

    codeRefactorerAgent, err := llmagent.New(llmagent.Config{
        Name:        "CodeRefactorerAgent",
        Model:       model,
        Description: "Refactors code based on review comments.",
        Instruction: `You are a Go Code Refactoring AI.
Your goal is to improve the given Go code based on the provided review comments.

**Original Code:**
'''go
{generated_code}
'''

**Review Comments:**
{review_comments}

**Task:**
Carefully apply the suggestions from the review comments to refactor the original code.
If the review comments state "No major issues found," return the original code unchanged.
Ensure the final code is complete, functional, and includes necessary imports.

**Output:**
Output *only* the final, refactored Go code block, enclosed in triple backticks ('''go ... ''').
Do not add any other text before or after the code block.`,
        OutputKey: "refactored_code",
    })
    if err != nil {
        return fmt.Errorf("failed to create code refactorer agent: %v", err)
    }

    codePipelineAgent, err := sequentialagent.New(sequentialagent.Config{
        AgentConfig: agent.Config{
            Name:        appName,
            Description: "Executes a sequence of code writing, reviewing, and refactoring.",
            SubAgents: []agent.Agent{
                codeWriterAgent,
                codeReviewerAgent,
                codeRefactorerAgent,
            },
        },
    })
    if err != nil {
        return fmt.Errorf("failed to create sequential agent: %v", err)
    }
import com.google.adk.agents.LlmAgent;
import com.google.adk.agents.SequentialAgent;
import com.google.adk.events.Event;
import com.google.adk.runner.InMemoryRunner;
import com.google.adk.sessions.Session;
import com.google.genai.types.Content;
import com.google.genai.types.Part;
import io.reactivex.rxjava3.core.Flowable;

public class SequentialAgentExample {

  private static final String APP_NAME = "CodePipelineAgent";
  private static final String USER_ID = "test_user_456";
  private static final String MODEL_NAME = "gemini-2.0-flash";

  public static void main(String[] args) {
    SequentialAgentExample sequentialAgentExample = new SequentialAgentExample();
    sequentialAgentExample.runAgent(
        "Write a Java function to calculate the factorial of a number.");
  }

  public void runAgent(String prompt) {

    LlmAgent codeWriterAgent =
        LlmAgent.builder()
            .model(MODEL_NAME)
            .name("CodeWriterAgent")
            .description("Writes initial Java code based on a specification.")
            .instruction(
                """
                You are a Java Code Generator.
                Based *only* on the user's request, write Java code that fulfills the requirement.
                Output *only* the complete Java code block, enclosed in triple backticks (```java ... ```).
                Do not add any other text before or after the code block.
                """)
            .outputKey("generated_code")
            .build();

    LlmAgent codeReviewerAgent =
        LlmAgent.builder()
            .model(MODEL_NAME)
            .name("CodeReviewerAgent")
            .description("Reviews code and provides feedback.")
            .instruction(
                """
                    You are an expert Java Code Reviewer.
                    Your task is to provide constructive feedback on the provided code.

                    **Code to Review:**
                    ```java
                    {generated_code}
                    ```

                    **Review Criteria:**
                    1.  **Correctness:** Does the code work as intended? Are there logic errors?
                    2.  **Readability:** Is the code clear and easy to understand? Follows Java style guidelines?
                    3.  **Efficiency:** Is the code reasonably efficient? Any obvious performance bottlenecks?
                    4.  **Edge Cases:** Does the code handle potential edge cases or invalid inputs gracefully?
                    5.  **Best Practices:** Does the code follow common Java best practices?

                    **Output:**
                    Provide your feedback as a concise, bulleted list. Focus on the most important points for improvement.
                    If the code is excellent and requires no changes, simply state: "No major issues found."
                    Output *only* the review comments or the "No major issues" statement.
                """)
            .outputKey("review_comments")
            .build();

    LlmAgent codeRefactorerAgent =
        LlmAgent.builder()
            .model(MODEL_NAME)
            .name("CodeRefactorerAgent")
            .description("Refactors code based on review comments.")
            .instruction(
                """
                You are a Java Code Refactoring AI.
                Your goal is to improve the given Java code based on the provided review comments.

                  **Original Code:**
                  ```java
                  {generated_code}
                  ```

                  **Review Comments:**
                  {review_comments}

                **Task:**
                Carefully apply the suggestions from the review comments to refactor the original code.
                If the review comments state "No major issues found," return the original code unchanged.
                Ensure the final code is complete, functional, and includes necessary imports and docstrings.

                **Output:**
                Output *only* the final, refactored Java code block, enclosed in triple backticks (```java ... ```).
                Do not add any other text before or after the code block.
                """)
            .outputKey("refactored_code")
            .build();

    SequentialAgent codePipelineAgent =
        SequentialAgent.builder()
            .name(APP_NAME)
            .description("Executes a sequence of code writing, reviewing, and refactoring.")
            // The agents will run in the order provided: Writer -> Reviewer -> Refactorer
            .subAgents(codeWriterAgent, codeReviewerAgent, codeRefactorerAgent)
            .build();

    // Create an InMemoryRunner
    InMemoryRunner runner = new InMemoryRunner(codePipelineAgent, APP_NAME);
    // InMemoryRunner automatically creates a session service. Create a session using the service
    Session session = runner.sessionService().createSession(APP_NAME, USER_ID).blockingGet();
    Content userMessage = Content.fromParts(Part.fromText(prompt));

    // Run the agent
    Flowable<Event> eventStream = runner.runAsync(USER_ID, session.id(), userMessage);

    // Stream event response
    eventStream.blockingForEach(
        event -> {
          if (event.finalResponse()) {
            System.out.println(event.stringifyContent());
          }
        });
  }
}