본문 바로가기
WIKI 기술 지식 베이스

Execute Agent Stream API

원문 보기 위키 갱신

출처: 문서

본문

Execute Agent Stream API

3.3에 도입 {: .label .label-purple }

이 기능은 실험적 기능으로, 프로덕션 환경에서의 사용은 권장하지 않아요. 기능 진행 상황에 대한 업데이트를 받거나 피드백을 남기고 싶다면 OpenSearch forum에서 진행 중인 논의에 참여해 주세요. {: .warning}

Execute Agent Stream API는 Execute Agent API와 동일한 기능을 제공하지만, 응답을 스트리밍 형식으로 반환해서 데이터가 준비되는 대로 청크 단위로 전달해요. 이 스트리밍 방식은 응답이 긴 대규모 언어 모델 상호작용에서 특히 유용한데, 전체 응답을 기다리지 않고도 부분 결과를 즉시 확인할 수 있기 때문이에요.

또는 gRPC를 통해 에이전트 실행을 스트리밍할 수도 있어요. 자세한 내용은 gRPC Execute Agent Stream API를 참고하세요. {: .note}

이 API는 다음 에이전트 타입을 지원해요:

엔드포인트

POST /_plugins/_ml/agents/{agent_id}/_execute/stream

사전 요구 사항

이 API를 사용하기 전에 다음 사전 요구 사항을 모두 충족했는지 확인하세요.

클러스터 설정

클러스터를 설정하려면 다음 단계를 따르세요.

1단계: 필요한 플러그인 설치

Execute Agent Stream API는 다음 플러그인에 의존해요. 이 플러그인은 OpenSearch 배포판에 포함되어 있지만, 다음과 같이 명시적으로 설치해야 해요:

bin/opensearch-plugin install transport-reactor-netty4
bin/opensearch-plugin install arrow-flight-rpc

자세한 내용은 Managing OpenSearch plugins를 참고하세요.

2단계: OpenSearch 설정 구성

다음 설정을 opensearch.yml 파일 또는 Docker Compose 구성에 추가하세요:

opensearch.experimental.feature.transport.stream.enabled: true

# Choose one based on your security settings
http.type: reactor-netty4        # security disabled
http.type: reactor-netty4-secure # security enabled

# Multi-node cluster settings (if applicable)
# Use network.host IP for opensearch.yml or node name for Docker
arrow.flight.publish_host: <ip>
arrow.flight.bind_host: <ip>

# Security-enabled cluster settings (if applicable)
transport.stream.type.default: FLIGHT-SECURE
flight.ssl.enable: true
transport.ssl.enforce_hostname_verification: false

{% include copy.html %}

보안 데모 인증서를 사용하는 경우, opensearch.yml 파일에서 plugins.security.ssl.transport.enforce_hostname_verification: false를 transport.ssl.enforce_hostname_verification: false로 변경하세요. {: .note}

실험적 기능 활성화에 대한 자세한 내용은 Experimental feature flags를 참고하세요.

3단계: JVM 옵션 구성

다음 설정을 jvm.options 파일에 추가하세요:

-Dio.netty.allocator.numDirectArenas=1
-Dio.netty.noUnsafe=false
-Dio.netty.tryUnsafe=true
-Dio.netty.tryReflectionSetAccessible=true
--add-opens=java.base/java.nio=org.apache.arrow.memory.core,ALL-UNNAMED

{% include copy.html %}

필요한 API 구성

다음 단계에 따라 API를 구성하세요.

1단계: 스트리밍 기능 플래그 활성화

스트리밍 기능 플래그를 활성화하려면 다음과 같이 클러스터 설정을 업데이트하세요:

PUT _cluster/settings
{
  "persistent" : {
    "plugins.ml_commons.stream_enabled": true
  }
}

{% include copy-curl.html %}

2단계: 호환되는 외부 호스팅 모델 등록

OpenAI Chat Completion 모델을 등록하려면 다음 요청을 보내세요:

POST /_plugins/_ml/models/_register
{
    "name": "OpenAI gpt-4o-mini",
    "function_name": "remote",
    "description": "OpenAI model",
    "connector": {
        "name": "OpenAI Chat Connector",
        "description": "The connector to OpenAI model service for gpt-4o-mini",
        "version": 1,
        "protocol": "http",
        "parameters": {
            "endpoint": "api.openai.com",
            "model": "gpt-4o-mini"
        },
        "credential": {
            "openAI_key": "<your_api_key>"
        },
        "actions": [
            {
                "action_type": "predict",
                "method": "POST",
                "url": "https://${parameters.endpoint}/v1/chat/completions",
                "headers": {
                    "Authorization": "Bearer ${credential.openAI_key}"
                },
                "request_body": "{ \"model\": \"${parameters.model}\", \"messages\": [{\"role\":\"developer\",\"content\":\"${parameters.system_prompt}\"},${parameters._chat_history:-}{\"role\":\"user\",\"content\":\"${parameters.prompt}\"}${parameters._interactions:-}]${parameters.tool_configs:-} }"
            }
        ]
    }
}

{% include copy-curl.html %}

Amazon Bedrock Converse Stream 모델을 등록하려면 다음 요청을 보내세요:

POST /_plugins/_ml/models/_register
{
    "name": "Amazon Bedrock Converse Stream model",
    "function_name": "remote",
    "description": "Amazon Bedrock Claude model",
    "connector": {
        "name": "Amazon Bedrock Converse",
        "description": "The connector to Amazon Bedrock Converse",
        "version": 1,
        "protocol": "aws_sigv4",
        "credential": {
            "access_key": "<your_aws_access_key>",
            "secret_key": "<your_aws_secret_key>",
            "session_token": "<your_aws_session_token>"
        },
        "parameters": {
            "region": "<your_aws_region>",
            "service_name": "bedrock",
            "model": "us.anthropic.claude-3-7-sonnet-20250219-v1:0"
        },
        "actions": [
            {
                "action_type": "predict",
                "method": "POST",
                "headers": {
                    "content-type": "application/json"
                },
                "url": "https://bedrock-runtime.${parameters.region}.amazonaws.com/model/${parameters.model}/converse",
                "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 %}

3단계: 대화형 에이전트 등록

에이전트를 등록할 때는 모델 타입에 맞는 _llm_interface 파라미터를 반드시 포함해야 해요:

  • OpenAI Chat Completion: openai/v1/chat/completions
  • Amazon Bedrock Converse Stream: bedrock/converse/claude

에이전트를 등록하려면 다음 요청을 보내세요:

POST /_plugins/_ml/agents/_register
{
    "name": "Chat Agent with RAG",
    "type": "conversational",
    "description": "This is a test agent",
    "llm": {
        "model_id": "<model_id_from_step_2>",
        "parameters": {
            "max_iteration": 5,
            "system_prompt": "You are a helpful assistant. You are able to assist with a wide range of tasks, from answering simple questions to providing in-depth explanations and discussions on a wide range of topics.\nIf the question is complex, you will split it into several smaller questions, and solve them one by one. For example, the original question is:\nhow many orders in last three month? Which month has highest?\nYou will spit into several smaller questions:\n1.Calculate total orders of last three month.\n2.Calculate monthly total order of last three month and calculate which months order is highest. You MUST use the available tools everytime to answer the question",
            "prompt": "${parameters.question}"
        }
    },
    "memory": {
        "type": "conversation_index"
    },
    "parameters": {
        "_llm_interface": "openai/v1/chat/completions"
    },
    "tools": [
        {
            "type": "IndexMappingTool",
            "name": "DemoIndexMappingTool",
            "parameters": {
                "index": "${parameters.index}",
                "input": "${parameters.question}"
            }
        },
        {
            "type": "ListIndexTool",
            "name": "RetrieveIndexMetaTool",
            "description": "Use this tool to get OpenSearch index information: (health, status, index, uuid, primary count, replica count, docs.count, docs.deleted, store.size, primary.store.size)."
        }
    ],
    "app_type": "my_app"
}

{% include copy-curl.html %}

요청 예시: 대화형 에이전트

POST /_plugins/_ml/agents/{agent_id}/_execute/stream
{
    "parameters": {
        "question": "How many indices are in my cluster?"
    }
}

{% include copy-curl.html %}

응답 예시: 대화형 에이전트

data: {"inference_results":[{"output":[{"name":"memory_id","result":"LvU1iJkBCzHrriq5hXbN"},{"name":"parent_interaction_id","result":"L_U1iJkBCzHrriq5hXbs"},{"name":"response","dataAsMap":{"content":"[{\"index\":0.0,\"id\":\"call_HjpbrbdQFHK0omPYa6m2DCot\",\"type\":\"function\",\"function\":{\"name\":\"RetrieveIndexMetaTool\",\"arguments\":\"\"}}]","is_last":false}}]}]}

data: {"inference_results":[{"output":[{"name":"memory_id","result":"LvU1iJkBCzHrriq5hXbN"},{"name":"parent_interaction_id","result":"L_U1iJkBCzHrriq5hXbs"},{"name":"response","dataAsMap":{"content":"[{\"index\":0.0,\"function\":{\"arguments\":\"{}\"}}]","is_last":false}}]}]}

data: {"inference_results":[{"output":[{"name":"memory_id","result":"LvU1iJkBCzHrriq5hXbN"},{"name":"parent_interaction_id","result":"L_U1iJkBCzHrriq5hXbs"},{"name":"response","dataAsMap":{"content":"{\"choices\":[{\"message\":{\"tool_calls\":[{\"type\":\"function\",\"function\":{\"name\":\"RetrieveIndexMetaTool\",\"arguments\":\"{}\"},\"id\":\"call_HjpbrbdQFHK0omPYa6m2DCot\"}]},\"finish_reason\":\"tool_calls\"}]}","is_last":false}}]}]}

data: {"inference_results":[{"output":[{"name":"memory_id","result":"LvU1iJkBCzHrriq5hXbN"},{"name":"parent_interaction_id","result":"L_U1iJkBCzHrriq5hXbs"},{"name":"response","dataAsMap":{"content":"","is_last":false}}]}]}

data: {"inference_results":[{"output":[{"name":"memory_id","result":"LvU1iJkBCzHrriq5hXbN"},{"name":"parent_interaction_id","result":"L_U1iJkBCzHrriq5hXbs"},{"name":"response","dataAsMap":{"content":"row,health,status,index,uuid,pri(number of primary shards),rep(number of replica shards),docs.count(number of available documents),docs.deleted(number of deleted documents),store.size(store size of primary and replica shards),pri.store.size(store size of primary shards)\n1,green,open,.plugins-ml-model-group,Msb1Y4W5QeiLs5yUQi-VRg,1,1,2,0,17.1kb,5.9kb\n2,green,open,.plugins-ml-memory-message,1IWd1HPeSWmM29qE6rcj_A,1,1,658,0,636.4kb,313.5kb\n3,green,open,.plugins-ml-memory-meta,OETb21fqQJa3Y2hGQbknCQ,1,1,267,7,188kb,93.9kb\n4,green,open,.plugins-ml-config,0mnOWX5gSX2s-yP27zPFNw,1,1,1,0,8.1kb,4kb\n5,green,open,.plugins-ml-model,evYOOKN4QPqtmUjxsDwJYA,1,1,5,5,421.5kb,210.7kb\n6,green,open,.plugins-ml-agent,I0SpBovjT3C6NABCBzGiiQ,1,1,6,0,205.5kb,111.3kb\n7,green,open,.plugins-ml-task,_Urzn9gdSuCRqUaYAFaD_Q,1,1,100,4,136.1kb,45.3kb\n8,green,open,top_queries-2025.09.26-00444,jb7Q1FiLSl-wTxjdSUKs_w,1,1,1736,126,1.8mb,988kb\n9,green,open,.plugins-ml-connector,YaJORo4jT0Ksp24L5cW1uA,1,1,2,0,97.8kb,48.9kb\n","is_last":false}}]}]}

data: {"inference_results":[{"output":[{"name":"memory_id","result":"LvU1iJkBCzHrriq5hXbN"},{"name":"parent_interaction_id","result":"L_U1iJkBCzHrriq5hXbs"},{"name":"response","dataAsMap":{"content":"There","is_last":false}}]}]}

data: {"inference_results":[{"output":[{"name":"memory_id","result":"LvU1iJkBCzHrriq5hXbN"},{"name":"parent_interaction_id","result":"L_U1iJkBCzHrriq5hXbs"},{"name":"response","dataAsMap":{"content":" are","is_last":false}}]}]}

data: {"inference_results":[{"output":[{"name":"memory_id","result":"LvU1iJkBCzHrriq5hXbN"},{"name":"parent_interaction_id","result":"L_U1iJkBCzHrriq5hXbs"},{"name":"response","dataAsMap":{"content":" ","is_last":false}}]}]}

data: {"inference_results":[{"output":[{"name":"memory_id","result":"LvU1iJkBCzHrriq5hXbN"},{"name":"parent_interaction_id","result":"L_U1iJkBCzHrriq5hXbs"},{"name":"response","dataAsMap":{"content":"9","is_last":false}}]}]}

data: {"inference_results":[{"output":[{"name":"memory_id","result":"LvU1iJkBCzHrriq5hXbN"},{"name":"parent_interaction_id","result":"L_U1iJkBCzHrriq5hXbs"},{"name":"response","dataAsMap":{"content":" indices","is_last":false}}]}]}

data: {"inference_results":[{"output":[{"name":"memory_id","result":"LvU1iJkBCzHrriq5hXbN"},{"name":"parent_interaction_id","result":"L_U1iJkBCzHrriq5hXbs"},{"name":"response","dataAsMap":{"content":" in","is_last":false}}]}]}

data: {"inference_results":[{"output":[{"name":"memory_id","result":"LvU1iJkBCzHrriq5hXbN"},{"name":"parent_interaction_id","result":"L_U1iJkBCzHrriq5hXbs"},{"name":"response","dataAsMap":{"content":" your","is_last":false}}]}]}

data: {"inference_results":[{"output":[{"name":"memory_id","result":"LvU1iJkBCzHrriq5hXbN"},{"name":"parent_interaction_id","result":"L_U1iJkBCzHrriq5hXbs"},{"name":"response","dataAsMap":{"content":" cluster","is_last":false}}]}]}

data: {"inference_results":[{"output":[{"name":"memory_id","result":"LvU1iJkBCzHrriq5hXbN"},{"name":"parent_interaction_id","result":"L_U1iJkBCzHrriq5hXbs"},{"name":"response","dataAsMap":{"content":".","is_last":false}}]}]}

data: {"inference_results":[{"output":[{"name":"memory_id","result":"LvU1iJkBCzHrriq5hXbN"},{"name":"parent_interaction_id","result":"L_U1iJkBCzHrriq5hXbs"},{"name":"response","dataAsMap":{"content":"","is_last":true}}]}]}

토큰 사용량 포함 요청 예시

3.6에 도입 {: .label .label-purple }

스트리밍 응답에서 상세한 토큰 사용량 지표를 받으려면 include_token_usage를 true로 설정하세요:

POST /_plugins/_ml/agents/{agent_id}/_execute/stream
{
    "parameters": {
        "question": "How many indices are in my cluster?",
        "include_token_usage": true
    }
}

{% include copy-curl.html %}

토큰 사용량 포함 응답 예시

스트리밍 응답에는 콘텐츠 청크 다음, 마지막 완료 청크 전에 전송되는 token_usage 청크가 포함돼요:

... (content chunks as shown above) ...

data: {"inference_results":[{"output":[{"name":"memory_id","result":"LvU1iJkBCzHrriq5hXbN"},{"name":"parent_interaction_id","result":"L_U1iJkBCzHrriq5hXbs"},{"name":"token_usage","dataAsMap":{"per_turn_usage":[{"turn":1,"model_id":"rk6okJwB_kOxOUbO6853","model_name":"gpt-4o-mini","model_url":"https://api.openai.com/v1/chat/completions","input_tokens":1042,"output_tokens":69,"total_tokens":1111,"cache_read_input_tokens":0}],"per_model_usage":[{"model_id":"rk6okJwB_kOxOUbO6853","model_name":"gpt-4o-mini","model_url":"https://api.openai.com/v1/chat/completions","call_count":1,"input_tokens":1042,"output_tokens":69,"total_tokens":1111,"cache_read_input_tokens":0}]}}]}]}

data: {"inference_results":[{"output":[{"name":"memory_id","result":"LvU1iJkBCzHrriq5hXbN"},{"name":"parent_interaction_id","result":"L_U1iJkBCzHrriq5hXbs"},{"name":"response","dataAsMap":{"content":"","is_last":true}}]}]}

요청 예시: AG-UI 에이전트

3.5에 도입 {: .label .label-purple }

이 기능은 실험적 기능으로, 프로덕션 환경에서의 사용은 권장하지 않아요. 기능 진행 상황에 대한 업데이트를 받거나 피드백을 남기고 싶다면 OpenSearch forum에서 진행 중인 논의에 참여해 주세요. {: .warning}

AG-UI 에이전트는 프런트엔드 통합을 위해 AG-UI 프로토콜 형식을 사용해요:

POST /_plugins/_ml/agents/{agent_id}/_execute/stream
{
    "threadId": "thread-xxxxx",
    "runId": "run-xxxxx",
    "messages": [
        {
            "id": "msg-xxxxx",
            "role": "user",
            "content": "How many indices are in my cluster?"
        }
    ],
    "tools": [],
    "context": [],
    "state": {},
    "forwardedProps": {}
}

{% include copy-curl.html %}

응답 예시: AG-UI 에이전트

AG-UI 에이전트는 AG-UI 프로토콜 형식을 사용해 SSE를 반환해요:

data: {"type":"RUN_STARTED","timestamp":1734567890123,"threadId":"thread-xxxxx","runId":"run-xxxxx"}

data: {"type":"TEXT_MESSAGE_START","timestamp":1734567890124,"messageId":"msg-xxxxx","role":"assistant"}

data: {"type":"TEXT_MESSAGE_CONTENT","timestamp":1734567890125,"messageId":"msg-xxxxx","delta":"Your cluster has 9 indices."}

data: {"type":"TEXT_MESSAGE_END","timestamp":1734567890140,"messageId":"msg-xxxxx"}

data: {"type":"RUN_FINISHED","timestamp":1734567890251,"threadId":"thread-xxxxx","runId":"run-xxxxx"}

설정 방법, 사전 요구 사항, 필드 정의, 구현 세부 사항을 포함한 전체 AG-UI 에이전트 문서는 AG-UI agents를 참고하세요.

응답 본문 필드

다음 표에 모든 응답 본문 필드가 나와 있어요.

필드 데이터 타입 설명
inference_results Array 에이전트가 반환한 스트리밍 응답 데이터를 포함해요.
inference_results.output Array 각 추론 결과에 대한 출력 객체를 포함해요.
inference_results.output.name String 출력 필드의 이름이에요. memory_id, parent_interaction_id, response, token_usage 중 하나예요.
inference_results.output.result String memory_id 및 parent_interaction_id 필드의 값이에요.
inference_results.output.dataAsMap Object 응답 콘텐츠와 메타데이터를 포함해요(response 및 token_usage 출력에 존재해요).
inference_results.output.dataAsMap.content String 에이전트의 응답 콘텐츠예요. 도구 호출, 도구 결과, 최종 텍스트 출력이 포함될 수 있어요.
inference_results.output.dataAsMap.is_last Boolean 스트림에서 마지막 청크인지 여부를 나타내요. 마지막 청크면 true, 청크가 더 있으면 false예요.
inference_results.output.dataAsMap.token_usage Object 에이전트 실행에 대한 토큰 사용량 지표예요. 마지막 완료 청크 전에 별도의 스트리밍 청크로 전달돼요. include_token_usage가 true로 설정된 경우에만 존재해요. 3.6에 도입

스트리밍 응답의 토큰 사용량

3.6에 도입 {: .label .label-purple }

스트리밍 응답에서 토큰 사용량 추적을 활성화하려면 요청에 parameters.include_token_usage 필드를 true로 설정하세요. 활성화하면 스트리밍 응답 순서는 다음과 같아요:

  1. 콘텐츠 청크가 스트리밍으로 전달돼요(is_last: false)
  2. 상세 지표를 담은 token_usage 청크가 전송돼요
  3. 마지막 완료 청크가 전송돼요(콘텐츠는 비어 있고 is_last: true)

토큰 사용량 청크는 비스트리밍 API 응답과 동일한 구조를 담고 있어요:

  • per_turn_usage: 에이전트 실행 중 각 LLM 호출에 대한 토큰 사용량 레코드 배열이에요. 각 레코드에는 turn(순번), model_id, model_name, model_url, input_tokens, output_tokens, total_tokens, 그리고 선택적인 캐시 관련 필드가 포함돼요.
  • per_model_usage: 모델별로 그룹화된 집계 토큰 사용량이에요. 각 레코드에는 model_id, model_name, model_url, call_count(LLM 호출 횟수), input_tokens, output_tokens, total_tokens, 그리고 선택적인 캐시 관련 필드가 포함돼요.

토큰 사용량 필드에 대한 전체 설명과 모델 제공자별 토큰 계산 방식은 Execute Agent API 문서의 Tracking token usage를 참고하세요.

더 알아보기 (Learn more)