| a secas pierde información: el segundo prompt solo recibe el string que
escupió el primero. RunnablePassthrough.assign(clave=subcadena) arregla eso ejecutando la
subcadena y añadiendo su resultado al diccionario que ya viaja por la tubería. Lo que fluye no es un
texto: es un dict que engorda en cada paso.
| a secas no bastaSi haces cadena_a | cadena_b, la salida de cadena_a es un str y es
todo lo que cadena_b va a ver. La pregunta original del usuario ya no está. Cualquier prompt
posterior que necesite dos variables a la vez se queda sin una.
El caso típico: generas SQL a partir de una pregunta, ejecutas el SQL, y para redactar la respuesta final necesitas las tres cosas —pregunta, consulta y resultado— en el mismo prompt.
assign: añadir sin perderfull_chain = (
RunnablePassthrough.assign(query=sql_chain)
| RunnablePassthrough.assign(result=lambda x: run_query(x["query"]))
| prompt_final
| llm
| StrOutputParser()
)
prompt_final puede pedir las tres porque las tres siguen ahí. Nada se ha sobrescrito:
assign solo agrega. Dos detalles que se olvidan:
assign ve el diccionario tal y como quedó tras el anterior, así que el orden importa: no
puedes leer x["query"] antes de haberla asignado.El paso result=lambda x: run_query(x["query"]) es la vía por la que el código imperativo entra en
una cadena declarativa. LangChain lo envuelve en un RunnableLambda por ti. Ahí es donde van las
llamadas a base de datos, las validaciones y el formateo de documentos.
Y es también donde conviene poner el try/except: si esa función lanza, se lleva la cadena entera
por delante.
run 1: OK
run 2: OperationalError: table ventas already exists
La guía anuncia «crearemos una DB temporal en memoria», pero el código hace
sqlite3.connect("ventas.db"), que es un fichero en disco: la segunda ejecución revienta en el
CREATE TABLE. Con CREATE TABLE IF NOT EXISTS deja de reventar, pero entonces los
INSERT se repiten y los totales salen multiplicados. (El total que da la guía, 6702.5,
sí es correcto en la primera ejecución: lo comprobé contra SQLite.)
run_query limpia el SQL del modelo con
query.replace("```sql", "").replace("```", ""). ¿Qué le pasa a ese parche si el modelo devuelve el
bloque con otra etiqueta, o añade una frase antes del código? ¿Y por qué la guía te propone ejecutar SQL
generado por un LLM contra una conexión con permisos de escritura?
Coge una cadena de dos pasos y mete un RunnablePassthrough.assign que no llame a ningún LLM: por
ejemplo assign(longitud=lambda x: len(x["question"])). Invócala e imprime el diccionario completo
del resultado. Confirma qué claves sobreviven al final y en qué orden aparecieron.