Coze技能开发实战:从Prompt模板到部署调试全攻略

发布时间:2026/10/22 10:18:47
Coze技能开发实战:从Prompt模板到部署调试全攻略 在Coze这类大模型应用工作台上折腾过一阵子的人基本都会遇到同一个坎想让Agent稳定完成一件具体的事靠那段冗长的system prompt不断堆约束效果越堆越飘模型偶尔还是会答非所问。后来接触到Skill技能这个概念才意识到整个写Prompt的思路都要变——不是把大模型当成一个听指令的闲人而是给工作台装上一个有明确输入输出规格的“功能件”。这篇文章我打算把Coze技能开发中最容易被忽略、也最决定成败的那部分——Skill的Prompt撰写从模板讲到落地整理成一条可以直接照做的路径。无论你是刚入门想给Bot加个查询技能还是已经在维护好几个技能包想统一规范这篇都能给你一套可复用的思路。1. 弄懂Coze Skill的组成才能写好那个Prompt很多人上来就打开编辑器写Prompt写完塞进去发现技能调用时经常“不在状态”要么不触发要么参数取错。问题往往出在没搞清技能包里到底有几样东西以及Prompt在其中扮演什么角色。1.1 Skill不是“一个提示词”先看技能包里的三个部件一个可部署的Skill我习惯叫它“技能包”因为它从来不是单一的一段文字而是由多个部件组成的项目结构。理解这个结构是写好Prompt的前提。技能包里至少有三个核心部件部件作用是否必须技能描述与配置信息定义技能名称、版本、功能描述、触发条件注册到平台上供模型识别必须指令文件技能的Prompt本体用自然语言描述角色、任务、输入、输出、处理流程必须资源文件外部依赖的参考数据、代码片段、文档、工具调用说明按需很多人只盯着中间那个指令文件写把配置信息当回事但没当重点。实际踩坑之后你会发现配置信息里的技能描述写得好不好直接影响模型能不能在合适的时机把技能调起来。指令文件写得好技能调起来了才能做对事配置信息写得好技能才能被“调起来”。这两者缺一不可。打个比方如果把Agent比作一家餐厅的店员技能描述相当于菜单上的菜名和简介客人用户看了才知道点什么店员模型看了才知道推荐什么指令文件则是后厨的做菜流程蛋炒饭是先炒蛋还是先炒饭得写清楚。菜单上写着“蛋炒饭”后厨却按“蒸米饭”来操作那端上来的东西肯定不对。1.2 Prompt在技能包中的位置从对话模板到指令文件的映射Skill这套机制的本质是把过去那些堆在system prompt里的“长尾指令”拆出来做成一个可独立复用、可测试、可版本化管理的小模块。模型在主流程里什么时候决定调用这个模块主要取决于两点技能描述和参数定义。而模块被调用之后怎么执行看的是指令文件里的Prompt。所以你的Prompt其实承担了两个层面的工作第一层是“入口层”写在技能描述里。它要做的是让模型能回答三个问题这个技能是干嘛的什么场景下应该用用的时候需要用户提供哪些信息描述越准确模型越不容易把技能误用或漏用。第二层是“执行层”写在指令文件里。它要做的是约束模型拿到参数后先做什么、再做什么中间有哪些判断分支最后输出什么格式的结果。很多人把这两个层次混在一起技能描述写成“一个通用助手”结果模型什么场景都想调用指令文件又写得过于笼统模型调用之后自由发挥。搞清楚这两层的分工写Prompt的思路就清晰了。1.3 模板化的价值为什么“标准格式”能省掉80%的调试时间既然每个技能都要同时维护入口层和执行层那就自然引出一个问题能不能把结构固定下来按模板填内容我的答案是必须能而且应该这么做。模板化至少带来四个直接好处可扫描。平台解析技能配置时通常会读取固定字段字段名规范后不容易报错。可测试。每个技能的结构一致测试用例可以批量设计不用每次重新读一遍Prompt才知道怎么测。可复用。同类技能的指令文件可以快速复制改造比如做一个“查天气”技能再做一个“查汇率”改参数描述和处理步骤就行。可版本对比。结构一致的情况下diff两个版本的差异非常清楚哪次改动引入问题一眼就能定位。有人担心模板化会不会限制表达导致Prompt僵硬。实际恰恰相反模板只是外壳里面的角色设定、步骤拆解、边界处理全是自由发挥的空间。把字段位置固定反而让你有更多精力去打磨内容质量而不是每次重新设计结构。2. 核心Prompt撰写要点把一句话讲成一套可执行流程明确了Skill的结构之后接下来进入正题怎么写那段决定技能质量的Prompt。我见过不少开发者写Prompt特别“大而全”面面俱到但模型执行起来依然潦草。原因在于Prompt缺少“流程感”只是一堆要求不是一套可执行的操作逻辑。2.1 角色设定与任务边界先回答“你是谁”“你能做什么”指令文件开头第一件事不是急着列步骤而是明确角色和职责边界。模型在技能执行时状态是独立的它并不知道自己在整个Agent里担当什么需要你在Prompt里重新给它一个“岗位说明”。我通常这样写# 角色 你是一个【信息抽取与结构化输出】助手仅负责处理与【XX业务】相关的查询。 # 任务边界 - 只处理输入参数中出现的业务内容不回答无关问题。 - 不进行任何推测缺失字段时必须明确提示。 - 不修改外部系统状态只负责生成结构化结果。这段看起来很朴素但非常关键。它告诉模型三个信息你是谁、你能做什么、你不能做什么。特别是“不做什么”很多人会忽略。模型没有边界感的时候经常会做出超出预期的行为比如你让它根据参数生成一个查询条件它顺手把结果也编出来了。任务边界的设计有个技巧与其抽象地写“不能编造信息”不如具体列场景。比如“当参数中没有指定城市时不要尝试补全城市名直接返回参数缺失错误”。具体场景越多模型踩线的概率越低。2.2 输入描述与参数声明让大模型知道要什么Skill和普通对话Prompt最大的区别之一就是它有一套参数体系。你在配置里声明参数列表用户对话时模型负责从对话中抽取参数值然后传给技能。所以参数声明绝不是随便写写它决定了模型能不能准确理解用户意图。参数声明通常包含这些字段字段含义写法建议名称参数标识符代码里调用时用统一小驼峰如 cityName类型string、integer、number、boolean、array等按实际业务选拿不准用string描述给模型看的说明决定抽取准确率写清楚边界如“用户要查询的城市名称如北京、上海不包括省份”必填是否必须提供非必填的参数务必写清楚缺省逻辑枚举值可枚举时给出候选列表能列就列能显著降低模型自由发挥的概率踩坑最多的就是“描述”。有人写“用户想查的城市”模型抽取时很容易把“我昨天去过的那座城市”这种表述也解析成城市名。更好的写法是“目标城市的官方中文名称必须是用户在对话中明确提到的地名若用户未提及则标记为缺失”。描述越具体抽取越准。另外非必填参数一定要“留后路”。我见过一个技能参数里有个可选的排序方式结果模型有时候传“asc”有时候传“升序”代码直接裂开。后来我在描述里加上“只能传asc或desc若用户未指定则传asc”世界清静了。2.3 处理步骤与输出格式从“自由发挥”到“固定协议”这是Prompt的核心段落。你要把任务的执行过程拆解成清晰的步骤并且规定每一步的产物。模型只有在明确的流程里才能稳定输出否则它会跳步骤、改逻辑。我习惯用有序列表来写处理步骤每步只干一件事# 处理步骤 1. 检查输入参数是否齐全若有必填参数缺失直接进入错误处理流程。 2. 根据参数中的用户ID在会话上下文中查找是否已存在用户画像。 3. 若存在用户画像结合画像与本次输入生成个性化结果。 4. 若不存在用户画像仅基于本次输入生成通用结果并追加提示语。 5. 将结果整理为指定JSON结构并返回。每步后面最好跟一句说明解释这一步的意图和边界。比如第2步可以追加“用户画像查找仅限于上下文窗口内不要自行推测用户偏好”。这样模型就不会自作主张。输出格式方面建议直接给出模板而不是描述。模型对“结构化输出”理解的可靠程度远没有对“严格按照以下格式回填”高。举个例子# 输出格式 严格按照以下JSON结构返回不要添加任何额外字段或说明文字 { recommendReason: 结果生成依据一句话概述, recommendList: [ { itemId: 内容ID, title: 内容标题, score: 0.9 } ], missingParamHint: 若有缺失参数提示用户补充无缺失则返回null }这里重点在“不要添加任何额外字段”。模型经常会在JSON外加一层“json”代码块标记或者多输出一段解释文本。如果你要对接API或插件这些额外内容全是脏数据。在Prompt里明确禁止比程序里做各种容错要省事得多。2.4 示例与边界处理Few-shot和拒绝逻辑光有步骤还不太够模型对“抽象规则”的遵循能力有限给它一两个正反示例效果立竿见影。这就是Few-shot的作用。正例要覆盖标准场景反例要覆盖易错场景。我通常会写两组# 示例 示例1标准场景 用户输入帮我把李白的一句诗翻译成英文 模型输出 { taskType: translate, sourceText: 李白的一句诗, targetLanguage: english, errorHint: null } 示例2参数缺失场景 用户输入帮我翻译成英文 模型输出 { taskType: translate, sourceText: null, targetLanguage: english, errorHint: 请提供需要翻译的源文本内容 }示例不用多每个场景一个就够。关键是示例要和你定义的输入输出协议严格一致不然模型会困惑到底参考哪条。你甚至可以在示例里把“缺失时应该怎么做”演一遍让模型学到这个反射。边界处理方面要主动定义“模型不认识的输入”该怎么回应。很多人只写了正常流程没写异常流程结果模型遇到覆盖不了的输入时就开始编。我会专门加一段# 无法处理的情况 若输入内容超出技能定义范围或缺少处理所需的关键参数直接返回 { errorCode: 40001, errorMessage: 无法处理该请求请检查参数是否完整, data: null }这段的作用是给模型一条“安全出口”让它知道遇到意外时不是继续硬着头皮生成而是规范地拒绝。反正模型无论如何都要输出点内容不如引导它输出一个你能识别的错误结构。3. 从模板到落地写出一份可直接部署的技能包写Prompt只是前半程真正让技能跑起来还要把它组装成技能包并完成部署。这一步很多人栽在“文件结构不对”“字段解析失败”“版本没更新导致改了无效”这些细节上。3.1 搭建最小技能包目录结构、文件命名与版本约定先给一个我日常用的目录结构适用性很广你拿到手可以直接套my_skill/ ├── manifest.json # 技能元信息描述、版本、触发条件 ├── instruction.md # 指令文件Prompt本体 ├── reference/ │ ├── example_1.md # 可选的参考示例 │ └── api_notes.md # 可选的接口说明 └── assets/ └── data.json # 可选的静态资源manifest.json是平台识别技能包的入口字段要规范。我常用的一套字段大致这样{ name: book_query_skill, version: 1.0.0, description: 根据用户提供的书名或作者返回书籍的基本信息、简介和评分。仅用于图书相关的查询。, author: dev, license: MIT }这里有几个细节name字段用英文小写加下划线别用中文也别用连字符很多平台对name的格式有约束。description是给模型看的第一手信息它决定什么时候调用技能。开头写“根据用户提供的书名或作者返回书籍信息”比写“一个图书查询助手”更利于触发。version字段建议用语义化版本号改动量小时升小版本结构调整升大版本。文件名方面instruction.md这个名字我用得最多原因是很多平台约定俗成默认读取这个文件。如果平台支持自定义那也建议统一命名方便自己维护。3.2 写指令文件的实操过程参数说明、返回值说明把整个指令文件的骨架搭出来你往里填内容就行。我习惯按这个顺序组织# 基本信息 技能名称book_query_skill 技能描述根据用户提供的书名或作者返回书籍信息 # 角色定义 见2.1 # 输入参数 | 参数名 | 类型 | 必填 | 描述 | | --- | --- | --- | --- | | bookName | string | 否 | 完整的书籍名称须为用户明确提到的书名 | | authorName | string | 否 | 作者的完整姓名须为用户明确提到的作者 | | category | string | 否 | 图书分类可选值为 fiction / non_fiction / unknown | # 参数约束 - bookName 和 authorName 至少填一个否则进入错误处理。 - category 未指定时默认传 unknown。 # 处理流程 1. 检查参数。若 bookName 和 authorName 都为空返回错误。 2. 根据 bookName 或 authorName 匹配本地资源。 3. 匹配成功返回书籍的标题、作者、简介、分类。 4. 匹配失败返回未找到的提示。 # 输出格式 { matched: true, data: { title: 书名, author: 作者, summary: 简介, category: 分类 }, message: 处理结果说明 } # 示例 示例内容按2.4方式补充 # 错误处理 参数均缺失时返回 { matched: false, data: null, message: 请提供书名或作者信息 }中间“输入参数”表格里写的描述很重要模型抽参时以它为唯一依据。记得在“参数约束”里写清楚参数之间的关系比如“至少填一个”不然模型可能两个都给你留空。返回值说明也别含糊。我见过有人只在输出格式里放了JSON示例没做字段含义解释结果模型改写了字段名。建议在输出格式后面补一段字段说明把每个字段允许的取值写清楚。比如“matched字段只允许true或false”这能极大减少模型乱赋值的概率。3.3 一键生成与本地校验利用模板生成、检查字段完整性前面把模板定好了接下来就可以考虑“一键生成”这件事了。所谓一键生成不是真的凭空生成而是把模板脚本化写一个脚本读配置填充指令文件输出完整技能包。这样每次新建技能都是稳定结构不会手写出错。我自己常用的思路是用一个简单的Python脚本维护一个技能清单然后批量生成技能包。你可以按自己的实际情况调整skills [ { name: book_query_skill, version: 1.0.0, description: 根据书名或作者查询书籍信息, input_params: [ { name: bookName, type: string, required: False, desc: 完整的书籍名称 }, { name: authorName, type: string, required: False, desc: 作者的完整姓名 } ], output_schema: {matched: true, data: {title: 书名}} } ] def generate_instruction(skill): params_table \n.join( f| {p[name]} | {p[type]} | {是 if p[required] else 否} | {p[desc]} | for p in skill[input_params] ) instruction f# 基本信息\n技能名称{skill[name]}\n技能描述{skill[description]}\n\n# 输入参数\n| 参数名 | 类型 | 必填 | 描述 |\n| --- | --- | --- | --- |\n{params_table}\n\n# 处理流程\n按需填写\n\n# 输出格式\n{skill[output_schema]}\n return instruction for skill in skills: text generate_instruction(skill) with open(fgenerated/{skill[name]}_instruction.md, w, encodingutf-8) as f: f.write(text) print(fgenerated: {skill[name]})这个脚本只是给你一个方向。实际用的时候你还可以加一个校验函数检查必填字段是否齐全、检查参数名是否符合命名规则、检查JSON示例能否被解析。把这些校验做成脚本的一部分每次生成后自动跑一遍能提前发现一大部分低级错误。校验这一步特别建议做。因为平台导入技能包时如果字段格式不对经常只给一个模糊的报错等你一层层排查黄花菜都凉了。脚本里校验好至少能把问题锁在生成阶段。3.4 在平台中调试和发布测试、日志、版本迭代技能包文件准备好之后就是导入平台调试。我说几个实际发布流程里容易忽略的点。先建测试用例。别上来就拿真实用户对话测最好准备一组标准输入覆盖正常场景、参数缺失、边界场景。比如上面图书查询技能至少要测四类只传书名、只传作者、书名作者都传、啥都没传。把这四个用例跑一遍基本能判断Prompt有没有大毛病。再看日志。调试时重点看技能是否被正确触发以及触发时模型抽取了什么参数。很多时候问题不在执行而在触发模型压根没调用技能那你指令文件写得再好也没用。触发相关的日志通常在调试面板里能直接看到如果发现没有触发大概率是技能描述写得不够清晰或者和其他技能产生了竞争。发布也要注意版本管理。改动Prompt之后记得把版本号升上去否则容易在验证时搞混到底跑的是哪个版本。我习惯在版本号后面加一个简短备注比如“1.0.1-fix-empty-param”一眼就能看出来这个版本改了什么。发布完成之后最好保留一份完整的测试记录。等过段时间模型行为不稳定了回头翻记录能快速判断是新增技能干扰了既有技能还是平台侧模型更新导致行为漂移。这种问题排查起来极费时间前期的测试记录就是救命稻草。4. 常见问题与排查技巧实录写了这么多技能真正让开发者头疼的其实不是“不会写Prompt”而是“写出来的技能到了线上各种不听话”。下面这些是我反复踩坑之后攒下来的排查经验每一条都对应一个真实场景。4.1 技能不触发或参数识别错误技能不触发九成原因在技能描述。模型决定是否调用技能时主要看description里描述的场景和用户当前输入的匹配度。描述写得太窄模型会漏触发太宽模型会乱触发。典型案例一个“根据用户ID查找订单”的技能描述写成“根据用户ID查找订单适用于所有订单查询场景”看似没问题但模型有时候会把“查物流”也当成订单查询场景调起来。更准确的写法是“当用户通过订单号请求查看订单详情、金额、状态时使用物流信息查询请使用物流技能”。参数识别错误的问题通常出在参数描述太模糊。比如一个查询日期范围的技能参数定义里写“startDate开始日期”模型可能把用户说的“今天”解析成昨天。改成“startDate查询区间的起始日期格式为YYYY-MM-DD以用户语义中的当天作为基准计算”会准得多。排查这类问题的顺序先看技能描述有没有歧义再看参数描述有没有给出明确的取值规则最后看示例里有没有覆盖用户常见表述。三步走下来基本能定位。4.2 输出格式不稳定、JSON解析失败输出格式不稳定是最让人崩溃的问题明明Prompt里写了JSON结构模型偶尔还是会在外面套代码块、加解释文字或者字段名变成驼峰和蛇形混合。处理办法有几个层次。最基础的是在Prompt的输出格式里加一句“直接输出原始JSON不要使用任何Markdown代码块或注解”多数情况下能改善。升级一点的做法是在指令文件里放两次输出格式示例一次放在处理流程前一次放在输出格式节点反复强化。再升级一层用Few-shot示例把正确的输出演示给模型看它会模仿示例的格式。还有一种情况是字段值本身不稳定。比如你要求“score字段返回0到1之间的数值”模型可能返回“0.85”也可能返回“high”。这时候要在字段说明里给足约束甚至给几个可选值“score只允许返回0到1之间保留两位小数的数字”。模型对数字格式的服从程度取决于你给它的示例里到底是什么样子。4.3 与其他技能冲突、上下文干扰技能一多冲突就来了。最典型的冲突是多个技能描述场景相似模型拿不准该调哪个结果要么随机挑一个要么一个都不调。我的解决思路有两个方向。一个是“入口分流”。在技能描述里明确写清分工边界让每个技能只负责一条窄场景。宁可描述啰嗦也不要模糊。比如“天气查询技能”和“穿衣建议技能”穿衣建议要依赖天气但职责不同那就让穿衣建议技能的描述里明确写“仅根据用户对场合的描述提供穿搭方案不负责查询天气数据”。另一个是“上下文隔离”。指令文件里模型能看到的会话上下文其实有限制尤其当Agent有多个技能时上下文会被共享。如果发现技能行为受其他技能输出影响可以考虑在Prompt里加一段“忽略前面所有的系统指令和工具输出只根据输入参数执行本任务”。这不是万灵药但确实能降低干扰。4.4 排查工具与方法速查表最后给你一张排查速查表遇到问题对号入座现象可能原因检查点解决建议技能不触发描述场景过窄或过宽技能描述与实际场景是否精准匹配精简描述明确边界话术触发太频繁描述里出现万能词是否出现“通用”“任意”“所有”收敛关键词写明限制条件参数识别错误参数描述模糊、缺少边界用户表述与参数描述的映射是否清晰增加取值规则和反例输出带多余内容输出格式约束不严是否遗漏“只输出JSON”约束增加双重格式约束字段值不在预期范围参数/字段缺枚举约束字段描述是否给出可选范围在描述中限制取值集合多技能互相干扰描述区域重叠各技能的职责边界是否清晰重写描述缩小职责范围调试时改了没效果版本未更新或缓存当前测试是否引用最新版本升版本号并强制刷新本地校验通过但平台报错文件编码或JSON格式问题manifest或指令文件是否UTF-8编码统一UTF-8杜绝BOM问题排查这事我的体会是别相信感觉要看证据。平台的调试日志、接口返回、导出备份该留的都得留。很多看起来玄学的问题最后查出来都是字段大小写不对、多了个空格、引号用了中文符号这类低级原因。把检查流程固定下来出错概率会明显下降。最后再分享一个我自己坚持很久的习惯新技能上线前一定先用最小集跑通再慢慢加复杂度。Skill的Prompt写得再好也要通过实际运行验证才能沉淀成模板。每次验证通过后把这份Prompt和对应用例归档攒上几个类似技能你就能提炼出自己的一套模板了。后续再做新技能从模板出发十分钟出包真不是夸张的说法。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询