Capítulo 4 — Indexado inteligente de Python
Commit:
141eaa7—aifs(16 enero 2024, 09:26)
¿Qué se construyó?
Este es uno de los commits más importantes del proyecto. En pocas horas desde el inicio, Killian se dio cuenta de que indexar código Python de forma genérica (fragmentando texto cada 500 caracteres) no era lo ideal.
El código tiene estructura: funciones, clases, docstrings. ¿Por qué no aprovecharla?
Se agregó:
- La función
minimally_index_python_file() - El módulo
astde Python para analizar código - El flag de entorno
AIFS_MINIMAL_PYTHON_INDEXING - Los primeros tests en
tests/test_aifs.py
El problema con indexar código como texto plano
Imagina este fragmento de código:
def calculate_total_price(items: list[Item], discount: float = 0.0) -> float:
"""Calculates the total price of items with optional discount."""
subtotal = sum(item.price for item in items)
return subtotal * (1 - discount)Si lo fragmentas cada 500 caracteres junto con el resto del archivo, el fragmento que obtienes podría ser:
...
return subtotal * (1 - discount)
def apply_tax(amount: float, rate: float) -> float:
"""Apply tax rate to amount."""
return amount * (1 + rate)
def format_currency(amount: floEse fragmento mezcla tres funciones a medias. Cuando buscas "calcular precio con descuento", puede que encuentres este fragmento, pero el resultado no es útil.
La solución: usar AST (Abstract Syntax Tree)
Python tiene un módulo integrado llamado ast que puede parsear código Python y darte su estructura interna.
import ast
with open(file_path, "r") as source:
tree = ast.parse(source.read())
for node in ast.walk(tree):
if isinstance(node, ast.FunctionDef):
chunks.append(node.name)
docstring = ast.get_docstring(node)
representations.append(docstring if docstring else node.name)El truco clave: separar chunk de representation
Esta es la idea más ingeniosa del commit:
chunks.append(node.name) # Lo que se retorna al usuario
representations.append(docstring) # Lo que se embeddea para buscar- chunk: el nombre de la función (ej:
calculate_total_price) - representation: el docstring (ej:
"Calculates the total price of items with optional discount.")
Se embeddea el docstring (lenguaje natural, semánticamente rico), pero cuando hay un hit, se retorna el nombre de la función (preciso y útil).
Si no hay docstring, se usa el nombre de la función tanto para buscar como para retornar.
La función format_function_details()
def format_function_details(func_def):
name = func_def.name
args = [(arg.arg, None if not arg.annotation else ast.unparse(arg.annotation))
for arg in func_def.args.args]
return_annotation = ast.unparse(func_def.returns) if func_def.returns else None
docstring = ast.get_docstring(func_def)
formatted_string = f"(function) def {name}(\n"
for arg_name, arg_annotation in args:
formatted_string += f" {arg_name}: {arg_annotation},\n"
formatted_string += f") -> {return_annotation}\n\n"
if docstring:
formatted_string += f"{docstring}\n"
return formatted_stringEsta función genera una representación legible de la firma completa de la función con sus tipos. Por ejemplo:
(function) def calculate_total_price(
items: list[Item],
discount: float,
) -> float
Calculates the total price of items with optional discount.Eso es mucho más rico semánticamente que solo el nombre.
El flag de entorno
# Note: Set AIFS_MINIMAL_PYTHON_INDEXING to True in your env vars to use
# a much simpler, faster Python index method.
use_minimal_python_method = os.getenv('AIFS_MINIMAL_PYTHON_INDEXING')
if use_minimal_python_method and use_minimal_python_method.lower() == 'true':
return minimally_index_python_file(path)Todavía no era un parámetro de función — era una variable de entorno. Esto sugiere que Killian lo pensó como una opción de configuración global, no por llamada. Esto cambiaría más adelante.
Los primeros tests
# tests/test_aifs.py
from aifs import search
def test_search():
results = search("test query", path="./tests")
assert len(results) > 0Simple pero fundamental: confirmar que el sistema no explota en el caso básico.
Lección del capítulo
Cuando indexas datos estructurados, aprovecha su estructura. El AST de Python es una herramienta poderosa que viene incluida — no necesitas instalar nada extra para analizar código Python.
Siguiente: Cap 05 — Tests y empaquetado