Escribir "enciende el LED" y ver el LED prendido 78 milisegundos después es la demostración con que Cactus Compute abre su artículo en el blog de Raspberry Pi. Corre sobre un Raspberry Pi 5 usando solo la CPU, sin un HAT de inteligencia artificial dedicado. El modelo se llama Needle 2 y pesa 14 MB.
Conviene partir por lo que Needle no es. No hay chatbot acá. El equipo de Cactus Compute lo entrenó para una sola tarea, ejecutar acciones estructuradas y confiables en el propio dispositivo.
"Needle 2 es bastante excelente", dijo Eben Upton, CEO de Raspberry Pi.
¿Cómo se declaran las funciones?
El planteamiento es directo. Uno declara funciones de Python y Needle escoge cuál llamar y completa los argumentos. En la prueba lo usaron para escribir notas locales, consultar vcgencmd e interactuar con los LED de un Raspberry Pi 5 sin acelerador.
python3 -m venv needle-env
source needle-env/bin/activate
python -m pip install cactus-needleDespués de la primera descarga, Needle funciona de forma local, sin API en la nube y sin conexión de red. El primer ejemplo no necesita hardware extra.
La clase Needle acepta funciones decoradas con @needle.tool. El decorador toma el nombre de cada función, su docstring y sus anotaciones de tipo para armar el esquema de la herramienta. En save_note, la anotación le indica a Needle que text tiene que ser un string.
@needle.tool
def save_note(text: str):
"""Append a note to a local text file."""
with notes_path.open("a", encoding="utf-8") as notes:
notes.write(text + "\n")
return {"text": text, "path": str(notes_path)}
@needle.tool
def get_temperature():
"""Return the current CPU temperature of this Raspberry Pi in Celsius."""
out = subprocess.check_output(["vcgencmd", "measure_temp"], text=True)
return {"temperature_c": float(out.split("=")[1].split("'")[0])}
agent = needle.Needle(tools=[save_note, get_temperature])
response = agent.run("Save a note that says the cooler is working.")Dentro de run(), el modelo primero produce la selección de herramienta, con la salida recortada para mayor claridad.
{
"type": "call",
"function_calls": [
{
"name": "save_note",
"arguments": { "text": "the cooler is working" }
}
]
}run() ejecuta la función e incluye su valor de retorno en response["results"]. En esa corrida, el complete() inicial que vive dentro de run() eligió la función y generó sus argumentos en 107 milisegundos.
La segunda consulta pregunta por la temperatura. Needle escoge get_temperature(), run() ejecuta vcgencmd y devuelve la lectura, que en el ejemplo marcó 49,9 grados Celsius. El equipo también declaró set_led(on), blink_led(times) y take_photo() por medio de rpicam-still para probar la selección de herramientas. Needle elige la herramienta y el código Python corriente decide qué pasa después.
¿Qué hace cuando la pregunta no calza con ninguna función?
Devuelve una lista vacía. Ante "¿cuál es la capital de Francia?", Needle responde { "function_calls": [] } en 92 milisegundos, porque ninguna de las herramientas declaradas puede contestar eso. Para un modelo de acción, rechazar una petición ajena es la respuesta correcta.
Cuánta memoria ocupa
La sesión nativa de Needle se mantiene cerca de los 28 MB. En la demostración en Python, el proceso completo tuvo picos de entre 43 MB y 46,4 MB, contando el intérprete.
Los ejemplos corrieron sobre un Raspberry Pi 5 de 8 GB con Raspberry Pi OS, solo CPU y la versión 2.0.7 de cactus-needle. Prefill y decode son los contadores de sesión de Needle, y cada latencia de la columna de tiempo corresponde al reloj de pared de una llamada a complete(), medida antes de que se ejecute la función de Python seleccionada.
Dónde está el código
Quien ya escribe funciones con GPIO Zero puede decorarlas y pasárselas tal cual, y lo mismo vale para el resto de la lógica de una aplicación. Needle además se puede afinar de forma local en un notebook para un conjunto particular de herramientas.
Los pesos y el código están en huggingface.co/Cactus-Compute/needle2 y en github.com/cactus-compute/needle. Los dos salen bajo licencia Apache 2.0.




