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 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 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 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 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
| Dimension | InjectedState | InjectedStore |
|---|---|---|
| Portée | Conversation en cours (exécution unique de l'Agent) | Inter-session (partagé entre plusieurs exécutions de l'Agent) |
| Cycle de vie | Disparaît lorsque la conversation se termine | Stockage persistant |
| Utilisations typiques | Lecture de l’historique des messages, des résultats intermédiaires de la conversation en cours | Préférences utilisateur, progression d’apprentissage, informations de configuration |
| Méthode d’injection | InjectedState (injecté automatiquement) | InjectedStore() (parenthèses obligatoires) |
| Organisation des données | Dictionnaire plat | Espace 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 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}"
Autres extensionsInjectedToolArg 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.