Questions Form
Present multiple clarifying questions as a Telegram inline-button form.
All questions appear at once; the user answers in any order, then submits.
When to Use
- - You need 2 or more clarifying questions answered before proceeding
- Questions have enumerable options (with optional free-text fallback)
- The channel is Telegram
Do not use this pattern for a single yes/no question — just send one message with buttons for that.
Form Protocol
Step 1: Compose the Form
Define each question internally as an object:
CODEBLOCK0
Initialize internal tracking state:
CODEBLOCK1
Step 2: Send the Header
Send a plain message (no buttons) as the form introduction:
CODEBLOCK2
Step 3: Send Each Question
For each question, send a separate message with inline buttons.
Put selectable options in rows of 2-3 buttons. Always put "Other" on its own last row.
The callback_data must follow this convention: INLINECODE1
Example:
CODEBLOCK3
CODEBLOCK4
CODEBLOCK5
Step 4: Send the Submit Button
After all questions, send the submit/cancel message:
CODEBLOCK6
Step 5: Handle Callbacks
When you receive a callback starting with form::
Regular option (form:<qid>:<value> where value is not other):
- - Record the answer: INLINECODE5
- Acknowledge: send INLINECODE6
"Other" option (form:<qid>:other):
- - Send: INLINECODE8
- Set INLINECODE9
- The next plain text message from the user is their free-text answer
- Record: INLINECODE10
- Acknowledge: INLINECODE11
- Clear INLINECODE12
Submit (form:submit):
- - Check
form_state for any null values - If incomplete: send INLINECODE16
- If complete: set
form_submitted = true and proceed with the collected answers
Cancel (form:cancel):
- - Discard INLINECODE19
- Send: INLINECODE20
Step 6: Use the Answers
Once submitted, reference the collected answers as structured data and proceed:
CODEBLOCK7
Now continue with the original task using these clarified requirements.
Changing Answers
Users can click a different button for any question at any time before submitting.
Simply overwrite the previous value and acknowledge the change:
INLINECODE21
Callback Data Convention
- - All form callbacks must use the
form: prefix - Format: INLINECODE23
- Keep question IDs short (2-8 chars) — Telegram has a 64-byte callback_data limit
- Keep values short and use underscores instead of spaces
- The agent identifies form callbacks by checking if the incoming message starts with INLINECODE24
Button Layout Rules
- - Maximum 2-3 buttons per row for clean rendering
- Keep button labels under 20 characters
- Use Title Case for option labels
- "Other" button always says: INLINECODE25
- "Other" button always goes on its own last row
- Submit button includes checkmark: INLINECODE26
- Cancel button includes X mark: INLINECODE27
Edge Cases and Advanced Patterns
See references/form-patterns.md for:
- - Handling timeouts and abandoned forms
- Dependent questions (show question B only after A is answered)
- Large option sets (>6 options)
- Multi-select questions (toggle pattern)
- Free-text disambiguation
- Resuming interrupted forms
问题表单
以Telegram内联按钮表单的形式呈现多个澄清性问题。
所有问题同时显示;用户可按任意顺序作答,最后提交。
使用场景
- - 在继续操作前需要回答2个或更多澄清性问题
- 问题具有可枚举选项(可选自由文本回退)
- 渠道为Telegram
不要对单个是/否问题使用此模式——只需发送一条带按钮的消息即可。
表单协议
步骤1:构建表单
在内部将每个问题定义为一个对象:
{ id: type, text: 项目类型是什么?, options: [网页应用, 移动应用, API] }
{ id: timeline, text: 你的时间线是什么?, options: [本周, 本月, 不着急] }
{ id: budget, text: 预算范围?, options: [< 1千美元, 1千-5千美元, 5千-1万美元, > 1万美元] }
初始化内部跟踪状态:
form_state = { type: null, timeline: null, budget: null }
awaiting_freetext = null
form_submitted = false
步骤2:发送标题
发送一条纯文本消息(无按钮)作为表单介绍:
json
{
action: send,
channel: telegram,
message: 在继续之前,我有几个问题想问您。\n请点击按钮回答每个问题,完成后点击提交。
}
步骤3:发送每个问题
为每个问题发送一条独立消息,附带内联按钮。
将可选选项按每行2-3个按钮排列。始终将其他放在单独的最后一行。
callback_data 必须遵循以下约定:form:<问题ID>:<值>
示例:
json
{
action: send,
channel: telegram,
message: 1. 这是什么类型的项目?,
buttons: [
[
{ text: 网页应用, callback_data: form:type:web },
{ text: 移动应用, callback_data: form:type:mobile },
{ text: API, callback_data: form:type:api }
],
[
{ text: 其他(输入你的答案), callback_data: form:type:other }
]
]
}
json
{
action: send,
channel: telegram,
message: 2. 你的时间线是什么?,
buttons: [
[
{ text: 本周, callbackdata: form:timeline:thisweek },
{ text: 本月, callbackdata: form:timeline:thismonth },
{ text: 不着急, callbackdata: form:timeline:norush }
],
[
{ text: 其他(输入你的答案), callback_data: form:timeline:other }
]
]
}
json
{
action: send,
channel: telegram,
message: 3. 预算范围?,
buttons: [
[
{ text: < 1千美元, callbackdata: form:budget:lt1k },
{ text: 1千-5千美元, callbackdata: form:budget:1k5k }
],
[
{ text: 5千-1万美元, callbackdata: form:budget:5k10k },
{ text: > 1万美元, callbackdata: form:budget:gt10k }
],
[
{ text: 其他(输入你的答案), callback_data: form:budget:other }
]
]
}
步骤4:发送提交按钮
在所有问题之后,发送提交/取消消息:
json
{
action: send,
channel: telegram,
message: 回答完以上所有问题后,请点击提交。,
buttons: [
[{ text: \u2713 提交所有答案, callback_data: form:submit }],
[{ text: \u2717 取消, callback_data: form:cancel }]
]
}
步骤5:处理回调
当收到以form:开头的回调时:
常规选项(form:<问题ID>:<值>,其中值不是other):
- - 记录答案:form_state[问题ID] = 值
- 确认回复:发送已收到 -- <问题标签>: <所选标签>
其他选项(form:<问题ID>:other):
- - 发送:请输入你对以下问题的答案:<问题文本>
- 设置awaitingfreetext = 问题ID
- 用户下一条纯文本消息即为他们的自由文本答案
- 记录:formstate[问题ID] = <用户文本>
- 确认回复:已收到 -- <问题标签>: <用户文本>
- 清除awaiting_freetext
提交(form:submit):
- - 检查formstate中是否有任何null值
- 如果不完整:发送你还需要回答:<未回答问题列表>
- 如果完整:设置formsubmitted = true并继续处理收集到的答案
取消(form:cancel):
- - 丢弃form_state
- 发送:表单已取消。请告诉我你希望如何继续。
步骤6:使用答案
提交后,将收集到的答案作为结构化数据引用并继续:
已收集:{ type: mobile, timeline: 三月底, budget: 1k_5k }
现在使用这些澄清后的需求继续原始任务。
更改答案
用户可以在提交前随时点击任何问题的不同按钮。
只需覆盖先前的值并确认更改:
已更新 -- <问题>: <新值>
回调数据约定
- - 所有表单回调必须使用form:前缀
- 格式:form:<问题ID>:<值>
- 保持问题ID简短(2-8个字符)——Telegram的callback_data限制为64字节
- 保持值简短,使用下划线代替空格
- 代理通过检查传入消息是否以form:开头来识别表单回调
按钮布局规则
- - 每行最多2-3个按钮以确保清晰渲染
- 按钮标签保持在20个字符以内
- 选项标签使用首字母大写
- 其他按钮始终显示为:其他(输入你的答案)
- 其他按钮始终放在单独的最后一行
- 提交按钮包含勾选标记:\u2713 提交所有答案
- 取消按钮包含叉号标记:\u2717 取消
边界情况和高级模式
参见references/form-patterns.md了解:
- - 处理超时和放弃的表单
- 依赖性问题(仅在回答A后显示问题B)
- 大型选项集(>6个选项)
- 多选问题(切换模式)
- 自由文本消歧
- 恢复中断的表单