Steps - wildberries-tech/universal-harvester GitHub Wiki

Шаги являются основным контентом Universal Harvester, который требует точного соотвествия информационному ландшафту, а значит и рефакторинга при его изменении. Шаги отвечаю за получение или обработку данных на функции выбранного источника (source) по заранее определённым или генерируемым параметрам. Результатом каждого шага является [] (list), который при наличии данных содержит "строки" данных (dict). Очевидно, что редактирование шага повлечёт за собой изменения во всех сценариях, где этот шаг используется.

Для написания шага нам потребуется:

  1. Корректный источник данных (с ключами при необходимости).
  2. Определить исполняемую функцию источника (определены в app/engine/engine.py ENGINE_SOURCES_AND_FUNCTIONS_MAP).
  3. Определить параметры, включая генерацию параметров или применение в качестве параметров других данных (apply).
  4. Определить блок query (основной блок данных запроса).
  5. Определить тестовые значения параметров для валидации написанного шага.

Step fields

{
    "description": "", # описание шага
    "example": "", # пример получения данных, например, сохранённый запрос в elastic
    "llm": # блок, описывающий данные для LLM (если предполагается обработка данных с помощью LLM)
    {
        "preprompt": "",
        "postprompt": ""
    },
    "source_function": "generic_query", # идентификатор функции источника
    "input_parameters": # входные параметры, определённые шага
    {
        "...":"..."
    },
    "query": # данные, которыми оперирует функция-исполнитель шага, сюда возможно инъектирование параметров
    {
        "...": "..."
    },
    "apply": # опциональный блок применение уже полученных данных, сюда возможно инъектирование параметров
    {
        "...":{}
    },
    "generate_parameters": # опциональный блок генерации параметров
    {
        "...":{}
    }
}

input_parameters

Входные параметры шага определяются в ноде input_parameters шага. Возможные типы данных параметров, обязательные поля и варианты автозаполнения определены в app/engine/steps.py TYPE_MAP. type -- тип данных параметра description -- описание параметра required -- флаг обязательности параметра (если false, то при его отсутствии он будет сгенерирован автоматически по полю default) default -- значение параметра по умолчанию (или функция автозаполнения) max_length -- только для string, максимальная длина строки format -- только для datetime, формат принимаемого параметра времени

Примеры входных параметров

"input_parameters":{
    "string_parameter":
    {
        "type": "string",
        "max_length": 100,
        "required": false,
        "default": "default_string",
        "description": ""
    },
    "datetime_parameter":
    {
        "type": "datetime",
        "format": "%Y-%m-%dT%H:%M:%S.%f%z",
        "required": true,
        "default": "now",
        "description": ""
    },
    "integer_parameter":
    {
        "type": "integer",
        "required": true,
        "default": 0,
        "description": ""
    },
    "ip_address_parameter":
    {
        "type": "ip_address",
        "required": false,
        "default": "127.0.0.1",
        "description": ""
    },
    "float_parameter":
    {
        "type": "float",
        "required": false,
        "default": 0.0,
        "description": ""
    },
    "boolean_parameter":
    {
        "type": "boolean",
        "required": false,
        "default": true,
        "description": ""
    },
    "list_parameter":
    {
        "type": "list",
        "required": false,
        "default":
        [
            "list"
        ],
        "description": ""
    },
    "dict_parameter":
    {
        "type": "dict",
        "required": false,
        "default":
        {
            "dict": "dict"
        },
        "description": ""
    }
}

generate_parameters

Генерация параметров применяется для создания новых параметров из имеющихся. Определены 3 функции генерации: -timedelta, +timedelta, copy. Копирование параметра обычно применяется для назначении параметру нового имени. Имя ноды определяет имя сгенерированного параметра. type -- тип генерации нового параметра timestamp_field -- имя параметра времени для типов -timedelta и +timedelta, от которого будет определяться смещение delta_field -- имя параметра (integer) для смещения в секундах output_format -- формат времени сгенерированного параметра copy_source -- существующий параметр для копирования, тип данных наследуется

Примеры генерации параметров:

"generate_parameters":{
    "gte":
    {
        "type": "-timedelta",
        "timestamp_field": "timestamp",
        "delta_field": "delta",
        "output_format": "%Y-%m-%dT%H:%M:%S.%f%z"
    },
    "lte":
    {
        "type": "+timedelta",
        "timestamp_field": "timestamp",
        "delta_field": "delta",
        "output_format": "%Y-%m-%dT%H:%M:%S.%f%z"
    },
    "copied":
    {
        "type": "copy",
        "copy_source": "other_parameter"
    }
}

apply

Применение данных в качестве параметров (apply) Universal Harvester имеет возможность применять данные, полученные в одних запрос, для подготовки других запросов. Для этого применятся нода шага apply. Имеется возможность использования результатов только одного уже выполненного шага. Применение apply определяет зависимость выполняемого шага от другого и влияет на очерёдность выполнения шагов в рамках сценария.

target_data -- уникальный идентификатор данных в рамках сценария или опредеяемый в специальном параметре outervision target_parameters -- список генерируемых параметров по столбцам данных target_data output_unique_fields -- (опционально) удаление дубликатов по указанным полям

Для каждого параметра необходимо указать: column_name -- имя колонки в применяемых данных as -- имя нового параметра в шаге

Опционально: format -- формат вывода, если это datetime pattern -- применение дополнительного паттерна инъектирования параметра

"apply":{
    "target_data": "target_data_name",
    "target_parameters":
    [
        {
            "column_name": "column_in_target_data_1",
            "as": "parameter_name_1"
        },
        {
            "column_name": "column_in_target_data_2_datetime",
            "as": "parameter_name_2",
            "format": "%Y-%m-%dT%H:%M:%S.%f%z"
        },
        {
            "column_name": "timestamp_stop",
            "as": "timestamp_stop",
            "pattern": "='%(__pattern_value__)s'"
        }
    ],
    "output_unique_fields":
    [
        "field_1",
        "field_2"
    ]
}

Инъектирование параметров

Инъектирование параметров производится для блоков шага query и apply до выполнения шага. Таким образом происходит доставка параметров и конфигурирование запросов к системам. Это значит, что указав в тексте блок %(parameter)[sifbldx], параметр parameter будет инъектирован в шаг по правилам типа инъекции [sifbldx].

s -- вместо блока %(parameter)s подставляется str(value) ("data":"text %(parameter)s text" -> "data":"text value text" при value = "value")

i -- вместо блока %(parameter)i подставляется int(value), если инъекция обрамляется ", то они будут опущены ("data":"%(parameter)i" -> "data":0 при value = 0)

f -- вместо блока %(parameter)f подставляется float(value), если инъекция обрамляется ", то они будут опущены ("data":"%(parameter)f" -> "data":0.0 при value = 0.0)

b -- вместо блока %(parameter)b подставляется bool(value), если инъекция обрамляется ", то они будут опущены ("data":"%(parameter)b" -> "data":true при value = true)

l -- вместо блока %(parameter)l подставляется json.dumps(value), если инъекция обрамляется ", то они будут опущены ("data":"%(parameter)l" -> "data":[] при value = [])

d -- вместо блока %(parameter)d подставляется json.dumps(value), если инъекция обрамляется ", то они будут опущены ("data":"%(parameter)d" -> "data":{} при value = {})

x -- вместо блока %(parameter)x подставляется str(value), если инъекция обрамляется ", то они будут опущены ("data":"%(parameter)x" -> "data":value_ при value = "value_")

Обязательно убедитесь в том, что после инъектирования у вас получится корректный JSON.

Injection string Type Test value Original string Result Comment
%(parameter)s string test "data":"query data %(parameter)s" "data":"query data test"
%(parameter)i integer 12 "data":"query data %(parameter)i" "data":"query data 12"
"data":"%(parameter)i" "data":12
%(parameter)f float 12.34 "data":"query data %(parameter)f" "data":"query data 12.340000000" "{0:0.9f}".format(value)
"data":"%(parameter)f" "data":12.340000000
%(parameter)b boolean true "data":"query data %(parameter)b" "data":"query data true"
"data":"%(parameter)b" "data":true
%(parameter)l list ["test"] "data":"query data %(parameter)l" "data":"query data ["test"]"
"data":"%(parameter)l" "data":["test"]
%(parameter)d dict {"foo":"bar"} "data":"query data %(parameter)d" "data":"query data {"foo":"bar"}"
"data":"%(parameter)d" "data":{"foo":"bar"}
%(parameter)x e__x__tended "["test"]" "data":"query data %(parameter)x" "data":"query data ["test"]"
"data":"%(parameter)x" "data":["test"] Прямое инъектирование, JSON должен быть валидным