This section restricts the tools that can be used according to the User role: internal User users can access the Data library, and external User users can only use Web Search.
Tool permissions cannot be restricted by prompt alone. A more reliable approach is to directly modify the visible tool list before the model adjusts Use, so that the model cannot see tools that should not be adjusted Use at all.
| User role | Use tools |
|---|---|
| internal | web_search + sql_query |
| external | web_search only |
Don't just tell external users "Don't check the Data library". Data library tools should be removed from middleware.
Notebook Prepare has two tools: web_search queries the Internet, and sql_query queries the local Chinook Data library.
tavily_client = TavilyClient()
db = SQLDatabase.from_uri("sqlite:///resources/Chinook.db")
@tool
def web_search(query: str) -> Dict[str, Any]:
"""Search the web for information"""
return tavily_client.search(query)
@tool
def sql_query(query: str) -> str:
"""Obtain information from the database using SQL queries"""
try:
return db.run(query)
except Exception as e:
return f"Error: {e}"SQL tools usually access internal Data. Production environments also require read-only restrictions, table-level permissions, row limit and audit logs.
User role passed in through runtime context. The default role is external.
from dataclasses import dataclass
@dataclass
class UserRole:
user_role: str = "external"User roles usually come from login systems, organizational permissions, or API tokens and should not be left to the model to determine from the User user text.
dynamic_tool_call reads request.runtime.context.user_role. If it is not internal, use request.override(tools=[web_search]) Put SQL tool to remove it.
from langchain.agents.middleware import wrap_model_call, ModelRequest, ModelResponse
from typing import Callable
@wrap_model_call
def dynamic_tool_call(
request: ModelRequest,
handler: Callable[[ModelRequest], ModelResponse],
) -> ModelResponse:
"""Dynamically call tools based on the runtime context"""
user_role = request.runtime.context.user_role
if user_role == "internal":
pass
else:
tools = [web_search]
request = request.override(tools=tools)
return handler(request)This model adjustment for external users will only seeweb_search, so it cannot be selectedsql_query。
Agent initially registers two tools, but the actual visible tools will be rewritten by middleware according to roles. When an external Use user asks for the number of Data libraries, the model does not have a SQL tool to use.
agent = create_agent(
model=deepseek_model(),
tools=[web_search, sql_query],
middleware=[dynamic_tool_call],
context_schema=UserRole,
)
response = agent.invoke(
{"messages": [HumanMessage(content="How many artists are in the database?")]},
context={"user_role": "external"},
)
print(response["messages"][-1].content)If you passcontext=UserRole(user_role="internal"), the middleware model can enable Use SQL tools without overriding the tool list.
request.override(tools=...)You can change the tool set used for this model adjustment.