跳到主要内容

数据截至 (上游 commit 5e1f1fb87d9a)

Semantic Kernel — 架构与原理

30 秒导读: Semantic Kernel(下称 SK)是微软的模型无关 SDK,帮你把大模型嵌进已有应用。它的核心动作只有一个:把「一段提示词」和「一个普通函数」抹平成同一种东西(KernelFunction),注册进一个叫 Kernel 的容器;之后模型说「我要调用 X 工具」,SK 就自动找到 X、校验参数、执行、把结果作为消息喂回模型,循环到模型不再要工具为止。Agent、多 agent 编排、流程框架,都是长在这个内核之上的薄层。

范围声明: 本套文档以仓库 python/ 目录下的实现为准,所有 file:line 引用相对克隆根、as-of 上面的 sourceCommit。.NET 与 Java 侧未逐行核对,不对其行为下断言。


1. 这是什么(零基础也能懂)

1.1 一句话定义

SK 是一层「胶水 SDK」:上接你的应用代码,下接任意一家大模型;中间提供一个容器(Kernel),让「提示词」和「你的业务函数」以同一种方式被模型调用。

1.2 解决什么问题、给谁用

想象你在一个已有的 .NET 或 Python 后端里,要加一个「能查订单、能改地址」的助手。你会撞上四件麻烦事:

麻烦SK 的答案
每家模型的 API/参数/工具格式都不一样统一到 ChatCompletionClientBase 抽象,换模型只换一行连接器
模型说「调用 get_order」之后,谁去找这个函数、校验参数、执行Kernel.invoke_function_call 全包(python/semantic_kernel/kernel.py:326)
提示词散在代码各处,没法版本化、没法当组件复用提示词也是 KernelFunction,可以从 YAML/目录加载
企业要日志、要审计、要权限拦截三类过滤器,装在管线上而不是塞进业务代码

给谁用: 已经有一套服务端应用(尤其是 .NET/Java 企业栈)、想把 LLM 当成一个可治理的组件接进来的工程团队。不是给「从零写一个 agent 玩具」的人用的。

1.3 上手前必须先知道的两件事

这两件事不讲清楚,你会在仓库里迷路。

事实一:这是三套并行实现的单仓库(monorepo),不是一套代码的三个绑定。

语言位置规模
.NETdotnet/src/(SemanticKernel.CoreAgentsConnectorsVectorData 等 14 个顶层目录)dotnet/ 下 2961 个 .cs 文件
Pythonpython/semantic_kernel/554 个 .py 文件
Javajava/ 只有一个 README,代码已搬到外部仓库 microsoft/semantic-kernel-java(java/README.md)仓库内 0 行

三套是各自独立写的,概念对齐、API 不逐一对齐。本组文档全部以 Python 侧为准讲原理,因为它最完整地暴露在一个可读的包里;.NET 的对应物只在必要时点名。

事实二:README 首屏声明 SK 的 agent 能力已由 Microsoft Agent Framework(MAF)承接。

README.md:3-6 的 IMPORTANT 块说 MAF 是 SK 的「enterprise-ready successor」,并给出迁移指南链接(:6)。这不是一句公关话——代码里有实打实的桥:

# python/semantic_kernel/functions/kernel_function.py:412
def as_agent_framework_tool(self, *, name=None, description=None, kernel=None) -> Any:
...
from agent_framework import AIFunction # 缺包就抛 ImportError,提示 pip install agent-framework-core

as_agent_framework_tool 把一个 KernelFunction 转成 MAF 的 AIFunction含义是:SK 从「你写 agent 的地方」退成了「给 MAF 供应工具和连接器的地方」。 这对你判断「该不该学 SK」很关键,见 §6 边界与局限

1.4 用起来什么样

最小例子来自 README.md:75-92 的 Quickstart —— 三行配置就是一个能对话的 agent:

# 来自 README.md 的 Quickstart(Python)
from semantic_kernel.agents import ChatCompletionAgent
from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion

agent = ChatCompletionAgent(
service=AzureChatCompletion(), # 换成 OpenAI/Ollama/Bedrock 只改这一行
name="SK-Assistant",
instructions="You are a helpful assistant.",
)
response = await agent.get_response(messages="Write a haiku about Semantic Kernel.")

注意你没有手写任何 Kernel Agentkernel 字段有默认工厂,会自己造一个(python/semantic_kernel/agents/agent.py:271)——内核一直在,只是被藏起来了。

这个例子把 SK 最有价值的部分藏起来了。真正体现 SK 心智模型的是下面这段(缩写自 python/samples/concepts/auto_function_calling/chat_completion_with_auto_function_calling.py:37-69):

# 精简自上述 sample,示意;真实文件有完整的服务选择逻辑
kernel = Kernel()
kernel.add_plugin(MathPlugin(), plugin_name="math") # 普通 Python 类 → 一组工具
kernel.add_plugin(TimePlugin(), plugin_name="time")

chat_function = kernel.add_function( # 提示词模板 → 也是一个函数
prompt_template_config=PromptTemplateConfig(template="{{$chat_history}}{{$user_input}}"),
plugin_name="ChatBot", function_name="Chat",
)
request_settings.function_choice_behavior = FunctionChoiceBehavior.Auto(
filters={"excluded_plugins": ["ChatBot"]} # 允许模型调什么,在这里划线
)

重点看两处: MathPlugin()(普通类)和 chat_function(提示词)走的是同一个 add_* 入口,最后都变成 KernelFunction;FunctionChoiceBehavior.Auto 是「把自动函数调用循环打开」的那个开关。

1.5 一句话直觉

Kernel 当成一个依赖注入容器(DI container),只不过它注入的东西是「模型能调的能力」。 你往里注册服务和插件,模型在运行时从里面挑;过滤器就是这个容器的 AOP 切面。


2. 顶层全景(它大概怎么转)

2.1 核心回路

先看一次带工具调用的对话是怎么绕圈的。怎么读这张图: 左半边是「提示词侧」,右半边是「代码侧」,两边都是同一种 KernelFunction;真正的循环发生在下方框里,最后那条回边是全部关键。

┌─────────────────── Kernel(容器)────────────────────┐
│ services 模型服务 plugins 函数库 │
│ filters 三类过滤器 ai_service_selector 选服务 │
└───────────┬──────────────────────┬───────────────────┘
│ 取出并渲染 │ 按名字查找
▼ ▼
KernelFunction(可调用单元) KernelFunction
「提示词做的」 「Python 方法做的」
│ ▲
│ 渲染成对话 │ ⑤ 执行工具
▼ │
AI service connector ──────────┤
(模型连接器) │
│ ③ 模型回 tool call │
▼ │
┌────────────────────────────────┐ │
│ 自动函数调用循环(默认 ≤5 轮) │─┘
│ ④ 校验参数 → ⑤ 执行 → ⑥ 结果当消息追加 → 回 ③
└────────────────────────────────┘

循环上限是 DEFAULT_MAX_AUTO_INVOKE_ATTEMPTS = 5(python/semantic_kernel/connectors/ai/function_choice_behavior.py:18);FunctionChoiceBehavior.NoneInvoke() 把它设成 0(即不自动执行),Required() 设成 1(function_choice_behavior.py:126/149/173)。

2.2 四层结构

Kernel 这一层之上,SK 又叠了三层。下层不知道上层的存在,上层全部踩在 Kernel 上。

第4层 process 流程框架:把业务流写成「步骤 + 事件边」的状态机
第3层 orchestration 五种多 agent 协作模式,跑在 actor 运行时上
第2层 agents Agent + AgentThread:会话状态与托管 agent 的统一外壳
──────────────────────────────────────────────────────────────
第1层 Kernel / KernelFunction / connector / filters ← 地基,只有这层是稳定的
  • Agent 层只是把「一段指令 + 一个 kernel + 一段对话历史」打包(agents/chat_completion/chat_completion_agent.py:117),最终还是去调内核的服务(chat_completion_agent.py:537-542)。
  • 编排层把每个 agent 包成一个 actor,跑在自带的进程内 actor 运行时上(agents/runtime/in_process/in_process_runtime.py:167)。
  • 流程框架是并行的另一支,走自己的超步循环(processes/local_runtime/local_process.py:191),不经过 agent 层。

第 2–4 层的绝大部分符号都打着 @experimental(Python 侧全库共 389 处该装饰器)。地基层不打。

2.3 部件一句话职责

部件干什么在哪个文件(符号)
Kernel容器 + 调度入口,持有 services / plugins / filterspython/semantic_kernel/kernel.py:62 Kernel
KernelFunction唯一的可调用单元抽象,提示词和方法都变成它functions/kernel_function.py:78 KernelFunction
KernelFunctionFromMethod原生方法那一支functions/kernel_function_from_method.py:21
KernelFunctionFromPrompt提示词那一支:渲染模板 → 调模型 → 包成结果functions/kernel_function_from_prompt.py:55
KernelPlugin一组函数的命名空间,可从对象/目录/OpenAPI 加载functions/kernel_plugin.py:36(from_openapi:346)
ChatCompletionClientBase模型连接器抽象,自动函数调用循环真正住在这里connectors/ai/chat_completion_client_base.py:35(循环在 :137-174)
FunctionChoiceBehavior工具调用策略:Auto / Required / None + 允许名单connectors/ai/function_choice_behavior.py:27
FilterTypes三类过滤器的枚举filters/filter_types.py:6
三类过滤器的洋葱构造拦截提示词渲染 / 函数调用 / 自动工具调用filters/kernel_filters_extension.py:108 construct_call_stack
Agent / AgentThreadAgent 外壳与会话状态;托管 agent 也套进同一个壳agents/agent.py:246 / :111
OrchestrationBase多 agent 编排统一入口agents/orchestration/orchestration_base.py:85
五种编排concurrent / sequential / group_chat / handoffs / magenticagents/orchestration/*.py
ProcessBuilder把流程画成「步骤 + 事件边」再 build()processes/process_builder.py:34
数据层向量存储与文本检索的统一模型data/vector.py:1133 / :1621data/text_search.py

3. 主线走一遍(高层,不进代码)

拿最短的入口 kernel.invoke_prompt("...") 走一遍。这条路径把上面所有部件串了一遍,机制细节留给 03-function-calling-and-filters.md

你写: await kernel.invoke_prompt("帮我算 3 的 5 次方,再告诉我现在几点")

① 包成临时函数 kernel.py:215 invoke_prompt
└ 现场 new 一个 KernelFunctionFromPrompt,函数名随机

② 进函数调用管线 kernel_function.py:274 construct_call_stack(FUNCTION_INVOCATION)
└ 所有 FUNCTION_INVOCATION 过滤器套成洋葱,最里层才是真正执行

③ 渲染提示词 kernel_function_from_prompt.py:270 _render_prompt
└ 先过 PROMPT_RENDERING 过滤器(:281),再把 {{$var}} 填成文本
└ 渲染完的字符串被解析成一段 ChatHistory(chat_history.py:331 from_rendered_prompt)

④ 挑模型服务 services/kernel_services_extension.py:52 select_ai_service

⑤ 进自动函数调用循环 chat_completion_client_base.py:137-174
├ 调模型 → 看返回里有没有 FunctionCallContent
├ 没有 → 直接返回,结束
└ 有 → 把模型这条消息追加进 ChatHistory,然后 ⑥

⑥ 执行工具 kernel.py:326 invoke_function_call(多个 tool call 用 asyncio.gather 并发)
├ 查允许名单 → 查函数 → 校验必填/多余参数
├ 过 AUTO_FUNCTION_INVOCATION 过滤器洋葱(kernel.py:444)
└ 结果包成 FunctionResultContent 追加进 ChatHistory

⑦ 回到 ⑤,最多 5 轮;超限就关掉工具再问模型最后一次

读这条路要抓的三个反直觉点:

  1. 循环不在 Kernel 里,在连接器基类里。 Kernel 只提供「执行一个 tool call」这个原子操作,「要不要再来一轮」由 ChatCompletionClientBase 决定。
  2. 提示词渲染有自己的过滤器管线,和函数调用的那层是两套洋葱,各自独立。
  3. ChatHistory 是被贯穿修改的那个对象,不是返回值。工具结果、模型消息全部就地追加进去。

4. 阅读地图

六章按「地基 → 上层」排。如果你只有半小时,读 01 和 03 就够拿到 SK 的全部核心思想。

顺序章节讲什么谁该读
101-kernel-and-functions.mdKernel 容器、KernelFunction 两种来源、插件加载的四条路、服务选择所有人必读
202-prompt-and-content.md三种模板语法、渲染后怎么被解析成对话、内容类型体系、历史裁剪关心提示词工程的人
303-function-calling-and-filters.md自动函数调用循环的每一步 + 三类过滤器的洋葱管线所有人必读,SK 的价值核心
404-agents-and-threads.mdAgent/AgentThread 抽象,托管 agent(Azure AI / Assistants / Bedrock)怎么塞进同一个壳;另含旧版群聊路径 AgentChannel/BroadcastQueue/AgentGroupChat,与第 05 章的编排体系是两套并存机制要用 agent 层的人
505-multi-agent-orchestration.mdactor 运行时 + 五种协作模式做多 agent 的人;注意涉及的每个类都是 @experimental
606-process-and-data.md流程框架(状态机式业务流)与数据/向量检索层只在你真要用这两个子系统时读

选读建议:

  • 只想接一个模型 + 一堆工具 → 01 → 03,结束。
  • 从别的框架迁过来看设计 → 本页 §5 + 03。
  • 评估要不要上生产 → 本页 §6 优先,再看 04/05 的实验性标记。

5. 巧妙之处(可借鉴的技术)

这七条是 SK 值得带走的东西。每条先说妙在哪,再给出处。

5.1 提示词与代码同构成同一种可调用单元

妙在哪: 大多数框架里「提示词模板」和「工具函数」是两种东西,各有各的注册表。SK 让它们都是 KernelFunction —— 于是一段提示词可以被模型当工具调用,一个 Python 方法也可以被写进提示词模板里调用。

KernelFunction(functions/kernel_function.py:78)是抽象基类,官方只有两个实现:KernelFunctionFromPromptKernelFunctionFromMethodinvoke(:240)在基类上,两边共享同一套追踪、计时、过滤器管线。

这条主线还有第三、第四个例子:每个 Agent 出生自带一个 KernelFunction 外壳(见 §5.4),TextSearch.create_search_function(data/text_search.py:66)把一次检索也返回成 KernelFunction提示词、原生代码、agent、检索——四种完全不同的东西,对外是同一个类型。

代价: 抽象泄漏在 is_prompt 这个属性上(functions/kernel_function.py:192)——总有地方要区分两者。

5.2 把工具调用的报错当成消息喂回模型,而不是抛异常

妙在哪: 模型调错工具是常态,不是异常。SK 的处理是把错误写成一条 tool result 消息追加进对话,下一轮模型自己看到「你参数写错了」,自己改。整个过程不 raise。

kernel.py:358-424 里有四种这样的「软失败」,每一道失败后都不 raise,而是把一句人话写成 tool 结果塞回对话历史,然后返回 None 让循环继续:

情况喂回模型的话(节选)
工具名不存在is not part of the provided tools, please try again...(kernel.py:363)
缺参数/多参数Missing required argument(s): ...(:386)+ Please revise the arguments to match the function signature.(:389)
参数不是合法 JSONThe tool call arguments are malformed. Arguments must be in JSON format. Please try again.(:405)
必填参数个数不够明确告诉它需要哪几个(:410-424,消息文本在 :412-417)

连工具本身抛的异常也被吞成文本:_inner_auto_function_invoke_handler(kernel.py:465)捕获所有 Exception,把 An error occurred while invoking the function ...: {exc} 当成函数返回值(:477-484)。

这是「自愈式 tool loop」最省事的实现,值得抄。它也解释了为什么循环上限是 5——留给模型几次自我纠错的机会。反面是:真正的基础设施故障(数据库挂了)也会被当成「模型该重试的事」,白烧 token。

5.3 三类过滤器的洋葱管线,核心只有 5 行

妙在哪: 中间件通常要一个复杂的 pipeline 类。SK 用 functools.partial 把列表折成嵌套闭包,就完事了。construct_call_stack(filters/kernel_filters_extension.py:108-118)连 def 与 docstring 共 11 行,核心是这 5 行:

# python/semantic_kernel/filters/kernel_filters_extension.py:114-118
stack: list[Any] = [inner_function] # 最里层:真正干活的
for _, filter in getattr(self, FILTER_MAPPING[filter_type]):
filter_with_next = partial(filter, next=stack[0]) # 把「下一层」绑进去
stack.insert(0, filter_with_next) # 新的一层裹在外面
return stack[0] # 返回最外层,调一次即穿透全部

每个过滤器签名是 (context, next),调 await next(context) 前后各能插一段代码 —— 标准洋葱。三类分别包在三个位置:

过滤器类型包住什么拼栈位置
PROMPT_RENDERING提示词渲染functions/kernel_function_from_prompt.py:281
FUNCTION_INVOCATION任何一次 KernelFunction.invokefunctions/kernel_function.py:274
AUTO_FUNCTION_INVOCATION循环里的每一次工具执行kernel.py:444

注意执行顺序的坑:add_filter 用的是 insert(0, ...)(filters/kernel_filters_extension.py:56),配合上面的 insert(0, ...),最终效果是「先注册的先执行、也最后返回」——docstring 在 :40-43 明确说了这个语义。

配套还有个短路能力:PromptRenderContext.function_result(filters/prompts/prompt_render_context.py:11)一旦被赋值,模型压根不会被调用(functions/kernel_function_from_prompt.py:170-176);AutoFunctionInvocationContext.terminate(filters/auto_function_invocation/auto_function_invocation_context.py:14)置 True,整个循环立刻收尾(chat_completion_client_base.py:169)。「缓存」和「提前终止」在这里不是两个功能,是同一个机制的两种用法。

5.4 每个 Agent 自动获得一个 _as_kernel_function,于是 agent 即工具

妙在哪: 每个 Agent 在 pydantic 的 model_post_init 里当场生成一个 kernel function(agents/agent.py:295-319):

# agents/agent.py:298-319(节选)
@kernel_function(name=self.name, description=self.description or self.instructions)
async def _as_kernel_function(messages, instructions_override=None) -> Any:
response_item = await self.get_response(messages=messages, ...)
return response_item.content
setattr(self, "_as_kernel_function", _as_kernel_function)

agent 的 name 变工具名、instructions 变工具描述(当没写 description 时)。于是把 agent B 注册进 agent A 的 kernel,A 就「看得见」B 了,走的还是普通的自动函数调用循环 —— 不需要第二套机制。

一句要紧的更正: 这把工具柄是「agent 即工具」和 as_mcp_server 的基础,但第 05 章的 handoff 编排并没有复用它HandoffAgentActor._add_handoff_functions(agents/orchestration/handoffs.py:190-222)自己造了一批名为 transfer_to_<名字> 的函数(:194),再配一个自动函数调用过滤器把当前 agent 掐停(:229-233)。全仓 grep _as_kernel_function 只有 agents/agent.py:299:319 两处命中。准确说法是:同一个「agent 变函数」的思路在 SK 里有两处独立实现。 详见 04-agents-and-threads.md §4。

5.5 Kernel 既能消费 MCP,也能整体导出为 MCP server

妙在哪: 双向对称。

  • 消费方向:connectors/mcp.py 有一个基类 MCPPluginBase:236 和四个传输实现 —— MCPStdioPlugin:605MCPSsePlugin:681MCPStreamableHttpPlugin:760MCPWebsocketPlugin:843,把远端 MCP 工具变成本地 KernelPlugin
  • 导出方向:kernel.py:579 as_mcp_server 一行把整个 kernel 的所有函数暴露成 MCP tools、把提示词模板暴露成 MCP prompts,返回标准的 mcp.server.lowlevel.Server

Agent 上也有同名方法(agents/agent.py:559)。结果是 SK 应用可以互相嵌套 —— 一个 SK kernel 消费另一个 SK kernel 导出的 MCP server。

5.6 Kernel.clone() 只深拷元数据,不深拷 callable

kernel.py:542-576。插件被重建为新的 KernelPlugin,函数经 function_copy() 复制——元数据深拷、可调用对象共享;services 与 selector 另行处理。docstring(:554-556)给了明确理由:插件可能包着 MCP 客户端会话这类含异步生成器、不可 pickle 的对象,深拷会直接炸。

这是个很实际的教训:给对象做「隔离副本」时,先想清楚哪一半必须共享。 handoff 编排正是靠 kernel.clone() 才敢往 agent 的 kernel 上挂交接函数而不污染原对象。

5.7 工具返回值执行完立刻做快照

# python/semantic_kernel/kernel.py:450-452
# Snapshot the tool's return value so later mutations don't leak back
if invocation_context.function_result and invocation_context.function_result.value is not None:
invocation_context.function_result.value = deepcopy(invocation_context.function_result.value)

工具返回一个可变对象(list/dict)时,后续轮次里插件把它改了,已经写进 ChatHistory 的那条记录也会跟着变——对话历史就成了「会自己变的历史」。深拷贝把这条路堵死。


6. 边界与局限(诚实版)

先说结论:地基层可以放心用,第 2 层以上要谨慎,第 3/4 层不要上生产。

6.1 MAF 承接后,agent 侧的定位变了

README.md:3-6 说 MAF 是 SK 的继任者,并给出迁移指南。代码里对应的桥是 as_agent_framework_tool(functions/kernel_function.py:412)——它的方向是单向的:SK function → MAF tool,没有反向的 from_agent_framework_*

意味着:新项目做 agent,起点应该是 MAF;SK 的价值退回到「函数抽象 + 连接器 + 过滤器」这一层,以及作为存量系统的迁移源。仓库里没有 SK agent 层的弃用(deprecation)警告,README 之外也没有停止维护的声明 —— 这是「官方推荐改用别的」,不是「这个不能用了」。

6.2 上层两个子系统全部实验性

子系统实验性程度
agents/orchestration/五个模式文件里每个公开类都挂 @experimental(如 group_chat.py 一个文件里就有 12 处)
processes/该目录下几乎每个文件都含 @experimental,含 dapr_runtimelocal_runtime 两套运行时
数据层 data/vector.py用的是更高一档的 @release_candidate(:277 VectorStoreField:421 VectorStoreCollectionDefinition)

Python 侧全库共 389 处 @experimental。这个数字本身就是「上层还在动」的证据。

6.3 Python 与 .NET 不对齐,且仓库没告诉你哪里不齐

FEATURE_MATRIX.md 是一个空壳——全文只说「已搬到 learn.microsoft.com 的 Supported Languages 页」,仓库内查不到具体差异矩阵。所以:

  • 想知道某个特性在哪个语言可用,必须去外部文档站,克隆里没有答案。
  • .NET 侧有 Python 侧完全没有的顶层目录(如 dotnet/src/VectorDatadotnet/src/Experimental);Python 侧的 data/ 只有 4 个文件。规模差距是 2961 个 .cs : 554 个 .py

6.4 docs/decisions/ 下 77 篇 ADR 是设计意图的一手来源

克隆里 docs/decisions/77 篇编号 ADR(另加 README 与两个模板)。这是理解「为什么这么设计」最可靠的材料,比源码注释密度高得多。和本组文档强相关的几篇:

ADR对应本组哪一章
0007-prompt-extract-template-engine.md0016-custom-prompt-template-formats.md02
0017-openai-function-calling.md0061-function-call-behavior.md0063-function-calling-reliability.md03
0005-kernel-hooks-phase1.md0018-kernel-hooks-phase2.md03(过滤器的前身是 hooks)
0070-declarative-agent-schema.md0072-agents-with-memory.md04
0071-multi-agent-orchestration.md05
0054-processes.md0058-vector-search-design.md0059-text-search.md06
0069-mcp.md§5.5

另有 docs/code_maps/Python.pdfdotNET.pdf 两份官方代码地图(PDF,本文档未解析其内容)。

6.5 其它已知糙点

  • 工具允许名单默认不校验。 invoke_function_call 里,不传 function_behavior 时只打一条 debug 日志就放行(kernel.py:350-356)。真正的名单校验只在传了 FunctionChoiceBehavior 且设了 filters 时才发生(kernel.py:342-349)——那道校验发生在执行之前,专治模型幻觉出的函数名。
  • 循环上限硬编码为 5 轮(function_choice_behavior.py:18)。超限后 SK 会关掉工具再问模型一次(chat_completion_client_base.py:171-174for ... else),可能得到一个「我没法完成」的敷衍回答——但至少一定有话说,不是报错。
  • Java 在本仓库里等于不存在。 别在 java/ 下找代码。

7. 横向对比(同 shelf 的三个兄弟)

四个框架都在解「让模型调工具并多轮推进」这件事,但各自认为最该被抽象的东西不同。本节比的是四个框架的核心抽象;SK Process 与 LangGraph Pregel 在「超步触发机制」层面的逐条差异,见 06-process-and-data.md §6.9。

框架它认为核心抽象是主循环长什么样状态放哪
semantic-kernel可调用单元(提示词与函数同构)连接器基类里的 for 循环,≤5 轮ChatHistory 就地追加,无内置持久化
langgraph图与超步(BSP 式并行推进)超步(superstep)调度,节点按通道触发checkpointer 持久化,可回放、可中断续跑
autogenactor 与消息actor 运行时收发消息,群聊靠 manager 选下一个发言人actor 内部状态 + 运行时
openai-agents-pythonagent 与 handoff单 agent 循环,交接靠把「另一个 agent」当工具session 抽象

取舍差异,三句话:

  • SK vs langgraph: langgraph 把「控制流」抬成一等公民(谁先跑、能不能并行、崩了从哪续),SK 的内核完全不管这些 —— SK 的 orchestration 层(第 3 层)才勉强对应,且是实验性的。要持久化、要人工介入、要断点续跑,SK 给不了,去 langgraph。
  • SK vs autogen: 有趣的是 SK 的 orchestration 层就跑在一个 actor 运行时上(agents/runtime/in_process/in_process_runtime.py:167 InProcessRuntime),思路和 autogen 同源。差别在 autogen 把 actor 当整个框架的地基,SK 只把它当第 3 层的实现细节。
  • SK vs openai-agents-python: 两者都把「agent 变成一个可调函数」来做交接,但 SK 的交接不是复用 _as_kernel_function,而是 handoff 编排现造的一批 transfer_to_<名字>(agents/orchestration/handoffs.py:194);OAI SDK 则是显式的 handoff 原语。SK 更隐式(运行期自动生成,你不用声明),OAI SDK 更显式(handoff 是一等概念,可控性更好)。

一句话选型: 存量 .NET/企业栈 + 需要过滤器式治理 → SK;要复杂控制流与持久化 → langgraph;只想快速搭 OpenAI 上的 agent → openai-agents-python。


8. 全库代码地图(导航索引)

grep 符号名比行号抗漂移,优先用符号名定位。路径均相对克隆根。

主题文件符号
容器与调度入口python/semantic_kernel/kernel.pyKernel
最短入口:提示词直调python/semantic_kernel/kernel.pyKernel.invoke_promptKernel.invokeKernel.invoke_stream
执行单个 tool call(含全部软失败)python/semantic_kernel/kernel.pyKernel.invoke_function_call
工具异常吞成文本python/semantic_kernel/kernel.pyKernel._inner_auto_function_invoke_handler
安全克隆 kernel(不 deepcopy callable)python/semantic_kernel/kernel.pyKernel.clone
整个 kernel 导出为 MCP serverpython/semantic_kernel/kernel.pyKernel.as_mcp_server
可调用单元抽象基类python/semantic_kernel/functions/kernel_function.pyKernelFunctionKernelFunction.invoke
通往 Microsoft Agent Framework 的桥python/semantic_kernel/functions/kernel_function.pyKernelFunction.as_agent_framework_tool
原生方法 → 函数python/semantic_kernel/functions/kernel_function_from_method.pyKernelFunctionFromMethodgather_function_parameters
提示词函数:渲染 + 调模型python/semantic_kernel/functions/kernel_function_from_prompt.pyKernelFunctionFromPrompt._invoke_internal._render_promptfrom_yamlfrom_directory
方法转函数的装饰器python/semantic_kernel/functions/kernel_function_decorator.pykernel_function
参数名片与 JSON Schemapython/semantic_kernel/functions/kernel_parameter_metadata.pyKernelParameterMetadatainfer_schema
插件与四种加载方式python/semantic_kernel/functions/kernel_plugin.pyKernelPlugin.from_object.from_directory.from_openapi.from_python_file
注册插件/函数、白名单过滤python/semantic_kernel/functions/kernel_function_extension.pyadd_pluginadd_functionget_functionget_list_of_function_metadata_filters
入参 / 出参载体python/semantic_kernel/functions/kernel_arguments.pyfunction_result.pyKernelArgumentsFunctionResult
自动函数调用主循环python/semantic_kernel/connectors/ai/chat_completion_client_base.pyChatCompletionClientBase.get_chat_message_contents.get_streaming_chat_message_contents
工具调用策略与 5 轮上限python/semantic_kernel/connectors/ai/function_choice_behavior.pyFunctionChoiceBehaviorDEFAULT_MAX_AUTO_INVOKE_ATTEMPTS
结果合并工具python/semantic_kernel/connectors/ai/function_calling_utils.pymerge_function_resultsmerge_streaming_function_results
三类过滤器的洋葱构造python/semantic_kernel/filters/kernel_filters_extension.pyKernelFilterExtension.construct_call_stack.add_filter
三类过滤器枚举与上下文python/semantic_kernel/filters/FilterTypesPromptRenderContextFunctionInvocationContextAutoFunctionInvocationContext
服务注册与选择python/semantic_kernel/services/kernel_services_extension.pyai_service_selector.pyadd_serviceselect_ai_serviceAIServiceSelector
模板引擎与三种格式python/semantic_kernel/prompt_template/template_engine/PromptTemplateBaseKernelPromptTemplateHandlebarsPromptTemplateJinja2PromptTemplate
对话历史与「渲染结果转对话」python/semantic_kernel/contents/chat_history.pyChatHistory.from_rendered_promptto_prompt
消息与内容项python/semantic_kernel/contents/ChatMessageContentFunctionCallContentFunctionResultContenthistory_reducer/
Agent 外壳与「agent 即工具」python/semantic_kernel/agents/agent.pyAgent.model_post_init(内含 _as_kernel_function)、AgentThreadAgentRegistry.create_from_yaml
最常用的本地 agentpython/semantic_kernel/agents/chat_completion/chat_completion_agent.pyChatCompletionAgent_inner_invokeChatHistoryAgentThread
托管 agent 家族python/semantic_kernel/agents/open_ai/azure_ai/bedrock/copilot_studio/OpenAIAssistantAgentOpenAIResponsesAgentAzureAIAgentBedrockAgent
旧版群聊路径(第 04 章 §9)python/semantic_kernel/agents/channels/agents/group_chat/AgentChannelChatHistoryChannelAgentChatAgentGroupChatBroadcastQueue
五种编排模式python/semantic_kernel/agents/orchestration/ConcurrentOrchestrationSequentialOrchestrationGroupChatOrchestrationHandoffOrchestrationMagenticOrchestration
编排底下的 actor 运行时python/semantic_kernel/agents/runtime/InProcessRuntimeRoutedAgentTypeSubscriptionTopicIdCancellationToken
流程框架构建器与超步执行python/semantic_kernel/processes/ProcessBuilder.add_step.on_input_event.buildLocalProcess.internal_executedapr_runtime/
向量存储模型与检索python/semantic_kernel/data/vector.pyvectorstoremodelVectorStoreFieldVectorStoreVectorStoreCollectionVectorSearch
检索即工具python/semantic_kernel/data/text_search.pyTextSearch.create_search_function
MCP 双向桥(客户端四种传输 + 服务端导出)python/semantic_kernel/connectors/mcp.pyMCPPluginBaseMCPStdioPluginMCPSsePluginMCPStreamableHttpPluginMCPWebsocketPlugincreate_mcp_server_from_kernel
设计意图一手来源(77 篇)docs/decisions/见 §6.4 的对照表
提示词模板样例 / Python 可运行样例prompt_template_samples/python/samples/(目录)
.NET 侧对应实现dotnet/src/SemanticKernel.Core/dotnet/src/Agents/概念对齐、API 不逐一对应

下一步:01-kernel-and-functions.md 开始,把 KernelKernelFunction 拆开看。