1. 从两个极端说起:AI 为什么总在“问”和“做”之间反复横跳
用 AI 写代码、做方案、处理文档的人,大概率都遇到过这两种让人血压升高的场景。
第一种,你让它帮你重构一个函数,它反手甩回来五个问题:“请问你希望用什么语言?”“这个函数的输入输出格式是什么?”“有没有性能要求?”“是否需要保留原有注释?”“目标运行环境是什么?”——你明明已经把代码贴上去了,它还在问。第二种,你给它一句“帮我写个用户登录模块”,它二话不说开始输出代码,结果用了一个你项目里根本不存在的框架,数据库字段名全是它自己编的,你看着那两百行代码,删也不是,改也不是。
这两个极端背后其实是同一个问题:AI 对“意图确定性”的判断能力不足。它不知道该在什么时候停下来确认,也不知道哪些信息已经足够支撑它直接动手。问太多,是因为它把所有不确定性都当成了必须澄清的阻塞项;闷头做错,是因为它把“用户没说”直接等同于“用户没要求”,然后用自己的默认值填满了所有空白。
我管这个叫AI 的“意图边界感”缺失。一个合格的协作对象,不管是人还是 AI,都应该具备这种能力:知道哪些信息是必须问清楚的,哪些是可以自己合理推断的,哪些是即使猜错了也不会造成严重后果的。这种判断力,在 Agent 开发领域有一个专门的方向叫clarify-intent,也就是意图澄清。
我最近花了不少时间在这件事上,写了一个 Skill,核心目标就一个:教 AI 在动手之前,先判断自己该不该问。这个 Skill 不是什么复杂的框架,本质上就是一套结构化的判断规则,写在SKILL.md文件里,配合 Agent 的调用逻辑来生效。但就是这么一套规则,把我日常用 AI 干活的返工率降了一大截。
这篇文章我会把这个 Skill 的设计思路、核心判断逻辑、SKILL.md的写法、实际接入 Agent 的过程,以及踩过的坑,全部拆开讲清楚。如果你也在做 Agent 开发,或者只是想让日常用 AI 的效率高一点,这套东西可以直接抄作业。
2. 核心设计思路:把“该不该问”变成一道可计算的判断题
2.1 为什么不是“多问”也不是“少问”,而是“问对”
很多人第一反应是:那就让 AI 少问点呗,别那么啰嗦。但实际用下来你会发现,单纯让 AI “少问”会导致更严重的后果——它开始瞎猜,而且猜得理直气壮。反过来,让 AI “多问”也不行,你每做一步都被打断,效率还不如自己写。
所以核心不是问多问少,而是问对。什么叫问对?我总结了一个判断标准:
一个问题值得被问出来,当且仅当:这个信息的缺失会导致输出结果产生不可逆的方向性错误,且这个信息无法从已有上下文中合理推断。
注意两个关键词:不可逆和无法推断。如果猜错了可以轻松改回来,那就不值得问,直接做,做完让用户确认就行。如果虽然用户没说,但从上下文能推断出合理默认值,那也不值得问,直接用推断值,但在输出里标注清楚“我假设了 XXX”。
这个判断标准听起来简单,但要让 AI 真的能执行,需要把它拆解成更具体的、可操作的规则。
2.2 三层判断模型:阻塞项、推断项、默认项
我把所有信息需求分成三类,对应三种处理策略:
第一类:阻塞项(Blocker)。这类信息缺失时,任何输出都是浪费。比如用户说“帮我改一下这个配置文件”,但没贴配置文件内容——这不是问不问的问题,是根本没法做。阻塞项的特征是:缺失时任务无法启动,或者启动后必然产生完全错误的结果。
第二类:推断项(Inferable)。这类信息用户没说,但从上下文、项目结构、行业惯例可以合理推断。比如用户让你写一个 Python 函数处理 CSV 文件,没说要用什么库——那 pandas 就是合理推断。推断项的处理方式是:直接推断,但在输出中显式标注假设。
第三类:默认项(Defaultable)。这类信息即使猜错了,修改成本也极低,或者有广泛接受的默认值。比如代码缩进用 4 个空格还是 2 个空格,变量命名用驼峰还是下划线——这些不值得问,直接用最常见默认值,用户不满意自己会改。
这三类的判断逻辑,就是整个 Skill 的核心。
2.3 为什么选择 Skill 而不是 Prompt 模板
你可能会问:这不就是写一段好的 System Prompt 吗?为什么要搞成 Skill?
区别在于可复用性和可组合性。Prompt 模板是每次都要手动粘贴的,而且不同任务需要不同的澄清策略。Skill 的好处是它可以被 Agent 自动调用,而且可以和其他 Skill 组合。比如我有一个专门做代码审查的 Skill,一个专门做文档总结的 Skill,clarify-intent 这个 Skill 可以在它们之前自动运行,先判断当前任务的信息完整度,再决定是否进入主流程。
另外,SKILL.md这种格式本身就有结构化优势。它可以用 Markdown 的标题层级来组织判断规则,用表格来列举常见场景,用代码块来给出判断逻辑的伪代码。Agent 在读取这个文件时,能比读一段纯文本 Prompt 更准确地提取规则。
3. SKILL.md 怎么写:从判断规则到可执行逻辑
3.1 文件结构设计
我的SKILL.md整体结构是这样的:
# Clarify Intent Skill ## 触发条件 ## 判断流程 ### 第一步:任务类型识别 ### 第二步:信息完整度评估 ### 第三步:阻塞项检测 ### 第四步:推断项处理 ### 第五步:输出策略选择 ## 常见场景速查表 ## 输出格式规范这个结构的关键在于判断流程是线性的、有顺序的。Agent 不需要一次性做复杂判断,而是按步骤走,每一步只做一个简单决策。这比让 AI “综合考虑”要可靠得多。
3.2 第一步:任务类型识别
不同类型的任务,对信息完整度的要求完全不同。我粗略分了几大类:
| 任务类型 | 典型特征 | 信息容忍度 | 澄清倾向 |
|---|---|---|---|
| 代码生成 | 需要明确语言、框架、接口 | 低 | 倾向多问 |
| 文档总结 | 输入即全部信息 | 高 | 倾向直接做 |
| 方案设计 | 需要明确目标和约束 | 中 | 先问目标再动手 |
| 数据转换 | 需要明确输入输出格式 | 低 | 格式必须问清 |
| 创意写作 | 风格和方向可推断 | 高 | 直接做,做完再调 |
| 问题排查 | 需要错误信息和环境 | 低 | 必须问清现象 |
这个表的作用是给 Agent 一个初始的“澄清倾向”基线。比如识别到是“文档总结”类任务,那默认策略就是“直接做,不问”,因为输入文档本身就是全部信息。识别到是“代码生成”,默认策略就是“先检查关键信息是否齐全”。
3.3 第二步:信息完整度评估
这一步是核心。我设计了一个简单的评分机制,让 Agent 对当前任务的信息完整度打分:
# 伪代码,实际写在 SKILL.md 里是自然语言描述 def assess_completeness(task): score = 0 # 目标明确性:用户是否说清楚了要做什么 if task.goal_is_clear: score += 30 # 输入明确性:用户是否提供了必要的输入材料 if task.input_is_provided: score += 30 # 约束明确性:用户是否说明了限制条件 if task.constraints_are_clear: score += 20 # 输出格式明确性:用户是否指定了期望的输出形式 if task.output_format_is_specified: score += 20 return score评分低于 50 分,说明信息严重不足,必须进入澄清流程。50 到 70 分之间,说明有部分信息缺失,但可能可以推断,进入推断流程。70 分以上,直接执行,在输出中标注假设即可。
这个评分机制的好处是可解释。当 Agent 决定要问问题时,它可以告诉用户“因为你的任务信息完整度评分只有 40 分,主要缺失在输入材料和输出格式上”,而不是莫名其妙地甩一堆问题过来。
3.4 第三步:阻塞项检测
阻塞项是必须问的,没有商量余地。我在SKILL.md里列了一个阻塞项清单:
- 输入缺失:用户说“帮我改一下”,但没给要改的东西
- 目标矛盾:用户的要求自相矛盾,比如“要快但要零延迟”
- 关键参数缺失:比如数据转换任务没说目标格式
- 环境依赖缺失:比如代码任务没说运行环境,且无法从上下文推断
检测到阻塞项时,Agent 应该只问阻塞项相关的问题,不要顺带问一堆非阻塞的。比如用户没给配置文件,那就只问“请提供配置文件内容”,不要同时问“你希望用什么格式输出”“有没有性能要求”之类的。
注意:阻塞项问题要一次性问完,不要挤牙膏。用户最烦的就是回答完一个问题,AI 又问一个,来回好几轮。把所有阻塞项列在一起,让用户一次回答完。
3.5 第四步:推断项处理
推断项的处理原则是:能推断就推断,推断后标注。我在SKILL.md里写了一段推断规则:
当遇到以下情况时,使用合理推断而非询问: - 编程语言未指定,但上下文中有代码片段 → 使用代码片段中的语言 - 库/框架未指定,但任务类型有主流选择 → 使用主流选择(如 Python 数据处理用 pandas) - 命名风格未指定 → 使用项目现有风格,无项目上下文则使用语言社区惯例 - 输出格式未指定 → 使用该任务类型最常见的格式关键点是:推断必须基于证据,不能凭空捏造。如果上下文里没有任何线索,那这个信息可能应该被归为阻塞项,而不是推断项。
3.6 第五步:输出策略选择
根据前面的判断,最终输出策略有三种:
策略 A:直接执行。信息完整度评分 ≥ 70,无阻塞项。直接输出结果,在开头用一句话标注所有推断的假设。
策略 B:推断后执行。信息完整度评分 50-70,无阻塞项。先列出推断的假设,然后执行,输出结果。
策略 C:澄清后执行。存在阻塞项,或信息完整度评分 < 50。列出需要澄清的问题,等待用户回答后再执行。
这三种策略的选择逻辑,是整个 Skill 的最终输出。
4. 接入 Agent:从文件到实际生效
4.1 Agent 如何读取和调用 Skill
不同的 Agent 框架对 Skill 的支持方式不同。我用过的几种方式:
方式一:System Prompt 注入。最简单的方式,把SKILL.md的内容直接拼接到 System Prompt 里。适合轻量级场景,缺点是每次对话都要带上一大段文本,消耗 token。
方式二:工具调用(Tool Use)。把 Skill 包装成一个工具,Agent 在需要时调用。比如定义一个clarify_intent工具,输入是当前任务描述,输出是判断结果和建议策略。这种方式更灵活,但需要 Agent 框架支持工具调用。
方式三:前置处理层。在 Agent 主流程之前加一个预处理步骤,专门运行 clarify-intent 判断。这种方式对 Agent 本身侵入最小,适合已有成熟 Agent 想加装这个能力的场景。
我目前用的是方式二和方式三结合:日常对话用方式二,复杂任务流用方式三。
4.2 实际接入的配置示例
以方式二为例,工具定义大概长这样:
{ "name": "clarify_intent", "description": "判断当前任务是否需要向用户澄清信息,返回建议的执行策略", "parameters": { "type": "object", "properties": { "task_description": { "type": "string", "description": "当前任务的完整描述" }, "context": { "type": "string", "description": "可用的上下文信息" } }, "required": ["task_description"] } }Agent 在接到用户请求后,先调用这个工具,根据返回的策略决定下一步。如果返回“直接执行”,就进入主流程;如果返回“澄清后执行”,就把需要问的问题展示给用户。
4.3 判断逻辑的代码化实现
虽然SKILL.md是自然语言写的,但实际运行时可以把它转成更确定的代码逻辑。我用 Python 写了一个简单的判断函数:
def decide_strategy(task_desc, context): # 识别任务类型 task_type = classify_task(task_desc) # 评估信息完整度 completeness = assess_completeness(task_desc, context) # 检测阻塞项 blockers = detect_blockers(task_desc, context) if blockers: return { "strategy": "clarify", "questions": blockers, "reason": "存在阻塞项,必须澄清" } if completeness >= 70: return { "strategy": "execute", "assumptions": extract_assumptions(task_desc, context), "reason": "信息完整,直接执行" } if completeness >= 50: return { "strategy": "infer_and_execute", "assumptions": infer_missing_info(task_desc, context), "reason": "部分信息缺失,使用推断值执行" } return { "strategy": "clarify", "questions": generate_questions(task_desc, context), "reason": "信息严重不足,需要澄清" }这段代码的核心逻辑就是前面说的三层判断模型。实际部署时,classify_task和assess_completeness可以用简单的规则引擎实现,也可以用一个小模型来做分类。
5. 常见问题与排查技巧实录
5.1 问题速查表
| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| AI 还是问太多 | 阻塞项清单太宽泛 | 检查哪些问题被归为阻塞项 | 收紧阻塞项定义,只保留真正必须的 |
| AI 不问直接做错 | 推断规则太激进 | 检查推断项是否基于足够证据 | 增加推断的证据要求,无证据时归为阻塞项 |
| 判断结果不稳定 | 任务类型识别不准 | 用相同输入多次测试 | 增加任务类型的关键词匹配规则 |
| 用户嫌问题太多 | 问题没有合并 | 检查是否一次性问完 | 把所有阻塞项合并成一条消息 |
| 推断假设没标注 | 输出格式没约束 | 检查输出模板 | 强制在输出开头标注假设 |
5.2 踩过的坑
坑一:把“用户没说”当成“用户没要求”。早期版本里,用户没说输出格式,AI 就直接用默认格式输出,结果用户其实心里有明确期望,只是忘了说。后来我加了一条规则:如果输出格式会影响用户后续使用(比如要导入其他系统),那格式就是阻塞项,必须问。
坑二:推断规则太具体导致过拟合。我一开始写了很多具体推断规则,比如“如果用户提到 Excel 就用 openpyxl”。结果遇到用户说“表格”但实际是 CSV 的情况,AI 就推断错了。后来改成更抽象的规则:“根据任务类型选择主流工具,如果有上下文线索则优先使用上下文线索”。
坑三:问题太多导致用户直接放弃。有一次 AI 一次性问了 8 个问题,用户直接回了一句“算了,我自己写”。后来我加了一个硬限制:单次澄清最多问 3 个问题,超过 3 个说明任务描述本身太模糊,应该建议用户重新组织需求。
坑四:Skill 和主流程冲突。有些 Agent 本身就有澄清机制,加上这个 Skill 后出现了双重询问。解决办法是在 Skill 里加一个检测:如果 Agent 已经有澄清机制,则 Skill 只做判断,不直接输出问题,而是把判断结果传给 Agent 的澄清机制。
5.3 独家避坑技巧
技巧一:用“假设清单”代替“问题清单”。当信息缺失但可以推断时,不要问“你希望用什么格式”,而是直接说“我将使用 JSON 格式输出,如果你需要其他格式请告诉我”。这样用户不需要回答,只需要在不对的时候纠正。这比问问题效率高得多。
技巧二:给问题加优先级。如果确实要问多个问题,标注哪些是“必须回答”的,哪些是“不回答我就用默认值”。用户可以选择只回答必须的,其他的让 AI 自己决定。
技巧三:记录用户的澄清偏好。如果用户多次对某类问题给出相同回答,比如每次都选 JSON 格式,那下次就直接用 JSON,不再问。这个可以通过简单的用户偏好记录来实现。
技巧四:用“反向澄清”处理模糊需求。当用户需求特别模糊时,不要问“你想要什么”,而是给出 2-3 个具体方案让用户选。比如“我理解你可能是想要 A 方案(特点...)或 B 方案(特点...),你倾向哪个?”这比开放式问题更容易得到有效回答。
6. 实际效果与适用边界
6.1 效果数据
我在自己的日常工作中用了大概两个月,粗略统计了一下:
- 代码生成任务的返工率从大概 40% 降到了 15% 左右
- 文档处理任务的澄清轮次从平均 2.3 轮降到了 0.8 轮
- 用户主动中断任务的比例从 12% 降到了 4%
这些数字不是严格实验得出的,只是我个人的使用记录,但趋势很明显:该问的时候问,不该问的时候不问,整体效率提升是显著的。
6.2 适用边界
这个 Skill 不是万能的。它最适合的场景是信息需求相对明确、任务类型可分类的工作,比如代码生成、数据处理、文档转换。对于高度创意性的任务,比如“帮我写个故事”,信息完整度评估本身就不太适用,因为创意任务的信息缺失是常态,而且缺失的信息往往需要通过迭代来发现,而不是通过澄清来补全。
另外,这个 Skill 的效果高度依赖任务类型识别的准确性。如果任务类型识别错了,后面的判断全都会偏。所以我在实际使用中,会把任务类型识别做得比较保守:不确定的时候,归为“通用任务”,使用中等澄清倾向。
6.3 后续可以扩展的方向
这个 Skill 目前还是规则驱动的,判断逻辑都是手写的。后续可以考虑用一个小模型来做任务类型识别和信息完整度评估,这样能处理更复杂的场景。另外,用户偏好记录目前还是简单的键值对,可以做成更结构化的用户画像,让推断更准确。
还有一个有意思的方向是跨会话的意图连续性。比如用户昨天让你写了一个 Python 脚本,今天说“再改一下”,这时候“改什么”是阻塞项,但“用什么语言”就是推断项——因为昨天已经确定了。这种跨会话的上下文利用,目前还比较粗糙,值得继续打磨。
我个人在实际操作中的体会是,写这个 Skill 最大的收获不是那套判断规则本身,而是被迫想清楚了一件事:AI 协作的本质不是让 AI 更聪明,而是让 AI 更懂边界。知道什么时候该问、什么时候该做、什么时候该猜,这比单纯提升模型能力更能解决实际问题。这套SKILL.md我还在持续迭代,每次遇到新的误判场景就加一条规则,慢慢打磨下来,它已经成了我日常用 AI 干活时最离不开的一个基础能力。