All articles

Le contrôle d’accès et les agents

Amrltqt
··4 min read
Le contrôle d’accès et les agents

Considérant que les agents sont des systèmes utilisant des modèles de langage pouvant invoquer des programmes en autonomie, à un moment...


Le contrôle d'accès et les agents

Considérant que les agents sont des systèmes utilisant des modèles de langage pouvant invoquer des programmes en autonomie, à un moment donné on souhaite les laisser accéder à des systèmes dont l'accès est limité.

La vaste majorité des logiciels en ligne sont soumis à des contrôles pour vérifier que les utilisateurs qui s'y connectent ont bien le droit de consommer ou modifier leurs propres ressources.

Comment permettre à des agents d'accéder à ces ressources sans risques pour la sécurité?

Cet article ne va pas vous donner toutes les clés pour y arriver mais quelques bases pour démarrer et progresser sur le sujet.

Comme je travaille pour une marketplace en ligne, on va utiliser un petit exemple sur le thème des commandes

text
from demo.models import Order, User

USERS = [
    User(id=0, email="alpha@demo.com"),
    User(id=1, email="beta@demo.com"),
]

ORDERS = [
    Order(id=0, user_id=0, model="battery", quantity=4, price_cts=7499),
    Order(id=1, user_id=1, model="shampoo", quantity=1, price_cts=1000),
]

class MarketplaceService:
    def __init__(self, trusted_user_id: int):
        self.users = USERS
        self.orders = ORDERS

        if not self.get_user(trusted_user_id):
            raise ValueError("User does not exists")

        self.trusted_user_id = trusted_user_id

    def get_order(self, id: int) -> Order:
        order = next(filter(lambda o: o.id == id and o.user_id == self.trusted_user_id, self.orders), None)
        if not order:
            raise ValueError("Order not found")
        return order

    def get_user(self, id: int) -> User:
        user = next(filter(lambda u: u.id == id, self.users), None)
        if not user:
            raise ValueError("User id not found")
        return user

On va créer un agent pour gérer l'accès à cette donnée en langage naturel. De nombreux frameworks existent déjà, pour vous dire à l'heure où j'écris, aws vient d'en sortir un dans la même veine.

De mon côté après avoir créé ma propre interprétation basée sur le papier REACT: SYNERGIZING REASONING AND ACTING IN LANGUAGE MODELS j'ai fini par me retourner sur l'implémentation proposée par OpenAI dans son agents-sdk.

text
from textwrap import dedent

from agents import Agent
from demo.tools import find_order_by_id
from demo.models import DemoContext

agent = Agent[DemoContext](
    name="orders",
    instructions=dedent(
        """
            Help the user to find their order information in the database.
            Tool is available to help you retrieve the data.
        """
    ),
    model="gpt-4.1-nano",
    tools=[
        find_order_by_id
    ]
)

Pour sécuriser l'accès, il faut faire attention à la fonction déclarée dans tools. Elle permet de lire le contenu d'une commande via son identifiant. On peut imaginer d'autres manières d'accéder aux données, l'exemple sera toujours pertinent.

Vous noterez que l'agent prend en charge un *Context.*C'est un objet qui vit dans l'interpréteur python et qui ne sera pas partagé avec le modèle de langage.

Vous pouvez y attacher des informations sensibles et maîtriser sa mise à jour à deux moments:

  • à l'initialisation lorsque l'agent est instancié
  • dans les fonctions appelée par les modèles de langage via lefunction calling

C'est là que nous allons intervenir. Nous voulons sécuriser que l'utilisateur connecté ne puisse accéder qu'a ses informations. Ce que supporte le service décrit plus haut.

Pour transmettre au service l'identifiant de l'utilisateur, nous utiliserons le Context via un champ trusted_user_idqui sera disponible dans chaque fonctions appelée par l'agent. Pour ça il faut inspecter la description de la fonction appelée.

text
from textwrap import dedent
from agents import function_tool, RunContextWrapper

from demo.service import MarketplaceService
from demo.models import DemoContext

def get_service(trusted_user_id: int) -> MarketplaceService:
    return MarketplaceService(trusted_user_id)


@function_tool
async def find_order_by_id(wrapper: RunContextWrapper[DemoContext], id: int) -> str:
    """
    Find an order given its integer identifier

    Args:
        id (int): integer identifier of an order

    Returns:
        str: a representation of the order for the agent
    """

    user_id = wrapper.context.trusted_user_id
    service = get_service(user_id)

    try:
        order = service.get_order(id)
        price = round(order.price_cts / 100, 2)
        return dedent(
            f"""
                Order ID: # {order.id}
                Model: {order.model}
                Price: {price}
            """
        )
    except:
        return "Impossible to retrieve the order"

Vous pouvez voir que l'identifiant de l'utilisateur est extrait du Context et qu'il sert à récupérer le services en l'initialisant à la volée. Rien n'empêche de le récupérer autrement si nécessaire.

La fonction construite ainsi garantie qu'il est impossible de récupérer via le service les commandes d'un autre utilisateur. Qu'importent les techniques de jailbreak qui pourraient être utilisées par un utilisateur malveillant.

L'identifiant de l'utilisateur utilisé pour l'accès contrôlé n'est jamaismanipulé par le modèle de langage.

Pour mettre en musique les exemples ci-dessus vous pouvez utiliser cette fonction.

text
async def main(user_id: int):
    context = DemoContext(
        trusted_user_id=user_id
    )
    answer_1 = await Runner.run(
        starting_agent=agent,
        input="Give me the order 0",
        context=context
    )

    print(answer_1.final_output)

    answer_2 = await Runner.run(
        starting_agent=agent,
        input="Give me the order 1",
        context=context
    )

    print(answer_2.final_output)

Je laisse à vos soins le fait d'étendre cette approche. Si vous voulez retrouver le code utilisé dans cette démonstration vous pouvez le retrouver sur mon compte Github. Secure data access with openai agents-sdk

Stay in the loop

Get new articles delivered directly to your inbox. No spam, unsubscribe anytime.