19 实践课-工具链注册分类与权限控制设计
工具链注册分类与权限控制设计
关联:索引
术语小抄(初学者版)
-
工具链(Toolchain):一组可被智能体调用的工具集合,覆盖通用处理、数据查询、设备控制、知识库检索等能力。
-
注册(Register):把工具与其元信息(分类、场景、权限、可靠性)登记到统一的“清单/注册表”里,便于管理与选择。
-
分类(Category):按功能归类(通用/查询/控制/知识库),也可按调用场景归类(分拣/运维/审计等)。
-
RBAC(基于角色的访问控制):先定义角色(管理员/操作员/访客),再给角色分配权限,最后在执行前做权限校验。
-
权限校验(Authorization):判断“某个用户/角色是否允许执行某个工具的某种操作”,不通过就拒绝并记录原因。
-
工具调用优先级:当有多个工具都能解决同一任务时,按“场景优先级 + 工具可靠性/成本”排序,优先使用更合适、更稳定的工具。
-
先修:已完成 13–18 中至少 2 类工具开发(建议:数据库查询工具 + 设备控制工具 + 知识库工具)。
-
运行环境:Python 3.10/3.11(与 01 口径一致);建议在同一虚拟环境中运行。
-
约定:本讲演示使用“JSON 字符串输出 + trace_id”的统一口径(与 13/18 对齐),并在拒绝/失败时同样返回结构化错误。
作业:布置(见文末)
1)工具链四类工具(功能视角)
- 通用工具(common):字符串/格式转换、简单计算、数据清洗等,不绑定业务系统。
- 数据查询工具(data_query):数据库/接口查询,只读优先,严格参数化与最小权限。
- 设备控制工具(device_control):对接 ROS2/产线设备,安全门禁优先,必须可追踪、可回执、可审计。
- 知识库工具(knowledge_base):检索证据 → 决策阈值 → 生成回答,必须给出来源与命中证据。
2)三类调用场景(场景视角)
sorting_ops:分拣生产操作(实时性要求高,设备控制风险高)。maintenance:运维与故障排查(需要更多诊断信息,但仍要权限与审计)。audit:审计与复盘(允许查证据与日志,但一般不允许控制设备)。
3)RBAC 角色(权限视角)
admin:管理员(具备全量权限,但必须审计)。operator:操作员(允许执行生产相关工具,但仅限被授权的控制动作)。viewer:访客/观察者(只读与知识查询为主,不允许设备控制)。
4)拒绝策略(安全优先)
- 权限不足时:宁可拒绝也不“猜测放行”。
- 拒绝必须可解释:返回
ok=false,包含error.code/error.message/trace_id。
AI 工具使用:权限方案设计 / 权限校验代码生成 / 优先级策略说明(学生可直接复制)
使用方法:把你的“工具清单、场景定义、角色定义、风险约束”粘贴到
{你的内容}。要求 AI 输出“方案 + 可复制代码 + 自测用例 + 校验点”。你必须做人工审计与回归测试,不可直接照搬上线。
模板目录:
- 模板 1:生成工具链注册分类清单(含元信息字段)
- 模板 2:生成分拣系统 RBAC 权限分配方案(多场景)
- 模板 3:生成权限校验核心代码(含审计记录与测试用例)
- 模板 4:讲解并生成“调用优先级排序”实现(含可验证示例)
模板 1:生成工具链注册分类清单
你是工业智能体工具链架构师。请根据我提供的工具列表,生成一个“工具链注册分类清单”,要求:
1)分类:common/data_query/device_control/knowledge_base;
2)每个工具给出元信息:tool_name、category、scenes、required_roles、reliability(0~1)、risk_level(low/medium/high)、description;
3)指出哪些工具必须加“二次确认”或“安全门禁”(例如设备控制);
4)输出为 JSON 数组,便于我直接保存为 tools_manifest.json。
我的工具列表与约束:{你的内容}
模板 2:生成分拣系统 RBAC 权限分配方案(多场景)
你是工业产线权限控制工程师。请为“分拣系统智能体工具链”设计 RBAC 权限方案,要求:
1)角色:admin/operator/viewer(可扩展,但要说明原因);
2)场景:sorting_ops/maintenance/audit;
3)输出:每个角色在每个场景下允许调用的工具列表与限制条件(例如只允许查询最近 24h、只允许 speed<=0.6);
4)给出 5 条“危险操作”示例,并说明如何通过权限与参数校验拦截;
5)最后给一份可以落地的 policy.json(结构清晰,可读可维护)。
我的工具清单与风险约束:{你的内容}
模板 3:生成权限校验核心代码(含审计与自测)
你是 Python 工程师。请根据我给的 policy.json 与工具清单,生成权限校验代码,要求:
1)函数 authorize(user_ctx, scene, tool_name, action, payload) -> (ok, error_code, message);
2)拒绝时返回结构化错误(含 trace_id),允许时返回 ok=true;
3)记录审计日志(JSONL,每行一条,字段含 ts_ms/user_id/role/scene/tool/action/ok/trace_id/reason);
4)给出至少 8 条自测用例(不同角色/场景/工具/参数组合),并说明每条的预期结果;
5)只用 Python 标准库,不引入第三方依赖。
我的 policy.json 与工具清单:{你的内容}
模板 4:调用优先级排序实现(按场景 + 可靠性)
请你讲解并给出一个可运行示例:当一个任务在某个场景下有多个候选工具时,如何按“场景优先级 + 工具可靠性 + 风险等级”排序并选择。
要求:
1)给出数据结构(tools_manifest 里要有哪些字段);
2)给出排序规则(用自然语言 + 伪代码 + Python 可运行代码);
3)用 2 个场景举例(sorting_ops 与 audit),展示排序结果为什么合理;
4)给出 3 条常见坑(例如只看可靠性导致高风险工具被优先)。
- 当工具从 3 个增长到 30 个,为什么“把 tools=[...] 直接塞给 Agent”会变成灾难?
- 如果一个操作员误调用了“设备急停解除工具”,后果是什么?你如何证明“权限校验起作用了”?
- 把你们组已开发的工具梳理成“工具链清单”,完成统一注册与分类(功能 + 场景双维度)。
- 为每个工具补齐最少的元信息字段:分类、场景、风险、可靠性、必需角色、描述。
工具实现(Python Tool 函数/StructuredTool)
└─ 工具注册表(registry:tool + meta)
├─ 分类视图(按 category)
├─ 场景视图(按 scene)
├─ 权限过滤(RBAC:role + scene + tool)
└─ 优先级排序(scene priority + reliability)
↓
给 Agent 的工具集(tools_for_this_user_in_this_scene)
↓
审计记录(调用通过/拒绝/失败都记录 trace_id)
关键点解释与自检要点:
- “注册表”不是为了炫技,而是为了让工具链可控:可查、可筛、可审计、可扩展。
- “权限过滤”必须发生在“工具执行前”,更推荐发生在“工具交给 Agent 之前”(减少模型越权尝试与误用概率)。
- “优先级排序”不是为了追求最强能力,而是为了降低风险与不确定性(工业场景以稳定为先)。
目标:用一份清单把“工具是谁、属于哪类、可用于哪些场景、谁能用、可靠不可靠”说清楚。
from __future__ import annotations
import json
import time
import uuid
from dataclasses import dataclass
from typing import Any, Callable, Dict, Iterable, List, Literal, Optional, Tuple
Role = Literal["admin", "operator", "viewer"]
Scene = Literal["sorting_ops", "maintenance", "audit"]
Category = Literal["common", "data_query", "device_control", "knowledge_base"]
RiskLevel = Literal["low", "medium", "high"]
@dataclass(frozen=True)
class UserContext:
user_id: str
role: Role
@dataclass(frozen=True)
class ToolMeta:
tool_name: str
category: Category
scenes: List[Scene]
required_roles: List[Role]
reliability: float
risk_level: RiskLevel
description: str
scene_priority: Dict[Scene, int]
ToolInvoke = Callable[ [Dict[str, Any]], str ]
class ToolRegistry:
def __init__(self) -> None:
self._tools: Dict[str, ToolInvoke] = {}
self._meta: Dict[str, ToolMeta] = {}
def register(self, tool: ToolInvoke, meta: ToolMeta) -> None:
name = meta.tool_name.strip()
if not name:
raise ValueError("tool_name is empty")
if name in self._tools:
raise ValueError(f"tool already registered: {name}")
if not (0.0 <= meta.reliability <= 1.0):
raise ValueError("reliability must be in [0, 1]")
self._tools[name] = tool
self._meta[name] = meta
def list_meta(self) -> List[ToolMeta]:
return list(self._meta.values())
def list_by_category(self, category: Category) -> List[ToolMeta]:
return [m for m in self._meta.values() if m.category == category]
def list_by_scene(self, scene: Scene) -> List[ToolMeta]:
return [m for m in self._meta.values() if scene in m.scenes]
def get(self, tool_name: str) -> Tuple[ToolInvoke, ToolMeta]:
return self._tools[tool_name], self._meta[tool_name]
def _now_ms() -> int:
return int(time.time() * 1000)
def _json_ok(data: Dict[str, Any], trace_id: str) -> str:
return json.dumps({"ok": True, "data": data, "trace_id": trace_id}, ensure_ascii=False)
def _json_err(code: str, message: str, trace_id: str) -> str:
return json.dumps({"ok": False, "error": {"code": code, "message": message}, "trace_id": trace_id}, ensure_ascii=False)
def make_demo_tools() -> Dict[str, ToolInvoke]:
def common_ping(payload: Dict[str, Any]) -> str:
trace_id = uuid.uuid4().hex[:8]
return _json_ok({"pong": True, "ts_ms": _now_ms(), "payload_preview": str(payload)[:120]}, trace_id)
def data_query_batch_stats(payload: Dict[str, Any]) -> str:
trace_id = uuid.uuid4().hex[:8]
batch_no = str(payload.get("batch_no", "")).strip()
if not batch_no:
return _json_err("INPUT_INVALID", "batch_no is required", trace_id)
return _json_ok({"batch_no": batch_no, "total": 1200, "grade_a": 860}, trace_id)
def device_control_arm_home(payload: Dict[str, Any]) -> str:
trace_id = uuid.uuid4().hex[:8]
device_id = str(payload.get("device_id", "")).strip() or "arm_01"
return _json_ok({"cmd": "arm.home", "device_id": device_id, "accepted": True}, trace_id)
def kb_qa(payload: Dict[str, Any]) -> str:
trace_id = uuid.uuid4().hex[:8]
query = str(payload.get("query", "")).strip()
if not query:
return _json_err("INPUT_EMPTY", "query is empty", trace_id)
return _json_ok({"answer": f"示例回答:{query}", "citations": ["doc:sorting_spec#safety"]}, trace_id)
return {
"common_ping": common_ping,
"data_query_batch_stats": data_query_batch_stats,
"device_control_arm_home": device_control_arm_home,
"kb_qa": kb_qa,
}
def build_demo_registry() -> ToolRegistry:
tools = make_demo_tools()
r = ToolRegistry()
r.register(
tools["common_ping"],
ToolMeta(
tool_name="common_ping",
category="common",
scenes=["sorting_ops", "maintenance", "audit"],
required_roles=["admin", "operator", "viewer"],
reliability=0.99,
risk_level="low",
description="连通性探针,用于验证工具链与输出口径是否正常。",
scene_priority={"sorting_ops": 50, "maintenance": 50, "audit": 50},
),
)
r.register(
tools["data_query_batch_stats"],
ToolMeta(
tool_name="data_query_batch_stats",
category="data_query",
scenes=["sorting_ops", "maintenance", "audit"],
required_roles=["admin", "operator", "viewer"],
reliability=0.9,
risk_level="medium",
description="查询批次统计(示例)。真实实现中应使用只读账号与参数化查询。",
scene_priority={"sorting_ops": 70, "maintenance": 60, "audit": 90},
),
)
r.register(
tools["device_control_arm_home"],
ToolMeta(
tool_name="device_control_arm_home",
category="device_control",
scenes=["sorting_ops", "maintenance"],
required_roles=["admin", "operator"],
reliability=0.8,
risk_level="high",
description="机械臂回零(示例)。高风险控制动作,必须权限校验与审计。",
scene_priority={"sorting_ops": 80, "maintenance": 95, "audit": 0},
),
)
r.register(
tools["kb_qa"],
ToolMeta(
tool_name="kb_qa",
category="knowledge_base",
scenes=["sorting_ops", "maintenance", "audit"],
required_roles=["admin", "operator", "viewer"],
reliability=0.85,
risk_level="low",
description="知识库问答(示例)。真实实现应返回命中证据与来源字段。",
scene_priority={"sorting_ops": 60, "maintenance": 70, "audit": 80},
),
)
return r
def main() -> None:
r = build_demo_registry()
print("all tools:", [m.tool_name for m in r.list_meta()])
print("device_control:", [m.tool_name for m in r.list_by_category("device_control")])
print("audit scene:", [m.tool_name for m in r.list_by_scene("audit")])
if __name__ == "__main__":
main()
解释与自检要点:
ToolMeta:把“分类/场景/权限/可靠性/风险/描述/场景优先级”固化为统一字段,后续所有管理都基于它做。scene_priority:同一工具在不同场景的重要性不同;例如device_control_arm_home在maintenance更重要(排障回零),但在audit不该出现(设置为 0 并且不加入 scenes)。- 演示工具是“伪实现”,目的是把工程结构跑通;你们组需要替换成自己已开发的真实工具函数或 Tool 对象。
运行方式(Windows/通用):
python tool_registry_demo.py
关键点解释:
-
输出的 3 行列表分别验证:全量工具、按分类过滤、按场景过滤是否正确。
-
若出现
tool already registered,说明你有重复工具名,需要统一命名与清理重复注册点。 -
至少登记 6 个工具,且四类分类至少覆盖 3 类(建议 4 类全覆盖)。
-
每个工具都有:
tool_name/category/scenes/required_roles/reliability/risk_level/description。 -
能输出两张视图:按分类列表、按场景列表。
-
工业产线里,“能调用工具”不等于“允许调用工具”。权限控制不是锦上添花,是安全底线。
-
权限校验要做到两件事:①拒绝有理由(可解释)②事后能追溯(可审计)。
-
设计 RBAC:至少
admin/operator/viewer三个角色,并给出分配依据。 -
实现权限校验:不同角色调用不同工具时,能正确放行/拒绝,并留下审计证据链。
-
设置工具调用优先级:在相同场景下对候选工具排序,并用测试日志证明排序生效。
- 最小权限:默认不允许,按需授权;尤其是
device_control工具。 - 角色清晰:角色数量要少且可解释,避免“角色爆炸”导致无法维护。
- 场景分层:同一角色在不同场景权限不同(生产操作 vs 审计)。
- 双重门禁:高风险工具除了角色权限,还要做参数级校验(例如速度上限、危险动作白名单)。
- 证据链:放行与拒绝都记录审计日志;每次调用都有
trace_id。
目标:把“是否允许调用”变成一段可运行、可测试的代码。
import json
import time
import uuid
from typing import Any, Dict, List, Tuple
def now_ms() -> int:
return int(time.time() * 1000)
def new_trace_id() -> str:
return uuid.uuid4().hex[:8]
def audit_line(
*,
user: UserContext,
scene: Scene,
tool_name: str,
action: str,
ok: bool,
trace_id: str,
reason: str,
) -> str:
rec = {
"ts_ms": now_ms(),
"user_id": user.user_id,
"role": user.role,
"scene": scene,
"tool": tool_name,
"action": action,
"ok": ok,
"trace_id": trace_id,
"reason": reason,
}
return json.dumps(rec, ensure_ascii=False)
def authorize(
*,
user: UserContext,
scene: Scene,
tool_required_roles: List[Role],
tool_risk_level: RiskLevel,
action: str,
payload: Dict[str, Any],
) -> Tuple[bool, str]:
if user.role not in tool_required_roles:
return False, "ROLE_DENY"
if tool_risk_level == "high":
if scene == "audit":
return False, "SCENE_DENY"
if user.role != "admin":
need_confirm = bool(payload.get("confirm", False))
if not need_confirm:
return False, "CONFIRM_REQUIRED"
return True, "ALLOW"
解释与自检要点:
authorize的输入必须包含:user/scene/tool_required_roles/tool_risk_level/action/payload,这样你才能做到“同角色不同场景不同放行策略”。ROLE_DENY/SCENE_DENY/CONFIRM_REQUIRED:错误码要稳定,便于测试与日志统计;不要只返回一句“没有权限”。
目标:给 Agent/业务层的不是“全量工具”,而是“当前用户在当前场景可用且排序后”的工具集。
from typing import List, Tuple
def select_tools_for_user_and_scene(
*,
registry: ToolRegistry,
user: UserContext,
scene: Scene,
) -> List[ToolMeta]:
candidates = registry.list_by_scene(scene)
allowed: List[ToolMeta] = []
for m in candidates:
ok, _reason = authorize(
user=user,
scene=scene,
tool_required_roles=m.required_roles,
tool_risk_level=m.risk_level,
action="invoke",
payload={},
)
if ok:
allowed.append(m)
def score(meta: ToolMeta) -> Tuple[int, float, int]:
scene_p = int(meta.scene_priority.get(scene, 0))
reliability = float(meta.reliability)
risk_penalty = 0 if meta.risk_level == "low" else 1 if meta.risk_level == "medium" else 2
return (scene_p, reliability, -risk_penalty)
return sorted(allowed, key=score, reverse=True)
解释与自检要点:
- 先权限过滤再排序:避免出现“排序把高风险工具排第一,但其实当前用户没权限”的误导。
score的最后一项是风险惩罚(risk_penalty):同等场景优先级与可靠性下,优先选择更低风险工具。
把下面测试代码追加到 main() 里,验证“不同角色/场景”下的工具集合差异与排序效果。
def demo_permissions_and_priority() -> None:
registry = build_demo_registry()
users = [
UserContext(user_id="u_admin", role="admin"),
UserContext(user_id="u_op", role="operator"),
UserContext(user_id="u_view", role="viewer"),
]
scenes: List[Scene] = ["sorting_ops", "maintenance", "audit"]
for s in scenes:
print("scene:", s)
for u in users:
metas = select_tools_for_user_and_scene(registry=registry, user=u, scene=s)
print(" ", u.role, [m.tool_name for m in metas])
def main() -> None:
demo_permissions_and_priority()
解释与自检要点:
- 你应当观察到:
viewer在任何场景都不应拿到device_control_*工具;audit场景通常也不应给出device_control_*工具。 - 若你们组设计了更细的限制(例如
operator只能调用部分控制动作),请把规则写进authorize的参数级校验里,并用日志证明拦截生效。
运行方式:
python tool_registry_demo.py
项目工坊主题:工具链注册分类与整体架构梳理 + RBAC 权限控制设计与校验实现 + 调用优先级排序与验证。
步骤(教师演示 + 学生分组):
- 演示:把全量工具塞给智能体时的风险(越权尝试、误选高风险工具、难审计)。
- 演示:统一 manifest + registry 后,如何按场景筛选与按分类展示工具链。
- 演示:RBAC 方案设计,把“谁能用什么”写成 policy,并实现校验与审计记录。
- 演示:调用优先级排序对结果的影响,并用测试日志证明排序生效。
- 学生分组:用你们组已有工具替换演示工具,完成注册、分类、权限、排序与测试证据链。
-
有一份可读的工具链清单:包含分类/场景/角色/风险/可靠性(可以是 JSON 或表格)。
-
有一份权限控制方案:写清角色、权限分配、拒绝策略与审计字段。
-
有权限校验代码:至少能演示 2 个“拒绝案例”(不同角色/不同场景)与 2 个“放行案例”。
-
有优先级验证证据:至少 1 个场景下展示排序结果与原因。
-
生成工具链注册分类管理脚本:根据你们组工具清单输出 manifest(JSON)与分类视图(按分类/按场景)。
-
设计多场景的权限控制方案(针对分拣系统):给出角色定义、权限矩阵与拒绝策略(含高风险二次确认)。
-
生成权限校验核心代码(基于角色):输出
authorize(...)与审计日志记录代码,并附带自测用例。 -
讲解工具调用优先级设置方法:按场景优先级与工具可靠性/风险排序,并给出可运行示例。
-
解答权限控制设计中的疑问:对“最小权限/场景分层/参数级门禁/审计字段/越权尝试”给出工程化建议与可验证步骤。
九、课程思政融入点(工业设备操作安全与工程伦理)
- 设备控制类工具“权限不清”就等于“安全边界不清”,会把风险从代码扩散到真实产线。
- 工程伦理要求:宁可多一步校验与拒绝,也不要把不确定性转成可能的误操作。
- 审计不是为追责而追责,而是为了形成可改进的证据链:哪里被拒绝、为什么拒绝、如何优化流程与权限配置。
作业:布置
1)提交工具链注册分类清单及架构梳理图,附分类说明。
2)提交权限控制方案文档(含角色定义、权限分配)及权限校验代码。
3)提交不同角色调用工具的测试日志(含权限校验通过 / 拒绝截图)。