Processor chains
출처: 문서
본문
프로세서 체인
3.3에 도입됨 {: .label .label-purple }
프로세서 체인은 입력 데이터와 출력 데이터를 모두 처리할 수 있는 유연한 데이터 변환 파이프라인을 제공해요. 여러 프로세서를 체인으로 연결하면 한 프로세서의 출력이 다음 프로세서의 입력이 되는 순차적 변환을 만들 수 있어요.
프로세서를 활용하면 다음과 같은 작업을 할 수 있어요:
- 데이터 형식 변환: 서로 다른 데이터 구조(문자열, JSON, 배열) 간에 변환해요.
- 특정 정보 추출: JSONPath나 정규식 패턴을 사용해 필요한 데이터를 추출해요.
- 콘텐츠 정리 및 필터링: 불필요한 필드를 제거하거나 포매팅 규칙을 적용해요.
- 데이터 표준화: 서로 다른 컴포넌트 전반에서 일관된 데이터 형식을 보장해요.
프로세서는 배열에 나타난 순서대로 실행돼요. 각 프로세서는 이전 프로세서의 출력을 입력으로 받아요. {: .note}
프로세서 체인은 ML 워크플로를 위해 특별히 설계됐으며, ingest 파이프라인과 search 파이프라인의 프로세서와는 차이가 있어요:
- Ingest 파이프라인: OpenSearch에 인덱싱하는 동안 문서를 변환해요.
- Search 파이프라인: 검색 작업 중에 쿼리와 검색 결과를 변환해요.
- 프로세서 체인: ML Commons 워크플로(agent 도구, 모델 입력/출력) 내에서 데이터를 변환해요.
프로세서 체인은 모델 응답 정리, LLM 출력에서 구조화된 데이터 추출, 모델 추론용 입력 준비 같은 AI/ML 사용 사례에 맞춘 특화된 데이터 변환 기능을 제공해요.
설정
프로세서는 다양한 컨텍스트에서 설정할 수 있어요:
- 도구 출력: 도구의
parameters섹션에output_processors배열을 추가해요. - 모델 출력:
_predict호출 시 모델의parameters섹션에output_processors배열을 추가해요. - 모델 입력:
_predict호출의 모델parameters섹션에input_processors배열을 추가해요.
전체 예제는 에이전트 사용 예제와 모델 사용 예제를 참고하세요.
지원되는 프로세서 타입
다음 표는 지원되는 모든 프로세서를 정리한 거예요.
| 프로세서 | 설명 |
|---|---|
conditional |
조건에 따라 서로 다른 프로세서 체인을 적용해요. |
extract_json |
텍스트 문자열에서 JSON 객체나 배열을 추출해요. |
for_each |
배열 요소를 순회하며 각 요소에 프로세서 체인을 적용해요. |
jsonpath_filter |
JSONPath 표현식을 사용해 데이터를 추출해요. |
process_and_set |
입력에 프로세서 체인을 적용하고 결과를 지정된 JSONPath 위치에 설정해요. |
regex_capture |
정규식 매치에서 특정 그룹을 캡처해요. |
regex_replace |
정규식 패턴을 사용해 텍스트를 치환해요. |
remove_jsonpath |
JSONPath를 사용해 JSON 객체에서 필드를 제거해요. |
set_field |
필드에 지정된 정적 값을 설정하거나 다른 필드의 값을 복사해요. |
to_string |
입력을 JSON 문자열 표현으로 변환해요. |
Conditional
조건에 따라 서로 다른 프로세서 체인을 적용해요.
파라미터:
path(string, optional): 조건 평가에 사용할 값을 추출하는 JSONPath 표현식이에요.routes(array, required): 조건-프로세서 매핑의 배열이에요.default(array, optional): 조건이 하나도 맞지 않을 때 사용할 기본 프로세서예요.
지원되는 조건:
- 정확한 값 일치:
"value" - 숫자 비교:
">10","<5",">=","<=","==5" - 존재 여부 확인:
"exists","null","not_exists" - 정규식 매칭:
"regex:pattern" - 텍스트 포함:
"contains:substring"
설정 예제:
{
"type": "conditional",
"path": "$.status",
"routes": [
{
"green": [
{"type": "regex_replace", "pattern": "status", "replacement": "healthy"}
]
},
{
"red": [
{"type": "regex_replace", "pattern": "status", "replacement": "unhealthy"}
]
}
],
"default": [
{"type": "regex_replace", "pattern": "status", "replacement": "unknown"}
]
}
입력 예제:
{"index": "test-index", "status": "green", "docs": 100}
출력 예제:
{"index": "test-index", "healthy": "green", "docs": 100}
extract_json
텍스트 문자열에서 JSON 객체나 배열을 추출해요.
파라미터:
extract_type(string, optional): 추출할 JSON 타입이에요."object","array","auto"중 하나예요. 기본값은"auto"예요.default(any, optional): JSON 추출이 실패했을 때 사용할 기본값이에요.
설정 예제:
{
"type": "extract_json",
"extract_type": "object",
"default": {}
}
입력 예제:
"The result is: {\"status\": \"success\", \"count\": 5} - processing complete"
출력 예제:
{"status": "success", "count": 5}
for_each
배열 요소를 순회하며 각 요소에 프로세서 체인을 적용해요. 누락된 필드 추가, 콘텐츠 필터링, 데이터 구조 정규화처럼 배열 요소를 일관되게 변환할 때 유용해요.
파라미터:
path(string, required): 순회할 배열을 가리키는 JSONPath 표현식이에요. 배열 요소에는 반드시[*]표기법을 사용해야 해요.processors(array, required): 각 배열 요소에 적용할 프로세서 설정 목록이에요.
동작 방식:
- 각 요소는 설정된 프로세서 체인으로 독립적으로 처리돼요.
- 프로세서 체인의 출력이 원래 요소를 대체해요.
- 경로가 존재하지 않거나 배열을 가리키지 않으면 입력이 그대로 반환돼요.
- 요소 처리가 실패하면 원래 요소가 유지돼요.
설정 예제:
{
"type": "for_each",
"path": "$.items[*]",
"processors": [
{
"type": "set_field",
"path": "$.processed",
"value": true
}
]
}
입력 예제:
{
"items": [
{"name": "item1", "value": 10},
{"name": "item2", "value": 20}
]
}
출력 예제:
{
"items": [
{"name": "item1", "value": 10, "processed": true},
{"name": "item2", "value": 20, "processed": true}
]
}
jsonpath_filter
JSONPath 표현식을 사용해 데이터를 추출해요.
파라미터:
path(string, required): 데이터를 추출하는 데 사용하는 JSONPath 표현식이에요.default(any, optional): 경로를 찾지 못했을 때 사용할 기본값이에요.
설정 예제:
{
"type": "jsonpath_filter",
"path": "$.data.items[*].name",
"default": []
}
입력 예제:
{"data": {"items": [{"name": "item1"}, {"name": "item2"}]}}
출력 예제:
["item1", "item2"]
process_and_set
입력에 프로세서 체인을 적용하고 결과를 지정된 JSONPath 위치에 설정해요.
파라미터:
path(string, required): 처리된 결과를 설정할 위치를 지정하는 JSONPath 표현식이에요.processors(array, required): 순차적으로 적용할 프로세서 설정 목록이에요.
경로 동작:
- 경로가 이미 존재하면 처리된 값으로 업데이트돼요.
- 경로가 존재하지 않으면 프로세서 체인이 경로 생성을 시도해요(단순 중첩 필드에서 동작해요).
- 새 필드 생성이 성공하려면 부모 경로가 반드시 존재해야 해요.
설정 예제:
{
"type": "process_and_set",
"path": "$.summary.clean_name",
"processors": [
{
"type": "to_string"
},
{
"type": "regex_replace",
"pattern": "[^a-zA-Z0-9]",
"replacement": "_"
}
]
}
입력 예제:
{"name": "Test Index!", "status": "active"}
출력 예제:
{"name": "Test Index!", "status": "active", "summary": {"clean_name": "Test_Index_"}}
regex_capture
정규식 매치에서 특정 그룹을 캡처해요. 정규식 문법에 대한 자세한 내용은 Java 정규식 문법을 참고하세요.
파라미터:
pattern(string, required): 캡처 그룹이 포함된 정규식 패턴이에요.groups(string or array, optional): 캡처할 그룹 번호예요."1"처럼 단일 숫자나"[1, 2, 4]"처럼 배열로 지정할 수 있어요. 기본값은"1"이에요.
설정 예제:
{
"type": "regex_capture",
"pattern": "(\\d+),(\\w+),(\\w+),([^,]+)",
"groups": "[1, 4]"
}
입력 예제:
"1,green,open,.plugins-ml-model-group,DCJHJc7pQ6Gid02PaSeXBQ,1,0"
출력 예제:
["1", ".plugins-ml-model-group"]
regex_replace
정규식 패턴을 사용해 텍스트를 치환해요. 정규식 문법에 대한 자세한 내용은 Java 정규식 문법을 참고하세요.
파라미터:
pattern(string, required): 매칭에 사용할 정규식 패턴이에요.replacement(string, optional): 치환 텍스트예요. 기본값은""예요.replace_all(Boolean, optional): 모든 매치를 치환할지 첫 번째만 치환할지 결정해요. 기본값은true예요.
설정 예제:
{
"type": "regex_replace",
"pattern": "^.*?\n",
"replacement": ""
}
입력 예제:
"row,health,status,index\n1,green,open,.plugins-ml-model\n2,red,closed,test-index"
출력 예제:
"1,green,open,.plugins-ml-model\n2,red,closed,test-index"
remove_jsonpath
JSONPath를 사용해 JSON 객체에서 필드를 제거해요.
파라미터:
paths(array, required): 제거할 필드를 나타내는 JSONPath 표현식 배열이에요.
설정 예제:
{
"type": "remove_jsonpath",
"paths": ["$.sensitive_data"]
}
입력 예제:
{"name": "user1", "sensitive_data": "secret", "public_info": "visible"}
출력 예제:
{"name": "user1", "public_info": "visible"}
set_field
필드에 지정된 정적 값을 설정하거나 다른 필드의 값을 복사해요.
파라미터:
path(string, required): 값을 설정할 위치를 지정하는 JSONPath 표현식이에요.value(any, conditionally required): 설정할 정적 값이에요.value또는source_path중 하나는 반드시 제공해야 해요.source_path(string, conditionally required): 값을 복사해 올 JSONPath 표현식이에요.value또는source_path중 하나는 반드시 제공해야 해요.default(any, optional):source_path가 존재하지 않을 때 사용할 기본값이에요.source_path와 함께만 사용돼요.
경로 동작:
- 경로가 이미 존재하면 새 값으로 업데이트돼요.
- 경로가 존재하지 않으면 프로세서 체인이 경로 생성을 시도해요(단순 중첩 필드에서 동작해요).
- 새 필드 생성이 성공하려면 부모 경로가 반드시 존재해야 해요.
설정 예제(정적 값):
{
"type": "set_field",
"path": "$.metadata.processed_at",
"value": "2024-03-15T10:30:00Z"
}
설정 예제(필드 복사):
{
"type": "set_field",
"path": "$.userId",
"source_path": "$.user.id",
"default": "unknown"
}
입력 예제:
{"user": {"id": 123}, "name": "John"}
출력 예제:
{"user": {"id": 123}, "name": "John", "userId": 123, "metadata": {"processed_at": "2024-03-15T10:30:00Z"}}
to_string
입력을 JSON 문자열 표현으로 변환해요.
파라미터:
escape_json(Boolean, optional): JSON 문자를 이스케이프할지 여부예요. 기본값은false예요.
설정 예제:
{
"type": "to_string",
"escape_json": true
}
입력 예제:
{"name": "test", "value": 123}
출력 예제:
"{\"name\":\"test\",\"value\":123}"
에이전트 사용 예제
다음 예제는 에이전트와 함께 프로세서 체인을 사용하는 방법을 보여줘요.
1단계: 출력 프로세서가 포함된 flow agent 등록
POST /_plugins/_ml/agents/_register
{
"name": "Index Summary Agent",
"type": "flow",
"description": "Agent that provides clean index summaries",
"tools": [
{
"type": "ListIndexTool",
"parameters": {
"output_processors": [
{
"type": "regex_replace",
"pattern": "^.*?\n",
"replacement": ""
},
{
"type": "regex_capture",
"pattern": "(\\d+,\\w+,\\w+,([^,]+))"
}
]
}
}
]
}
{% include copy-curl.html %}
2단계: 에이전트 실행
이전 단계에서 반환된 agent_id를 사용해요:
POST /_plugins/_ml/agents/{agent_id}/_execute
{
"parameters": {
"question": "List the indices"
}
}
{% include copy-curl.html %}
출력 프로세서가 없으면 ListIndexTool이 헤더와 추가 컬럼이 포함된 장황한 CSV 출력을 그대로 반환해요:
row,health,status,index,uuid,pri,rep,docs.count,docs.deleted,store.size,pri.store.size
1,green,open,.plugins-ml-model-group,DCJHJc7pQ6Gid02PaSeXBQ,1,0,1,0,12.7kb,12.7kb
2,green,open,.plugins-ml-memory-message,6qVpepfRSCi9bQF_As_t2A,1,0,7,0,53kb,53kb
3,green,open,.plugins-ml-memory-meta,LqP3QMaURNKYDZ9p8dTq3Q,1,0,2,0,44.8kb,44.8kb
출력 프로세서는 다음 과정을 거쳐 장황한 CSV 출력을 깔끔하고 읽기 쉬운 형식으로 변환해요:
regex_replace: CSV 헤더 행을 제거해요.regex_capture: 필수 정보(행 번호, health, status, 인덱스 이름)만 추출해요.
출력 프로세서를 사용하면 에이전트가 필수 인덱스 정보만 담긴 깔끔하게 포맷된 데이터를 반환해요:
1,green,open,.plugins-ml-model-group
2,green,open,.plugins-ml-memory-message
3,green,open,.plugins-ml-memory-meta
모델 사용 예제
다음 예제는 Predict API 호출 중에 모델과 함께 프로세서 체인을 사용하는 방법을 보여줘요.
예제: 입력 프로세서
이 예제는 input_processors를 사용해 처리 전에 텍스트를 치환하는 방식으로 모델 입력을 수정하는 방법을 보여줘요:
POST _plugins/_ml/models/{model_id}/_predict
{
"parameters": {
"system_prompt": "You are a helpful assistant.",
"prompt": "Can you summarize Prince Hamlet of William Shakespeare in around 100 words?",
"input_processors": [
{
"type": "regex_replace",
"pattern": "100",
"replacement": "20"
}
]
}
}
{% include copy-curl.html %}
이 예제에서 regex_replace 프로세서는 모델로 전송되기 전에 프롬프트를 수정해 "100 words"를 "20 words"로 바꿔요.
예제: 출력 프로세서
이 예제는 output_processors를 사용해 모델 출력을 처리해서 JSON 데이터를 추출하고 포맷하는 방법을 보여줘요. 이 예제에서 출력 프로세서는 먼저 JSONPath를 사용해 모델 응답에서 콘텐츠를 추출해요. 그다음 텍스트 응답에서 JSON 객체를 파싱해서 추출해요:
POST _plugins/_ml/models/{model_id}/_predict
{
"parameters": {
"messages": [
{
"role": "system",
"content": [
{
"type": "text",
"text": "${parameters.system_prompt}"
}
]
},
{
"role": "user",
"content": [
{
"type": "text",
"text": "Can you convert this into a json object: user name is Bob, he likes swimming"
}
]
}
],
"system_prompt": "You are a helpful assistant",
"output_processors": [
{
"type": "jsonpath_filter",
"path": "$.choices[0].message.content"
},
{
"type": "extract_json",
"extract_type": "auto"
}
]
}
}
{% include copy-curl.html %}
출력 프로세서가 없으면 응답에 방대한 메타데이터와 중첩 구조가 포함된 전체 모델 출력이 그대로 담겨요:
{
"inference_results": [
{
"output": [
{
"name": "response",
"dataAsMap": {
"id": "test-id",
"object": "chat.completion",
"created": 1.759580469E9,
"model": "gpt-4o-mini-2024-07-18",
"choices": [
{
"index": 0.0,
"message": {
"role": "assistant",
"content": "Sure! Here is the information you provided converted into a JSON object:\n\n```json\n{\n \"user\": {\n \"name\": \"Bob\",\n \"likes\": \"swimming\"\n }\n}\n```",
"refusal": null,
"annotations": []
},
"logprobs": null,
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 33.0,
"completion_tokens": 42.0,
"total_tokens": 75.0,
"prompt_tokens_details": {
"cached_tokens": 0.0,
"audio_tokens": 0.0
},
"completion_tokens_details": {
"reasoning_tokens": 0.0,
"audio_tokens": 0.0,
"accepted_prediction_tokens": 0.0,
"rejected_prediction_tokens": 0.0
}
},
"service_tier": "default",
"system_fingerprint": "test-fingerprint"
}
}
],
"status_code": 200
}
]
}
출력 프로세서를 사용하면 응답이 단순해져서 추출하고 파싱한 JSON 데이터만 포함해요:
{
"inference_results": [
{
"output": [
{
"name": "response",
"dataAsMap": {
"user": {
"name": "Bob",
"likes": "swimming"
}
}
}
],
"status_code": 200
}
]
}