La legislación es el código fuente de nuestra sociedad, pero a menudo se escribe de una manera que es inaccesible a las personas que gobierna. Las leyes pueden ser decenas de páginas largas, llenas de densa legalese y referencias cruzadas que las hacen casi imposibles de leer al azar. Como desarrollador creo en el uso de la tecnología para hacer que el gobierno sea más transparente y accesible. ¿Qué pasaría si pudiera usar los datos para entrenar a una IA para leer cualquier factura y escribir su propio resumen?Esta es la historia de cómo construí una tubería de extremo a extremo para hacer eso: desde raspar los datos, limpiar la confusión, hasta ajustar un modelo de lenguaje potente. El primer paso fue obtener las URLs para cada factura que había pasado, junto con su texto completo y resumen oficial. El Kit de herramientas básicas: BeautifulSoup Para páginas HTML simples y urllib de Python son las herramientas perfectas. Comenzé escribiendo un guión (all_house_texas.py) para navegar por las páginas de factura, encontrar todos los enlaces a historias de factura individuales, y extraer información básica como el autor y el subtítulo. Parte 1: El rascado de un sitio web del gobierno del siglo XX # From: leg_module_test.py # A simple function to turn a URL into a BeautifulSoup object import urllib.request from bs4 import BeautifulSoup def make_soup(url): thepage = urllib.request.urlopen(url) soupdata = BeautifulSoup(thepage, "lxml") return soupdata website ='https://capitol.texas.gov/BillLookup/History.aspx?LegSess=89R&Bill=SB1' soup = make_soup(website) # Find the author's name by its specific HTML ID for data in soup.findAll('td', id = 'cellAuthors'): print(data.text) Esto funcionó bien para los datos iniciales, pero rápidamente golpeé un muro. Descargar Selenium para JavaScript El pedazo de datos más valioso no era un enlace estándar. Estaba oculto detrás de un evento de clic en JavaScript que abrió una nueva ventana emergente. BeautifulSoup no puede ejecutar JavaScript, por lo que no podía ver el enlace. Este es un desafío común en el rascado web moderno. la solución? Con Selenium, mi script podría cargar la página, esperar a que el JavaScript rendiera, y luego interactuar con el botón como un ser humano lo haría. Selenium Mi script texas_leg_txt_from_list.py usó este enfoque para obtener finalmente la URL de resumen. # From: texas_leg_txt_from_list.py # Using Selenium to find an element and extract its JavaScript-based URL from selenium import webdriver from selenium.webdriver.common.by import By import time # ... (driver setup code) ... driver.get('https://capitol.texas.gov/BillLookup/Text.aspx?LegSess=75R&Bill=HB1') time.sleep(1.5) # Wait for the page to load # Find the summary link by its ID bill_summary_link = driver.find_element(By.ID, 'lnkBillSumm') # Get the 'onclick' attribute, which contains the JavaScript function call onclick_attribute = bill_summary_link.get_attribute('onclick') # A bit of string manipulation to extract the URL from the JavaScript start_index = onclick_attribute.find("('") + 2 end_index = onclick_attribute.find("'", start_index) bill_summary_url = 'https://capitol.texas.gov/BillLookup/' + onclick_attribute[start_index:end_index] print(f"Successfully extracted summary URL: {bill_summary_url}") driver.quit() Con esto, podía recopilar de manera fiable mis tres datos clave para miles de facturas: la URL de la página principal, el texto completo inscrito y el texto resumen oficial. Parte 2: La tripulación de limpieza - Transformando el caos de HTML en datos limpios Los datos web son desordenados.El texto rasgado estaba lleno de espacio blanco extra, caracteres de espacio no rompe (\xa0), y otros artefactos HTML. Antes de poder alimentar esto a un modelo, necesitaba una limpieza seria. Mi script start_analysis_0.py fue dedicado a este paso crucial. Primero, escribí una función de limpieza simple usando regex para estandarizar el espacio blanco y eliminar los caracteres de spam. # From: start_analysis_0.py # A function to clean raw text scraped from the web import re def clean_text(text): if not isinstance(text, str): return "" # Handle cases where data might not be a string text = re.sub(r'\s+', ' ', text) # Collapse all whitespace into single spaces text = text.replace('\xa0', ' ') # Remove non-breaking spaces text = text.replace('__','') # ... other replacements ... return text.strip() Luego, implementé un control crítico de calidad. Un buen resumen debe ser significativamente más corto que el texto original, pero no tan corto que sea inútil. Decidí mantener solo pares donde la longitud del resumen era entre el 10% y el 100% de la longitud de la factura completa. Esto filtró los rastros malos y los datos irrelevantes. Después de procesar miles de facturas, planeé un histograma de esta proporción. Esta visualización confirmó que la mayoría de mis datos cayeron en una distribución sana, validando mi lógica de filtración.El conjunto de datos final, limpiado, se guardó como un único archivo JSON, listo para el evento principal. Parte 3: Fine-Tuning de un modelo T5 Ahora para la parte divertida. eligió usar el un potente y versátil Transformador de Transferencia de Texto a Texto. T5 es excelente para la instrucción siguiendo tareas. Mi tubo de entrenamiento fue construido en start_analysis_1.py y start_analysis_2.py utilizando los transformadores Hugging Face y las bibliotecas TensorFlow. El modelo T5 pequeño. Enseña el modelo El proceso principal se ve así: Tokenización: Este paso convierte el texto en ID numéricos que el modelo puede entender. Creé una función de preprocesamiento para manejar esto tanto para el texto de la factura (la entrada) como el resumen (la etiqueta de destino). # From: start_analysis_1.py # This function tokenizes the text and prepares it for the model from transformers import AutoTokenizer tokenizer = AutoTokenizer.from_pretrained('t5-small') prefix = "summarize: " def preprocess_function(examples): # Prepare the input text with the "summarize: " prefix inputs = [prefix + doc for doc in examples["source_text"]] model_inputs = tokenizer(inputs, max_length=512, truncation=True) # Tokenize the target summaries with tokenizer.as_target_tokenizer(): labels = tokenizer(examples["target_text"], max_length=128, truncation=True) model_inputs["labels"] = labels["input_ids"] return model_inputs Carga de datos: Cargué el JSON limpiado en un objeto de Hugging Face Dataset y aplicé la tokenización. Entrenamiento: Finalmente, configuré el modelo, configuré un optimizador y inicié el proceso de entrenamiento con model.fit().Este es el lugar donde ocurre la magia.El modelo itera a través de los datos, hace predicciones, las compara con los resúmenes oficiales y ajusta sus pesos internos para mejorar y mejorar. # From: start_analysis_1.py # The final training call in TensorFlow import tensorflow as tf from transformers import TFAutoModelForSeq2SeqLM, AdamWeightDecay # ... (dataset and data_collator setup) ... optimizer = AdamWeightDecay(learning_rate=2e-5, weight_decay_rate=0.01) model = TFAutoModelForSeq2SeqLM.from_pretrained("t5-small") model.compile(optimizer=optimizer) # Let's train! model.fit( x=tf_train_set, validation_data=tf_test_set, epochs=3 ) # Save the fine-tuned model for later use model.save_pretrained("./t5_small_texas_bills") Parte 4: El veredicto - ¿Funcionó? Después de horas de entrenamiento, llegó el momento de la verdad. alimenté al modelo una factura que nunca había visto antes para ver lo que produciría. Aquí un ejemplo: ** Texto original del proyecto de ley (Snippet): "...Una ley relativa a la reducción de la limitación del importe total de los impuestos ad valorem que un distrito escolar puede imponer a los hogares de residencia de los ancianos o discapacitados para reflejar cualquier reducción en la tasa de impuestos del distrito escolar y proteger a un distrito escolar contra cualquier pérdida de ingresos locales resultantes..." ** Texto original del proyecto de ley (Snippet): "...Una ley relativa a la reducción de la limitación del importe total de los impuestos ad valorem que un distrito escolar puede imponer a los hogares de residencia de los ancianos o discapacitados para reflejar cualquier reducción en la tasa de impuestos del distrito escolar y proteger a un distrito escolar contra cualquier pérdida de ingresos locales resultantes..." **Síntesis oficial: "Esta Ley reduce la limitación de los impuestos ad valorem del distrito escolar para los propietarios de viviendas mayores o discapacitados para reflejar las reducciones de la tasa de impuestos. Asegura que los distritos escolares son compensados por cualquier pérdida de ingresos resultante. **Síntesis oficial: "Esta Ley reduce la limitación de los impuestos ad valorem del distrito escolar para los propietarios de viviendas mayores o discapacitados para reflejar las reducciones de la tasa de impuestos. Asegura que los distritos escolares son compensados por cualquier pérdida de ingresos resultante. ** AI-Generated Resumen: "El proyecto de ley se refiere a una reducción de la limitación sobre el importe total de impuestos ad valorem que puede ser impuesto por un distrito escolar en los hogares de residencia de los ancianos o discapacitados. ** AI-Generated Resumen: "El proyecto de ley se refiere a una reducción de la limitación sobre el importe total de impuestos ad valorem que puede ser impuesto por un distrito escolar en los hogares de residencia de los ancianos o discapacitados. Análisis: El modelo identificó correctamente los temas principales (impuestos ad valorem, distritos escolares, hogares de ancianos / discapacitados) y la acción principal (reducción de la limitación fiscal). Aunque no es tan elocuente como el resumen humano, es preciso y captura la esencia de la factura perfectamente. Es un TL;DR funcional y útil. El modelo identificó correctamente los temas principales (impuestos ad valorem, distritos escolares, hogares de ancianos / discapacitados) y la acción principal (reducción de la limitación fiscal). Aunque no es tan elocuente como el resumen humano, es preciso y captura la esencia de la factura perfectamente. Es un TL;DR funcional y útil. Analysis: Conclusión Este proyecto fue un poderoso recordatorio de que las aplicaciones de IA más impactantes a menudo se basan en una fundación de ingeniería de datos.Construir el modelo fue el último 20% del trabajo; el primer 80% fue el proceso arduo de adquirir y limpiar los datos. El modelo final no es perfecto, pero es una poderosa prueba de concepto. Demostra que podemos usar la IA moderna para hacer que la información cívica compleja sea más accesible para todos. What's next? Despliegue como una API: convierte esto en un servicio público donde cualquiera puede enviar el texto de una factura y obtener un resumen. Crear un bot: Para publicar resúmenes de facturas recién presentadas. Expansión a otros estados: Cada legislatura tiene el mismo problema. Si eres un desarrollador, te animo a que mires los datos de tu propio gobierno local.Puedes encontrar una oportunidad similar para construir algo que no solo afianzará tus habilidades, sino que también sirva a tu comunidad.