Building a plan-execute-reflect agent
출처: 문서
본문
plan-execute-reflect 에이전트 빌드하기
이 기능은 실험적인 기능이므로 프로덕션 환경에서 사용하는 것은 권장하지 않아요. 기능 진행 상황에 대한 업데이트를 확인하거나 피드백을 남기고 싶다면 관련 GitHub issue를 참고하세요.
{: .warning}
이 튜토리얼에서는 plan-execute-reflect 에이전트를 빌드하고 사용하는 방법을 설명해요. 이 에이전트는 다단계 실행과 추론이 도움이 되는 복잡한 문제를 해결할 때 사용할 수 있어요. 이 예제에서는 OpenSearch 인덱스에 있는 항공편 데이터를 분석해 달라고 에이전트에게 요청하게 돼요. 이 에이전트에 대해 자세히 알아보려면 Plan-execute-reflect agents를 참고하세요.
your_ 접두사로 시작하는 플레이스홀더는 여러분의 값으로 바꾸세요.
{: .note}
사전 준비
OpenSearch Dashboards 홈 페이지에 로그인한 후 Add sample data를 선택하고 Sample Flight data를 추가하세요.
1단계: LLM 준비하기
plan-execute-reflect 에이전트가 동작하려면 대규모 언어 모델(LLM)이 필요해요. 이 튜토리얼에서는 Amazon Bedrock에서 호스팅되는 Anthropic Claude 3.7 모델을 사용해요. 지원되는 다른 LLM을 사용할 수도 있어요.
1단계(a): 커넥터 생성하기
모델용 커넥터를 생성하세요:
POST /_plugins/_ml/connectors/_create
{
"name": "Amazon Bedrock Claude 3.7-sonnet connector",
"description": "Connector to Amazon Bedrock service for the Claude model",
"version": 1,
"protocol": "aws_sigv4",
"parameters": {
"region": "your_aws_region",
"service_name": "bedrock",
"model": "us.anthropic.claude-3-7-sonnet-20250219-v1:0"
},
"credential": {
"access_key": "your_aws_access_key",
"secret_key": "your_aws_secret_key",
"session_token": "your_aws_session_token"
},
"actions": [
{
"action_type": "predict",
"method": "POST",
"url": "https://bedrock-runtime.${parameters.region}.amazonaws.com/model/${parameters.model}/converse",
"headers": {
"content-type": "application/json"
},
"request_body": "{ \"system\": [{\"text\": \"${parameters.system_prompt}\"}], \"messages\": [${parameters._chat_history:-}{\"role\":\"user\",\"content\":[{\"text\":\"${parameters.prompt}\"}]}${parameters._interactions:-}]${parameters.tool_configs:-} }"
}
]
}
{% include copy-curl.html %}
커넥터 ID를 기억해 두세요. 모델을 등록할 때 사용하게 돼요.
1단계(b): 모델 등록하기
모델을 등록하세요:
POST /_plugins/_ml/models/_register
{
"name": "Bedrock Claude Sonnet model",
"function_name": "remote",
"description": "Bedrock Claude 3.7 sonnet model for Plan, Execute and Reflect Agent",
"connector_id": "your_connector_id"
}
{% include copy-curl.html %}
모델 ID를 기억해 두세요. 이후 단계에서 사용하게 돼요.
1단계(c): 재시도 정책 구성하기
이 에이전트는 여러 단계를 실행하는 장시간 실행 에이전트이기 때문에 커넥터에 재시도 정책을 구성하는 것을 강력히 권장해요. 자세한 내용은 Request body fields의 client_config 필드를 참고하세요. 예를 들어 무제한 재시도를 구성하려면 max_retry_times를 -1로 설정하세요:
PUT /_plugins/_ml/connectors/{connector_id}
{
"client_config": {
"max_retry_times": -1,
"retry_backoff_millis": 300,
"retry_backoff_policy": "exponential_full_jitter"
}
}
{% include copy-curl.html %}
모델을 배포했거나 predict 호출을 수행한 적이 있다면, client_config를 업데이트하기 전에 먼저 모델을 undeploy해야 해요. 자세한 내용은 Undeploy Model API를 참고하세요.
모델 배포에 대한 자세한 내용은 Deploy Model API를 참고하세요.
2단계: 에이전트 생성하기
다음 정보로 구성된 plan_execute_and_reflect 에이전트를 생성하세요:
- 메타 정보:
name,type,description. - LLM 정보: 에이전트는 LLM을 사용해 추론하고, 작업을 완료하기 위한 계획을 세우고, 적절한 도구를 사용해 계획의 단계를 실행하며, 계획을 최적화하기 위해 중간 결과를 되돌아봐요.
- 도구: 도구는 에이전트가 실행할 수 있는 함수예요. 각 도구는 자체적인
name,description,parameters,attributes를 정의할 수 있어요. - 메모리: 채팅 메시지를 저장해요. OpenSearch는
conversation_index라는 한 가지 메모리 타입을 지원해요.
모든 요청 필드에 대한 자세한 내용은 Register Agent API를 참고하세요.
에이전트를 등록하려면 다음 요청을 보내세요. 이 예제에서는 ListIndexTool, SearchIndexTool, IndexMappingTool을 갖춘 에이전트를 생성해요:
POST _plugins/_ml/agents/_register
{
"name": "My Plan Execute and Reflect agent with Claude 3.7",
"type": "plan_execute_and_reflect",
"description": "this is a test agent",
"llm": {
"model_id": "your_llm_model_id_from_step1",
"parameters": {
"prompt": "${parameters.question}"
}},
"memory": {
"type": "conversation_index"
},
"parameters": {
"_llm_interface": "bedrock/converse/claude"
},
"tools": [
{
"type": "ListIndexTool"
},
{
"type": "SearchIndexTool"
},
{
"type": "IndexMappingTool"
}
],
}
{% include copy-curl.html %}
에이전트 ID를 기억해 두세요. 다음 단계에서 사용하게 돼요.
필요에 따라 사용 사례와 관련된 다른 도구를 구성할 수도 있어요. 다른 도구를 구성할 때는 해당 도구의 attributes 필드를 반드시 제공하세요. 도구 실행 시 기대되는 입력 스키마를 LLM에게 알려주는 역할을 하는 것이 attributes이기 때문에 이는 매우 중요해요.
ListIndexTool, SearchIndexTool, IndexMappingTool, WebSearchTool에는 사전 정의된 attributes가 포함되어 있어요. 예를 들어 ListIndexTool은 다음과 같은 attributes를 제공해요:
tools: [{
"type": "ListIndexTool",
"attributes": {
"input_schema": {
"type": "object",
"properties": {
"indices": {
"type": "array",
"items": {
"type": "string"
},
"description": "OpenSearch index name list, separated by comma. for example: [\"index1\", \"index2\"], use empty array [] to list all indices in the cluster"
}
},
},
"strict": false
}
}]
에이전트 테스트하기
다음 팁을 활용하면 plan_execute_and_reflect 에이전트를 효과적으로 테스트할 수 있어요:
-
에이전트 실행 추적: Get Message Traces API를 사용하면 상세한 실행 단계를 확인할 수 있어요:
GET _plugins/_ml/memory/message/your_message_id/traces -
할루시네이션 완화: LLM은 잘못된 도구를 선택하거나 작업을 잘못 해석하는 방식으로 "할루시네이션"을 일으킬 수 있어요. 특히 에이전트에 너무 많은 도구가 구성되어 있을 때 그렇죠. 할루시네이션을 피하려면 다음 방법들을 시도해 보세요:
- 에이전트에 구성하는 도구의 수를 제한하세요.
- 각 도구에 명확하고 구체적인 설명을 제공하세요.
- 작업에 필요한 모든 도구에 에이전트가 접근할 수 있도록 하세요.
- 프롬프트에 클러스터에 대한 관련 컨텍스트를 포함하세요. 예를 들어
Can you identify the error in my cluster by analyzing the "spans" and "logs" indexes?와 같아요.
-
재시도 구성: LLM 호출은 가끔 실패할 수 있어요. 신뢰성을 높이려면 재시도를 설정하세요. 자세한 내용은 Request body fields의
client_config필드를 참고하세요.
에이전트를 테스트하려면 Execute Agent API를 사용해 에이전트를 실행하세요. 이 에이전트는 장시간 실행되는 작업을 수행하기 때문에 타임아웃을 피하려면 비동기로 실행하는 것을 권장해요. async=true 쿼리 파라미터를 사용하면 에이전트를 별도의 작업으로 실행할 수 있어요:
POST _plugins/_ml/agents/your_agent_id/_execute?async=true
{
"parameters": {
"question": "How many flights from Beijing to Seattle?"
}
}
{% include copy-curl.html %}
응답에서 task_id와 memory_id를 기억해 두세요. 진행 상황을 추적하고 결과를 확인할 때 사용하게 돼요.
작업이 아직 실행 중인지 아니면 완료되었는지 확인하려면 다음 요청을 사용하세요:
GET _plugins/_ml/tasks/your_task_id
{% include copy-curl.html %}
작업이 완료되면 에이전트의 응답이 반환돼요:
{
"task_type": "AGENT_EXECUTION",
"function_name": "AGENT",
"state": "COMPLETED",
"worker_node": [
"q5yAqa75RM-rv0I67V1VVQ"
],
"create_time": 1746148548710,
"last_update_time": 1746148706345,
"is_async": false,
"response": {
"memory_id": "bzWQjpYBKhItn1nNYHtu",
"inference_results": [
{
"output": [
{
"result": "bzWQjpYBKhItn1nNYHtu",
"name": "memory_id"
},
{
"result": "cDWQjpYBKhItn1nNYHuS",
"name": "parent_interaction_id"
},
{
"result": "dTWQjpYBKhItn1nNbHsw",
"name": "executor_agent_memory_id"
},
{
"result": "YjWQjpYBKhItn1nN6oYk",
"name": "executor_agent_parent_interaction_id"
},
{
"name": "response",
"dataAsMap": {
"response": """# Comprehensive Analysis Report: Flights from Beijing to Seattle
## Executive Summary
After analyzing the OpenSearch sample flight dataset, I found that there are 0 direct flights from Beijing to Seattle in the dataset.
## Analysis Process
### Step 1: Identify Available Data Sources
I began by examining the indices available in the OpenSearch cluster to locate flight-related data. This search revealed one relevant index: `opensearch_dashboards_sample_data_flights`, which contains 13,059 flight records with comprehensive information including origin and destination cities, flight numbers, carriers, and other flight details.
### Step 2: Data Schema Analysis
I analyzed the index structure and confirmed it contains the necessary fields for this investigation, including:
- Origin/destination city names (`OriginCityName`, `DestCityName`)
- Airport codes (`Origin`, `Dest`)
- Airport IDs (`OriginAirportID`, `DestAirportID`)
- Geographic information for origins and destinations
### Step 3: Query Construction and Execution
I created and executed a search query to find flights where:
- Origin city is Beijing (also checked for "Beijing Capital International Airport" and airport code "PEK")
- Destination city is Seattle (also checked for "Seattle Tacoma International Airport" and airport code "SEA")
### Step 4: Result Verification
To ensure the search was properly constructed, I verified that:
1. Flights from Beijing to other destinations exist in the dataset
2. Flights to Seattle from other origins exist in the dataset
This confirmed that both cities are represented in the data, but no flights connect them directly.
## Key Findings
- Beijing appears as an origin city in the dataset, with flights to destinations including Warsaw, Pittsburgh, Xi'an, Vienna, and Chicago/Rockford
- Seattle appears as both origin and destination in the dataset, with connections to cities like Vienna, Istanbul, New Orleans, St Louis, and Treviso
- The dataset contains 0 flights from Beijing to Seattle
## Conclusion
Based on a comprehensive search of the OpenSearch flight sample dataset, there are 0 flights from Beijing to Seattle in this dataset. While both cities appear in the dataset with connections to other locations, this specific route is not represented in the sample data."""
}
}
]
}
]
}
}
에이전트 실행 응답에는 몇 가지 주요 필드가 포함되어 있어요:
memory_id:plan_execute_and_reflect에이전트와 LLM 사이에서 오간 모든 메시지를 저장하는 메모리의 ID예요.parent_interaction_id: 플래닝 에이전트에서 대화를 시작한 부모 메시지의message_id예요.executor_agent_memory_id: 내부 executor 에이전트와 LLM 사이에서 오간 메시지를 저장하는 메모리의 ID예요.executor_agent_parent_interaction_id: executor 에이전트 대화에서 부모 메시지의message_id예요.response: 모든 단계가 실행된 후 에이전트가 산출한 최종 결과예요.
plan-execute-reflect 에이전트를 비동기로 실행하면 에이전트가 시작되는 즉시 API가 플래너 에이전트의 memory_id와 parent_interaction_id를 반환해요.
최종 응답에서는 계획의 각 단계를 수행하는 내부 executor 에이전트에 해당하는 executor_agent_memory_id와 executor_agent_parent_interaction_id도 API가 함께 반환해요. executor_agent_memory_id와 executor_agent_parent_interaction_id는 에이전트가 실행을 완료하기 전이라도 사용 가능해지는 즉시 작업에 업데이트돼요. 덕분에 실행 과정을 실시간으로 추적할 수 있어요.
에이전트의 메시지 히스토리를 살펴보려면 Get Memory API를 사용하세요:
GET _plugins/_ml/memory/your_memory_id/messages
{% include copy-curl.html %}
자세한 내용은 Memory APIs를 참고하세요.
관련 메시지의 message_id를 기억해 두고, 이를 사용해 단계별 실행 트레이스를 가져오세요:
GET _plugins/_ml/memory/message/your_message_id/traces
{% include copy-curl.html %}
자세한 내용은 Get Message Traces API를 참고하세요.
대화형 메모리 테스트하기
같은 대화를 이어서 진행하려면 에이전트를 실행할 때 해당 대화의 memory_id를 지정하세요. 이전 메시지들이 추출되어 모델에 컨텍스트로 제공돼요. 대화를 이어가려면 플래너 에이전트의 memory_id를 사용하세요:
POST _plugins/_ml/agents/your_agent_id/_execute?async=true
{
"parameters": {
"question": "your_question",
"memory_id": "your_memory_id",
}
}
{% include copy-curl.html %}
다음 단계
- 다른 모델을 사용하는 방법은 Supported LLMs를 참고하세요.
- 커스텀 프롬프트로 에이전트를 생성하는 방법은 Modifying default prompts를 참고하세요.