Skip to content

CORE-22 — Política de transición de la API pública SymfonicAgent (v11)

Estado y autoridad

Esta es una política propuesta para que el owner decida. No aprueba una deprecación, no cambia contratos y no autoriza retirar engine.py. La evidencia actual contradice cualquier anuncio de reemplazo completo:

  • symfonic.agent.SymfonicAgent sigue publicado en __all__ y su cargador perezoso lo resuelve desde symfonic.agent.engine (src/symfonic/agent/__init__.py:24, __getattr__).
  • El top-level symfonic.Agent sí es la fachada canónica nueva (src/symfonic/__init__.py:44, _ORIGINS), pero no es un alias de SymfonicAgent y no se exporta desde symfonic.agent.
  • El propio contrato de top-level afirma que SymfonicAgent es tier 1 y lo orienta para HMS, scopes, sesiones, checkpoints, resume, subagents y callbacks (src/symfonic/__init__.py docstring). Eso es una promesa vigente, no documentación apta para ignorar.
  • SymfonicAgent.run puede delegar al kernel, pero una invocación no admitida llega a _legacy_run_impl (src/symfonic/agent/engine.py:SymfonicAgent.run, _legacy_run_impl). FrameworkConfig() queda explícitamente fuera de la admisión por auto_hydrate=True (tests/agent/cutover/test_retrieval_bundle_admission.py: TestTheEnvelopeStaysClosedWithoutEvidence.test_the_stock_defaults_are_still_refused_without_a_bundle).

Conclusión: la API pública no puede marcarse deprecated ni sustituirse por Agent mientras no haya una decisión explícita y evidencia de paridad para cada contrato que publique.

Superficies públicas y mapeo nativo

Contrato de SymfonicAgent Evidencia viva Sustituto Agent actual Estado de mapeo
Construcción con FrameworkConfig, graph/vector/embedding backends, orchestrator, conversation manager y subagents engine.py:SymfonicAgent.__init__ Agent(model_provider, instructions, model, tools, capabilities, max_model_rounds) en facade.py:Agent.__init__ No equivalente uno-a-uno. Configuración y dependencias HMS requieren capabilities/roots nativos.
run(query, scope, callbacks, session_id, history, response_model, **state_overrides)AgentResponse engine.py:SymfonicAgent.run; agent/types.py:AgentResponse Agent.run(prompt, attachments, history, state, session_id, output_type)AgentResult (facade.py:Agent.run; facade_types.py:AgentResult) Parcial y con ruptura de firma/resultado. scope, callbacks y metadata no tienen sustituto directo.
stream(...)StreamChunk engine.py:SymfonicAgent.stream; agent/types.py:StreamChunk Agent.stream(...)AgentEvent (facade.py:Agent.stream; facade_types.py:AgentEvent) Parcial y con contrato de eventos distinto.
stream_typed, stream_text engine.py:SymfonicAgent.stream_typed, stream_text Ningún método homónimo en Agent Sin mapeo público demostrado.
resume, resume_interrupt y tokens de pausa engine.py:SymfonicAgent.resume, resume_interrupt Agent.continue_recovered(RecoveredContinuation) (facade_continuation.py) No equivalente público: exige recuperación/autenticación por host y no acepta el token legacy.
get_chat_model, load_plugin, validate_action, on_user_correction, flush_background_tasks, lifecycle async engine.py métodos públicos de SymfonicAgent Ningún método homónimo en Agent Sin sustituto público demostrado.
HTTP legacy /chat, /stream, /stream/typed, /resume agent/fastapi/router.py:create_agent_router Scaffold/roots kernel-native, no adaptador de firma equivalente probado Sin mapeo de transporte aprobado.

Un resultado con texto coincidente no prueba equivalencia: AgentResult usa text, mensajes tipados y stop_reason; AgentResponse publica, entre otros, final_response, memoria usada, session_id, delegación y activation_log (agent/facade_types.py:AgentResult; agent/types.py:AgentResponse).

Compatibilidad que debe conservarse o ser decidida

Errores y validación

La transición debe especificar por fila si conserva tipo, código, momento de fallo y representación HTTP. La ruta legacy expone SymfonicAgentError y el router traduce su .code a HTTP (agent/errors.py:SymfonicAgentError; agent/fastapi/router.py:_status_from_agent_error). La fachada nativa expone ConfigurationError y ContractViolationError (core/contracts/errors.py). Cambiar un error sin un adaptador es una ruptura de API y del transporte, incluso si la ejecución correcta es kernel-native.

Streaming

No se puede ofrecer una equivalencia implícita entre StreamChunk(event_type, data, timestamp, run_id) y AgentEvent(kind, index, text, tool_call, result, error, interrupt, stage) (agent/types.py:StreamChunk; agent/facade_types.py:AgentEvent). La política debe elegir y congelar uno de estos contratos por versión; un shim tendrá que proyectar explícitamente, incluidos done, error, cancelación, herramientas e interrupciones.

Pausa, resume y sesión

Agent.continue_recovered es intencionalmente host-owned: recibe una RecoveredContinuation, no un token ni scope elegido por el llamador (agent/facade_continuation.py:AgentContinuationMixin). Por tanto, ningún consumidor de resume/resume_interrupt puede migrar por sustitución textual. El owner debe decidir el protocolo de recuperación, autenticación, expiración y mapeo de errores antes de anunciar una fecha de deprecación.

Consumidores que la decisión debe cubrir

El inventario reproducible actual encuentra 36 archivos bajo src/symfonic con construcción/importación de SymfonicAgent, y 233 archivos bajo examples/tests con referencia. Los números son señales de alcance, no una prueba de todos los consumidores externos.

Rutas productivas confirmadas que no deben romperse por una deprecación solo de import:

  • agent/fastapi/router.py:create_agent_router y routers de memoria, privacidad, grafo y mantenimiento aceptan SymfonicAgent.
  • diagnostics/cli.py construye el agente para symfonic doctor; el runner y sus checks dependen de esa forma.
  • cli/chat.py construye SymfonicAgent para los modos HMS/domain/DSN.
  • agent/builder.py:AgentBuilder y el grafo de subagents preservan la forma legacy de construcción.

Antes de cualquier fecha, CORE-22 debe pedir además: telemetría de imports y fallbacks cuando esté disponible (CORE-24), búsqueda de distribuidores y adopters soportados, y una lista versionada de ejemplos/tests que representan cada contrato externo. La búsqueda interna no descubre paquetes de terceros.

Opciones de decisión para el owner

Opción A — Compatibilidad administrada (recomendada para evaluar)

Mantener from symfonic.agent import SymfonicAgent y sus métodos como shim documentado. Cada método delega solo a un target kernel que haya superado su contrato diferencial; si no, permanece compatible o rechaza de forma explícita según una decisión de fila. Añadir advertencia deprecable únicamente cuando la fila tiene sustituto público, guía de migración, telemetría y periodo de soporte definidos. La retirada física solo sigue a los gates de CORE-18/25.

Ventaja: no confunde disponibilidad de Agent con equivalencia. Coste: se mantiene el shim hasta cerrar todos los contratos publicados.

Opción B — Ruptura mayor explícita

En una major futura, retirar el import/métodos legacy y publicar un adaptador o guía de migración fuera de la API estable. Exige declarar expresamente que se rompen APIs de HMS, transporte, streaming y resume donde no haya equivalente. No es deprecación silenciosa y no es compatible con la promesa tier-1 actual sin un cambio de contrato aprobado.

Opción C — Mantener dos APIs soportadas

Promover Agent para composición nueva y conservar SymfonicAgent como API de alto nivel con lifecycle propio. Eliminar engine.py seguiría requiriendo reimplementar internamente todas sus superficies, pero no se prometería una migración de consumidor que el código aún no soporta.

Ninguna opción está aprobada en este documento.

Secuencia mínima si se elige una transición

  1. Congelar el catálogo de métodos y transportes de SymfonicAgent, con contrato observable, adopter representativo y owner por fila.
  2. Para cada fila, registrar target nativo, diferencial Agent/legacy, métrica de errores/fallback y resultado: preservar, adaptar, deprecar o romper.
  3. Implementar y probar adaptadores de resultado, streaming, errores y recuperación antes de emitir advertencias.
  4. Migrar primero composition roots propios (FastAPI, CLI, diagnostics, builder), manteniendo pruebas de compatibilidad de entrada/salida.
  5. Publicar una ventana de soporte con versión inicial, versión de advertencia, criterios de adopción y versión de eliminación. Estos cuatro valores deben venir de una decisión de release owner, no de este documento.
  6. Solo tras cero rutas legacy admitidas/fallback medido, contratos cerrados y gates de retiro satisfechos, evaluar eliminar el shim y engine.py.

Decisión exacta solicitada al owner

Registrar una sola resolución con:

  1. opción A, B o C;
  2. versión objetivo para advertir, duración de soporte y versión objetivo para ruptura/eliminación (si aplica);
  3. lista de contratos permitidos para adaptador versus contratos que se consideran ruptura explícita, especialmente streaming, resume, callbacks, scope/HMS y respuesta;
  4. política de tipo/código de error y proyección SSE/HTTP durante la ventana;
  5. owner y evidencia de paridad para cada fila antes de cambiar su estado.

Sin esos cinco datos, CORE-22 queda pendiente de decisión, y SymfonicAgent sigue siendo una API pública viva.

Verificación reproducible

rg -n "SymfonicAgent|__all__|def __getattr__" src/symfonic/agent/__init__.py src/symfonic/__init__.py
rg -n "^    async def (run|stream|stream_typed|resume|resume_interrupt)|^    def (get_chat_model|load_plugin)" src/symfonic/agent/engine.py
rg -n "^class Agent|^    async def run|^    def stream|continue_recovered" src/symfonic/agent/facade.py src/symfonic/agent/facade_continuation.py
rg -l "from symfonic\\.agent(?:\\.engine)? import SymfonicAgent|SymfonicAgent\\(" src/symfonic --glob '*.py'