4、Semantic Kernel提示词模板

4、Semantic Kernel提示词模板

提示词工程

为了大模型的结果更可预测,最简单有效的办法之一就是应用提示工程,其中最常见且有效的方法如下:

  1. 使提示更具体
  2. 使用格式化为输出添加结构
  3. 提供带有少量提示的示例
  4. 告诉AI该怎么做以避免做错什么(设定据答策略)
  5. 为AI提供上下文
  6. 在聊天完成提示中使用角色
  7. 鼓励AI
  8. 运用思维链
  9. 使用更优秀的模型

理解 LLM 参数

LLM 本质上是一个统计预测引擎,通过预测下一个最有可能出现的词(token)*[最新的MTP技术,已经支持一次可以预测多个Token]*,来生成连贯的文本。它并不是在‘理解’文本本身的含义,而是依靠在海量语料中学到的统计模式,预测哪些词在给定上下文中最可能出现。这种预测能力使它能够完成从写诗、写代码到回答问题等各种任务。

而 LLM 中的配置参数,就是用来控制模型在生成文本时的行为,比如生成的多样性、回答的长度、逻辑的一致性等。这些配置选项包括例如 temperature(温度系数)、top-k 和 top-p(核采样)等,它们决定了模型在预测下一个词时,是更保守(只选概率最高的词)还是更自由(允许一些低概率但合理的词被选中)。调整这些参数可以让生成的结果更有创意或更稳重,视具体应用需求而定。

提示工程中的LLM的参数配置就像给AI装”方向盘”,能精准控制回答的风格、长度和内容边界。

📝【配置参数表】

下面用更通俗的比喻来解释这四个常用参数,并给出推荐取值范围:

参数通俗含义作用建议取值范围
temperature 温度就像调味料的辣度:温度高,味道更“刺激”(输出更有创意、更多变);温度低,味道更“清淡”(答案更稳定、可预测)。控制生成时对高概率词和低概率词的偏好:温度越高,模型越敢选小概率词;越低,就只挑最稳妥的词。- 需要精确、正式内容时:0.0–0.3
- 希望一点创意但不发散:0.4–0.7
- 追求多样、灵感写作:0.8–1.2
top‑p 核心采样好比“先从最有人气的菜开始,选到菜品总热度达到 p% 为止”,动态决定看多少道菜。按概率从大到小累加,直到累计概率 ≥ p,再在这些词里随机挑;保证既不错又有余地创新。- 平衡稳妥与多样:0.8–0.9
- 更保守:0.7–0.8
- 更开放:0.9–0.95
top‑k 限制候选就是“只列出最热门的前 k 道菜,让你在这 k 道里选”,不管它们的累积热度。直接把词表裁剪到概率最高的 k 个,再在其中随机;简单粗暴地控制候选范围。- 日常对话:k=40–60
- 更精炼:k=10–20
- 极度探索:k=80–100
max‑token 最大长度类似“这顿饭最多只能吃多少碗饭”,给回答的字数、长度划个上限,防止跑太长。限制模型输出的最大 token 数(1 token ≈0.75 个汉字),输出到达上限就停。- 简短回答:100–200
- 普通段落:300–500
- 详细说明:800–1,500

小贴士

  • 这四个参数可以混合使用,例如 temperature=0.7 + top-p=0.9 + max‑token=500,既保留一定创造力,又能确保答案长度合适。
  • 如果你发现答案太乏味,就可以提高 temperature 或 top‑p;太发散、不切题,就降低 temperature、top‑p 或 top‑k,甚至同时加上 max‑token 限制长度。

理解top-p

这里再用一个简单的例子,逐步说明 top‑p(nucleus sampling)是怎么工作的:

  1. 先给所有候选词按概率从高到低排序
    假设模型预测下一个词的概率分布(简化成五个词):

    1
    2
    3
    4
    5
    6
    词语    概率
    “我” 0.40
    “你” 0.25
    “他” 0.15
    “她” 0.10
    “它” 0.10
  2. 设定 p,比如 p = 0.8(80%)

  3. 累加最高概率词,直到累计≥ p

    • 首先选 “我”,累计概率 = 0.40
    • 再加上 “你”,累计 = 0.40 + 0.25 = 0.65
    • 再加上 “他”,累计 = 0.65 + 0.15 = 0.80 → 刚好够 80%
  4. 把这三词 (“我”、“你”、“他”) 作为采样候选集

  5. 在候选集内按它们的相对概率重新采样

    • 在 {“我”:0.40, “你”:0.25, “他”:0.15} 里随机取一个,概率比例变成 {0.40/0.80, 0.25/0.80, 0.15/0.80}

这样做的好处是:

  • 动态:候选集大小不固定,会根据分布“自动”选最重要的几项;
  • 平衡:既保留了高概率词,又允许偶尔选到概率稍低的“新意”词。

相比之下,top‑k 是不管累积概率,只固定挑前 k 个词;而 top‑p 则是 “选出最能覆盖 p 概率质量的最小词集”。

常见任务参数配置参考:

任务类型temperaturetop‑ptop‑k说明
信息问答(Fact QA)0.0–0.20.8–0.920–40强调准确性与一致性,避免“胡编”
技术写作/代码生成0.2–0.50.8–0.940–60保证逻辑严谨,减少语法或 API 错误
文本摘要(Summarization)0.0–0.30.8–0.940突出关键信息,保持简洁
创意写作/故事生成0.8–1.20.9–1.050–100更大胆地探索多样化表达,激发灵感
自然对话/聊天机器人0.6–0.80.950在连贯和多样间取得平衡,保持对话自然
翻译/数学计算/推理(Math Calculation)0.01.01这类任务,避免任何随机性,保证模型沿着概率最高的路径输出最确定的解答

工程运用

假设现在需要构建一个铁路票务对话系统,识别用户意图(订票/退票/咨询)并驱动后端服务。

1
2
3
4
5
6

using PolyglotKernel= Microsoft.DotNet.Interactive.Kernel;// 引入交互式的内核命名空间,以便用户输入
var aiProviderCode = await PolyglotKernel.GetInputAsync("请输入AI服务提供商编码:");

var kernel = GetKernel(aiProviderCode);
var chatCompletionService = kernel.GetRequiredService<IChatCompletionService>();

设定参数

对于意图识别这类对“分类”或“结构化输出”任务,关键是最大程度降低随机性,保证结果的一致性和可复现性。

推荐参数配置(确保稳定性)

模式temperaturetop_ptop_k
完全贪心0.01.01
轻度采样0.10.950
  • 完全贪心:绝对一致,适用于生产环境的意图分类。
  • 轻度采样:少量多样性,适合提示调优和边缘场景测试。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
using Microsoft.SemanticKernel.Connectors.OpenAI;

var executionSettings = new OpenAIPromptExecutionSettings()
{
Temperature = 0.1f,
MaxTokens = 50,
TopP = 0.9f
};

async Task Chat(string userPrompt)
{
var response = await chatCompletionService.GetChatMessageContentAsync(userPrompt, executionSettings);
response.Display();
}

使提示更具体

1
var userRequest = "帮我订一张从北京到上海的票,明天出发。";
1
2
3
4
5
6
7
var originPrompt =$"""
请帮我识别用户意图:
用户请求是:{userRequest}。
意图:
""";

await Chat(originPrompt);

用户的意图是:预订交通票务

从以上的答复看,虽然限制了max-token,但回复还是有点啰嗦,因此可以加上提示词:仅回复意图,无需解释,让模型不要啰嗦。

1
2
3
4
5
6
7
var optimizedPrompt =$"""
请帮我识别用户意图,仅返回意图,不要解释。
用户请求是:{userRequest}。
意图:
""";

await Chat(optimizedPrompt);

订票

对于意图识别应用,意图一般是明确的,因此可以进一步限定范围:

1
2
3
4
5
6
7
8
9
10
11
12
var optimizedPrompt = $"""
用户会输入与票务相关的文本信息,你的任务是从输入中识别以下三种意图之一:

* 订票意图:用户希望购买或预订车票;
* 退票意图:用户希望退掉已购买的车票;
* 咨询意图:用户只是询问车票相关信息,不涉及购买或退票。

用户请求是:{userRequest}。
意图:
""";

await Chat(optimizedPrompt);

订票意图

格式化输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var optimizedPrompt = $@"
用户会输入与票务相关的文本信息,你的任务是从输入中识别以下三种意图之一:

* 订票意图:用户希望购买或预订车票;
* 退票意图:用户希望退掉已购买的车票;
* 咨询意图:用户只是询问车票相关信息,不涉及购买或退票。

请你以如下JSON格式返回识别结果:
{{
""intention"": ""<订票意图/退票意图/咨询意图>"",
""reason"": ""<简要说明识别此意图的原因>""
}}

用户请求是:{userRequest}
意图:
";


await Chat(optimizedPrompt);

1
2
3
4
{
"intention": "订票意图",
"reason": "用户明确表示希望购买从北京到上海的票,并指定了出发日期。"
}

提供带少量提示的示例

根据实际效果,示例,可以从0到1到多个。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
var optimizedPrompt = $@"
用户会输入与票务相关的文本信息,你的任务是从输入中识别以下三种意图之一:

* 订票意图:用户希望购买或预订车票;
* 退票意图:用户希望退掉已购买的车票;
* 咨询意图:用户只是询问车票相关信息,不涉及购买或退票。

请你以如下JSON格式返回识别结果:
{{
""intention"": ""<订票意图/退票意图/咨询意图>"",
""reason"": ""<简要说明识别此意图的原因>""
}}

<example>
用户请求:我的票能退吗?
意图:
{{
""intention"": ""退票意图"",
""reason"": ""询问是否可以退票""
}}
</example>


用户请求是:{userRequest}
意图:
";

await Chat(optimizedPrompt);

设定角色

1
2
3
4
5
6
async Task ChatWithHistory(ChatHistory chatHistory)
{
var response = await chatCompletionService.GetChatMessageContentAsync(chatHistory, executionSettings);
response.Display();
chatHistory.AddAssistantMessage(response.Content);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
var optimizedPrompt = $@"
你是一名铁路票务意图识别专家,负责在用户界面与后端服务之间准确判断用户意图。

* 订票意图:用户希望购买或预订车票;
* 退票意图:用户希望退掉已购买的车票;
* 咨询意图:用户只是询问车票相关信息,不涉及购买或退票。

请你以如下JSON格式返回识别结果:
{{
""intention"": ""<订票意图/退票意图/咨询意图>"",
""reason"": ""<简要说明识别此意图的原因>""
}}

<example>
用户请求:我的票能退吗?
意图:
{{
""intention"": ""退票意图"",
""reason"": ""询问是否可以退票""
}}
<example>

";

var chatHistory = new ChatHistory();
chatHistory.AddSystemMessage(optimizedPrompt);
chatHistory.AddUserMessage(userRequest);

await ChatWithHistory(chatHistory);

提供上下文

在某些情况下,您可能希望为AI提供上下文,以便它更好地理解用户的请求。这对于长时间运行的聊天场景尤其重要用户的意图可能需要来自先前消息的上下文,也或是来自于系统。
历史消息也属于上下文的一种,下面的示例中在系统提示词中预设了系统当前时间和用户Id等信息,提供会话级上下文。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
var optimizedPrompt = $@"
你是一名铁路票务意图识别专家,负责在用户界面与后端服务之间准确判断用户意图。

* 订票意图:用户希望购买或预订车票;
* 退票意图:用户希望退掉已购买的车票;
* 咨询意图:用户只是询问车票相关信息,不涉及购买或退票。

请你以如下JSON格式返回识别结果:
{{
""intention"": ""<订票意图/退票意图/咨询意图>"",
""reason"": ""<简要说明识别此意图的原因>""
}}

<context>
系统当前时间:2025年4月20日 18点00分
用户Id:shengjie
</context>

<example>
用户请求:我的票能退吗?
意图:
{{
""intention"": ""退票意图"",
""reason"": ""询问是否可以退票""
}}
</example>

";

var userRequest = "帮我订一张从北京到上海的票,后天出发。";

var chatHistory = new ChatHistory();
chatHistory.AddSystemMessage(optimizedPrompt);
chatHistory.AddUserMessage(userRequest);

await ChatWithHistory(chatHistory);

设定据答策略

虽然大模型是基于互联网上的大量数据进行训练而成,对现实世界有所了解,但它仍旧存在知识盲区,如果它没有接入互联网接入,则它对时事也一无所知。为了有效避免“幻觉”,避免模型一本正经的胡说八道,可以通过设定拒答策略给模型一个出口。即让模型在没有把握的时候拒绝回答问题,提高生成数据的质量。设定拒答策略很简单,仅需在提示词中加入类似以下的文字即可:

  1. 如果您不确定或没有足够的信息来提供可靠的答案,只需说“我不知道”或“我不确定”。
  2. 只有当你知道答案或能够做出有根据的预测时,才能回答下面的问题,否则,请告诉我你不知道答案。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
var optimizedPrompt = $@"
你是一名铁路票务意图识别专家,负责在用户界面与后端服务之间准确判断用户意图。

* 订票意图:用户希望购买或预订车票;
* 退票意图:用户希望退掉已购买的车票;
* 咨询意图:用户只是询问车票相关信息,不涉及购买或退票。

仅从以上三种意图中选择,不要新增意图;
若无法明确判断,则默认“咨询意图”,理由写“输入不够明确,默认为咨询”。

请你以如下JSON格式返回识别结果:
{{
""intention"": ""<订票意图/退票意图/咨询意图>"",
""reason"": ""<简要说明识别此意图的原因>""
}}

<example>
用户请求:我的票能退吗?
意图:
{{
""intention"": ""退票意图"",
""reason"": ""询问是否可以退票""
}}
<example>

";

var userRequest ="今天天气不错,适合去哪旅游呢?";

var chatHistory = new ChatHistory();
chatHistory.AddSystemMessage(optimizedPrompt);
chatHistory.AddUserMessage(userRequest);

await ChatWithHistory(chatHistory);
1
2
3
4
{
"intention": "咨询意图",
"reason": "输入不够明确,默认为咨询"
}

使用思维链

思维链(Chain-of-Thought, CoT)用于增强大型语言模型(Large Language Models, LLMs)在复杂推理任务上的性能。思维链提示技术通过鼓励模型生成一系列推理步骤来解决多步骤的问题。这种方法使得模型能够将复杂问题分解为更小的、相互关联的子问题,从而提高模型在数学、常识、推理等方面的准确性。简单的做法就是在每个提示词后面添加一句让我们一步一步思考。对于复杂任务,可以明确告诉模型按照指定流程进行执行。

如何使用两个容积分别为5升和6升的水壶从池塘取3升水?

⬇⬇⬇⬇

如何使用两个容积分别为5升和6升的水壶从池塘取3升水?让我们一步一步思考。

没有输出,思考就不会发生!模型必须输出其思维才能真正“思考”。

也就是说,如果将提示词改写为:使用两个容积分别为5升和6升的水壶从池塘取3升水需要几步?让我们一步一步思考。请给出最终结果,不要解释。 可能会影响得到正确的结果。

让大模型帮忙优化提示词

通过提供初始提示词并要求大模型优化,可以获得更高效、更准确的提示。例如:

  1. 提供初始提示词:明确任务目标和上下文。
  2. 要求大模型优化:通过提示词让模型生成更简洁或更具体的版本。
  3. 验证优化效果:测试优化后的提示词是否提升了生成质量。

这种方法可以反复迭代,直到提示词达到最佳效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var userPrompt = optimizedPrompt;
var prompt = $"""
你是一名提示工程师,专注于提升提示词的清晰度、具体性和执行效果。请按照以下要求对我提供的原始提示进行优化:

1. **输出格式**
- **优化后提示**:给出改写并增强后的版本
- **改进说明**:简要说明优化了哪些方面(如更具体、结构化、避免歧义等)
- **进一步建议**:列出 2–3 条可选的改进思路或注意点

2. **优化目标**
- 用词精准,避免模糊或冗长
- 明确受众与任务边界
- 结构清晰,易于模型解析

---

原始提示: {userPrompt}

""";

var response = await kernel.InvokePromptAsync<string>(prompt);

response.Display();

使用更优的模型

大模型每一刻都在学习,AI 服务提供商不断推出更优秀的大模型,因此对于同样的问题,在不同的大模型表现会各不相同,因此为了更优的性能表现,推荐尝试更优的模型。

详情参见Prompt Engineering-提示工程,值得注意的是在SemanticKernel中定义角色是这样定义的

1
2
3
<message role="user"></message>
<message role="system"></message>
<message role="assistant"></message>

测试准备工作

1
2
//根据上面的文章获取IChatCompletionService
var chatCompletionService = kernel.GetRequiredService<IChatCompletionService>();

内联提示词

内联提示词,顾名思义,提示词直接写在了代码中,对于简单的提示词,直接内联在代码中,无可厚非,对于复杂的提示词将不太好进行代码管理,尤其是还要涉及一些参数配置。如下就是一个将文案改写为小红书风格的提示词的内联提示词的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using Microsoft.SemanticKernel.Connectors.OpenAI;

var article = """
9月6日16时20分,今年第11号超强台风“摩羯”在海南文昌沿海登陆。超强台风“摩羯”风力有多大?记者只能“抱团”出镜。
""";

var prompt = $"""
请使用 Emoji 风格编辑以下段落,该风格以引人入胜的标题、每个段落中包含表情符号和在末尾添加相关标签为特点。
请确保保持原文的意思。
{article}
""";
// 参数配置
PromptExecutionSettings settings = new OpenAIPromptExecutionSettings(){
Temperature = 1,
TopP = 1,
FrequencyPenalty = 0,
PresencePenalty = 0
};

settings.Display();

var response = await chatCompletionService.GetChatMessageContentAsync(prompt, settings);
response.Content.Display();
🌪️🌊 超强台风“摩羯”来袭!🌬️🏝️

9月6日16时20分,超强台风“摩羯”(今年第11号)🌀猛烈袭击了海南文昌沿海。风力到底有多强?💨记者们只能“抱团”出镜报道,现场形势严峻!📹🙈

#台风摩羯 #海南天气 #记者报道
参数名称类型是否必需默认值描述
max_tokens数字可选null生成的最大标记数。提示的标记数加上max_tokens不能超过模型的上下文长度。
temperature数字可选1采样温度。值越高,模型越大胆。适合创意写作或需要明确答案的应用。
top_p数字可选1核采样方法,仅考虑累积概率质量大于或等于top_p的最小标记集。
presence_penalty数字可选0介于-2到2之间的值,惩罚新标记是否出现在文本中,减少其出现的可能性。
frequency_penalty数字可选0介于-2到2之间的值,惩罚新标记在文本中的现有频率,减少其重复的可能性。

定义变量

在以上的内联提示词中我们使用的是字符串拼接的方式构造的提示词,但在SK中其提供了特定的语法{{$variableName}}进行变量定义,举例而言,以上示例可以改写为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var prompt = """
请使用 Emoji 风格编辑以下段落,该风格以引人入胜的标题、每个段落中包含表情符号和在末尾添加相关标签为特点。
请确保保持原文的意思。
{{ $article }}
""";
// 参数配置
PromptExecutionSettings settings = new OpenAIPromptExecutionSettings(){
Temperature = 1,
TopP = 1,
FrequencyPenalty = 0,
PresencePenalty = 0
};

var rewriteFunction = kernel.CreateFunctionFromPrompt(prompt, settings);
var article = """
9月6日16时20分,今年第11号超强台风“摩羯”在海南文昌沿海登陆。超强台风“摩羯”风力有多大?记者只能“抱团”出镜。
""";

var rewriteResult = await kernel.InvokeAsync(rewriteFunction, new() { ["article"] = article });

以上示例,如果不需要指定设置的情况下,也可以简写为以下模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
var prompt = """
请使用 Emoji 风格编辑以下段落,该风格以引人入胜的标题、每个段落中包含表情符号和在末尾添加相关标签为特点。
请确保保持原文的意思。
{{$article}}
""";

var article = """
9月6日16时20分,今年第11号超强台风“摩羯”在海南文昌沿海登陆。超强台风“摩羯”风力有多大?记者只能“抱团”出镜。
""";

var result = await kernel.InvokePromptAsync(prompt, new() { ["article"] = article });

Console.WriteLine(result);
🌪️ **超强台风“摩羯”登陆海南!** 🌀

9月6日16时20分,今年的第11号超强台风“摩羯”在海南文昌沿海地区强势登陆。🌊 风力强劲到何种程度?记者们只能紧紧“抱团”出现在镜头前。📸 #台风#海南#强风

文件模板提示词

在上面的示例中演示了如何创建和运行内联提示并使用变量。但是,在大多数情况下,需要在单独的文件中创建提示词,以便可以轻松地将它们导入到多个Sk的项目中的中并进行共享。

txt format prompt template (skprompt.txt & config.json)

接下来就来介绍下如何将提示词使用txt格式模板化并保存到文件中。首先创建以下的目录文件结构。

1
2
3
4
5
6
Prompts

└─── RewriteRedBookStyle
|
└─── config.json
└─── skprompt.txt

其中RewriteRedBookStyle 就代表一个Plugin,具体的skprompt.txt 就代表一个语义函数(Semantic Function),这个后续讲解Plugin 再具体讲解。

其中skprompt.txt用来定义具体的提示词,config.json用来进行具体的配置。

首先将上面小红书的提示词保存到skprompt.txt 的文件中:

1
2
请使用 Emoji 风格编辑以下段落,该风格以引人入胜的标题、每个段落中包含表情符号和在末尾添加相关标签为特点。请确保保持原文的意思。
{{$article}}

在将config.json 配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"schema": 1,
"type": "completion",
"description": "A function that rewrite with Emoji style",
"execution_settings": {
"default": {
"max_tokens": 1000,
"temperature": 0
}
},
"input_variables": [
{
"name": "article",
"description": "The user's request.",
"required": true
}
]
}

其中配置的参数解释如下:

英文术语中文翻译描述
type类型提示的类型。在这种情况下,我们使用的是completion 类型。
description描述提示的作用描述。用于自动编排计划与功能。
completion完成设置完成模型的设置。对于OpenAI模型,包括最大令牌数和温度属性。
max_tokens最大令牌数模型生成的最大令牌数。
temperature温度控制生成文本的随机性。
input输入定义提示中使用的变量(例如,输入)。

接下来就可以通过以下方式来使用文件中定义的提示词模板:

1
var kernelPlugins = kernel.CreatePluginFromPromptDirectory("Prompts");
1
2
3
4
var article = @"9月6日16时20分,今年第11号超强台风“摩羯”在海南文昌沿海登陆。超强台风“摩羯”风力有多大?记者只能“抱团”出镜。";

var result = await kernel.InvokeAsync(kernelPlugins["RewriteRedBookStyle"], new() { ["article"] = article });
result.Display();

yaml format prompt template

以上使用的txt格式的提示词模板,将提示词和配置一拆为二,是比较主流的方案,但SK 也支持将二者合二为一,具体就是通过yaml 格式进行定义,但需要用到额外的NuGet包:Microsoft.SemanticKernel.Yaml

1
#r "nuget:Microsoft.SemanticKernel.Yaml"

接下来还是以上例为基础进行改写,在Prompts 文件夹下定义一个RewriteEmojiStyle的文件夹,然后定义一个rewritwithemoji.yaml

1
2
3
4
5
Prompts

└─── RewriteEmojiStyle
|
└─── rewritwithemoji.yaml

具体rewritewithemoji.yaml 的内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
name: RewriteEmojiStyle
template: |
请使用 Emoji 风格编辑以下段落,该风格以引人入胜的标题、每个段落中包含表情符号和在末尾添加相关标签为特点。请确保保持原文的意思。
{{$article}}
template_format: semantic-kernel
description: A function that rewrite with Emoji style.
input_variables:
- name: article
description: The user's request.
is_required: true
output_variable:
description: The rewrite emoji content.
execution_settings:
default:
temperature: 0.6

接下来就可以使用以下方式来测试yaml格式的提示词模板:

1
2
3
4
5
6
7
8
var prompt = await File.ReadAllTextAsync("Prompts/RewriteEmojiStyle/rewirtewithemoji.yaml");

var rewriteFunction = kernel.CreateFunctionFromPromptYaml(prompt);

var article = @"9月6日16时20分,今年第11号超强台风“摩羯”在海南文昌沿海登陆。超强台风“摩羯”风力有多大?记者只能“抱团”出镜。";

var result = await kernel.InvokeAsync(rewriteFunction, new() { ["article"] = article });
result.Display();

SK 提示词模板

SK自带的提示词模板是一种使用纯文本定义和组合AI函数的简单方法。您可以使用它来创建自然语言提示、生成响应、提取信息、调用其他提示或执行任何其他可以用文本表示的任务。
该模板支持三个基本特性,允许:

  1. 包含变量
  2. 调用外部函数
  3. 将参数传递给函数

无需编写任何代码或导入任何外部库,只需使用双花括号{{…}}在提示词中嵌入表达式。语义内核将解析您的模板并执行其背后的逻辑。这样,您可以轻松地将AI集成到您的产品中。

Variables(变量)

要在提示符中包含变量值,请使用{{$VariableName}}语法。

例如,如果您有一个名为name的变量来保存用户名,您可以编写:

Hello {{$name}}, welcome to Semantic Kernel!

花括号中的空格会被忽略,如果为了提高可读性,可以适当添加空格:

Hello {{ $name }}, welcome to Semantic Kernel!

Function calls (函数调用)

要调用外部函数并将结果嵌入到提示符中,请使用{{namesspace.FunctionName}}语法。
例如,如果您有一个名为weather.getForecast (位于weather命名空间下)的函数,它返回给定位置的天气预报。

1
2
3
4
5
6
7
namespace weather;
[KernelFunction("getForecast")]
[Description("获取给定位置的天气预报")]
[return: Description("指定位置的天气预报")]
public Weather GetForecast(string localtion){
//....
}

您可以这样编写提示词:

The weather today is {{weather.getForecast}}.

上面的提示词等价于:

The weather today is {{weather.getForecast $localtion}}

这是因为SK 调用函数时会自动根据函数定义的参数名来传递函数参数。

Function Parameters(函数参数)

调用外部函数并向其传递参数,使用{{namespace.functionName $varName}}{{namespace.functionName "value"}} 语法.

例如,如果要向天气预报函数传递不同的输入,可以编写:

The weather today in {{$city}} is {{weather.getForecast $city}}.

1
2
3
4
5
6
7
8
9
10
11
12
13
using System.ComponentModel;
using Microsoft.SemanticKernel;

public class WeatherPlugin
{
[KernelFunction("GetForecast")]
[Description("获取给定位置的天气预报")]
[return: Description("指定位置的天气预报")]
public static string GetForecast( string location )
{
return $"Sunny, 23℃";
}
}
1
2
3
4
5
6
7
var prompt = "The weather today in {{ $location }} is {{GetForecast}}.";

kernel.Plugins.Clear();
kernel.Plugins.AddFromType<WeatherPlugin>();

var res = await kernel.InvokePromptAsync(prompt, new (){ ["location"]= "Shenzhen" });
res.Display();

The weather in Shenzhen today is sunny with a temperature of 23 degrees Celsius.

1
2
3
4
5
6
7
var prompt = "The weather today in {{$city}} is {{GetForecast $city}}.";

kernel.Plugins.Clear();
kernel.Plugins.AddFromType<WeatherPlugin>();

var res = await kernel.InvokePromptAsync(prompt, new (){ ["city"]= "Shenzhen" });
res.Display();

The weather in Shenzhen today is sunny with a temperature of 23 degrees Celsius.

注意事项

关于特殊字符的注意事项:
语义函数模板是文本文件,因此无需转义特殊字符像换行符和制表符。但是,有两种情况需要特殊语法:

提示模板中需要包含双花括号

双花括号有一个特殊的用例,它们用于注入变量、值和函数到模板中。
如果您需要在提示中包含{{`和`}}序列,这可能会触发特殊的渲染逻辑,最好的解决方案是使用引号括起来即可,比如:

{{ "{{" }} and {{ "}}" }}

举例而言:

{{ "{{" }} and {{ "}}" }} are special SK sequences.

以上提示词会渲染成:

{{ and }} are special SK sequences.

1
2
3
4
5
6
7
8
9
10
var prompt = """
{{ "{{" }} and {{ "}}" }} are special SK sequences.
""";

var promptTemplateFactory = new KernelPromptTemplateFactory();
promptTemplateFactory.TryCreate(new PromptTemplateConfig(prompt), out var template);

var renderedPrompt = await template.RenderAsync(kernel);

renderedPrompt.Display();
{{ and }} are special SK sequences.

包含引号

值可以用单引号和双引号括起来。为避免需要特殊语法:

  1. 在使用包含单引号的值,我们建议用双引号括起来。

...text... {{ functionName "one 'quoted' word" }} ...text...

  1. 当使用包含双引号的值,用单引号括起来。

...text... {{ functionName 'one "quoted" word' }} ...text...

简而言之,就是用双引号括住单引号,用单引号括住双引号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var prompt = """
{{ "'shengjie'" }}
{{'"shengjie"' }}
""";

var promptTemplateFactory = new KernelPromptTemplateFactory();
var config = new PromptTemplateConfig(prompt);

config.AllowDangerouslySetContent = true;
promptTemplateFactory.TryCreate(config, out var template);

var renderedPrompt = await template.RenderAsync(kernel);

renderedPrompt.Display();
'shengjie' 
"shengjie" 

使用转移符

对于那些值同时包含单引号和双引号的情况,您将需要使用\符号进行转义。

  1. 对于需要双引号,使用\"... {{ "quotes' \"escaping\" example" }} ...
  2. 对于需要单引号,使用\': ... {{ 'quotes\' "escaping" example' }} ...
  3. 对于需要反斜杠,使用\\... {{ 'c:\\documents\\ai' }} ...

前两个示例都会渲染为:... quotes' "escaping" example ...

注意:\' 总是会渲染为'\"总是会渲染为",因此如果需要渲染\'\":则需要使用以下\\\'\\\"

1
2
3
4
5
6
7
8
9
10
11
12
13
var prompt = """
{{ 'quotes\' "escaping" example' }}
""";

var promptTemplateFactory = new KernelPromptTemplateFactory();
var config = new PromptTemplateConfig(prompt);
config.AllowDangerouslySetContent = true;

promptTemplateFactory.TryCreate(config, out var template);

var renderedPrompt = await template.RenderAsync(kernel);

renderedPrompt.Display();
quotes' "escaping" example

4、Semantic Kernel提示词模板

https://bubuweiying.site/4SemanticKernelpromote/

作者

步步为营

发布于

2025-03-20

更新于

2025-10-28

许可协议