Bloques lógicos
Puede utilizar bloques lógicos para incluir lógica ligera y limitada de Python en su flujo de trabajo de agente para transformar datos y aplicar reglas de negocio deterministas. Los bloques lógicos están restringidos intencionadamente y no proporcionan un entorno de ejecución completo Python.
Cuando el flujo de trabajo de agente llega a un bloque lógico, ejecuta la lógica en un entorno restringido de Python y luego continúa con el siguiente nodo descendente. Puede utilizar un bloque lógico para procesar transformaciones de datos, implementar lógica personalizada o dar formato a los mensajes antes de que continúen con el siguiente paso en el flujo de trabajo del agente. El procesamiento por bloques lógicos es rápido, predecible y no produce efectos secundarios.
El bloque lógico actúa como un punto de decisión o transformación programable en el flujo de trabajo de su agente, lo que le proporciona una mayor flexibilidad y control.
Los bloques lógicos solo admiten lógica en línea y transformaciones de datos simples. Si necesita bibliotecas externas, llamadas a API, lógica reutilizable o cálculos de larga duración, utilice una herramienta en lugar de un bloque lógico.
Capacidades del bloque lógico
Las principales capacidades de los bloques lógicos son:
Rendimiento predecible: los bloques lógicos se ejecutan de forma coherente, por lo que los flujos de trabajo se comportan siempre de la misma manera.
Funcionamiento seguro: los bloques lógicos se ejecutan en un entorno controlado que reduce los riesgos de seguridad y estabilidad.
Escalabilidad fiable: los bloques lógicos funcionan de forma fiable incluso cuando aumenta la carga de trabajo o la complejidad.
Puedes utilizar bloques lógicos para lo siguiente:
Inicialización de variables
Transformación de datos
Aplicación de la lógica empresarial
Formato de los mensajes
Procesamiento de archivos
Para obtener más información sobre estos casos de uso y ejemplos, consulte Casos de uso y ejemplos.
Capacidades excluidas de los bloques lógicos
Los bloques lógicos se ejecutan en un entorno fijo y restringido de Python. Un bloque lógico no debe utilizarse para lo siguiente:
Importación de bibliotecas externas de Python
Ejecutar lógica arbitraria o de larga duración
Llamadas a servicios externos o API
Actúa como una función reutilizable o una biblioteca compartida
Sustitución de una herramienta o un microservicio basado en Python
Estos límites se han establecido deliberadamente para garantizar un entorno de trabajo seguro, predecible y eficiente.
Por qué los bloques lógicos están sujetos a restricciones
Los bloques lógicos se ejecutan en línea como parte de un flujo de trabajo. El uso sin restricciones de Python conlleva los siguientes riesgos:
Ralentiza el flujo de trabajo
Introduce riesgos de seguridad
Los flujos de trabajo son más difíciles de depurar y escalar
Para evitar estos problemas, los bloques lógicos admiten una lógica rápida y predecible, mientras que las herramientas pueden manejar procesamientos más pesados u operaciones externas.
Bibliotecas compatibles con Python
Dentro de un bloque lógico, puede leer variables de diferentes partes del flujo de trabajo del agente, aplicar lógica personalizada y definir variables de salida para su uso en nodos posteriores.
En los bloques lógicos solo están disponibles las siguientes bibliotecas estándar, con el fin de garantizar un entorno de trabajo seguro y eficiente:
arraycalendarcollectionsdatetimeenumjsonmathrandomrestringtimeyamlzoneinfo
Soporte para las funciones Python
Ciertas funciones de Python se excluyen intencionadamente de los bloques lógicos para mitigar los riesgos de seguridad y evitar comportamientos impredecibles.
Las siguientes funciones no están disponibles en los bloques lógicos, pero puede utilizar las alternativas recomendadas y más seguras en su lugar:
La función
eval()no es compatible. Utiliza(my_str == "True")en lugar deeval(my_str), dondemy_strpuede ser «True» o «False».Solo para
yamlelsafe_load()módulo,safe_load_all()se admiten las funciones de lasafe_dump_all()API,safe dump(), y.Para el módulo
stringstring.Formatterno es compatible. Utilizastring.safe_format()en lugar destring.Formatter.format().Para la función
strstr.format()no es compatible. No se admiten funciones dexxx.format_map()xxx.format()API.Algunas funciones integradas de « Python » no son compatibles, entre ellas
import,class, ytype(). Para comprobar los tipos, utilizasafe_type()en lugar detype().
La lista de módulos disponibles en Python es fija y no puede modificarse ni ampliarse.
Es posible que algunas bibliotecas de los módulos Python no estén disponibles.
Las variables pueden referenciarse utilizando ["varname"] o.varname. El uso de ["varname"] permite utilizar espacios y comas.
Python objetos del diccionario
El generador de flujos de trabajo de Agentic utiliza prácticas de programación de tipo « Python », por lo que flow, self, y parent se tratan como diccionarios de tipo « Python ». Si se produce una excepción al leer o asignar un valor a un objeto «self», por ejemplo: self["input"]["customer"]["discount_rate"], es probable que se deba a que uno de los objetos que lo contienen (como "customer") aún no existe. En ese caso, primero hay que inicializar los objetos contenedores.
Cuando se ejecuta un bloque lógico, utiliza objetos del diccionario Python para representar las entradas y salidas. Puede utilizar la sintaxis de expresión para referenciar o establecer estos valores según sea necesario. Para obtener más información sobre la sintaxis, consulte la sección «Expresiones de flujo» del kit de desarrollo de aplicaciones (ADK) de IBM wastonsx Orchestrate.
A continuación se muestra un ejemplo de diccionario de flujos de trabajo de tipo «agente»:
# flow represents the inputs and outputs of the outermost flow
flow = {
"input": {
"birthday": "1-10-2000",
"first_name": "John",
"last_name": "Doe",
"address": {
"street": "123 ABC Street",
"city": "NY",
"state": "NY",
"country": "USA"
},
"customer_status": "Bronze"
},
"output": {
"current_date": "<current date>",
"current_datetime": "<current datetime>",
"age": <number>,
"address": <str>
}
}
# self represents the inputs and outputs of the current node
self = {
"input": {
"customer_status": "Bronze",
"price": 1000
},
"output": {
"discount_rate": <number>
}
}
Tipos de datos
En la siguiente tabla se enumeran los tipos de datos compatibles con los flujos de trabajo de Agent, junto con sus tipos equivalentes en « Python » y JSON utilizados en expresiones y bloques lógicos.
Tipos de datos de flujo |
Python tipos de datos |
Tipos de datos de JSON |
|---|---|---|
Booleano |
booleano |
Booleano |
Fecha |
str |
Serie |
Hora |
str |
Serie |
Fecha y hora |
str |
Serie |
Decimal |
flotante |
Número |
Archivo |
WxoFile |
Serie |
Entero |
ent |
Entero |
Objecto |
dict |
Objecto |
Serie |
str |
Serie |
Usuario |
str |
Serie |
Nota:
Para definir un tipo de datos de objeto en los flujos de trabajo de agentic, debe adherirse al estándar del esquema JSON. Para obtener más información, consulta la documentación de JSON Schema.
En Python, las fechas son cadenas que utilizan el formato de fecha ISO 8601. Las fechas JSON son cadenas con formato "date", que utiliza el formato de fecha ISO 8601, por ejemplo:
"startDate": {
"type": "string",
"description": "Employee's start date",
"format": "date"
}
Para obtener más información, consulta «Formato de fecha y hora para bloques de código y expresiones ».
Conversión del tipo de datos
Las conversiones de tipos de datos permiten transformar un valor de un formato a otro. Los siguientes bloques lógicos proporcionan ejemplos de expresiones de e Python e de diferentes tipos de conversión de datos.
Ejemplos
Conversión de cadenas
my_str = str("10")
my_int = int(my_str)
test_results += "my_str: " + my_str + " converted to my_int: " + str(my_int) + "\n"
my_str = str("1.5")
my_num = float(my_str)
test_results += "my_str: " + my_str + " converted to my_num: " + str(my_num) + "\n"
my_str = str("False")
my_bool = (my_str == "True")
test_results += "my_str: " + my_str + " converted to my_bool: " + str(my_bool) + "\n"
my_str = str("True")
my_bool = (my_str == "True")
test_results += "my_str: " + my_str + " converted to my_bool: " + str(my_bool) + "\n"
"%Y-%m-%d"my_str = str("2021-01-01")
my_date_str = my_str
test_results += "my_str: " + my_str + " converted to my_date_str: " + my_date_str + "\n"
my_date_date = datetime.datetime.strptime(my_date_str, "%Y-%m-%d").date()
my_date_date = my_date_date + datetime.timedelta(days=1)
my_date_str = my_date_date.strftime("%Y-%m-%d")
test_results += "added one day to my_date_str: " + my_date_str + "\n"
Conversión de números enteros
my_int = int(10)
my_str = str(my_int)
test_results += "my_int: " + str(my_int) + " converted to my_str: " + my_str + "\n"
my_int = int(10)
my_num = float(my_int)
test_results += "my_int: " + str(my_int) + " converted to my_num: " + str(my_num) + "\n"
my_int = int(0)
my_bool = bool(my_int)
test_results += "my_int: " + str(my_int) + " converted to my_bool: " + str(my_bool) + "\n"
my_int = int(1)
my_bool = bool(my_int)
test_results += "my_int: " + str(my_int) + " converted to my_bool: " + str(my_bool) + "\n"
my_int = int(739470)
my_date_date = datetime.date.fromordinal(my_int)
my_date_str_isoformat = my_date_date.isoformat()
test_results += "my_int: " + str(my_int) + " converted to my_date_date: " + my_date_str_isoformat + "\n"
Conversión de números
my_num = float(1.5)
my_str = str(my_num)
test_results += "my_num: " + str(my_num) + " converted to my_str: " + my_str + "\n"
my_num = float(1.5)
my_int = int(my_num)
test_results += "my_num: " + str(my_num) + " converted to my_int: " + str(my_int) + "\n"
my_int = math.ceil(my_num)
test_results += "my_num: " + str(my_num) + " converted to my_int using math.ceil(): " + str(my_int) + "\n"
my_int = math.floor(my_num)
test_results += "my_num: " + str(my_num) + " converted to my_int using math.floor(): " + str(my_int) + "\n"
my_num = float(0)
my_bool = bool(my_num)
test_results += "my_num: " + str(my_num) + " converted to my_bool: " + str(my_bool) + "\n"
my_num = float(1)
my_bool = bool(my_num)
test_results += "my_num: " + str(my_num) + " converted to my_bool: " + str(my_bool) + "\n"
my_num = float(739470)
my_date_date = datetime.date.fromordinal(int(my_num))
my_date_str_isoformat = my_date_date.isoformat()
test_results += "my_num: " + str(my_num) + " converted to my_date_date: " + my_date_str_isoformat + "\n"
Conversión booleana
my_bool = bool(True)
my_str = str(my_bool)
test_results += "my_bool: " + str(my_bool) + " converted to my_str: " + my_str + "\n"
my_bool = bool(False)
my_str = str(my_bool)
test_results += "my_bool: " + str(my_bool) + " converted to my_str: " + my_str + "\n"
my_bool = bool(True)
my_int = int(my_bool)
test_results += "my_bool: " + str(my_bool) + " converted to my_int: " + str(my_int) + "\n"
my_bool = bool(False)
my_int = int(my_bool)
test_results += "my_bool: " + str(my_bool) + " converted to my_int: " + str(my_int) + "\n"
my_bool = bool(True)
my_num = float(my_bool)
test_results += "my_bool: " + str(my_bool) + " converted to my_num: " + str(my_num) + "\n"
my_bool = bool(False)
my_num = float(my_bool)
test_results += "my_bool: " + str(my_bool) + " converted to my_num: " + str(my_num) + "\n"
Conversión de fechas
my_date_date = datetime.date.today()
my_date_str_isoformat = my_date_date.isoformat()
my_date_str = my_date_date.strftime("%a %d %B %Y")
test_results += "my_date_date: " + my_date_str_isoformat + " converted to my_date_str: " + my_date_str + "\n"
my_date_date = datetime.date.today()
my_date_str_isoformat = my_date_date.isoformat()
my_int = int(my_date_date.toordinal())
test_results += "my_date_date: " + my_date_str_isoformat + " converted to my_int: " + str(my_int) + "\n"
my_date_date = datetime.date.today()
my_date_str_isoformat = my_date_date.isoformat()
my_num = float(my_date_date.toordinal())
test_results += "my_date_date: " + my_date_str_isoformat + " converted to my_num: " + str(my_num) + "\n"
self.output.test_results = test_results
Casos prácticos y ejemplos
Puede utilizar el nodo de bloque lógico para personalizar la forma en que su flujo de trabajo de agente maneja los datos. Es útil cuando los nodos estándar no satisfacen sus necesidades específicas. A continuación, se muestran algunos casos de uso común.
Formato del mensaje
Puede dar formato o reestructurar los mensajes según sea necesario, al tiempo que traslada la información al siguiente nodo. El siguiente ejemplo de bloque lógico muestra una aplicación práctica del formato de mensajes.
Ejemplos
# Format address
addr = flow["input"]["address"]
formatted_address = f"{addr['street']}, {addr['city']}, {addr['state']}, {addr['country']}"
self["output"]["address"] = formatted_address
Integración flexible
Puede colocar el nodo del bloque lógico en cualquier lugar del flujo de trabajo del agente para integrarlo con otros nodos y herramientas. El siguiente ejemplo de bloque lógico muestra una aplicación práctica de la integración flexible.
Ejemplos
today = datetime.date.today()
self.output.todays_date = today.isoformat()
Bloque lógico que inicializa un conjunto de variables de salida utilizando tipos de datos primitivos: cadena, entero, número y booleano.
Las variables de salida son:
name: Cadenaage: Enterosalary: NúmeroisActive: Booleano
En el bloque lógico, cada variable se inicializa con un valor de muestra.
self.output.name = "John Doe"
self.output.age = 37
self.output.salary = 12345.67
self.output.isActive = True
Transformación de datos personalizada
Puede escribir una lógicas Python para transformar, filtrar, inicializar variables o mejorar los datos existentes a medida que pasan por el flujo de trabajo del agente. Los siguientes ejemplos de bloques lógicos muestran una aplicación práctica de la transformación de datos personalizada.
Ejemplos
address_json = '''
{
"street": "123 ABC Street",
"city": "NY",
"state": "NY",
"country": "USA"
}
'''
address = json.loads(address_json)
flow["input"]["address"]["state"] = address["state"]
{
"employee": {
"type": "object",
"description": "",
"properties": {
"id": {
"type": "integer",
"description": "Unique identifier for the employee"
},
"firstName": {
"type": "string",
"description": "Employee's first name"
},
"lastName": {
"type": "string",
"description": "Employee's last name"
},
"email": {
"type": "string",
"description": "Employee's email address"
},
"department": {
"type": "string",
"description": "Employee's department"
},
"salary": {
"type": "number",
"description": "Employee's salary"
},
"isActive": {
"type": "boolean",
"description": "Whether the employee is currently active"
},
"startDate": {
"type": "string",
"format": "date",
"description": "Employee's start date"
},
"badges": {
"type": "array",
"items": {
"type": "string"
},
"description": "Employee's badges"
},
"address": {
"type": "object",
"description": "",
"properties": {
"street": {
"type": "string"
},
"city": {
"type": "string"
},
"zipCode": {
"type": "string"
}
},
"default": ""
}
}
}
}
Implementación de la lógica de negocio
Puede aplicar lógica condicional, realizar cálculos y tomar decisiones, o ejecutar operaciones personalizadas según sea necesario en el flujo de trabajo del agente. Los siguientes ejemplos de bloques lógicos muestran una aplicación práctica de la implementación de la lógica empresarial.
Ejemplos
# Get today's date
today = datetime.date.today()
# Get the user's birthday from the output of the previous user activity
birthday_str = flow["User activity 1"]["Ask for date of birth"].output.value
# Convert the user's birthday into a date object
birthday = datetime.datetime.strptime(birthday_str, "%Y-%m-%d").date()
# Calculate the user's age
age = today.year - birthday.year - ((today.month, today.day) < (birthday.month, birthday.day))
# Return the user's age
self.output.age = age
product = flow.input.product
age = flow.input.age
if product == "Alcohol" and age < 18:
self.output.accept_payment = False
elif product == "Fireworks" and age < 18:
self.output.accept_payment = False
else:
self.output.accept_payment = True
# Ensure customer_status and price are available
status = flow["input"].get("customer_status")
price = flow["input"].get("price", 0)
discount_rate = 0
if status == "Bronze":
if price < 100:
discount_rate = 0
elif price < 500:
discount_rate = 0.01
elif price < 900:
discount_rate = 0.02
else:
discount_rate = 0.03
elif status == "Silver":
if price < 100:
discount_rate = 0.02
elif price < 500:
discount_rate = 0.04
elif price < 900:
discount_rate = 0.05
else:
discount_rate = 0.07
elif status == "Gold":
if price < 100:
discount_rate = 0
elif price < 500:
discount_rate = 0.05
elif price < 900:
discount_rate = 0.07
else:
discount_rate = 0.10
self["output"]["discount_rate"] = discount_rate
# Format address using if-then logic
addr = flow["input"]["address"]
formatted_address = addr["street"]
if addr.get("city"):
formatted_address += f", {addr['city']}"
if addr.get("state"):
formatted_address += f", {addr['state']}"
if addr.get("country"):
formatted_address += f", {addr['country']}"
self["output"]["address"] = formatted_address
today = datetime.date.today()
three_days_ago = today - datetime.timedelta(days=3)
self.output.three_days_ago = three_days_ago.isoformat()
Procesamiento de archivos
Puede utilizar el nodo de bloque lógico para gestionar operaciones con archivos, como gestionar cargas de uno o varios archivos, extraer o transformar el contenido de archivos y generar archivos para su descarga. El siguiente ejemplo muestra un caso práctico de procesamiento de archivos.
Funciones disponibles para el procesamiento de archivos
Puede utilizar funciones de archivos del sistema o métodos WxoFile de instancia para ejecutar operaciones con archivos.
Funciones de los archivos del sistema
A continuación se describen los system.file métodos para trabajar con archivos:
system.file.get_name(file)– Devuelve el nombre del archivosystem.file.get_type(file)– Devuelve el tipo de archivosystem.file.get_size(file)– Devuelve el tamaño del archivosystem.file.get_content(file)– Devuelve el contenido del archivo en bytes
WxoFile Métodos de instancia
A continuación se muestran los métodos de instancia disponibles en un WxoFile objeto :
file.get_name()– Devuelve el nombre del archivofile.get_type()– Devuelve el tipo de archivofile.get_size()– Devuelve el tamaño del archivofile.get_content()– Devuelve el contenido del archivo en bytes
A continuación se muestran ejemplos prácticos que muestran cómo acceder a archivos desde entradas de flujo, formularios de actividad de usuario y carga de archivos de actividad de usuario, y recuperar su URL, nombre, tipo y tamaño utilizando tanto system.file funciones como métodos WxoFile de instancia.
input_file = flow.input.externalFile
self.output.file_name = system.file.get_name(input_file)
self.output.file_type = system.file.get_type(input_file)
self.output.file_size = system.file.get_size(input_file)
self.output.file_content = system.file.get_content(input_file)
input_file = flow.input.externalFile
self.output.file_name = input_file.get_name()
self.output.file_type = input_file.get_type()
self.output.file_size = input_file.get_size()
self.output.file_content = input_file.get_content()
form_single_file = flow["User activity 1"]["Form 1"].output["single file upload"]
self.output.file_name = system.file.get_name(form_single_file)
self.output.file_type = system.file.get_type(form_single_file)
self.output.file_size = system.file.get_size(form_single_file)
self.output.file_content = system.file.get_content(form_single_file)
form_single_file = flow["User activity 1"]["Form 1"].output["single file upload"]
self.output.file_name = form_single_file.get_name()
self.output.file_type = form_single_file.get_type()
self.output.file_size = form_single_file.get_size()
self.output.file_content = form_single_file.get_content()
form_multi_file = flow["User activity 1"]["Form 1"].output["Multi file upload"]
self.output.file_count = len(form_multi_file)
self.output.first_file_name = system.file.get_name(form_multi_file[0])
self.output.first_file_type = system.file.get_type(form_multi_file[0])
self.output.first_file_size = system.file.get_size(form_multi_file[0])
self.output.first_file_content = system.file.get_content(form_multi_file[0])
form_multi_file = flow["User activity 1"]["Form 1"].output["Multi file upload"]
self.output.file_count = len(form_multi_file)
self.output.first_file_name = form_multi_file[0].get_name()
self.output.first_file_type = form_multi_file[0].get_type()
self.output.first_file_size = form_multi_file[0].get_size()
self.output.first_file_content = form_multi_file[0].get_content()
normal_single_file = flow["User activity 1"]["File upload 1"].output.value
self.output.file_name = system.file.get_name(normal_single_file)
self.output.file_type = system.file.get_type(normal_single_file)
self.output.file_size = system.file.get_size(normal_single_file)
self.output.file_content = system.file.get_content(normal_single_file)
normal_single_file = flow["User activity 1"]["File upload 1"].output.value
self.output.file_name = normal_single_file.get_name()
self.output.file_type = normal_single_file.get_type()
self.output.file_size = normal_single_file.get_size()
self.output.file_content = normal_single_file.get_content()
Métodos de búsqueda de usuarios
Puedes utilizar el nodo de bloque lógico para recuperar información del usuario, como el nombre, la dirección de correo electrónico y el ID.
Utiliza los siguientes métodos para buscar usuarios y recuperar un objeto de usuario:
system.user.search_by_name(name)system.user.search_by_email(email)system.user.search_by_id(id)
Estos métodos devuelven una lista de usuarios que coinciden. A continuación, puedes utilizar el objeto de usuario devuelto para acceder a los atributos del usuario.
Una vez recuperado un objeto User, puedes utilizar los siguientes métodos de atributos de usuario para acceder a los datos del usuario:
user.get_name()osystem.user.get_name(user)– Devuelve el nombre del usuario especificado.user.get_email()osystem.user.get_email(user)– Devuelve la dirección de correo electrónico del usuario especificadouser.get_id()osystem.user.get_id(user)– Devuelve el ID del usuario especificado
Los siguientes ejemplos muestran cómo recuperar información del usuario utilizando tanto los métodos del tipo de datos «User» como las funciones de « system.user ».
- Un bloque lógico que recupera los datos del usuario mediante los métodos del tipo de datos del usuario.
user_variable = system.user.search_by_email("user1@ibm.com")[0] self.output.user_name = user_variable.get_name() self.output.user_email = user_variable.get_email() self.output.user_id = user_variable.get_id() - Un bloque lógico que recupera los datos del usuario mediante funciones de « system.user ».
user_variable = system.user.search_by_email("user1@ibm.com")[0] self.output.user_name = system.user.get_name(user_variable) self.output.user_email = system.user.get_email(user_variable) self.output.user_id = system.user.get_id(user_variable
Variables de contexto del sistema
Las variables de contexto del sistema le permiten almacenar y reutilizar valores en todo un flujo de trabajo. Actúan como puntos de datos compartidos, y las variables de contexto del sistema persisten durante toda la duración del flujo de trabajo. Si una variable de contexto se actualiza al inicio del flujo de trabajo, como en un bloque lógico, el valor permanece disponible durante toda la ejecución del flujo de trabajo.
Están disponibles los métodos siguientes:
get_variable método
El get_variable método recupera una variable de contexto del sistema por su nombre.
nombre_variable : Especifica la clave de la variable.
system.context.get_variable("variable_name")
set_variable método
El set_variable método almacena un valor en el sistema utilizando la clave variable.
nombre_variable : Especifica la clave de la variable.
valor_variable : Especifica el valor que se va a almacenar.
system.context.set_variable('variable_name', "variable_value")
Variables de contexto del sistema predefinidas
Las variables predefinidas siempre están disponibles en el flujo de trabajo. No es necesario configurarlos manualmente utilizando el set_variable método. Utilice el get_variable método para acceder a ellos en cualquier momento del flujo de trabajo.
Las variables predefinidas son:
wxo_email_idwxo_tenant_idwxo_user_name