在这里插入图片描述

结论先给:LangGraph、AutoGen、CrewAI、OpenAI Assistants API、Semantic Kernel——5个框架全部没有不可变授权对象。它们的"授权"本质都是运行时可变的进程内配置,同进程代码随时能改。dsh-superagent的Mandate(创建后冻结、只能收回)是目前唯一把授权做成数据结构的方案。上下文/权限分离在多数框架中存在,但不可变授权是dsh独有。


0. 为什么做这个对比

上一篇《我审了DeepSeek开源的超级Agent原型:三个安全命题,两个半成立》里,我审了DeepSeek开源的dsh-superagent-prototype,验证了它的三个安全命题:Mandate不可变、Subscription不扩权、没有自主Watchdog。审计过程中我一直在想一个问题:这个"不可变授权"的设计,在主流Agent框架里有没有人做过?

这个问题直接决定了一个实践判断:生产环境部署Agent,授权模型该抄谁的?抄LangGraph的middleware拦截,还是抄dsh-superagent的Mandate冻结?这两条路的底层逻辑完全不同。

我把5个主流Agent框架的官方文档翻了一遍,只看授权模型相关的技术细节,不看营销话术。对比维度四个:

  1. 工具调用的权限控制方式——有没有授权对象?怎么限制Agent能调什么工具?
  2. 上下文与权限是否分离——Agent"看到"的信息和"有权做"的操作是不是两条独立管道?
  3. 有没有不可变授权对象——类似dsh-superagent的Mandate:创建后不能改,只能收回?
  4. 人工介入/审批机制——有没有human-in-the-loop?怎么触发?

先说结论:5个框架全部没有不可变授权对象。 dsh-superagent的Mandate是孤例。


1. LangGraph:middleware拦截,审批原语最完整

LangGraph是LangChain的图执行框架,授权控制靠HumanInTheLoopMiddleware。

1.1 权限控制方式

创建agent时传入interrupt_on字典,按工具名映射审批策略:

HumanInTheLoopMiddleware(
  interrupt_on={
    "write_file": True,                                        # 全部决策允许
    "execute_sql": {"allowed_decisions": ["approve","reject"]},# 不允许edit
    "read_data": False,                                        # 免审批
  })

粒度是工具级+参数级。InterruptOnConfig的when谓词可以检查参数——比如只对路径不在/workspace/下的写文件、非SELECT的SQL触发审批。这是5个框架里参数级控制做得最完整的。

1.2 上下文与权限分离

是,两条独立管道。模型照常看到工具schema并发出tool_call(可见性管道),middleware在执行前拦截(权限管道)。另外LangGraph Platform还有独立的平台层RBAC,@auth.on处理器对threads/assistants/crons/store资源做访问控制,与agent内部工具控制完全解耦。

1.3 不可变授权对象

没有。interrupt_on是普通dict,进程内任何代码都能改。checkpoint持久化的是图执行状态,不是授权凭证。interrupt是一次性审批事件,不是"可收回的授权"。

1.4 人工介入

触发条件是tool_call命中interrupt_on策略→图暂停、状态写入checkpointer→人工用Command(resume={"decisions":[...]})恢复。四种决策类型:approve(原样执行)、edit(改参数后执行)、reject(拒绝并反馈)、respond(直接回复作为工具结果)。

这是5个框架里审批原语最完整的。但注意:**审批是事件流(interrupt→决策→恢复),不是授权物。**审批完就结束了,没有留下一个"不能被改的授权凭证"。


2. AutoGen:双轨注册,分离最显式但粒度最粗

AutoGen分两个版本世代:0.2经典版(ConversableAgent)和0.4+ AgentChat。

2.1 权限控制方式

没有工具级ACL。权限模型=“工具挂在哪个agent上、谁有执行权”。0.2中UserProxyAgent是代码和工具的唯一执行主体,AssistantAgent只产代码/调用请求。粒度为agent级,无参数级内置策略。

2.2 上下文与权限分离

是,且是5个框架中表达最显式的。0.2的双装饰器设计:

  • @assistant.register_for_llm——只把schema放进LLM的tools[]("思考"侧)
  • @user_proxy.register_for_execution——只把可执行函数绑定到执行agent("动手"侧)
  • register_function便捷方法同时注册两侧

即"模型知道工具存在"与"某个agent有权执行"是两次独立注册。0.4中工具直接传给AssistantAgent(tools=[...]),可见性与执行权重新耦合在该agent内,但跨agent边界(handoff)仍然隔离。

2.3 不可变授权对象

没有。注册表是agent实例上的可变dict。

2.4 人工介入

0.2用human_input_mode三档:NEVER(不问)、TERMINATE(默认,仅在终止条件满足时问)、ALWAYS(每次响应前问)。0.4改为UserProxyAgent(input_func=...)作为团队成员:团队轮到它时阻塞整个run等待输入。

注意:0.4的HITL是"对话轮次级"介入,不是工具调用级审批。模型已经决定调工具了,人只能在整个run结束后给反馈,不能在单个tool_call级别拦截。


3. CrewAI:三层控制,但全是可变配置

3.1 权限控制方式

三层——

  1. Agent级白名单:Agent(tools=[...])决定agent能用哪些工具(不在列表里的工具该agent既看不到也调不到)
  2. 执行时拦截:hook返回False即阻断执行。ToolCallHookContext暴露tool_name/tool_input/tool/agent/task/crew,可按工具名、参数值(如..路径检测)、agent role做条件阻断
  3. 企业版RBAC:HITL管理有两个权限位manage_human_feedback和respond_to_human_feedback

粒度:工具级+参数级+agent级,覆盖面最杂但都是代码逻辑而非声明式策略。

3.2 上下文与权限分离

半分离。Agent(tools=[...])同时决定可见性和可执行性(耦合);hook是执行侧的独立第二道管道(模型已决定调用后仍可被拦)。没有独立的"可见性过滤器"。

3.3 不可变授权对象

没有。hook反而可被unregister_*/clear_all_tool_call_hooks()动态注销——是可变配置的典型形态。

3.4 人工介入

三种形态:hook内context.request_human_input()同步控制台审批;Task(human_input=True)任务完成后暂停等人工复核;Enterprise webhook任务进入Pending Human Input状态后回调通知。


4. OpenAI Assistants API:架构强制分离,但无审批原语

前置事实:该API已于2025-03宣布弃用,目标关停时间为2026年上半年,继任者是Responses API + Agents SDK。以下按原API机制描述。

4.1 权限控制方式

创建assistant时用tools数组声明可用工具(code_interpreter/file_search/function),可用tool_choice强制本次run用某工具。

**关键架构事实:OpenAI从不执行你的function。**模型决定调用后,run进入requires_action,客户端轮询拿到tool_calls(含函数名+参数),由你自己的代码决定执行与否,再POST submit_tool_outputs把结果喂回去。

粒度:工具级,无参数级策略、无审批原语。审批逻辑100%由客户端在暂停点自实现。

4.2 上下文与权限分离

对function工具是架构性强制分离——模型只能产参数,执行天然在客户端,可见性(schema上送)与执行权(客户端代码)物理隔离。但对内置工具(code_interpreter、file_search)是OpenAI沙箱内自动执行,客户端没有执行闸门,只能靠不声明该工具来禁用。

4.3 不可变授权对象

没有。assistant和run都是可变资源对象;唯一的时效性约束是run创建10分钟后过期,这是TTL而非可收回的授权凭证。

4.4 人工介入

无内置机制。实务模式就是利用requires_action暂停点:客户端收到tool_call后先问人、人同意才执行并提交输出。继任者OpenAI Agents SDK补上了原语:工具可声明needs_approval。


5. Semantic Kernel:三管道最清晰,但filter可运行时改

5.1 权限控制方式

双轨制——

  • 可见性侧:FunctionChoiceBehavior(Auto/Required/None)+过滤器dict(included_plugins/excluded_plugins等),控制哪些已注册插件/函数会播发给模型
  • 执行侧:过滤器用next(context)委托链实现——不调next操作就永远不会执行。FunctionInvocationContext含插件名、函数名、完整参数,可做参数级校验

粒度:插件级+函数级+参数级,且filter可覆写函数结果(执行前可缓存、执行后可脱敏)。

5.2 上下文与权限分离

是,架构上最明确的双管道。FunctionChoiceBehavior管"模型看到什么",Filters管"函数能不能跑",IPromptRenderFilter还能在prompt发给模型前查看/改写/拦截(官方用例:PII脱敏)。三条管道互不依赖。

5.3 不可变授权对象

没有。filter注册在Kernel上,运行时可通过Kernel.FunctionInvocationFilters属性增删改排序(官方文档明确说"can be added, removed, or reordered at runtime")。

5.4 人工介入

SK本体无原生审批原语,官方推荐模式是在IFunctionInvocationFilter里自己写交互。其后继Microsoft Agent Framework补齐了原语:@tool(approval_mode="always_require")声明式审批。


6. 横向对比:一张表看清楚

框架核心机制授权粒度上下文/权限分离不可变授权对象原生审批原语
LangGraphHITL Middleware + interrupt_on工具级+参数级✅ middleware管道❌✅ approve/edit/reject/respond
AutoGen 0.2双轨注册 + human_input_modeagent级✅ 双注册最显式❌⚠️ 轮次级,非工具级
CrewAI工具白名单 + Tool Hooks工具级+参数级+agent级⚠️ 半分离❌⚠️ hook内手写/webhook
Assistants APIrequires_action客户端执行工具级✅ function工具架构强制❌❌(Agents SDK补了)
Semantic KernelFilters + FunctionChoiceBehavior插件/函数级+参数级✅ 三管道最清晰❌❌(Agent Framework补了)
dsh-superagentMandate + Subscriptionscope文本级✅ 双管道物理隔离✅ 运行时无写入路径⚠️ 人工stop/retry

7. 核心发现:两种范式的本质差异

把6个方案摆在一起,能看到两种完全不同的授权范式。

7.1 主流框架的范式:事件流审批

LangGraph/AutoGen/CrewAI/SK的授权模型本质上是事件流:模型发出tool_call→middleware/hook/filter拦截→(可选)人工审批→执行→结束。审批完就结束了,没有留下一个"不能被改的授权凭证"。

这个范式的特点是:

  • 授权是过程,不是对象。权限体现在拦截逻辑里,不在一个独立的数据结构里。
  • 运行时可变。策略dict、hook函数、filter列表都是进程内可变配置,同进程代码随时能改。不仅授权可变,连"什么时候需要审批"这个规则本身也是可变的。
  • 审批是事件,不是授权。interrupt/approve完了就完了,没有"这个agent从此只能做这个scope的事"的持久保证。

7.2 dsh-superagent的范式:授权物冻结

dsh-superagent的Mandate是一个不可变授权对象:accept时创建,包含strength/policyVersion/basis/actor/scope,创建后运行时无任何写入路径,只能由人工stop或revalidate失败来收回。

这个范式的特点是:

  • 授权是对象,不是过程。权限固化在一个数据结构里,独立于执行流程存在。
  • 运行时不可变。创建后冻结,代码里没有写入路径(我审计过,93处引用全是读)。
  • 授权可收回但不可篡改。人工可以stop,但没有任何代码路径能改scope或提strength。

7.3 哪种更硬?

如果你的威胁模型是"Agent被诱导后自行扩大权限",dsh-superagent的范式更硬——因为即使LLM被prompt注入说服了,它也改不了Mandate,代码里没有写入路径。

如果你的威胁模型是"Agent需要在执行中动态调整策略",主流框架的范式更灵活——因为你可以随时改hook/filter/策略dict。

但灵活性是有代价的。 CrewAI的hook可以被clear_all_tool_call_hooks()一键清空;SK的filter可以运行时增删改;LangGraph的interrupt_on是普通dict。这些"灵活"在攻击者眼里就是"可篡改"。

dsh-superagent的Mandate不可变,代价是scope在accept时就定死了,执行中不能扩大也不能缩小——要改只能stop后重新accept。这是用灵活性换安全性。


8. 上下文/权限分离:多数框架有,但强度不同

上下文/权限分离不是dsh-superagent独有的。5个框架里有4个做到了某种程度的分离:

  • Semantic Kernel:三管道最清晰(可见性/执行/prompt渲染互不依赖)
  • LangGraph:middleware管道分离,平台层还有独立RBAC
  • AutoGen 0.2:双注册是最显式的分离(register_for_llm vs register_for_execution)
  • Assistants API:function工具架构强制分离

CrewAI是半分离(白名单同时决定可见性和可执行性)。

但dsh-superagent的分离有一个其他框架没有的特点:Subscription扇出的上下文Input不携带任何授权字段。其他框架的分离是"模型看到工具schema但执行被拦截",dsh的分离是"上下文流动和权限冻结是两条物理隔离的管道"——上下文可以自由流动,但权限管道是封死的。

这个差异的后果是:在其他框架里,Agent"看到"一个工具后可能会试图调用它,然后被middleware拦住;在dsh里,Agent拿到的上下文Input里根本没有授权信息,它"看到"的东西和"有权做"的东西在数据结构层面就是分离的。


9. 一句话总结

5个主流Agent框架的授权模型都是"运行时可变的进程内配置",审批是事件流而非授权物。dsh-superagent的Mandate是目前唯一把授权做成不可变数据结构的方案——创建后冻结、只能收回不能篡改。上下文/权限分离在多数框架中存在,但不可变授权是dsh独有。

如果你要在生产环境部署Agent,授权模型的选择取决于你的威胁模型:要灵活性抄LangGraph/SK,要安全性抄dsh-superagent,要两者兼得——目前没有现成的,得自己造。


附:调研来源

框架官方文档
LangGraphdocs.langchain.com/oss/python/langchain/human-in-the-loop、docs.langchain.com/oss/python/langgraph/interrupts
AutoGenmicrosoft.github.io/autogen/0.2/docs/tutorial/human-in-the-loop、microsoft.github.io/autogen/stable/user-guide/agentchat-user-guide/tutorial/human-in-the-loop.html
CrewAIdocs.crewai.com/en/learn/tool-hooks、docs.crewai.com/en/learn/human-in-the-loop
Assistants APIplatform.openai.com/docs、openai.github.io/openai-agents-python/human_in_the_loop/
Semantic Kernellearn.microsoft.com/en-us/semantic-kernel/concepts/enterprise-readiness/filters、learn.microsoft.com/en-us/semantic-kernel/concepts/ai-services/chat-completion/function-calling/function-choice-behaviors
dsh-superagentgithub.com/deepseek-ai/dsh-superagent-prototype(审计见上一篇)
Logo

更多推荐