Accès aux outils LangChain -- InjectedState et InjectedStore

Parfois, les outils ont besoin d'accéder à davantage d'informations contextuelles, telles que l'état actuel de la conversation, les données persistantes de l'utilisateur, etc.

LangChain utilise un mécanisme d'injection de dépendances pour permettre aux fonctions d'outil d'obtenir automatiquement ces informations.


InjectedState — Accéder à l'état de l'Agent dans les outils

Par défaut, les outils ne peuvent recevoir que les données fournies par le modèle via des paramètres. Mais parfois, les outils ont besoin de connaître le contexte de la conversation actuelle — comme l'historique des conversations précédentes, les informations déjà confirmées par l'utilisateur, etc.

InjectedStatePermet aux outils de lire directement l'état complet de l'Agent.

Exemple

from typing import Annotated, Any
from dotenv import load_dotenv
load_dotenv()

from langchain.tools import tool, InjectedState
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
from langchain.messages import HumanMessage


@tool
def remember_preference(
    preference: str,
    state: Annotated[dict[str, Any], InjectedState],
) -> str:
    """Mémoriser les préférences de l'utilisateur.

    Arguments :
preference: le contenu des préférences de l'utilisateur
state : l'état actuel de l'Agent automatiquement injecté par le système
    """

    # Obtenir l'historique des messages précédents depuis le state
    messages = state.get("messages", [])
    message_count = len(messages)

    # Il est possible de lire n'importe quel champ dans le state
    previous_prefs = state.get("user_preferences", "none")

    return (
        f"Préférence mémorisée : {preference}."
        f"(La conversation actuelle comporte {message_count} messages,"
        f"Préférence précédente : {previous_prefs})"
    )


# InjectedState est automatiquement injecté, l'Agent n'a pas besoin de passer ce paramètre
result = remember_preference.invoke({
    "preference": "dark theme",
    # state n'a pas besoin d'être transmis, il est automatiquement injecté par le framework
})
print(result)

Résultat de l'exécution :

已记住偏好: 暗色主题。(当前对话共 0 条消息,之前偏好: 无)

Utilisation d'InjectedState dans un agent

Exemple

from typing import Annotated, Any
from langchain.tools import tool, InjectedState
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
from langchain.messages import HumanMessage


@tool
def conversation_stats(
    state: Annotated[dict[str, Any], InjectedState],
) -> str:
    """Obtenir des informations statistiques sur la conversation actuelle, telles que le nombre de messages, la longueur de la conversation, etc.

Aucun paramètre n'est nécessaire ; les statistiques sont automatiquement lues à partir de l'état actuel.
    """

    messages = state.get("messages", [])
    human_msgs = [m for m in messages if m.type == "human"]
    ai_msgs = [m for m in messages if m.type == "ai"]
    tool_msgs = [m for m in messages if m.type == "tool"]

    return (
        f"Statistiques de la conversation : {len(messages)} messages au total | "
        f"Messages humains : {len(human_msgs)} | "
        f"Réponses de l'IA : {len(ai_msgs)} | "
        f"Appels d'outils : {len(tool_msgs)}"
    )

InjectedState vous permet d'accéder à tous les champs d'AgentState. Si vous étendez state_schema (en ajoutant des champs personnalisés), ces champs peuvent également être lus par InjectedState.


InjectedStore — Accéder au stockage persistant dans les outils

L'état de l'agent est au niveau de la conversation ; il disparaît lorsque la conversation se termine. Alors queStoreest un stockage persistant inter-session, qui peut être utilisé pour enregistrer des informations à long terme telles que les préférences utilisateur, la progression d'apprentissage, etc.

InjectedStorePermet aux outils de lire et d'écrire directement dans le Store.

Exemple

from typing import Annotated
from langgraph.store.base import BaseStore
from langgraph.store.memory import InMemoryStore
from langchain.tools import tool, InjectedStore, InjectedState
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
from langchain.messages import HumanMessage


# Créer un store persistant (dans de vrais projets, une base de données peut être utilisée)
store = InMemoryStore()
# Préremplir quelques données
store.put(("users", "user_001"), "profile", {
    "data": {
        "name": "Xiao Ming",
        "level": "Débutant",
        "completed_courses": ["Tutoriel sur les bases du HTML"]
    }
})


@tool
def get_user_profile(
    store: Annotated[BaseStore, InjectedStore()],
) -> str:
    """Récupère les informations du profil d'apprentissage de l'utilisateur actuel.

Lire les données utilisateur depuis le stockage persistant.
    """

    # Lire les données depuis le Store
    # Le Store utilise un espace de noms (namespace, key) pour organiser les données
    item = store.get(("users", "user_001"), "profile")

    if item is None:
        return "Profil utilisateur introuvable"

    profile = item.value["data"]
    return (
        f"Profil utilisateur : nom={profile['name']},"
        f"niveau={profile['level']},"
        f"cours terminés={', '.join(profile['completed_courses'])}"
    )


@tool
def save_course_progress(
    course_name: str,
    store: Annotated[BaseStore, InjectedStore()],
) -> str:
    """Enregistre la progression d'apprentissage de l'utilisateur dans le stockage persistant.

    Arguments :
course_name: le nom du cours terminé
store: le stockage persistant injecté automatiquement par le système
    """

    # Lire les données existantes
    item = store.get(("users", "user_001"), "profile")
    profile = item.value["data"] if item else {"name": "Xiao Ming", "level": "Débutant", "completed_courses": []}

    # Mettre à jour la liste des cours
    if course_name not in profile["completed_courses"]:
        profile["completed_courses"].append(course_name)

    # Réécrire dans le Store
    store.put(("users", "user_001"), "profile", {"data": profile})

    return (
        f"Progression d'apprentissage mise à jour ! {len(profile['completed_courses'])} cours terminés :"
        f"{', '.join(profile['completed_courses'])}"
    )


# Tester l'outil
print(get_user_profile.invoke({}))
print(save_course_progress.invoke({"course_name": "Tutoriel de base Python3"}))
print(get_user_profile.invoke({}))

Résultat de l'exécution :

用户档案:姓名=小明,水平=入门,已完成课程=HTML 基础教程
学习进度已更新!已完成 2 门课程:HTML 基础教程, Python3 基础教程
用户档案:姓名=小明,水平=入门,已完成课程=HTML 基础教程, Python3 基础教程

Utilisation du Store dans un Agent

Passez le Store à create_agent(), et tous les outils de l'Agent peuvent y accéder via InjectedStore :

Exemple

from typing import Annotated
from langgraph.store.base import BaseStore
from langgraph.store.memory import InMemoryStore
from langchain.tools import tool, InjectedStore
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
from langchain.messages import HumanMessage

# Créer un Store et des données prédéfinies
store = InMemoryStore()
store.put(("example", "courses"), "catalog", {
    "data": {
        "Tutoriel de base Python3": {"price": "Gratuit", "duration": "20 heures"},
        "Analyse de données Python": {"price": "Membre", "duration": "30 heures"},
        "Tutoriel de base HTML": {"price": "Gratuit", "duration": "15 heures"},
    }
})


@tool
def query_course_price(
    course_name: str,
    store: Annotated[BaseStore, InjectedStore()],
) -> str:
    """Interroger les informations de prix d'un cours spécifié sur EXAMPLE.

    Arguments :
course_name: le nom du cours
    """

    item = store.get(("example", "courses"), "catalog")
    catalog = item.value["data"] if item else {}

    if course_name in catalog:
        info = catalog[course_name]
        return f"{course_name} - Prix : {info['price']}, Durée d'apprentissage : {info['duration']}"
    return f"Cours introuvable : {course_name}"


model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
agent = create_agent(
    model=model,
    tools=[query_course_price],
    store=store,  # Passer le Store à l'Agent
    system_prompt="Vous êtes un consultant de cours chez EXAMPLE.",
)

result = agent.invoke({
    "messages": [HumanMessage(content="Combien coûtent respectivement Python3 Basic Tutorial et Python Data Analysis ?")]
})
print(result["messages"][-1].content)

Résultat de l'exécution :

根据查询结果:
- 《Python3 基础教程》是免费的,学习时长约 20 小时
- 《Python 数据分析》是会员课程,学习时长约 30 小时

建议先从免费的 Python3 基础教程开始学习。

Comparaison InjectedState vs InjectedStore

DimensionInjectedStateInjectedStore
PortéeConversation en cours (exécution unique de l'Agent)Inter-session (partagé entre plusieurs exécutions de l'Agent)
Cycle de vieDisparaît lorsque la conversation se termineStockage persistant
Utilisations typiquesLecture de l’historique des messages, des résultats intermédiaires de la conversation en coursPréférences utilisateur, progression d’apprentissage, informations de configuration
Méthode d’injectionInjectedState (injecté automatiquement)InjectedStore() (parenthèses obligatoires)
Organisation des donnéesDictionnaire platEspace de noms + hiérarchie de clés

InjectedToolArg — Marquage des paramètres injectés génériques

En plus des InjectedState et InjectedStore dédiés, vous pouvez également utiliserInjectedToolArgpour marquer tout paramètre qui doit être injecté par le framework:

Exemple

from typing import Annotated
from langchain.tools import tool, InjectedToolArg


@tool
def my_tool(
    normal_param: str,
    injected_param: Annotated[str, InjectedToolArg],
) -> str:
    """Un exemple d'outil contenant un paramètre injecté.

    Arguments :
normal_param: ce paramètre est fourni par le modèle
injected_param: ce paramètre est injecté par le framework (l'Agent n'a pas besoin de le fournir)
    """

    return f"normal={normal_param}, injected={injected_param}"

InjectedToolArg est un marqueur d'injection générique ; InjectedState, InjectedStore et InjectedToolCallId sont tous implémentés à partir de celui-ci. Dans la plupart des cas, utiliser les marqueurs d'injection dédiés suffit ; InjectedToolArg sert à étendre la logique d'injection personnalisée.

Autres extensions