如何使用开源的 Semantic Kernel

open-source入门12 分钟阅读2026/7/11

几个月前,我在开发一个客服自动化工具,结果碰壁了。一开始我用 Python 的 LangChain 来搞这个项目,因为嘛,大家都在用。但随着项目越滚越大,这个库那种“狂野西部”般的随意性就开始坑我了。到处都是抽象泄漏,一更新代码就挂,说实话,缺乏强类型让重构简直成了噩梦。我花在跟框架较劲上的时间,比写业务功能的时间还多。

我需要点更有条理的东西。就在那时,我偶然发现了 Semantic Kernel(SK),微软开源的 SDK。一开始我是持怀疑态度的——微软的 AI 工具有时候感觉就像个围墙花园,变着法儿把你往 Azure 上引。但我发现,它是一个真正与模型无关、基于 MIT 协议的工具包,感觉更像是企业级中间件,而不只是个凑合的封装层。下面我就来说说,我是怎么用完全开源的模型把它跑起来的,根本不需要 API Key。

为什么选 Semantic Kernel?

Semantic Kernel 的核心卖点很简单:它是一个轻量级中间件,位于你的代码和 AI 模型之间。你可以把它当成一个翻译官。模型说“我需要查一下客户的订单”,SK 就会把它翻译成你代码库中实际的函数调用,执行完毕后,再把结果交回给模型。

真正让我心动的是它对 v1.0+ 版本的承诺。无论是 C#、Python 还是 Java,SDK 都非常稳定,并承诺不会有破坏性更新。在受够了 LangChain 那种没完没了的折腾之后,这种可靠性简直让人神清气爽。另外,它开箱即用就是与模型无关的。你可以用 OpenAI、Azure OpenAI、Hugging Face,或者——我最喜欢的——通过兼容接口在本地跑开源模型。

搭建环境

我想向自己证明,SK 并不只是个通向 Azure 的引路人。所以我决定用本地 LLM 完全离线构建一个简单的 Agent。为此,我用了 LM Studio,这应用超赞,可以让你在本地下载并运行开源模型,而且还能暴露一个兼容 OpenAI 的 API 服务器。

首先,我下载了 LM Studio 并拉了一个模型。我选了 Mistral-7B-Instruct,因为它足够小,能在我的 16GB 内存笔记本上跑起来,而且处理函数调用也还算凑合。

在 LM Studio 里下载好模型后,我启动了本地服务器:

  1. 点击 LM Studio 里的 "Local Server" 标签页
  2. 加载你的模型
  3. 点击 "Start Server"

默认情况下,它跑在 http://localhost:1234/v1 上。这就是最神奇的地方——因为它模拟的是 OpenAI API 的格式,Semantic Kernel 可以直接跟它对话,根本不知道对面其实不是真正的 OpenAI。

用 Python 构建 Kernel

我选了 Python SDK 来做这个项目,因为我团队的后端是 Python。安装很简单:

pip install semantic-kernel

接下来,就是我踩的第一个坑。我一开始照着网上过时的教程,试着用老的 kernel.add_text_service() 方法来配置连接。这个方法在 v1.0+ 版本里已经废弃了。正确的做法是使用异步设置,并把 OpenAI 连接器指向我们的本地服务器。

下面是能跑通的配置代码:

import semantic_kernel as sk
from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion

async def initialize_kernel():
    kernel = sk.Kernel()
    
    # 指向本地 LM Studio 服务器,而不是 OpenAI
    chat_service = OpenAIChatCompletion(
        ai_model_id="local-model",  # 随便起个名就行,LM Studio 会忽略这个
        endpoint="http://localhost:1234/v1",
        api_key="not-needed",  # LM Studio 不需要密钥,但 SK 必须传一个
    )
    
    kernel.add_service(chat_service)
    return kernel

那个 api_key="not-needed" 的地方坑了我一下。SK 的 OpenAI 连接器要求必须传个 API Key 参数,哪怕你的本地服务器根本不用。随便传个字符串进去就能正常工作了。

创建你的第一个插件

Semantic Kernel 的真正威力在于插件。插件是你把现有代码暴露给 AI 模型的方式。你描述一下你的函数是干嘛的,模型就会自己决定什么时候该调用它。

假设我想让我的 Agent 查找客户订单。我会像这样写个插件:

from semantic_kernel.functions.kernel_function_decorator import kernel_function

class OrderPlugin:
    @kernel_function(
        description="Looks up a customer's order by their order ID",
        name="get_order"
    )
    def get_order(self, order_id: str) -> str:
        # 实际开发中,这里应该是查询数据库
        orders = {
            "ORD-123": "Status: Shipped - 2 items, arriving tomorrow",
            "ORD-456": "Status: Processing - 1 item, estimated ship date Friday",
            "ORD-789": "Status: Delivered - 3 items, delivered on Monday"
        }
        return orders.get(order_id, f"No order found with ID {order_id}")

@kernel_function 装饰器是关键。description 是给模型看的——模型靠它来决定要不要调用这个函数。描述一定要具体、清晰;模型完全依赖这些描述来判断什么时候该用这个工具。

现在,把这个插件注册到 kernel 里:

kernel = await initialize_kernel()
kernel.add_plugin(OrderPlugin(), plugin_name="orders")

运行 Agent

kernel 配置好了,插件也注册了,咱们来实际跑个对话看看:

from semantic_kernel.contents.chat_history import ChatHistory
from semantic_kernel.functions.kernel_arguments import KernelArguments

async def chat_with_agent(user_input: str, chat_history: ChatHistory):
    chat_function = kernel.add_function(
        plugin_name="orders",
        function_name="get_order"
    )
    
    # 开启自动函数调用
    settings = kernel.get_prompt_execution_settings_from_service_id("local-model")
    settings.auto_invoke_kernel_functions = True
    
    chat_history.add_user_message(user_input)
    
    result = await kernel.invoke_prompt(
        prompt="{{$chat_history}}",
        arguments=KernelArguments(chat_history=chat_history),
        template_format="semantic-kernel",
        settings=settings
    )
    
    return str(result)

# 测试一下
chat_history = ChatHistory()
chat_history.add_system_message(
    "You are a customer support agent. Help customers with their orders."
)

response = await chat_with_agent(
    "Can you check on my order ORD-123?",
    chat_history
)
print(response)

我第一次跑这段代码的时候,啥也没发生——模型只是回了一段文字说“我帮您查一下订单”,但根本没去调用函数。排查了一阵后我才发现,我忘了开启 auto_invoke_kernel_functions。这个设置就是告诉 SK,当模型请求调用函数时,真的去执行插件函数。没它的话,模型只会说它调用某个函数,但 SK 不会真的去执行。

开启这个设置后,整个流程就完美了:

  1. 用户询问订单 ORD-123
  2. 模型意识到需要调用 get_order 函数
  3. SK 拦截这个请求,调用 OrderPlugin.get_order("ORD-123")
  4. 结果("Status: Shipped...")被传回给模型
  5. 模型组织出自然的回复:“您的订单 ORD-123 目前已发货,预计明天送达!”

实话实说的局限性

咱们来聊聊用 Semantic Kernel 和开源模型时遇到的一些取舍:

小模型做函数调用不太稳定。 Mistral-7B 能处理基本的函数调用,但有时候会瞎编参数或者调错函数。如果你要构建生产级且依赖工具调用的应用,大概率需要更大的模型,比如 Mixtral-8x7B 或 Llama-3-70B,这就意味着需要更好的硬件或者去租 GPU 算力。

文档还有待完善。 微软的文档严重偏向 Azure OpenAI 的例子。想找到本地开源接口的正确配置,得去 GitHub 的 issue 和源码里刨根问底。社区挺活跃的,但你确实得花时间去挖。

Python SDK 感觉像是从 C# 搬过来的。 如果你是 C# 开发者,用 SK 会觉得很顺手。但在 Python 里,有些模式相比原生的 Python 写法显得太啰嗦了。如果你只是写个简单的同步脚本,这种“万物皆异步”的路子也挺让人头疼的。

提示词模板语法跟 LangChain 不一样。 如果你是迁移过来的,得学学 SK 的 {{$variable}} 语法和模板格式。不难,但毕竟是个新东西,得学。

实用建议

用了 SK 几个月,这是我的一些建议:

  1. 本地开发先用 LM Studio。 在你做实验的时候,它能帮你省下 API 费用,而且它兼容 OpenAI 的服务器意味着,以后你把代码指向真正的 OpenAI 或 Azure 接口时,SK 代码完全不用改就能跑。

  2. 函数描述一定要写得极其清晰。 模型能不能正确使用你的插件,完全取决于你描述得怎么样。写“Gets order”是不及格的。写“Retrieves the current status and details of a customer order using a unique order ID in the format ORD-XXX”(使用 ORD-XXX 格式的唯一订单 ID 获取客户订单的当前状态和详情)才是好描述。

  3. 用钩子和过滤器来做可观测性。 SK 内置了钩子,能让你记录模型发起的每一次函数调用。当我的 Agent 跑偏的时候,这功能帮我省了几个小时的调试时间。早点把日志钩子加上,别等出了问题才补。

  4. 别跟异步较劲。 乖乖接受 SK 就是异步优先的事实吧。如果你需要在同步代码里调用它,用 asyncio.run() 就行。非要绕着走只会让你更头疼。

Semantic Kernel 不是 AI SDK 领域里最花哨的选项,但对于那些需要真正落地并稳定运行的项目来说,它已经成了我的首选。与模型无关的设计意味着,我可以在本地用开源模型开发,然后部署到任何适合生产的接口上,完全不用重写编排逻辑。光凭这一点,就值得你去花时间学它。

相关 Agent

M

Meta AI

Meta AI 是一个开源AI平台,用于研究和开发先进的语言模型及生成式AI工具。

了解更多 →