Docs Herramientas

Burbujas y modelos locales

Haz que cualquier unidad del juego muestre burbujas de diálogo con cualquier identidad; conecta un LLM local y cada frase que entra recibe una respuesta sobre la cabeza de la unidad.

Las burbujas son una capa para el espectador: no afectan al resultado y sirven para directos, comentarios y depuración.

  • Cualquier unidad puede hablar con cualquier identidad; varias unidades pueden hablar a la vez;
  • El tamaño de letra, el color, el ancho, la cola, la opacidad y la velocidad de escritura de cada burbuja se pueden personalizar por separado;
  • Se conecta directamente a un LLM local (LM Studio) con salida en streaming: la burbuja se actualiza mientras se genera el texto.

Desde un Bot

Lo más sencillo es el say que incluye el SDK:

g.say(hero, "¡Conmigo, a la carga!", seconds=4)

Arranque e interfaz

Lo más cómodo: el “Centro de control” de la página de inicio de Farsight. Primero pulsa “LLM local → Iniciar y cargar el modelo” (el servicio local de LM Studio + cargar en la VRAM el modelo configurado) y después “Burbujas de diálogo → Iniciar”. En las tarjetas puedes ver el log, detener y reiniciar.

La interfaz está en la página “Burbujas de diálogo” de la barra lateral izquierda de Farsight: hacer hablar a unidades (elegir la unidad, escribir el texto, ajustar el estilo, conversar con el modelo), tertulia de campesinos, diálogos de cámara, reacciones a la partida y ajustes del modelo; todo actúa sobre la instancia elegida en la barra superior.

También desde la línea de comandos:

python speech/speak_launch.py              # arranca el servicio del modelo local + carga y precalienta el modelo + arranca la API de burbujas
python speech/speak_launch.py --restart    # reinicia la API tras cambiar el código
python speech/speak_launch.py --stop       # detiene la API y descarga el modelo de la VRAM

Cada paso es “si ya está, se omite”, así que ejecutarlo varias veces no tiene efectos secundarios.

API HTTP

Por defecto, http://127.0.0.1:8872/ (el puerto está en ports.speech de openwar3.json); cualquier programa puede llamarla.

Hacer hablar a una unidad POST /api/say

{
  "inst": 16,
  "bubbles": [
    { "unit": "0x14A12614", "name": "Rey de la Montaña", "text": "¡Conmigo, a la carga!" },
    { "unit": "0x14A12924", "name": "Archimago", "text": "Yo lanzo la Ventisca.",
      "style": { "font_px": 26, "text_color": "#FFE080", "bg_color": "#C0102040" } },
    { "screen": [960, 110], "key": 1, "name": "Narrador", "text": "La primera oleada de orcos llega en 30 segundos.",
      "style": { "tail": false, "type_ms": 0 } },
    { "world": [-4684, 2644], "key": 2, "text": "Punto de reunión", "style": { "font_px": 16 } }
  ]
}
CampoDescripción
unit / world / screenUno de los tres: sigue a una unidad (si tiene barra de vida, justo encima de ella) / coordenadas del mapa / píxeles de pantalla (para el narrador)
nameQuién habla, en la primera línea; texto libre, no tiene por qué ser esa unidad
textTexto, con salto de línea automático
duration_msCuánto tiempo se muestra; 0 = automático, 3 ~ 5 segundos
keyNúmero de una burbuja de mundo / pantalla; un mensaje nuevo con la misma key sustituye al anterior
updateSi ya existe esa burbuja, solo cambia el texto sin reiniciar el temporizador (para streaming)
stylefont_px, max_width_px, text_color, bg_color, border_color, tail, side_px, opacity, type_ms, font…

Como máximo 32 burbujas a la vez; el coste medio por fotograma es de unos 0.1 ~ 0.2 ms.

Conversar con un modelo local POST /api/chat

{
  "inst": 16, "unit": "0x14A12614", "name": "Rey de la Montaña",
  "persona": "Eres Muradin Barbabronce, el Rey de la Montaña de Warcraft: campechano y amante de la cerveza. Responde con una o dos frases coloquiales, de no más de 40 caracteres.",
  "message": "Hay un grupo de ogros delante, ¿cargamos o no?",
  "stream": true
}

Devuelve {"reply": "...", "first_token_ms": 283, "total_ms": 342}, y para entonces la respuesta ya está sobre la cabeza de esa unidad. Cada unidad recuerda las últimas 6 rondas de conversación.

Otros

EndpointDescripción
GET /api/instancesPartidas en curso
GET /api/units?inst=16&mine=true&heroes=trueLista de unidades (con nombre en chino, coordenadas y vida)
POST /api/clearBorra una burbuja o todas
GET /api/llm, POST /api/llmVer / cambiar la configuración del modelo (base_url, model, max_tokens, temperature)
POST /api/banterTertulia de campesinos: los trabajadores de la base se quejan por turnos según su personaje, con una presentación al inicio (todos los datos de la partida son reales)
POST /api/camtalkDiálogos de cámara: los héroes y sus acompañantes que salen en cámara conversan según su identidad
POST /api/eventsReacciones a la partida: empieza un combate, termina, cae un héroe, se sube de tier, destruyen un edificio… solo hablan cuando pasa algo

Cómo elegir un modelo local

Medido en una RTX 5090 (5 frases de diálogo del juego):

ModeloVRAMVelocidadUna respuestaConclusión
Qwen3.6-35B-A3B (MoE, solo 3B activos por paso), Q4, sin razonamiento20.6 GBAprox. 142 token/sAprox. 0.3 s (primer token en aprox. 0.27 s)Recomendado: rápido y con un rol natural en chino
gpt-oss-20b (MXFP4), razonamiento low11.3 GBAprox. 280 token/s0.3 ~ 0.8 sSi vas justo de VRAM; en chino, algo plano
Qwen3.6-27B (denso), Q417.2 GBAprox. 39 token/sA los 5.5 s seguía pensandoNo sirve para diálogo en tiempo real
  • La velocidad depende de los “parámetros activos por paso”, no del total: el MoE de 35B solo activa 3B y es 3 ~ 4 veces más rápido que el denso de 27B.
  • Hay que desactivar el “razonamiento”: si no, todos los tokens se van en pensar y no responde nada.
  • Las burbujas escriben unos 22 caracteres por segundo, así que la velocidad de generación ya no es el cuello de botella; lo que de verdad marca la experiencia es la latencia del primer token.

Que los diálogos parezcan reales

Pasa siempre al modelo datos reales de la partida (partidas jugadas, victorias y derrotas, tropas, recursos) y dile claramente “usa solo estos hechos”. En las pruebas, sin esa restricción, el modelo se inventaba combates que nunca ocurrieron.