Inicio / Artículos

Todo es un plugin: el diseño de Cordis detrás de DeepSeek Harness

Cómo Build de DeepSeek crea un entorno de ejecución para agentes a partir de plugins, utilizando ideas de Cordis como efectos reversibles, dependencias reactivas y un bucle de agente reemplazable.

Editor: REACTAPP.TOP19 min de lectura4403 palabras

Fuentes: DeepSeek Quietly Built a Different Kind of AI Agent Framework

La receta habitual y sus puntos débiles

La mayoría de los frameworks para agentes comienzan con los mismos componentes: un modelo de lenguaje, unos pocos herramientas, una conversación almacenada y un bucle que abarca todo ello. El esquema a continuación muestra su estructura.

User asks something
      ↓
LLM thinks
      ↓
LLM calls a tool
      ↓
Tool returns data
      ↓
LLM continues
      ↓
Final answer

Por definición, eso ya se considera un agente. Los problemas comienzan cuando el sistema crece. Se acumulan requisitos: múltiples proveedores de modelos, herramientas del navegador, ejecución desde la shell, servidores MCP, flujos de aprobación, memoria, telemetría, autenticación, diferentes interfaces de usuario, agentes en segundo plano, comandos personalizados, permisos y recarga en tiempo real. Pronto cada parte depende de las demás. Cambiar el proveedor de modelos afecta al ciclo del agente, eliminar un plugin deja listeners de eventos pendientes, y recargar una herramienta puede dañar a los agentes que se están ejecutando en ese momento. Los plugins también dependen de servicios que pueden aparecer o desaparecer durante su ejecución.

DeepSeek Harness aborda el problema de manera muy diferente. Su idea central suena casi demasiado simple: todo es un plugin, incluidas las partes que la mayoría de los frameworks consideran fundamentales.

Bajo todo esto existe un marco llamado Cordis, que surgió de un artículo de investigación titulado “Un paradigma de programación para la composibilidad espaciotemporal”. Ese artículo es interesante porque el proyecto no es simplemente otro sistema para organizar módulos de JavaScript; aborda una cuestión de ingeniería más profunda: ¿cómo pueden los componentes unirse, separarse, reemplazarse entre sí y cambiar las dependencias en un programa en ejecución sin que el entorno de ejecución caiga en el caos?

Qué es un harness

DeepSeek Harness, abreviado como dsh, es un harness de agente de código abierto lanzado por DeepSeek. Un harness es la infraestructura que rodea al modelo. Ya sea que el modelo sea de DeepSeek, Claude, GPT, Gemini o uno local, el harness se encarga de todo lo relacionado con él.

                    ┌───────────────┐
                    │      LLM      │
                    └───────┬───────┘
                            │
                 ┌──────────▼──────────┐
                 │    Agent Harness    │
                 │                     │
                 │ Prompt construction │
                 │ Tool execution      │
                 │ Session history     │
                 │ Permissions         │
                 │ Streaming           │
                 │ Agent lifecycle     │
                 │ Model adapters      │
                 └─────────────────────┘

Esta diferencia es muy importante. Por lo general, la atención se centra en cuán inteligente es el modelo, pero crear algo como Claude Code, Codex, Cursor o un agente autónomo que funcione durante mucho tiempo demuestra que el entorno de ejecución es igualmente crucial. DeepSeek Harness gestiona ese entorno de ejecución como un conjunto de plugins compuestos dinámicamente, y no como una sola aplicación monolítica; esta decisión proviene directamente de Cordis.

Un acertijo de dependencias

Imagínese crear un agente de programación a partir de tres plugins.

GitPlugin
TerminalPlugin
CodingAgent

El agente de codificación necesita Git y Terminal. Al iniciar, el enfoque más sencillo es cargarlos en orden.

load Git
load Terminal
load CodingAgent

Es sencillo, y la mayoría de los frameworks de inyección de dependencias lo gestionan bien. La pregunta más complicada es qué ocurre si el complemento de Git desaparece mientras el programa está en ejecución. Pudo haber sido actualizado, su configuración podría haber cambiado, algún usuario podría haberlo desactivado, o podría haberse implementado otra solución en su lugar. En ese caso, el entorno de ejecución debe detener todo lo que dependa de ese servicio.

CodingAgent
   ↓ -------->depends on
GitPlugin

Si Git desaparece, el agente de programación no debería seguir actuando como si existiera. Y si más tarde aparece otro proveedor de Git, quizás el agente debería volver a funcionar. Aquí se esconden dos problemas separados, que el artículo de Cordis denomina composabilidad temporal y composabilidad espacial.

Composabilidad temporal: salir de forma ordenada

Supongamos que un complemento se carga y registra un oyente de eventos.

events.on("message", handler)

Más tarde, el plugin se descarga, y la pregunta es qué sucede con el listener. Si nada lo elimina, el plugin desaparece en nombre mientras que una parte de él sigue funcionando. Además, es posible que el plugin haya iniciado temporizadores, abierto conexiones a bases de datos, registrado comandos, modificado el estado compartido, añadido herramientas, adjuntado controladores de eventos y lanzado procesos en segundo plano. Descargarlo implica revertir cada uno de esos pasos.

Esto es composibilidad temporal: un componente que se une al sistema debe dejarlo en condiciones de deshacer completamente lo que ese componente hizo. Cordis trata tales cambios como efectos reversibles.

Efectos sin jerga

Un efecto es simplemente algo que tu código hace y que modifica el mundo exterior o el estado en tiempo de ejecución. Tomemos como ejemplo el registro de una herramienta.

toolRegistry.add("search", searchTool)

Antes de la llamada, el registro se ve así.

tools = {}

Después, contiene la nueva herramienta.

tools = {
    search: searchTool
}

La operación de avance es una suma.

add(searchTool)

Pero Cordis quiere algo más. Desea que la operación tenga su propio inverso. Conceptualmente, un efecto empareja una acción con su deshacer.

Effect:
     do:   add search tool
     undo: remove search tool

Sin notación matemática, la idea se expresa de la siguiente manera.

state --effect--> new state
new state --inverse--> original state

Esa inversión es el elemento clave, y Cordis denomina a todo este conjunto un efecto reversible.

Cada cambio conlleva su proceso de limpieza

Cordis expone esto a través de un patrón similar al siguiente, en el que una función aplica un cambio y devuelve la función que lo deshace.

ctx.effect(() => {
    registerSomething()
    return () => {
        unregisterSomething()
    }
})

El tiempo de ejecución aplica el efecto, almacena la función de limpieza y la llama más tarde cuando el plugin deja de funcionar.

apply effect
      ↓
store cleanup function
      ↓
plugin runs
      ↓
plugin unloads
      ↓
run cleanup

Cuando un complemento contiene varios efectos, estos se aplican en un único orden.

effect A
effect B
effect C

Luego las operaciones de limpieza se ejecutan en orden inverso, como cuando se despliega una pila.

undo C
undo B
undo A

¿Por qué invertir? Considere un plugin que comienza de esta manera.

1. open database
2. start service using database
3. start agent using service

Apagarlo en el mismo orden sería peligroso, ya que la base de datos se cierra mientras aún hay procesos que la utilizan.

close database
stop service
stop agent

El orden seguro es lo opuesto.

stop agent
stop service
close database

Eso es último en entrar, primero en salir.

Last In
First Out

Cordis integra este comportamiento en la gestión del ciclo de vida de los componentes.

La limpieza inversa por sí sola no es suficiente

Aquí el artículo se vuelve más interesante. Supongamos que el Plugin A modifica un valor compartido, luego el Plugin B modifica ese mismo valor, y después A intenta restaurar su estado original. Eso podría borrar la modificación realizada por B.

Initial: x = 10

Plugin A: x = 20

Plugin B: x = 30

Plugin A unloads: restore x = 10

B sigue estando activo, pero su estado ya no existe. Por lo tanto, los efectos reversibles solo son seguros bajo ciertas suposiciones de independencia, que el artículo formaliza como independencia de efectos. De forma informal, dos efectos son independientes cuando aplicar o revertir uno no invalida el significado del otro. El hecho de que los diferentes herramientas actúen en ubicaciones distintas suele cumplir con este requisito.

Plugin A registers tool "search"
Plugin B registers tool "calculator"

Sustituir la misma configuración probablemente no sirva de nada.

Plugin A replaces configuration X
Plugin B also replaces configuration X

Esta distinción vuelve a aparecer más adelante cuando se trata del sistema de contexto de Cordis.

Composabilidad espacial: saber qué necesita un complemento

La composabilidad temporal pregunta si un componente puede eliminarse de forma segura. La composabilidad espacial pregunta si el sistema comprende de qué depende el componente. Imaginen este complemento.

CodingAgent

Necesita tres cosas.

LLM
ToolRegistry
SessionStore

El código tradicional podría recurrir a variables globales.

global.llm
global.tools
global.sessions

En ese caso, la arquitectura no posee conocimiento real sobre las dependencias. Cordis las hace explícitas. Conceptualmente, un plugin declara qué elementos inyecta.

inject: [
    "llm",
    "tools",
    "sessions"
]

Ahora el entorno de ejecución puede ver los requisitos del plugin como un árbol.

CodingAgent
    ├── requires LLM
    ├── requires ToolRegistry
    └── requires SessionStore

Eso le permite reaccionar cuando esos servicios cambian.

Dependencias reactivas

Puede ser la mejor idea en Cordis. Supongamos que un componente necesita un LLM.

LLM

Al principio, no existe ninguno.

LLM unavailable

Por lo tanto, el componente no puede activarse. Más tarde, se carga un plugin de proveedor que proporciona uno.

OpenAI Plugin loads
      ↓
provides LLM
      ↓
dependency becomes satisfied
      ↓
component activates

Si se elimina ese proveedor, la funcionalidad desaparece con él.

OpenAI Plugin unloads
      ↓
LLM disappears
      ↓
dependency becomes unsatisfied
      ↓
dependent component unloads

Cuando se carga otro proveedor, la capacidad vuelve a estar disponible.

DeepSeek Plugin
      ↓
provides LLM
      ↓
component becomes active again

Por lo tanto, las dependencias no se verifican una sola vez al iniciar. Son reactivas, y por eso el artículo habla de coefectos reactivos.

Efectos versus coefectos

La terminología puede parecer compleja, pero la distinción es sencilla. Un efecto describe algo que un componente modifica, fluyendo desde el complemento hacia el entorno.

Plugin → Environment

Los ejemplos incluyen estas acciones.

register tool
open database
subscribe to event
start timer

Un coefecto describe algo que el componente requiere de su entorno, fluyendo en la dirección opuesta.

Environment → Plugin

Ejemplos típicos son estas necesidades.

need an LLM
need a logger
need session storage
need a tool registry

Al combinarlos, los requisitos fluyen en una sola dirección.

requirements
Environment ───────────────────> Plugin

mientras los efectos fluyen de vuelta.

Environment <─────────────────── Plugin
                    effects

Esa es la idea básica detrás de Cordis. Un componente indica qué necesita, qué proporciona y qué efectos reversibles realiza.

I NEED these services.
I PROVIDE these services.
I PERFORM these reversible effects.

El tiempo de ejecución coordina entonces el ciclo de vida.

De dónde proviene “espaciotemporal”

Ahora el título del artículo tiene sentido. La composabilidad espacial se refiere a las relaciones entre los componentes.

A depends on B
B provides X
C depends on X

La composibilidad temporal se refiere a cómo los componentes evolucionan a lo largo del tiempo.

load
activate
replace
deactivate
unload
reload

Juntos forman toda la filosofía.

Spatiotemporal composability = dependency-aware composition + reversible lifecycle changes

El objeto de contexto

DeepSeek Harness no pasa docenas de servicios a través de constructores manualmente. Los complementos interactúan mediante un contexto compartido, que generalmente se denomina de esta manera.

ctx

Conceptualmente, muestra los principales servicios.

ctx
├── llm
├── tools
├── sessions
├── agents
├── agentLoop
├── logger
└── ...

Pero ctx es más que un objeto de JavaScript repleto de variables globales. A través de él, los plugins encuentran sus dependencias, ofrecen servicios, registran efectos, escuchan eventos, aíslan recursos, interceptan el acceso a servicios y coordinan sus ciclos de vida; por eso Cordis presenta el contexto como una abstracción unificadora.

De la teoría al proyecto real

DeepSeek Harness expone sus principales subsistemas a través de ese contexto compartido.

ctx.sessions
ctx.systemPrompt
ctx.tools
ctx.agents
ctx.agentLoop
ctx.llm

Observe algo inusual: incluso el bucle de agentes es simplemente un servicio.

ctx.agentLoop

Por lo tanto, el bucle puede ser reemplazado, y no se trata de código sagrado del framework. Esto conduce al principio rector del diseño: no existe un núcleo privilegiado al que todas las funcionalidades deban adaptarse.

Por qué es importante el bucle como plugin

La mayoría de los frameworks para agentes contienen un bucle central similar a este.

while not finished:
response = model(history)
    if response.tool_call:
        result = execute_tool(response.tool_call)
        history.append(result)
    else:
        return response

Luego, cada función avanzada comienza a modificarlo. ¿Aprobación antes de ejecutar comandos? Se corrige el bucle. ¿Rastreo? Se corrige el bucle. ¿Reintentos, delegación multiagente, otro backend de inferencia? Se corrige el bucle. Al final termina siendo un centro con herramientas, modelos y memoria todos conectados a él.

                    Agent Loop
                   /    |    \
                  /     |     \
             tools    models   memory
             /          |        \
        approvals     retries    tracing

El bucle se convierte en el objeto principal de la aplicación. DeepSeek Harness evita esto al permitir que el bucle participe en la misma arquitectura de plugins que todos los demás componentes.

La estructura de una interacción del agente

En el fondo, una interacción pasa por etapas más o menos como estas.

turn/start
    ↓
claim incoming user input
    ↓
assemble prompt + available tools
    ↓
agent/pre-step
    ↓
step/start
    ↓
record user message
    ↓
reconstruct conversation history
    ↓
send model request
    ↓
stream assistant response
    ↓
tool call?
   │
   ├── yes → execute tool
   │          record result
   │          continue another step
   │
   └── no  → finish
    ↓
step/end
    ↓
turn/end

Lo importante es que cada etapa expone eventos y puntos de conexión de servicios, lo que permite a los complementos participar sin tener que reescribir todo el entorno de ejecución.

Eventos en modelo en cascada como middleware

Cordis admite varios modos para ejecutar eventos. Un modo útil, el en cascada, funciona como middleware. Imagine una solicitud que pasa por un pipeline.

request
   ↓
Plugin A
   ↓
Plugin B
   ↓
Plugin C
   ↓
  LLM

El plugin B puede envolver la ejecución con un manejador que recibe el siguiente paso.

async function handler(next) {
    console.log("before")
    const result = await next()
    console.log("after")
    return result
}

La ejecución se anida entonces, con cada plugin actuando antes y después de la llamada interna.

Plugin A before
    Plugin B before
        Plugin C before
            LLM
        Plugin C after
    Plugin B after
Plugin A after

Eso incluye permisos, reescritura de solicitudes, telemetría, caché, enrutamiento de modelos, verificaciones de seguridad e inyección de contexto; ninguno de estos elementos necesita estar integrado directamente en el adaptador del modelo.

El modelo se encuentra detrás de una separación funcional

El conjunto de herramientas también abstracta al LLM, por lo que un agente no necesita comprender cada SDK del proveedor; simplemente interactúa con un único servicio.

Agent
  ↓
ctx.llm
  ↓
Model Adapter
  ↓
Provider API

Un proveedor podría implementarlo para la API de DeepSeek.

DeepSeek API

Otro trata sobre OpenAI.

OpenAI

Un tercero se refiere a Anthropic.

Anthropic

El resto de la arquitectura del agente aborda la misma capacidad del LLM. La documentación de Harness a veces denomina esto una “costura de capacidades”, compuesta por una definición de servicio y un proveedor de servicios.

Service Definition
   ↓
Service Provider
   ↓
Consumer

Para el LLM, esa cadena va desde la interfaz hasta un proveedor específico.

LLM interface
     ↓
OpenAI provider
     ↓
Agent loop

Por lo tanto, agregar un proveedor de modelos no implica necesariamente reescribir el entorno de ejecución. Se proporciona otra implementación de la misma funcionalidad.

Sesiones basadas en eventos

Las conversaciones no se almacenan en el sistema como un único array mutable que sirva como fuente de verdad. En su lugar, las sesiones se guardan como un flujo de eventos de solo inserción, más o menos de esta manera.

turn/start
user/message
assistant/chunk
assistant/chunk
assistant/message
tool/call
tool/result
assistant/message
turn/end

Se puede reconstruir el historial del modelo a partir de esos eventos, lo que convierte al registro de la sesión en la fuente de verdad.

Session log = source of truth

Eso reemplaza la alternativa común.

Mutable chat history = source of truth

Por qué los agentes se benefician del origen de eventos

Los agentes se han vuelto lo suficientemente complejos como para que almacenar una simple lista de mensajes a menudo no sea suficiente.

[
  {"role": "user", "content": "..."},
  {"role": "assistant", "content": "..."}
]

El depuración puede requerir conocer qué herramienta se utilizó y cuál fue invocada, qué modelo escribió la respuesta, qué prompt del sistema se aplicó, dónde comenzó una solicitud, dónde finalizó un paso de razonamiento, qué configuración estaba activa, si se intentó nuevamente una llamada y qué salida en tiempo real regresó. El sistema registra eventos estructurados de la sesión para que todo lo que el modelo pudo ver pueda ser reconstruido. Uno de los principios en su diseño establece que todo lo visible para el modelo debe poder recuperarse del registro. En lugar de especular sobre por qué el agente actuó de esa manera, se puede reconstruir la solicitud que lo motivó.

Fibras y estado del ciclo de vida

Cordis representa los componentes en ejecución mediante una Fiber. Esto no tiene nada que ver con las fibras del sistema operativo. Una Fiber de Cordis es una instancia de componente en tiempo de ejecución, y pasa por estados como estos.

PENDING
   ↓
LOADING
   ↓
ACTIVE
   ↓
UNLOADING
   ↓
DISPOSED

También existen estados de fallo. Lo que es más importante, una fibra registra varios datos sobre sí misma.

what dependencies I need
what providers currently satisfy them
what effects I have created
what cleanup functions belong to me

Cuando las dependencias cambian, la fibra puede actualizar su ciclo de vida.

Por qué es importante la identidad del proveedor

Supongamos que un componente depende de un servicio de base de datos.

database

Por el momento, es suministrado por un único proveedor.

DatabaseProvider A

Durante una recarga en tiempo real, es reemplazado por otro.

DatabaseProvider B

Incluso cuando ambos devuelven objetos JavaScript equivalentes, Cordis puede distinguir las identidades del proveedor. La razón es que el significado de la dependencia no se limita únicamente a que exista un valor.

"value exists"

También puede significar que el valor proviene de una instancia de componente diferente.

"this value came from a different component instance"

Por lo tanto, cambiar de proveedor puede hacer que los componentes dependientes se recarguen correctamente, lo cual evita una gran cantidad de errores por referencias obsoletas.

Descarga en orden de dependencia

Considere una cadena en la que un plugin proporciona la base de datos.

Plugin A provides database

Un segundo complemento depende de la base de datos, y un tercero depende del segundo.

Plugin B depends on databasePlugin C depends on Plugin B

La cadena de dependencias se ve así.

A
↓
B
↓
C

Ahora se elimina el primero. Un entorno de ejecución ingenuo podría cerrar la base de datos de inmediato, aunque los otros dos aún estén en ejecución. En su lugar, Cordis coordina el grafo de dependencias. Comienza la descarga.

A begins unloading

Luego, los dependientes se liberan en orden antes de que el original complete su limpieza.

↓ mark A unavailableB notices dependency disappeared↓ B unloadsC notices B disappeared↓ C unloadsthenA completes cleanup

El orden es de suma importancia, ya que evita que los dependientes utilicen algo que ya está medio destruido.

Aislamiento: un nombre, mundos separados

Un único espacio de nombres para servicios globales puede resultar limitante. Imagine dos agentes que necesitan bases de datos diferentes.

Agent A → Database A
Agent B → Database B

Ambos siguen pidiendo lo mismo.

database

Cordis admite el aislamiento, por lo que la resolución de servicios funciona desde una pareja.

service name + isolation realm

y no solo por un nombre.

service name

Conceptualmente, cada reino asigna el mismo nombre a su propia instancia.

Realm A: database → DB_A
Realm B: database → DB_B

Por lo tanto, la misma dependencia lógica puede resolverse de manera diferente según el contexto, lo cual es útil en sistemas multiinquilinos, con múltiples agentes, entornos de pruebas, plugins aislados y espacios de trabajo separados.

Intercepción

Cordis también puede regular el acceso a los servicios mediante metadatos. Considere un registro de herramientas donde un agente administrador podría ver estas herramientas.

shell
browser
filesystem
deploy

mientras que un agente con restricciones ve un conjunto más pequeño.

browser
filesystem-readonly

En lugar de crear un registro completamente separado, el acceso puede ser interceptado según el contexto.

ctx + metadata
      ↓
service resolution
      ↓
modified view of capability

Ese es un patrón sólido para los sistemas de políticas.

Sustitución segura de módulos en tiempo real

La recarga en tiempo real parece sencilla.

file changed
    ↓
reload module

Durante la ejecución de un plugin, las cosas se complican. ¿Qué pasa si el nuevo módulo contiene un error? ¿Y qué ocurre con la versión antigua que aún funciona? El cargador Cordis admite un comportamiento de recarga que funciona como una transacción.

  working plugin
        ↓
    code changes
        ↓
  attempt new import
        ↓
     success?
 ┌───────┴───────┐
yes               no
 ↓                 ↓
replace         rollback
plugin          old module

Esto encaja perfectamente con los efectos reversibles. Un complemento que cuenta con un ciclo de vida claro, un conjunto de dependencias y un procedimiento de limpieza es mucho menos problemático para reemplazarlo.

Un estudio de caso del mundo real

Cordis no se creó únicamente para DeepSeek Harness. El artículo analiza Koishi, un marco de chatbot de código abierto desarrollado sobre él, que cuenta con un ecosistema comunitario formado por miles de complementos. Un verdadero ecosistema hace que la composición dinámica sea mucho más convincente que diez módulos creados manualmente. Sin embargo, el artículo es cauteloso y no afirma que pruebas controladas hayan demostrado grandes mejoras en rendimiento o productividad. Se necesita más evaluación.

No todo se puede deshacer

Aquí la teoría se encuentra con la realidad. Algunos efectos en tiempo de ejecución son realmente reversibles, como abrir y cerrar un archivo.

open file
→ close file

Otras también se combinan igual de bien.

allocate memory
→ free memorystart worker
→ terminate workerregister event listener
→ unregister listener

Luego está enviar un correo electrónico.

send email

¿Cuál sería su inverso?

unsend email?

Por lo general, eso es imposible, y lo mismo ocurre con un grupo de otras acciones.

send HTTP request
transfer money
publish message
delete external data

Después de que algo afecte al mundo exterior, puede que no exista una verdadera reversión. La mejor opción disponible es la compensación.

payment → refund
message → follow-up correction
database mutation → compensating transaction

La compensación no es lo mismo que la inversión matemática. El documento reconoce explícitamente esta distinción, y Cordis funciona mejor para los efectos que el tiempo de ejecución puede controlar y recuperar realmente.

Cordis confía en su limpieza

Supongamos que un complemento inicia un servidor dentro de un efecto y devuelve una función de limpieza.

ctx.effect(() => {
    startServer()
    return () => {
        // supposedly cleanup
    }
}))

Cordis no tiene forma de demostrar que la operación de limpieza realmente invierte el proceso inicial. El modelo formal establece que lo inverso debe devolver el entorno a su estado anterior, pero la implementación depende de que el programador escriba un desestructurador válido. Por lo tanto, asociar un efecto con una operación de limpieza constituye una disciplina en sí misma.

effect + cleanup

No se trata de una verificación formal automática, y esa es una diferencia importante.

Por qué esto es adecuado para los agentes de IA

Las aplicaciones tradicionales permanecen bastante estables después de su inicio. Los agentes, en cambio, van en dirección opuesta. Los sistemas de agentes del futuro podrían realizar continuamente operaciones como estas.

install tool
remove tool
change model
spawn agent
kill agent
load skill
reload code
switch permissions
connect service
disconnect service
modify environment

Imagínese un agente que decide que necesita soporte de GitHub y carga una capacidad de GitHub, para luego descartarla cuando ya no es útil o reemplazar una implementación que falló. Un sistema así requiere semánticas de composición en tiempo de ejecución mucho más robustas que las que ofrece un inyector de dependencias convencional. Sin ellas, un agente en ejecución prolongada podría acumular elementos sobrantes.

orphan listeners
stale tools
dangling processes
broken dependencies
zombie services
duplicated handlers

Cordis intenta convertir la composición en tiempo de ejecución en un concepto de software de primera clase.

Por qué el diseño parece elegante

Dado que Cordis se encarga de la composición, DeepSeek Harness puede modelar casi todo de la misma manera.

Model provider     → plugin
Tool               → plugin
Agent loop         → plugin
Session store      → plugin
UI                 → plugin
Telemetry          → plugin
Approval system    → plugin
Sandbox            → plugin
Credential store   → plugin

Las nuevas funciones no se convierten en casos especiales dentro del framework. Se vuelven más participantes en un mismo sistema de composición.

Perfiles y paquetes

Si todo es un plugin, nadie quiere ensamblar cincuenta de ellos a mano cada vez. Por eso, el framework añade composiciones de nivel superior. Un perfil describe una configuración de sistema con nombre, y un paquete distribuye grupos de plugins junto con su configuración. Un paquete base podría verse así.

Base Bundle
├── model adapters
├── tools
├── sessions
├── sandbox
├── approvals
├── credentials
├── settings
└── telemetry

Otro paquete podría incluir una aplicación web.

Web Application

Otro diferente podría generar un agente sin cabeza.

Headless Agent

La arquitectura mantiene su carácter modular sin dificultar el uso cotidiano.

Cinco ideas que vale la pena recordar

Aun cuando todo lo demás se olvide, estos cinco puntos son los que deben quedar en la memoria.

Los efectos reversibles son lo más importante: cualquier cambio que realice un componente debe permitir revertirlo.

change + inverse

El segundo son los coefectos reactivos. Los componentes declaran qué necesitan, y el tiempo de ejecución responde cuando las dependencias aparecen, desaparecen o cambian.

dependency available → activate
dependency gone      → deactivate

El tercero es el contexto. Los efectos, las dependencias y el acceso a los servicios operan dentro de un único contexto de ejecución unificado.

ctx

Cuarto está el cálculo de componentes dinámicos, que describe un componente en función de lo que necesita, lo que proporciona y lo que cambia, brindando al tiempo de ejecución la estructura necesaria para razonar sobre el cargado y descargado.

Dependencies + Provisions + Effects

El quinto es la composibilidad espacio-temporal, que combina los aspectos espaciales y temporales.

spatial:
who depends on whom?
+
temporal:
how can components safely enter and leave?

En una sola frase, se trata de un cambio reversible sumado a una dependencia reactiva.

Por qué es importante más allá de un proyecto

La mayoría de los frameworks actuales para agentes aún asumen una aplicación bastante estática: se inicia el programa, se cargan las herramientas, se carga el modelo, se ejecuta el agente y luego se detiene. Los agentes autónomos buscan entornos de ejecución que puedan modificarse a sí mismos mientras permanecen activos. Imagine un agente capaz de seguir una secuencia como esta.

discover a capability
install it
use it
upgrade it
replace it
remove it
continue running

En ese punto, la gestión del ciclo de vida de los complementos deja de ser una característica arquitectónica agradable y se convierte en una infraestructura fundamental. Eso es lo que hace interesante el proyecto. El modelo no es la parte más novedosa; lo es el entorno de ejecución que lo rodea.

La lección más profunda

La arquitectura de software suele centrarse en la composición en un momento determinado. Se ensamblan las partes y se ejecuta el programa.

A + B + C

Cordis plantea preguntas más difíciles.

What if A disappears?
What if B is replaced?
What if C suddenly gains a dependency?
What if another implementation of A appears?
What if all of this happens while the system is running?

Eso cambia por completo el problema. Los componentes dejan de ser bloques estáticos y actúan como participantes vivos: llegan, establecen relaciones, modifican su entorno, dependen de otros y finalmente se van.

La conclusión

DeepSeek Harness puede parecer simplemente otro marco de agentes, y su atractivo no radica en que pueda llamar a un LLM o ejecutar herramientas, algo que ya hacen muchos otros marcos. La idea principal es que el propio sistema de composición constituye la arquitectura. Cada capacidad es un componente, cada dependencia es explícita, cada efecto controlable sabe cómo limpiarse por sí mismo, los componentes responden cuando cambian las dependencias, e incluso el bucle del agente puede ser reemplazado y no es código de marco intocable. El principio rector es convertir cada capacidad en un componente, hacer que cada dependencia sea explícita y que cada efecto cuente con su propia función de deshacer.

En aplicaciones ordinarias, se trata de una arquitectura de plugins ordenada. En agentes de IA que funcionan durante mucho tiempo y se modifican a sí mismos, esto puede ser aún más importante. Si los agentes terminan cambiando su propio entorno de ejecución, el software necesitará un método basado en principios para realizar esos cambios sin dañar gradualmente el entorno en el que opera. Ese es el problema que DeepSeek Harness, y la investigación de Cordis detrás de él, buscan resolver.