• Featured post

Embeddings, Vector Search & BM25

Un ordenador no puede entender texto ni relación semántica o significado entre palabras. Solo puede entender números. Esto lo resolvemos mediante el uso de embeddings.

Un embedding es la representación de texto (en forma de números) en un espacio vectorial. Esto permite a los modelos de IA comparar y operar sobre el significado de las palabras.

flowchart TD
    A["perro"] --> B
    B --> C["[-0.003, 0.043, ..., -0.01]"]
    
    N1["(texto que queremos convertir)"]:::note --> A
    N2["(vectores con contenido semántico)"]:::note --> C
    
    classDef note fill:none,stroke:none,color:#777;    

Los vectores de cada palabra o documento capturan el significado semántico del texto.

  • perro estará cerca de mascota
  • contrato estará lejos de playa

Vector vs SQL databases

El problema con las BBDD típicas es que solo buscan matches exactos. Si yo busco por coche solo me sacará las entradas que contengan coche.

En cambio, como las BBDD vectoriales pueden interpretar la semántica de las palabras mediante los vectores, si busco por coche puede sacarme valores como sedán, SUV, Land Rover, etc.

Las BBDD vectoriales son muy buenas cuando necesitamos buscar items similares por proximidad uno respecto al otro. Un ejemplo de uso es buscar películas parecidas (Netflix). Otro ejemplo son los recomendadores de items parecidos en tiendas online (Amazon).

Como ejecutar una búsqueda (query) mediante vectores

(You can see the code here)

Necesitamos:

  • Una BBDD Vectorial (CosmosDB)
  • Un modelo para transformar los embeddings (text-embedding-3-large)

El flujo completo es el siguiente:

  1. Usar un embedding model para obtener los vectores del contenido que queremos indexar
  2. Insertar el texto original y los vectores del contenido en una BBDD vectorial
  3. Cuando queramos ejecutar una query usar el mismo embedding model de antes con la query a buscar. Con el embedding resultante buscamos vectores similares en la BBDD y sacamos el texto original de original_text

    Introducir vectores en CosmosDB

    Para poder buscar necesitamos rellenar antes la BBDD con contenido. Lo mantenemos simple. Metemos

    • un ID a mano
    • el texto original
    • los vectores resultado de hacer el embedding sobre el texto original

El pseudocódigo se ve así y se ejecuta de uno en uno

text = "A shiba walks alone in the park"
# this sends the text to the model text-embedding-3-large 
vectors = createEmbeddingsForText(text)
item = {
	"id": "1",
	"original_text": text,
	"vectors": vectors
}
uploadToCosmosDB(item)

ejemplos de los datos que guardo

{
	"id": "1",
	"original_text": "A shiba walks alone in the park",
	"vectors": [-0.003, 0.043, ..., -0.001]
}

Read More

Dependency injection w. multiple implementations

I have the class package with a type, plus more info not relevant to the question

public class Package
{
	public string Type { get; set; }
	// ... more package properties
}

I have the following interface service to mail packages

public interface IMailService
{
	public Task<string> SendPackage(Package package);
}

Then I have several implementations for this interface

public class MailNewPackageService : IMailService
{
	public async Task<string> SendPackage(Package package)
	{
		Console.WriteLine("send package through new service");
		// implementation ...
	}
}

public class MailOldPackageService : IMailService
{
	public async Task<string> SendPackage(Package package)
	{
		Console.WriteLine("send package through old service");
		// implementation ...
	}
}

Read More

C# Events (pub/sub)

Permite a una clase notificar a otras cuando ocurre algo de interés.

public class Publicador
{
	// Declaracion de un evento
	public event EventHandler EventoSimple;

	// trigger
	public void DispararEvento()
	{
		// verificacion de subscripcion
		if(EventoSimple != null)
		{
			EventoSimple(this, EventArgs.Empty);
		}
	}
}
public class Suscriptor
{
	public void Suscribirse(Publicador publicador)
	{
		// suscripcion al evento
		publicador.EventoSimple += ManejadorEvento;
	}

	private void ManejadorEvento(object sender, EventArgs e)
	{
		// do whatever you need to do and/or send here
	}
}
// usage
var publicador = new Publicador();
var suscriptor = new Suscriptor();
suscriptor.Suscribirse(publicador);

C# Async await best practices

Don’t use async void

An async method may return Task, Task<T> and void. Natural return types are all but void.

Any sync method returning void becomes an async method returning Task. example:

// synchronous work
void MyMethod()
{
	Thread.sleep(1000);
}

// asynchronous work
async Task MyAsyncMethod()
{
	await Task.Delay(1000);
}

When an exception is thrown out of an async Task or async Task<T> method that exception is captured and placed on the Task object. For async void methods, exceptions aren’t caught.

Read More

C# Streams and Files

MemoryStream

Los streams en memoria se usan sobre todo para leer de ficheros, o para almacenar información que queremos guardar en ficheros.

Si se inicializa mediante el siguiente constructor no se puede cambiar su tamaño.

MemoryStream ms = new MemoryStream(150);
ms.Capacity;
ms.Length;
ms.Position;

// 0 bits from Begin
ms.Seek(0, SeekOrigin.Begin);
// 5 bits from current position
ms.Seek(5, SeekOrigin.Current);
// go back 10 bits from current position
ms.Seek(-10, SeekOrigin.Current);

Files

Ejemplo para leer y escribir un fichero.

write file

FileStream fsEscribir = new FileStream("miArchivo.txt", FileMode.Create);

string cadena = "this is an example";

fsEscribir.Write(ASCIIEncoding.ASCII.GetBytes(cadena), 0, cadena.Length);
fsEscribir.Close();

read file

byte[] infoArchivo = new byte[100];

FileStream fs = new FileStream("miArchivo.txt", FileMode.Open);
fs.Read(infoArchivo, 0, (int) fs.Length);

Console.WriteLine(ASCIIEncoding.ASCII.GetString(infoArchivo));
Console.ReadKey();

fs.Close();

Servicebus vs queue

ServiceBus

Sistema de mensajeria COMPLEJO que permite comunicacion entre MULTIPLES SERVICIOS usando patrones publication/subscription

Permite a un mensaje ser consumido por MULTIPLES CONSUMERS Ideal para flujos de trabajo complejos

Queue

Mecanismo mas simple y directo. Permite la transmision de mensajes de un punto a otro. Un mensaje enviado a la cola es recibido y procesado por un unico consumidor Se centra en GARANTIZAR LA ENTREGA del mensaje, pero es mas limitado.

C# Delegates

Un delegado es un placeholder para un método. Permite pasar una función como parámetro a un método.

Hay varios tipos de delegado:

  • Action<T> consume una clase, devuelve void Ejemplo Console.WriteLine();
  • Predicate<T> consume una clase, devuelve bool Por ejemplo para abstraer condiciones
  • Func<T, K> consume una clase y devuelve otra. Por ejemplo para abstraer mappings

Usaremos el struct Book para todos los ejemplos

public struct Book
{
	public string Title;        // Title of the book.
	public string Author;       // Author of the book.
	public decimal Price;       // Price of the book.
	public bool Paperback;      // Is it paperback?

	public Book(string title, string author, decimal price, bool paperBack)
	{
		Title = title;
		Author = author;
		Price = price;
		Paperback = paperBack;
	}
}

Action (Consumer)

Tenemos el ejemplo BookDBActionService

public class BookDBActionService
{
	// List of all books in the database
	List<Book> list = new List<Book>();

	// initialize test data on constructor
	public BookDBActionService()
	{
		list.Add(new Book("title1", "author1", 10, true));
		list.Add(new Book("title2", "author2", 20, false));
	}

	// Aqui tenemos el Action<Book> donde delegamos como se procesa cada libro
	public void ProcessPaperbackBooks(Action<Book> processBook)
	{
		foreach (Book b in list)
		{
			if (b.Paperback)
			{
				// delegate call
				processBook(b);
			}
		}
	}
}

Llamada donde ejecutamos el código y llamamos al Action<Book>

public static void Main(string[] args)
{
var bookDBAction = new BookDBActionService();
bookDBAction.ProcessPaperbackBooks(book => Console.WriteLine(book.Title));
}

Read More

Introduction to C#

Nullables

Check this post on c# nullables and how to use them

readonly

La palabra clave readonly hace que no se pueda asignar después de que el constructor acabe.

public class Age
{
	private readonly int _year;

	public Age(int year)
	{
		_year = year;
	}

	public void OverwriteYear()
	{
		_year = 1967; // Compilation error
	}
}

readonly vs const

readonly values can be computed dynamically, (!) but need to be assigned before the constructor exits (!).

In the other hand const are implicitly static.

(More info on this subject)

public class ReadonlyVsConst
{
	public const int I_VALUE = 2; // HAS TO be assigned on declaration.
	public readonly int I_RO_VALUE; // Can be assigned on the constructor.

	public ReadonlyVsConst()
	{
		I_RO_VALUE = 3;
	}
}

Read More

C# Methods

Pass by value or pass by reference

In C# you may choose how you want to pass a method’s variable.

by value example (default)

// by value
int number = 25;
PassByValue(number); 
Console.Write(number); // prints 25
public static void PassByValue(int number)
{
	// this won't take effect
	number = 12;
}

by reference example (out)

// by reference
int number;
PassByOutReference(out number);
Console.Write(number); // prints 12
public static void PassByOutReference(out int number)
{
	number = 12;
}

by reference example (ref)

// by reference (ref)
int number = -1;
PassByReference(out number);
Console.Write(number); // prints 12
public static void PassByRefReference(ref int number)
{
	number = 12;
}

out vs ref

(!) You should use out unless you explicitly need ref (!)

  • out doesn’t need the variable to be initialized first
  • ref needs the variable to be initialized first. This could confuse readers as it looks as if the initial values were relevant, but they’re not.

Read More

C# Classes

Auto properties

set vs private set

  • La variable que está como public x { get; set; } se puede llamar de manera externa e interna.
  • Mientras que la variable public x { get; private set; } se puede hacer un get de manera externa pero no un set

ejemplo

public class Person
{
	public string Name { get; set; }
	public string Surname { get; private set; }

	public Person()
	{
		Name = "ctor name";
		Surname = "ctor surname";
	}

	public void SetInternalValues()
	{
		Name = "name internal value";
		// if we'd have public string Surname { get; } without set; 
		//  then we couldn't set Surname inside its own class either 
		Surname = "surname internal value";
	}
}
var person = new Person(); 
person.Name = "name external value";
// person.Surname = "this isn't possible"; // cannot be set as we use { private set; }

Read More

Visual Studio (Code)

Shortcuts and QoL features

  • ctrl + F12 - over a method call -> go directly to the method inside the interface’s implementation
  • ctrl + - - go back where you where after entering a method
  • ctrl + G - go to line number
  • ctrl + T - search for a class. If you want to search for UserController.cs you can type UCont and it will find it
  • ctrl + shift + P - search inside Visual Studio options
  • ctrl + D - duplicate actual line
  • alt + ↑ / ↓ - move code lines

Auto-complete placeholders

  • prop - creates a new property for a class
  • ctor - creates a new constructor
  • cw - creates a new Console.WriteLine() statement
  • try - creates a try-catch statement

To create a new property, type prop, hit twice tab and it creates a property template which you can navegate and override

vstudio 2

Multi-caret and multi-cursor editing

(check this for more details)

For lines that are aligned

  • alt + mouse click - selects a block to edit
  • alt + shift + arrow - same with keyboard

For multiple places that are not aligned

  • ctrl + alt + mouse click - click where you want to add a caret
  • (select the word you want to match) alt + shift + ; - vstudio selects all locations that match selected text in the current document and you may edit them

    Debug in VsCode

    Watch a variable

    En la pestaña watch se puede introducir el nombre de una variable y al hacer debug mostrará siempre el valor de esta variable.

vstudio-4

Read More