LangChain LangGraph
Module 1 Module 2 Module 3
Module 3 · 中间件与 HITL
1 3.2 Managing Msgs
2 3.3 HITL
3 3.4 Dyn Models
4 3.4 Dyn Prompts
5 3.4 Dyn Tools
6 3.5 Email Agent
SUM Module Summary
HomeLangChainModule 3 · 中间件与 HITL3.4 Dyn Tools
📓 Notebook 📖 Explained
LANGCHAIN MODULE 3 · LESSON 3.4c

动态工具权限
User Role · request.override(tools=...)

本节根据用户角色限制可用工具:内部用户可以访问数据库,外部用户只能使用 Web Search。

1 为什么要动态限制工具

工具权限不能只靠 prompt 约束。更可靠的做法是在模型调用前直接修改本次可见的工具列表,让模型根本看不到不该调用的工具。

用户角色可用工具
internalweb_search + sql_query
externalweb_search only
核心安全点

不要只告诉外部用户“不要查数据库”。应该在 middleware 中移除数据库工具。

2 定义 Web Search 和 SQL 工具

Notebook 准备两个工具:web_search 查询互联网,sql_query 查询本地 Chinook 数据库。

Python
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 工具通常访问内部数据。生产环境还需要只读限制、表级权限、行数限制和审计日志。

3 定义用户角色 context

用户角色通过 runtime context 传入。默认角色是 external

Python
from dataclasses import dataclass

@dataclass
class UserRole:
    user_role: str = "external"
为什么用 context

用户角色通常来自登录系统、组织权限或 API token,不应该让模型自己从用户文本中判断。

4 用 middleware 覆盖工具列表

dynamic_tool_call 读取 request.runtime.context.user_role。如果不是 internal,就用 request.override(tools=[web_search]) 把 SQL 工具移除。

Python
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)
硬限制

外部用户的本次模型调用只会看到 web_search,因此无法选择 sql_query

5 创建 Agent 并测试外部用户

Agent 初始注册两个工具,但实际可见工具会被 middleware 按角色改写。外部用户问数据库数量时,模型没有 SQL 工具可用。

Python
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)
测试 internal

如果传 context=UserRole(user_role="internal"),middleware 不覆盖工具列表,模型就可以使用 SQL 工具。

6 本课 takeaway