5.3 提示词:用户控制的消息模板 本节摘要:本节讲三大原语的第三类——提示词(Prompt),用户控制的命名消息模板。提示词的本质是「一个函数,返回一段会变成用户消息注入对话的文本」。它解决的问题是:用户经常重复输入一长段指令(「请用中文总结这段代码,要点不超过 5 条……」),而提示词把这些预设指令做成「按名字调用」的模板,用户选个名字、填个参数就行。我们会讲清它的注册、参数模型(只能扁平字符串)、返回值(变用户消息),以及为什么它最适合「保存的查询」这个类比。读完本节,你能区分三类原语的边界,各得其所地使用。
本节摘要:本节讲三大原语的第三类——提示词(Prompt),用户控制的命名消息模板。提示词的本质是「一个函数,返回一段会变成用户消息注入对话的文本」。它解决的问题是:用户经常重复输入一长段指令(「请用中文总结这段代码,要点不超过 5 条……」),而提示词把这些预设指令做成「按名字调用」的模板,用户选个名字、填个参数就行。我们会讲清它的注册、参数模型(只能扁平字符串)、返回值(变用户消息),以及为什么它最适合「保存的查询」这个类比。读完本节,你能区分三类原语的边界,各得其所地使用。
先看一个真实痛点:用户每次想「总结代码」,都要手敲一长串指令:
用户(没有提示词): 「请分析下面这段代码,用中文总结其功能, 要点不超过 5 条,每条不超过 30 字, 最后给出一行改进建议: <粘贴代码>」 → 每次都要敲这么多,烦
提示词把这个「反复使用的指令模板」做成命名调用:
用户(有提示词): 选「analyze_code」提示词,填参数 code=<粘贴代码> → 提示词函数渲染出那段长指令,注入对话 → 用户只需选名字 + 填代码
这就是提示词的价值——把重复的指令预制化,用户按名字一键触发。
提示词的注册写法与工具很像:
@mcp.prompt() def analyze_code(code: str, language: str = "python") -> str: """Analyze code and summarize its functionality.""" return f"""请分析下面这段{language}代码: - 用中文总结功能 - 要点不超过 5 条 - 每条不超过 30 字 - 最后给一行改进建议 代码: {code}"""
读出来的元数据:
| 元数据 | 来源 |
|---|---|
提示词名 analyze_code |
函数名 |
描述 Analyze code... |
文档字符串 |
参数 code(必填)、language(可选,默认 python) |
函数签名 |
装饰器同样能覆盖默认推断:
@mcp.prompt(name="analyze", description="分析代码并总结") def analyze_code(code: str): ...
这是提示词与工具最大的差别——参数只能是扁平的字符串列表,没有 JSON Schema:
# ✅ 合法:参数都是字符串 @mcp.prompt() def analyze(code: str, language: str = "python") -> str: ... # ❌ 不合法:参数不是字符串(工具可以,提示词不行) @mcp.prompt() def process(items: list[str], count: int) -> str: ...
为什么有这个限制?第 3.2 节讲过:提示词由用户在界面上触发,UI 要为每个参数渲染一个输入框。复杂的 JSON Schema(嵌套对象、数组)在 UI 上渲染困难,而「每个参数一个文本框」简单可靠。所以协议规定提示词参数必须是扁平字符串。
| 原语 | 参数模型 | 为什么 |
|---|---|---|
| 工具 | 完整 JSON Schema | 模型能处理复杂结构 |
| 提示词 | 扁平字符串列表 | UI 要简单渲染输入框 |
⚠️ 注意:如果你的提示词需要复杂参数(如列表),只能让用户传一个 JSON 字符串,在函数里自己解析。这是提示词的固有限制,设计时要考虑到。
提示词函数的返回值,会变成一条用户消息注入对话:
@mcp.prompt() def summarize(text: str) -> str: return f"请用一句话总结:\n\n{text}"
调用流程:
用户在界面选「summarize」,填 text="..." │ ▼ 客户端发 prompts/get summarize 服务端执行 summarize("..."),返回渲染后的文本 │ ▼ 宿主把文本当「用户消息」注入对话 模型看到:「用户说:请用一句话总结:...」 │ ▼ 模型据此推理 模型回复总结
关键点:提示词产生的是「用户消息」——不是工具结果、不是资源内容。从模型视角看,就像用户自己说了那段话。这与工具(产生工具结果)、资源(产生上下文)的定位完全不同。
最简单的返回是单个字符串(变一条用户消息)。提示词也能返回更结构化的消息:
# 形态一:返回字符串 → 一条用户消息 @mcp.prompt() def simple(text: str) -> str: return f"总结:{text}" # 形态二:返回消息列表(含角色)→ 多条消息 @mcp.prompt() def with_system(text: str) -> list: return [ {"role": "system", "content": "你是一个代码评审专家。"}, {"role": "user", "content": f"评审:{text}"}, ]
形态二让你能注入「系统消息 + 用户消息」的组合,适合需要预设角色或背景的提示词。
💡 技巧:多数提示词用形态一(单字符串)就够。只有当你需要预设模型角色(如「你是专家」)或提供多轮背景时,才用形态二。别过度设计,简单优先。
把三类原语的边界再巩固一次:
| 维度 | 工具 | 资源 | 提示词 |
|---|---|---|---|
| 谁决定调用 | 模型 | 应用 | 用户 |
| 触发方式 | 模型推理中决定 | 宿主预加载 | 用户在界面选 |
| 参数模型 | 完整 JSON Schema | URI 占位符 | 扁平字符串 |
| 返回内容 | 工具结果(content+structured) | 资源内容(数据) | 用户消息 |
| 典型用途 | 执行动作(查询、写入、调用) | 提供数据(配置、资料) | 预设指令(总结、评审、生成) |
提示词的独特定位是「用户主动触发的预设指令」——它不是模型自动用的,也不是应用预加载的,而是用户在界面上主动选的。这让它最适合「保存的查询」「常用指令模板」这类场景。
几个适合做成提示词的场景:
| 场景 | 提示词名 | 作用 |
|---|---|---|
| 代码评审 | review_code |
预设「评审这段代码」的长指令 |
| 生成提交信息 | gen_commit |
预设「根据 diff 生成提交信息」 |
| 翻译 | translate |
预设「翻译成某语言」的指令 |
| 解释概念 | explain |
预设「用通俗语言解释」的指令 |
| 格式化 | format_json |
预设「格式化这段 JSON」 |
共同特征:指令是固定的、反复使用的、用户主动触发的。这些场景里,提示词比让用户每次手敲指令高效得多。
@mcp.prompt(),函数名当提示词名,文档字符串当描述。三类原语都清楚了,最后一节讲补全——为资源模板与提示词参数提供自动建议。