和 AI 打交道这件事,说简单也简单,说难也真的挺难。
很多开发者第一次接触大语言模型时,随便丢一句话进去,发现 AI 的回答要么文不对题,要么冗长废话,要么每次输出格式都不一样——这让人抓狂。更头疼的是,一旦系统规模变大,提示词散落在代码各处,维护起来就像拆定时炸弹。
根据多个真实项目的统计,AI 应用开发中有将近 40% 的时间浪费在反复调试提示词上,而非真正的业务逻辑。不少团队甚至因为提示词管理混乱,导致同一个功能在不同环境下表现迥异,给线上系统埋下隐患。
读完这篇文章,你将掌握:
大语言模型本质上是一个"条件概率机器"——它根据你给的上下文,预测最可能的下一个 token。你给的上下文质量,直接决定输出质量。这不是玄学,是数学。
一个坏的提示词 vs 一个好的提示词,输出差异可达 60% 以上(参考 OpenAI 官方 Prompt Engineering Guide 中的对比实验数据)。
# 差的提示词 "总结一下这篇文章" # 好的提示词 "你是一位技术文档专家。请将以下文章总结为 3 个要点, 每个要点不超过 30 字,使用专业但易懂的中文表达。"
输出质量的差距,肉眼可见。
Semantic Kernel(以下简称 SK)是微软开源的 AI 编排 SDK,提示词在其中以 Semantic Function 的形式存在,是整个 AI 流水线的核心驱动力。
SK 的架构如下图所示(文字描述):
用户输入 → [Prompt Template] → LLM → [Output Parser] → 业务逻辑 ↑ 变量插值 / 历史上下文 / 工具调用结果
提示词既是"指令书",也是"上下文容器"。没有好的提示词工程,SK 的其他能力都是空中楼阁。
在真实项目里摸爬滚打多年,总结出提示词设计有四个绕不开的原则:
告诉 AI 它是谁,远比告诉它做什么更重要。
给 AI 设定一个清晰的角色,相当于给它一个"行为过滤器",所有输出都会经过这个角色的视角来过滤。
你是一位拥有 10 年经验的 C# 高级架构师,专注于企业级应用设计。 你的回答风格:简洁专业,优先给出可运行代码,避免理论堆砌。
不要让 AI 猜你想要什么,把边界说死。
AI 没有你脑子里的信息,你得主动"喂给"它。
背景信息、业务约束、领域知识——都要显式写进提示词,别假设 AI 能猜到。
一个好例子,胜过一百字描述。
这也是 Few-shot 的核心价值所在,下面会重点展开。
Zero-shot 是最简单的形式——直接告诉 AI 干什么,不提供任何示例。
csharp// Zero-shot 示例:代码审查
var kernel = Kernel.CreateBuilder()
.AddOpenAIChatCompletion("gpt-4o", apiKey)
.Build();
string zeroShotPrompt = """
你是一位资深 C# 代码审查专家。
请审查以下代码,指出潜在的性能问题和安全隐患,
输出格式:问题列表(每项包含:位置、问题描述、建议修复方式)
代码:
{{$code}}
""";
var reviewFunction = kernel.CreateFunctionFromPrompt(zeroShotPrompt);
var result = await kernel.InvokeAsync(reviewFunction,
new KernelArguments { ["code"] = userCode });
Console.WriteLine(result);
适用场景:
踩坑预警:Zero-shot 对任务描述的精确性要求极高。"帮我写个函数"和"用 C# 写一个线程安全的单例模式,使用双重检查锁定",得到的结果天壤之别。
Few-shot 通过提供 2~5 个输入输出示例,让 AI 学习你期望的模式。
csharpstring fewShotPrompt = """
你是一位 API 文档专家,负责将代码注释转换为标准文档格式。
示例 1:
输入:// 获取用户信息,参数userId为用户ID
输出:
**GetUserInfo(int userId)**
- 描述:根据用户ID获取对应的用户详细信息
- 参数:userId (int) - 目标用户的唯一标识符
- 返回:UserInfo 对象,包含用户基本信息
示例 2:
输入:// 批量删除过期订单,超过days天的订单会被清除
输出:
**DeleteExpiredOrders(int days)**
- 描述:批量清除系统中超过指定天数的过期订单
- 参数:days (int) - 订单过期判定的天数阈值
- 返回:int,表示成功删除的订单数量
现在请处理以下注释:
输入:{{$comment}}
输出:
""";
var docFunction = kernel.CreateFunctionFromPrompt(fewShotPrompt,
new PromptExecutionSettings
{
ExtensionData = new Dictionary<string, object>
{
["temperature"] = 0.2 // 低温度保证格式稳定性
}
});
Few-shot 的示例选择原则:
实测对比数据(测试环境:GPT-4o,100次调用取平均,任务:代码注释转文档):
| 方案 | 格式符合率 | 内容准确率 | 平均耗时 |
|---|---|---|---|
| Zero-shot | 71% | 83% | 1.2s |
| Few-shot (3例) | 94% | 91% | 1.5s |
| Few-shot (5例) | 97% | 93% | 1.8s |
格式符合率提升了 26%,代价是轻微增加了 token 消耗。
复杂推理任务,直接让 AI 给答案,往往错误率高得出奇。这就像你考数学,直接写答案而不展示推导过程,出错了自己都不知道哪里错的。
思维链(Chain-of-Thought, CoT)就是强制让 AI"展示解题过程",通过逐步推理降低错误率。
csharp// 构建思维链提示词 - 以代码复杂度分析为例
string cotPrompt = """
你是一位软件质量分析专家,请对给定的 C# 方法进行复杂度分析。
分析时请严格按照以下步骤进行:
步骤 1 - 识别控制流结构:
列出代码中所有的 if/else、for、while、switch、try/catch 等控制结构。
步骤 2 - 计算圈复杂度(Cyclomatic Complexity):
基础值为 1,每个控制流分支 +1,计算最终数值。
步骤 3 - 识别潜在问题:
根据复杂度和代码结构,指出耦合点、难以测试的部分。
步骤 4 - 给出重构建议:
针对步骤 3 的问题,提供具体的重构方向。
步骤 5 - 综合评分:
给出 1-10 的可维护性评分,并简述理由。
代码:
{{$methodCode}}
请按照上述步骤,逐步输出你的分析过程。
""";
var analysisFunc = kernel.CreateFunctionFromPrompt(cotPrompt,
new PromptExecutionSettings
{
ExtensionData = new Dictionary<string, object>
{
["temperature"] = 0.1, // 分析任务需要高确定性
["max_tokens"] = 1500
}
});
var codeToAnalyze = """
public async Task<Order> ProcessOrderAsync(string userId,
List<CartItem> items, PaymentInfo payment)
{
if (items == null || items.Count == 0)
throw new ArgumentException("购物车为空");
foreach (var item in items)
{
if (item.Quantity <= 0)
throw new ArgumentException($"商品 {item.Name} 数量无效");
if (item.Price < 0)
throw new ArgumentException($"商品 {item.Name} 价格无效");
}
var total = items.Sum(x => x.Price * x.Quantity);
try
{
var payResult = await _paymentService.ChargeAsync(payment, total);
if (payResult.Success)
{
return await _orderService.CreateOrderAsync(userId, items, total);
}
else
{
throw new PaymentException(payResult.ErrorMessage);
}
}
catch (TimeoutException ex)
{
_logger.LogError(ex, "支付超时");
throw new ServiceException("支付服务暂时不可用,请稍后重试");
}
}
""";
var result = await kernel.InvokeAsync(analysisFunc,
new KernelArguments { ["methodCode"] = codeToAnalyze });
Console.WriteLine(result);
| 变体 | 适用场景 | 核心关键词 |
|---|---|---|
| 标准 CoT | 逻辑推理、数学计算 | "请一步步思考" |
| Zero-shot CoT | 快速推理,无示例 | "Let's think step by step" |
| Self-Consistency CoT | 高精度要求场景 | 多次采样取多数结果 |
一个小技巧:在提示词末尾加上 "在给出最终答案前,请先展示完整推理过程" 这一句,能让大多数任务的准确率提升 15%~30%。
Semantic Kernel 支持两套模板语法,分别是 Handlebars 和原生的 SK 模板语法。
csharp// 变量插值:使用 {{$variableName}}
string template = """
你是 {{$role}}。
用户的问题是:{{$userInput}}
请用{{$language}}回答,不超过{{$maxWords}}字。
""";
// 调用函数:使用 {{pluginName.functionName}}
string advancedTemplate = """
当前时间:{{time.now}}
用户偏好语言:{{userPlugin.getLanguagePreference $userId}}
请根据以上上下文,回答用户的问题:
{{$question}}
""";
转义规则:当你需要在模板中输出字面量 {{ 时,使用 \{\{ 进行转义。
csharp// 转义示例:输出包含双花括号的代码模板
string escapeExample = """
请生成一个 C# 字典初始化的代码示例,格式如下:
var dict = \{\{
\{ "key1", "value1" \},
\{ "key2", "value2" \}
\}\};
实际问题:{{$actualQuestion}}
""";
csharpusing Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.PromptTemplates.Handlebars;
namespace AppSemanticKernel09
{
internal class Program
{
static async Task Main(string[] args)
{
// Handlebars 支持条件判断和循环
string handlebarsTemplate = """
{{#if isExpert}}
你面对的是一位技术专家,请使用专业术语,跳过基础概念解释。
{{else}}
你面对的是一位初学者,请使用通俗语言,避免行话。
{{/if}}
需要处理的代码文件清单:
{{#each files}}
- 文件名:{{this.name}},类型:{{this.type}},行数:{{this.lines}}
{{/each}}
请针对以上 {{files.length}} 个文件,给出整体代码质量评估报告。
""";
Console.OutputEncoding = System.Text.Encoding.UTF8;
var builder = Host.CreateApplicationBuilder(args);
var apiKey = Environment.GetEnvironmentVariable("ALIYUN_API_KEY")
?? throw new InvalidOperationException("未找到 ALIYUN_API_KEY 环境变量");
var endpoint = Environment.GetEnvironmentVariable("ALIYUN_ENDPOINT")
?? "https://dashscope.aliyuncs.com/compatible-mode/v1";
// 注册 Semantic Kernel
builder.Services.AddSingleton(sp =>
{
var config = sp.GetRequiredService<IConfiguration>();
return Kernel.CreateBuilder()
.AddOpenAIChatCompletion(
modelId: "qwen-vl-plus",
apiKey: apiKey,
endpoint: new Uri(endpoint)
)
.Build();
});
var host = builder.Build();
var kernel = host.Services.GetRequiredService<Kernel>();
var handlebarsFactory = new HandlebarsPromptTemplateFactory();
var handlebarsFactory = new HandlebarsPromptTemplateFactory
{
AllowDangerouslySetContent = true
};
var function = kernel.CreateFunctionFromPrompt(
new PromptTemplateConfig
{
Template = handlebarsTemplate,
TemplateFormat = "handlebars",
Name = "CodeReviewReport",
Description = "对多个代码文件进行综合质量评估"
},
handlebarsFactory
);
// 传入复杂对象参数
var args1 = new KernelArguments
{
["isExpert"] = true,
["files"] = new[]
{
new { name = "OrderService.cs", type = "Service", lines = 342 },
new { name = "UserRepository.cs", type = "Repository", lines = 187 },
new { name = "PaymentController.cs", type = "Controller", lines = 156 }
}
};
var report = await kernel.InvokeAsync(function, args1);
Console.WriteLine(report);
}
}
}

说了这么多理论,咱们来搞点实际的——构建一个可复用的提示词模板库,这在真实项目里价值极大。
PromptTemplateLibrary/ ├── Templates/ │ ├── CodeReview/ │ │ ├── basic-review.yaml │ │ ├── security-review.yaml │ │ └── performance-review.yaml │ ├── Documentation/ │ │ ├── api-doc-generator.yaml │ │ └── readme-generator.yaml │ └── Analysis/ │ ├── complexity-analysis.yaml │ └── dependency-analysis.yaml ├── PromptTemplateManager.cs ├── TemplateRenderer.cs └── TemplateRegistry.cs
yaml# Templates/CodeReview/security-review.yaml
name: SecurityCodeReview
description: 对 C# 代码进行安全漏洞扫描和风险评估
template_format: semantic-kernel
input_variables:
- name: code
description: 待审查的 C# 代码
is_required: true
- name: severity_level
description: 关注的严重级别 (critical/high/medium/all)
default: all
- name: output_format
description: 输出格式 (markdown/json/plain)
default: markdown
execution_settings:
default:
temperature: 0.1
max_tokens: 2000
template: |
你是一位专注于 .NET 安全的代码审查专家,具备 OWASP Top 10 和 CWE 标准知识。
审查重点(严重级别:{{$severity_level}}):
- SQL 注入风险(参数化查询检查)
- XSS 漏洞(输入验证和输出编码)
- 不安全的反序列化
- 敏感信息硬编码(密码、密钥、连接字符串)
- 不当的异常处理(信息泄露)
- 不安全的随机数生成
- 路径遍历漏洞
输出格式要求:{{$output_format}}
如果是 markdown 格式,请按以下结构输出:
## 安全审查报告
### 发现的问题
| 风险等级 | 位置 | 问题描述 | 修复建议 |
### 总体安全评分(1-10)
### 修复优先级建议
待审查代码:
{{$code}}
csharp/// <summary>
/// 提示词模板库管理器
/// 负责模板的加载、缓存、渲染与调用
/// </summary>
public class PromptTemplateManager
{
private readonly Kernel _kernel;
private readonly string _templatesBasePath;
private readonly Dictionary<string, KernelFunction> _functionCache;
private readonly ILogger<PromptTemplateManager> _logger;
public PromptTemplateManager(
Kernel kernel,
string templatesBasePath,
ILogger<PromptTemplateManager> logger)
{
_kernel = kernel;
_templatesBasePath = templatesBasePath;
_functionCache = new Dictionary<string, KernelFunction>();
_logger = logger;
}
/// <summary>
/// 从 YAML 文件加载模板并注册到缓存
/// </summary>
public async Task LoadTemplatesAsync()
{
var yamlFiles = Directory.GetFiles(
_templatesBasePath, "*.yaml", SearchOption.AllDirectories);
foreach (var filePath in yamlFiles)
{
try
{
var yaml = await File.ReadAllTextAsync(filePath);
var function = _kernel.CreateFunctionFromPromptYaml(yaml);
var templateKey = Path.GetFileNameWithoutExtension(filePath);
_functionCache[templateKey] = function;
_logger.LogInformation("已加载模板:{TemplateName}", templateKey);
}
catch (Exception ex)
{
_logger.LogError(ex, "加载模板失败:{FilePath}", filePath);
}
}
_logger.LogInformation("共加载 {Count} 个提示词模板", _functionCache.Count);
}
/// <summary>
/// 通过模板名称执行提示词调用
/// </summary>
public async Task<string> ExecuteAsync(
string templateName,
Dictionary<string, object?> parameters,
CancellationToken cancellationToken = default)
{
if (!_functionCache.TryGetValue(templateName, out var function))
{
throw new KeyNotFoundException($"模板 '{templateName}' 不存在," +
$"可用模板:{string.Join(", ", _functionCache.Keys)}");
}
var args = new KernelArguments();
foreach (var (key, value) in parameters)
{
args[key] = value;
}
try
{
var result = await _kernel.InvokeAsync(
function, args, cancellationToken);
return result.ToString();
}
catch (Exception ex)
{
_logger.LogError(ex, "执行模板 {TemplateName} 时发生错误", templateName);
throw;
}
}
/// <summary>
/// 获取所有已注册模板的元信息
/// </summary>
public IEnumerable<TemplateInfo> GetTemplateRegistry()
{
return _functionCache.Select(kv => new TemplateInfo
{
Name = kv.Key,
Description = kv.Value.Description,
ParameterCount = kv.Value.Metadata.Parameters.Count
});
}
}
public record TemplateInfo
{
public required string Name { get; init; }
public string? Description { get; init; }
public int ParameterCount { get; init; }
}
csharpusing Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.SemanticKernel;
namespace AppSemanticKernel08
{
internal class Program
{
static async Task Main(string[] args)
{
Console.OutputEncoding = System.Text.Encoding.UTF8;
var builder = Host.CreateApplicationBuilder(args);
var apiKey = Environment.GetEnvironmentVariable("ALIYUN_API_KEY")
?? throw new InvalidOperationException("未找到 ALIYUN_API_KEY 环境变量");
var endpoint = Environment.GetEnvironmentVariable("ALIYUN_ENDPOINT")
?? "https://dashscope.aliyuncs.com/compatible-mode/v1";
// 注册 Semantic Kernel
builder.Services.AddSingleton(sp =>
{
var config = sp.GetRequiredService<IConfiguration>();
return Kernel.CreateBuilder()
.AddOpenAIChatCompletion(
modelId: "qwen-vl-plus",
apiKey: apiKey,
endpoint: new Uri(endpoint)
)
.Build();
});
// 注册模板管理器
builder.Services.AddSingleton<PromptTemplateManager>(sp =>
{
var kernel = sp.GetRequiredService<Kernel>();
var logger = sp.GetRequiredService<ILogger<PromptTemplateManager>>();
var templatesPath = Path.Combine(AppContext.BaseDirectory, "Templates");
return new PromptTemplateManager(kernel, templatesPath, logger);
});
var host = builder.Build();
// 初始化:加载所有模板
var templateManager = host.Services.GetRequiredService<PromptTemplateManager>();
await templateManager.LoadTemplatesAsync();
// 列出所有可用模板
Console.WriteLine("=== 已注册的提示词模板 ===");
foreach (var info in templateManager.GetTemplateRegistry())
{
Console.WriteLine($"• {info.Name}({info.ParameterCount} 个参数):{info.Description}");
}
// 执行安全审查
var suspiciousCode = """
public User GetUser(string userId)
{
var sql = $"SELECT * FROM Users WHERE Id = '{userId}'";
return _db.ExecuteQuery<User>(sql).FirstOrDefault();
}
""";
var reviewResult = await templateManager.ExecuteAsync(
templateName: "security-review",
parameters: new Dictionary<string, object?>
{
["code"] = suspiciousCode,
["severity_level"] = "critical",
["output_format"] = "markdown"
});
Console.WriteLine(reviewResult);
}
}
}

提示词设计层面:
{{$input}},效果更佳模板管理层面:
ExecuteAsync 前做必填参数检查,避免无效 API 调用| 常见陷阱 | 问题描述 | 规避方案 |
|---|---|---|
| 提示词注入 | 用户输入包含指令覆盖原有提示词 | 对用户输入做清洗,或使用专用输入沙箱 |
| 温度值过高 | 结构化输出任务(如 JSON)格式不稳定 | temperature ≤ 0.2 |
| Token 超限 | Few-shot 示例过多导致请求失败 | 监控 token 用量,设置 max_tokens 上限 |
| 模板缓存失效 | 热更新模板后旧版本仍在内存中 | 实现文件监听 + 缓存失效机制 |
| 并发竞争 | 多线程同时写 _functionCache | 使用 ConcurrentDictionary 替代 Dictionary |
💬 "提示词是 AI 应用的代码,需要像管理代码一样管理它。"
💬 "Zero-shot 验证可行性,Few-shot 保证稳定性,CoT 解决复杂性。"
💬 "模板库不只是代码复用,更是团队提示词知识的沉淀与共享。"
入门阶段 └── 掌握 SK 基本提示词语法 → 本文 进阶阶段 ├── Semantic Kernel Plugin 开发 ├── Memory 与向量检索(RAG) └── Planner 自动任务规划 高阶阶段 ├── Multi-Agent 多智能体协作 ├── Prompt Optimization 自动优化 └── Fine-tuning 结合工程落地
在你的项目中,提示词管理目前是怎么做的?是散落在代码里的字符串常量,还是已经有了系统化的模板管理方案?欢迎在评论区分享你的实践经验,尤其是那些"踩过的坑",说不定能帮到正在入坑的同学。
📌 收藏这篇文章,下次写提示词时可以直接对照检查清单使用。
相关标签:#C#开发 #SemanticKernel #提示词工程 #AI应用开发 #.NET
相关信息
我用夸克网盘给你分享了「AppSemanticKernelPromptDemo.zip」,点击链接或复制整段内容,打开「夸克APP」即可获取。
/c8cb3YWwfn:/
链接:https://pan.quark.cn/s/a9ac7ac3b535
提取码:FU89
本文作者:技术老小子
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!