Sistema de Presupuesto de Diseño Integral
Esta habilidad renderiza datos de listas de cantidades de obra en presupuestos PDF profesionales, con soporte para salida de múltiples páginas: portada, tabla de totales y página de detalles.
1. Cuándo usar esta habilidad
Condiciones de activación
Los siguientes escenarios deben usar esta habilidad:
- El usuario ingresa
/presupuestoo/quote— para generar un presupuesto en PDF - El usuario menciona convertir datos de presupuesto de una tabla multidimensional o Excel a un PDF de presupuesto
Los siguientes escenarios no deben usar esta habilidad:
- El usuario solo está editando datos de una tabla multidimensional (debe usar la habilidad
lark-base) - El usuario solo está viendo el historial de presupuestos (no es necesario regenerar)
Dependencias previas
- Habilidad
lark-base— para leer datos de la tabla multidimensional de Feishu - Habilidad
lark-im— para enviar el PDF a la conversación de Feishu - Habilidad
lark-drive— para subir el PDF a los documentos de Feishu - Habilidad
xlsx— para analizar archivos Excel (cuando el usuario proporciona un Excel) - Entorno de ejecución Node.js + Playwright (ya instalado en el directorio del proyecto)
Paso 0: Verificación de primer uso
Cuando el usuario usa la habilidad por primera vez, o encuentra errores relacionados con dependencias al ejecutar comandos, el agente debe seguir el siguiente proceso de verificación.
1. Verificar el entorno Node.js
Intente ejecutar node --version, requiere >= 18. Si falla o la versión es demasiado baja:
- Indique al usuario que instale Node.js (>= 18): https://nodejs.org
- Sugiera usar nvm para gestionar la versión
2. Verificar el navegador de Playwright
Verifique si npx playwright install chromium se ha completado en el directorio del proyecto. Si la ejecución de render.js reporta un error Executable doesn't exist:
- Ejecute en el directorio del proyecto:
npx playwright install chromium - Reintente la renderización
3. Verificar lark-cli
Intente ejecutar lark-cli --version. Si falla (command not found):
- Indique al usuario que instale lark-cli (herramienta de línea de comandos de Feishu)
- Después de la instalación, ejecute:
lark-cli auth login
4. Verificar estado de inicio de sesión en Feishu
Intente ejecutar lark-cli base +table-list --base-token LfjJbLrTHacijesHmIjcDtSpnJf para probar el estado de inicio de sesión. Si devuelve un error de autenticación:
- Indique al usuario que ejecute:
lark-cli auth login - Vuelva a intentarlo después de completarlo
5. Proporcionar plantilla de Feishu
El usuario necesita copiar la plantilla del proyecto a su propio espacio de Feishu:
- Enlace de la plantilla: https://li1fn1sw90.feishu.cn/base/LfjJbLrTHacijesHmIjcDtSpnJf?from=from_copylink
- Guíe al usuario para que haga clic en el enlace → haga clic en "Copiar esta tabla multidimensional" → complete los datos del proyecto y los detalles del presupuesto en su propio espacio
- Después de completarlo, envíe el enlace de la nueva tabla multidimensional al agente
Procesamiento de fuente de datos Excel
Cuando el usuario proporciona un archivo Excel, no se analiza ni renderiza directamente. Flujo de guía:
- Sugerencia: Se recomienda importar primero a la tabla multidimensional de Feishu, los campos y el orden se alinean mejor
- Guíe al usuario para importar Excel a Feishu:
- Abra Feishu → Nueva tabla multidimensional → Importar → Seleccione el archivo Excel
- O copie directamente la tabla multidimensional de la plantilla y pegue los datos de Excel en ella
- Una vez que el usuario proporcione el enlace de la tabla multidimensional, continúe con el flujo
/presupuesto
2. Flujo de trabajo
Usuario activa → Verificación del entorno → Confirmar fuente de datos → Leer datos → Validación de datos → Confirmar logotipo → Confirmar tasa de impuesto → Confirmar plantilla → Renderizar PDF → Enviar a Feishu
Paso 1: Confirmar fuente de datos
El usuario debe proporcionar una de las siguientes opciones:
- Enlace de tabla multidimensional de Feishu — como
https://xxx.feishu.cn/base/XXX - Archivo Excel — ruta de archivo local o subido a través de la conversación
Si el usuario no lo proporciona, pregúntele qué fuente de datos desea usar.
Paso 2: Leer datos
Fuente de datos: tabla multidimensional de Feishu
- Extraiga el
base-tokendel enlace proporcionado por el usuario (la parte después de/base/en la URL) - Liste las tablas:
lark-cli base +table-list --base-token <token> - Encuentre los
table_idde las tablas Información del proyecto y Detalles del presupuesto - Lea la información del proyecto:
lark-cli base +record-list --base-token <token> --table-id <table_id de Información del proyecto> --format json - Primero consulte las vistas:
lark-cli base +view-list --base-token <token> --table-id <table_id de Detalles del presupuesto>, tome elview_idde la vista grid predeterminada (como "Todos los proyectos") - Lea los detalles del presupuesto (debe incluir
--view-id, de lo contrario el orden devuelto no coincidirá con el orden después de arrastrar en la interfaz de Feishu):lark-cli base +record-list --base-token <token> --table-id <table_id de Detalles del presupuesto> --view-id <ID de vista> --format json --limit 200 - Extraiga de la información del proyecto: nombre del proyecto, número de obra, fecha de preparación, persona que preparó, correo electrónico de contacto, logotipo de la empresa, tasa de impuesto, gastos de gestión
- Extraiga de los detalles del presupuesto: área, categoría de trabajo, nombre del proyecto, características del proyecto, unidad, cantidad, precio unitario compuesto, precio total, notas (no es necesario leer el número de secuencia, se genera automáticamente al renderizar)
Fuente de datos: Excel
Use la habilidad xlsx para analizar el Excel y extraer datos con la misma estructura. El Excel puede tener varias hojas; debe encontrar la hoja que contiene los detalles del presupuesto (generalmente tiene encabezados de columna como "N.º", "Categoría de trabajo", "Precio unitario compuesto", etc.).
Paso 3: Validación de datos
Antes de renderizar el PDF, el agente debe validar los datos leídos. No los omita.
Lista de verificación (ejecutar elemento por elemento):
3.1 Integridad de la información del proyecto
Verifique los tres campos Nombre del proyecto, Número de obra y Fecha de preparación:
- Cualquiera vacío → Informe al usuario qué campo falta específicamente y pídale que lo complete en la tabla "Información del proyecto" de la tabla multidimensional de Feishu
- Todos pasan → Continuar
3.2 Número de elementos de los detalles del presupuesto
Verifique el array items:
- Es 0 o no existe → "No hay datos en los detalles del presupuesto. Agregue elementos en la tabla 'Detalles del presupuesto' de la tabla multidimensional de Feishu y vuelva a intentarlo"
- Hay datos → Continuar
3.3 Verificación de campos por elemento
Para cada registro, verifique los campos obligatorios (categoría de trabajo, nombre del proyecto, unidad, cantidad, precio unitario compuesto) y genere un informe resumido:
Resultados de validación de datos:
- Total de elementos: 104
- Área faltante: 3 (elementos n.º 6, 9, 20)
- Categoría de trabajo faltante: 2 (elementos n.º 5, 18)
- Nombre del proyecto faltante: 0
- Unidad faltante: 1 (elemento n.º 12)
- Cantidad faltante: 0
- Precio unitario compuesto faltante: 3 (elementos n.º 5, 7, 22)
- Categoría de trabajo no está en la lista estándar: 0
3.4 Manejo de campos faltantes
- Elementos faltantes ≤ 20% del total: Informe al usuario qué elementos específicos tienen problemas y pregunte "¿Omitir estos elementos y continuar con la generación?"
- Elementos faltantes > 20%: Solicite al usuario que corrija los datos y vuelva a intentarlo; no continúe con la renderización
- El usuario confirma omitir → Filtre los elementos problemáticos y continúe con los datos válidos
3.7 Verificación de área faltante (solo cuando se agrupa por área)
Cuando el usuario selecciona agrupar por área (Paso 5.6 selecciona "Por área"), verifique adicionalmente el campo Área de cada registro:
Área faltante: X (elemento n.º X)
- Elementos faltantes ≤ 20% del total: Informe al usuario qué elementos específicos carecen de área y pregunte "¿Agrupar estos elementos en 'Otros' y continuar con la generación?"
- Elementos faltantes > 20%: Solicite al usuario que complete el área y vuelva a intentarlo
- El usuario confirma → Los elementos sin área se agrupan en "Otros" (esto es mutuamente excluyente con la lógica de omisión del Paso 3.4; el usuario elige una: omitir o agrupar en Otros)
3.5 Corrección automática del precio total
Para cada registro, verifique si el Precio total es igual a Cantidad × Precio unitario compuesto:
- Desviación < 1 yuan → No hacer nada
- Desviación ≥ 1 yuan o vacío → Calcular automáticamente
Precio total = Cantidad × Precio unitario compuesto, escribir el valor en los datos de renderización - Elementos corregidos superan el 10% → Notifique al usuario "Se han corregido automáticamente X precios totales" y sugiera revisar la fórmula de la tabla multidimensional
3.6 Lista estándar de categorías de trabajo
Las siguientes son las 13 categorías estándar:
Medidas del proyecto, Trabajos de demolición, Trabajos de albañilería, Trabajos de hormigón y acero,
Trabajos de estructura metálica, Trabajos de impermeabilización, Trabajos de aislamiento térmico, Trabajos de revestimiento de suelos,
Trabajos de revestimiento de paredes y tabiques, Trabajos de techo, Trabajos de pintura y barnizado,
Otros trabajos de decoración, Trabajos de instalación
Las categorías que no estén en la lista aún se pueden renderizar, pero informe al usuario "La categoría 'XXX' no está en la lista estándar".
Paso 4: Manejo del logotipo
Debe ejecutarse, no se puede omitir. Independientemente de si la fuente de datos es una tabla multidimensional o Excel, se debe verificar y manejar el logotipo.
- Leer desde la tabla multidimensional: Verifique el campo adjunto
Logotipo de la empresaen la tabla de información del proyecto- Si hay un adjunto, use la API de media para descargarlo (
drive +downloadno admite archivos adjuntos de tabla multidimensional):lark-cli api GET /open-apis/drive/v1/medias/{file_token}/download --output ./logo.png - Convierta la imagen a data URI base64 e inclúyala en el campo
logo_urlde los datos de renderización
- Si hay un adjunto, use la API de media para descargarlo (
- Leer desde Excel: Verifique si el Excel tiene una imagen de logotipo (generalmente incrustada en la hoja o como archivo adjunto)
- Si la tiene, extráigala y conviértala a data URI
- Cuando falta el logotipo (debe preguntar al usuario):
- Si no se encuentra una imagen de logotipo en la fuente de datos, debe preguntar al usuario:
- "No se encontró un logotipo de empresa en la fuente de datos. ¿Cómo desea proceder?"
- Opción A: Subir una imagen de logotipo
- Opción B: Proporcionar una URL de imagen de logotipo
- Opción C: No usar logotipo (el PDF mostrará la marca de texto predeterminada "R M")
- No omita este paso de forma predeterminada, incluso si el usuario dice "generar directamente", confirme la forma de manejar el logotipo
- Si no se encuentra una imagen de logotipo en la fuente de datos, debe preguntar al usuario:
Paso 5: Confirmar tasa de impuesto y gastos de gestión
Priorice el uso de los valores de los campos Tasa de impuesto y Gastos de gestión de la información del proyecto de la tabla multidimensional. Si la tabla multidimensional no tiene estos campos, pregunte al usuario.
Reglas de tarifas (comunes para tasa de impuesto y gastos de gestión):
- Valor > 1 → Se considera un porcentaje entero, se divide automáticamente por 100 (por ejemplo,
3→ 3%,10→ 10%) - Valor ≤ 1 → Se usa directamente como decimal (por ejemplo,
0.03→ 3%,0.1→ 10%) - Valor vacío: tasa de impuesto predeterminada 3%, gastos de gestión predeterminados 0 (no se cobran)
Orden de cálculo:
Subtotal = Σ Precio total de cada elemento
Gastos de gestión = Subtotal × Tasa de gastos de gestión
IVA = (Subtotal + Gastos de gestión) × Tasa de impuesto
Total = Subtotal + Gastos de gestión + IVA
Cuando los gastos de gestión > 0, la tabla de totales muestra la fila "Gastos de gestión"; cuando los gastos de gestión = 0, no se muestra (compatible con datos antiguos).
Paso 5.5: Confirmar estilo de plantilla
Debe preguntar, no se puede omitir. Incluso si se usó una plantilla anteriormente, confírmela cada vez.
Pregunte al usuario que seleccione una plantilla:
Seleccione el estilo de plantilla para el presupuesto:
1. Swiss IKB (predeterminado) — Portada de fondo azul completo + títulos de categoría bilingües
2. Swiss IKB Zebra — Igual que el anterior + filas de detalles con patrón cebra (blanco/azul claro alternado)
3. B&W — Portada de fondo blanco + énfasis gris claro, ideal para impresión en blanco y negro
4. B&W Zebra — Igual que el anterior + filas de detalles con patrón cebra (blanco/gris claro alternado)
Si la respuesta del usuario contiene un nombre o número de plantilla claro, use la plantilla correspondiente:
swiss-ikb— Swiss IKB (predeterminado)swiss-ikb-zebra— Swiss IKB Zebrabw— B&W para impresión en blanco y negrobw-zebra— B&W Zebra con patrón cebra para impresión en blanco y negro
En desarrollo (no disponible temporalmente): Los códigos de las plantillas Editorial / Editorial B&W / Card / Card B&W ya existen, pero se habilitarán después de que el usuario los modifique y mejore. Si el usuario solicita explícitamente usar estas plantillas, puede ejecutar comandos como
node scripts/render.js --template editorial, pero se recomienda priorizar las 4 plantillas en línea mencionadas anteriormente.
Paso 5.6: Confirmar modo de agrupación
Debe preguntar, no se puede omitir. Confirme antes de cada renderización, no use la opción anterior de forma predeterminada.
Pregunte al usuario que seleccione el modo de agrupación:
Seleccione el modo de agrupación del presupuesto:
1. Por área (Vestíbulo, Salón, Dormitorio principal…) — La tabla de totales se resume por área, la tabla de detalles agrega una columna de categoría de trabajo después del número de serie
2. Por categoría de trabajo (Medidas del proyecto, Trabajos de demolición…) — Modo tradicional, la tabla de detalles no incluye la columna de categoría de trabajo
Después de que el usuario responda con un número o nombre claro, use el modo correspondiente:
area— Agrupar por área, comando de renderización agregar--group-by areacategory— Agrupar por categoría de trabajo (predeterminado), no pasar parámetro o agregar--group-by category
Nota: Al seleccionar "Por área", asegúrese de que el campo
Áreade la tabla de detalles del presupuesto esté completado (el Paso 3.7 lo verificará). Si los datos aún no tienen área, se recomienda usar "Por categoría de trabajo".
Paso 5.7: Traducción al inglés de áreas (solo cuando se agrupa por área)
Los nombres de las áreas varían según el proyecto, deben traducirse dinámicamente antes de cada renderización, está prohibido usar un mapeo fijo escrito en código.
- Recoja todos los nombres de áreas que aparecen en los detalles del presupuesto (sin duplicados)
- El agente traduce los nombres de área uno por uno al inglés (traducción habitual en la industria del diseño, por ejemplo, 玄关→Foyer, 客厅→Living Room, 阳光房→Sunroom)
- Escriba los resultados de la traducción como un objeto
region_namesen el JSON de datos de renderización:
{
"region_names": {
"玄关": "Foyer",
"阳光房": "Sunroom"
}
}
- Durante la renderización, render.js usa primero las traducciones en
region_names(los nombres de área no proporcionados recurrirán al mapeo de áreas comunes incorporado y, como último recurso, mostrarán AREA) - Después de la renderización, enumere la tabla de traducción de áreas de esta ocasión en el informe; el usuario puede corregirla y la próxima cotización se volverá a traducir
Paso 6: Renderizar PDF
Ejecute en el directorio del proyecto:
node scripts/render.js --input <data.json> --template <nombre de plantilla> --vat-rate <tasa de impuesto> --group-by <area|category> --output ./output/<Nombre del proyecto>_<Número de obra>.pdf
--group-by area— Agrupar por área (categoría principal = área, detalles incluyen columna de categoría de trabajo)--group-by categoryo no pasar — Agrupar por categoría de trabajo (comportamiento original)
Formato JSON de datos de renderización:
{
"Nombre del proyecto": "xxx",
"Número de obra": "xxx",
"Fecha de preparación": "xxx",
"Persona que preparó": "xxx",
"Correo electrónico de contacto": "xxx",
"logo_url": "data:image/png;base64,...",
"Tasa de impuesto": 0.08,
"Gastos de gestión": 0.1,
"items": [
{
"Área": "Vestíbulo",
"Categoría de trabajo": "Medidas del proyecto",
"Nombre del proyecto": "Andamio",
"Características del proyecto": "Andamio interior",
"Unidad": "Partida",
"Cantidad": 1,
"Precio unitario compuesto": 3200,
"Precio total": 3200
}
]
}
Regla de generación automática del número de serie: El campo
N.ºno es necesario en los datos (la tabla multidimensional de Feishu puede no tener esta columna). Durante la renderización, se genera automáticamente con el formatoN.º de grupo.N.º dentro del grupo: cuando se agrupa por área, Área 01 →1.1, 1.2, …, Área 02 →2.1, 2.2, …; cuando se agrupa por categoría de trabajo, Medidas del proyecto →1.x, Trabajos de demolición →2.x… Trabajos de instalación →13.x. El orden dentro del grupo = orden de los registros en Feishu (se puede ordenar arrastrando registros en la tabla multidimensional).
Tasa de impuesto y gastos de gestión (ver Paso 5):
- Valores de
Tasa de impuesto/Gastos de gestión> 1 se consideran porcentajes enteros (3 → 3%), ≤ 1 se consideran decimales (0.08 → 8%) - Gastos de gestión = Subtotal × Tasa de gastos de gestión; IVA = (Subtotal + Gastos de gestión) × Tasa de impuesto; Total = Subtotal + Gastos de gestión + IVA
- Si no se proporcionan gastos de gestión o son 0 → No se cobran, la tabla de totales no muestra la fila de gastos de gestión
- Si no se proporciona el parámetro
--vat-rate, render.js lee primero el campoTasa de impuestode los datos
Paso 7: Guardar y enviar
Después de que se renderice el PDF, debe preguntar al usuario que seleccione el método de guardado:
PDF generado: <Nombre del proyecto>_<Número de obra>_<Nombre de plantilla>.pdf (XX elementos, ¥XXX con impuestos)
¿Cómo desea guardarlo?
1. Enviar a la ventana de chat actual
2. Guardar en documentos en la nube de Feishu, enviar enlace del documento
3. Ambos
Ejecute según la selección del usuario:
Opción 1 — Enviar a la ventana de chat
lark-cli im +messages-send --type media --file <ruta del pdf>
Opción 2 — Guardar en documentos en la nube de Feishu
lark-cli drive +upload --file <ruta del pdf> --title "<Nombre del proyecto>_<Número de obra>"
Obtenga el enlace del archivo después de la subida y responda al usuario:
Guardado en documentos en la nube de Feishu: <Nombre del proyecto>_<Número de obra>_<Nombre de plantilla>.pdf
Enlace del documento: https://xxx.feishu.cn/drive/xxx
Opción 3 — Ambos
Primero ejecute la opción 2 (subir), luego ejecute la opción 1 (enviar archivo + enlace).
Limpiar archivos temporales
Después de enviar/guardar, elimine los archivos JSON temporales y los archivos de logotipo descargados (si los hay) generados durante el proceso de renderización.
3. Estructura del proyecto
quote-generator-skill/
├── package.json # Configuración del proyecto
├── setup.sh # Script de instalación con un solo comando
├── SKILL.md # Definición de la habilidad
├── scripts/
│ ├── render.js # Motor de renderización HTML → PDF
│ ├── demo.js # Demostración lista para usar
│ └── generate-large-test.js # Generador de datos de prueba grandes
├── references/
│ ├── templates/
│ │ ├── content.html # Plantilla Handlebars para PDF (página de contenido)
│ │ ├── cover-screen.html # Plantilla de portada independiente Swiss IKB
│ │ ├── cover-bw.html # Plantilla de portada para impresión B&W en blanco y negro
│ │ ├── swiss-ikb.json # Configuración de Swiss IKB
│ │ ├── swiss-ikb-zebra.json # Configuración de Swiss IKB Zebra
│ │ ├── bw.json # Configuración de B&W para impresión en blanco y negro
│ │ └── bw-zebra.json # Configuración de B&W Zebra
│ ├── helpers.js # Helper personalizado de Handlebars
│ └── bitable-config.json # Configuración de campos de tabla multidimensional
├── docs/plans/ # Documentos de diseño
├── output/ # Directorio de salida de PDF generados
└── README.md
4. Descripción de las plantillas
La plantilla PDF contiene tres tipos de páginas:
- Portada — Nombre del proyecto, número, fecha, persona que preparó, logotipo (esquina superior derecha)
- Tabla de totales — Resumen de importes por categoría de trabajo + Subtotal/IVA/Total
- Página de detalles — Aproximadamente 8 filas de datos por página, con título de categoría, filas de detalles, fila de subtotal, número de página
5. Notas importantes
- El
Precio totalen la tabla multidimensional es un campo de fórmula (=Cantidad × Precio unitario compuesto), ya está calculado al leerlo N.ºno necesita mantenerse en Feishu: se genera automáticamente durante la renderización (N.º de grupo.N.º dentro del grupo, el orden dentro del grupo sigue el orden de los registros). Para ajustar el orden de los detalles, arrastre los registros para ordenarlos en la tabla multidimensional de FeishuCategoría de trabajoes un campo de selección única, las 13 opciones corresponden a las 13 categorías de trabajoÁreaes un campo de texto libre (se recomienda cambiarlo a selección única en Feishu: Vestíbulo, Salón, Comedor, Cocina, Dormitorio principal, Dormitorio secundario, Baño, Balcón, Estudio, Vestidor, Pasillo, Toda la casa, etc.), solo se usa al agrupar por área, puede estar vacío- Los títulos de grupo en modo área se muestran en chino e inglés (por ejemplo,
Living Room 客厅). Los nombres de área varían según el proyecto; el agente los traduce dinámicamente antes de cada renderización según el Paso 5.7 y los escribe enregion_names; el mapeo de áreas comunes incorporado solo sirve como respaldo, mostrandoAREAsi no coincide Unidades un campo de selección única, admite extensiones personalizadas- El logotipo admite dos formas: imagen (recomendada) y texto
- La tasa de IVA la especifica el usuario cada vez, no es fija
- El PDF de salida es de tamaño A4, adecuado para impresión
6. Manejo de problemas comunes
| Síntoma del error | Causa | Método de manejo del agente |
|---|---|---|
command not found: lark-cli | lark-cli no está instalado | Indique al usuario que instale lark-cli, consulte el Paso 0.3 |
lark-cli devuelve error de autenticación | No ha iniciado sesión o el token ha expirado | Indique al usuario que ejecute lark-cli auth login |
Executable doesn't exist | Chromium de Playwright no está instalado | Ejecute npx playwright install chromium |
| El PDF renderizado está vacío o tiene un formato incorrecto | El formato del JSON de datos es incorrecto | Verifique que Nombre del proyecto y Número de obra no estén vacíos, y que items tenga al menos 1 elemento |
| El número de serie de detalle no coincide con el ingresado manualmente | El número de serie ahora se genera automáticamente | Fenómeno normal: el número de serie se renumera automáticamente según la agrupación (por ejemplo, 01 Toda la casa → 1.1, 1.2…). Para ajustar el orden, arrastre los registros en Feishu |
| Tamaño/fuente anormal | Ocurre raramente (las fuentes ya están incorporadas como WOFF2, no dependen de CDN externos) | Verifique que los archivos de fuente en el directorio references/fonts/ del proyecto estén completos |
| Error al descargar el logotipo | Problema de permisos de la API de archivos adjuntos de la tabla multidimensional | Confirme que el usuario haya autorizado, o seleccione "No usar logotipo" |
No se puede extraer base-token de la URL | El usuario no proporcionó un enlace de tabla multidimensional | Indique al usuario que proporcione un enlace de tabla multidimensional de Feishu (la URL debe contener /base/) |