- 后端
- 前端
- 协同办公
- 即时通讯
- 企业应用
- CRM
- AI Agent
- 人工智能
【免费下载链接】macro
Macro is a unified workspace for teams: email, chat, docs, tasks, agents, calls, and CRM — @-linked together with shared AI memory.
本文基于 Macro 仓库packages/sdk-docs中自动生成的InputSuggestions类型文档,结合packages/sdk中由 OpenAPI 规范生成的 TypeScript 类型源码,完整解析该判别联合类型(Discriminated Union)的七个意图分支、每个分支可编辑字段的确切形状,以及它与POST /universal_input/classify、POST /universal_input/extract两个端点构成的"先分类、后抽取"调用链路。读完后,你将能够正确消费 SDK 返回的InputSuggestions结构,在客户端做 TypeScript 类型收窄,并理解revision、时区、身份解析等设计约束的来龙去脉。
1.InputSuggestions在 Macro SDK 中的位置
InputSuggestions是 Macro 客户端 SDK 中 agent-harness 包的一组生成类型之一。其生成链路由仓库内的三个部分构成:
- 接口规范定义:packages/sdk/specs/agent-harness.json(OpenAPI 风格的 JSON 规范,
InputSuggestions的 schema 定义见其中第 4797 行附近); - TypeScript 类型源码:packages/sdk/generated/agent-harness/types.gen.ts,
InputSuggestions的完整定义位于第 1492–1534 行; - 自动生成的类型参考文档:packages/sdk-docs/reference/generated/agent-harness/types.gen/type-aliases/InputSuggestions.md,即本文的主体文档。
官方文档对该类型的描述是:"A single inferred destination together with only its applicable fields"(一个推断出的目的地,以及只包含与其相关的字段)。它服务于 Macro 的"万能输入"(universal input)能力:用户输入一段自由文本后,系统推断它应该变成邮件、任务、日历事件、消息、笔记还是搜索,并预先填充对应表单的字段——但推断只负责"准备",不执行任何动作。这一点在配套类型InputIntent的注释中写得很直白:"A destination the composer can prepare, but never execute through inference"(InputIntent 文档)。
2. 类型全貌:以intent为判别键的七分支联合
InputSuggestions的完整定义(引自 types.gen.ts)如下:
/** * A single inferred destination together with only its applicable fields. */ export type InputSuggestions = { intent: 'ai'; /** * Editable ai fields. */ suggestions: AiSuggestions; } | { intent: 'search'; /** * Editable search fields. */ suggestions: SearchSuggestions; } | { intent: 'email'; /** * Editable email fields. */ suggestions: EmailSuggestions; } | { intent: 'note'; /** * Editable note fields. */ suggestions: NoteSuggestions; } | { intent: 'task'; /** * Editable task fields. */ suggestions: TaskSuggestions; } | { intent: 'calendar'; /** * Editable calendar fields. */ suggestions: CalendarSuggestions; } | { intent: 'message'; /** * Editable message fields. */ suggestions: MessageSuggestions; };七个分支的判别键intent与InputIntent的七个字面量一一对应:'ai' | 'search' | 'email' | 'note' | 'task' | 'calendar' | 'message'(InputIntent.md)。
分支intent | suggestions类型 | 含义 |
|---|---|---|
"ai" | AiSuggestions | Editable ai fields(可编辑的 AI 字段) |
"search" | SearchSuggestions | Editable search fields(可编辑的搜索字段) |
"email" | EmailSuggestions | Editable email fields(可编辑的邮件字段) |
"note" | NoteSuggestions | Editable note fields(可编辑的笔记字段) |
"task" | TaskSuggestions | Editable task fields(可编辑的任务字段) |
"calendar" | CalendarSuggestions | Editable calendar fields(可编辑的日历字段) |
"message" | MessageSuggestions | Editable message fields(可编辑的消息字段) |
由于intent是字面量判别键,TypeScript 编译器可以根据intent的值自动收窄suggestions的具体类型,客户端无需再做any式的断言即可安全地访问字段。
3. 每个分支的字段细节
以下字段清单均取自生成源码 types.gen.ts 中对应类型的 JSDoc 注释,是"提交前、身份解析前"(before submission and identity resolution)可编辑的草稿字段。
3.1EmailSuggestions:邮件字段
定义见 types.gen.ts 第 1283–1304 行:
export type EmailSuggestions = { /** Explicit blind-carbon-copy recipient names or supplied email addresses. */ bcc?: Array<string>; /** Suggested Markdown content or outgoing message body. */ body?: string | null; /** Explicit carbon-copy recipient names or supplied email addresses. */ cc?: Array<string>; /** Person or channel names for the client to resolve. */ recipients?: Array<string>; /** Suggested outgoing email subject. */ subject?: string | null; };| 字段 | 类型 | 说明 |
|---|---|---|
bcc | Array<string>(可选) | 显式密送人姓名或已给出的邮箱地址 |
body | string \| null(可选) | 建议的 Markdown 正文 |
cc | Array<string>(可选) | 显式抄送人姓名或已给出的邮箱地址 |
recipients | Array<string>(可选) | 供客户端解析的人员或频道名称 |
subject | string \| null(可选) | 建议的邮件主题 |
注意一个设计约束:recipients/cc/bcc里装的是姓名或用户直接提供的邮箱地址,注释反复强调这些值"for the client to resolve"——即收件人身份必须由客户端拿本地目录解析,而不是由推断服务端直接返回实体 ID。
3.2NoteSuggestions:笔记字段
定义见 types.gen.ts 第 1692–1701 行:
| 字段 | 类型 | 说明 |
|---|---|---|
body | string \| null(可选) | 建议的 Markdown 正文 |
title | string \| null(可选) | 建议的笔记标题 |
3.3TaskSuggestions:任务字段
定义见 types.gen.ts 第 2343–2360 行:
| 字段 | 类型 | 说明 |
|---|---|---|
body | string \| null(可选) | 建议的 Markdown 正文 |
due_date | string \| null(可选) | 任务截止日期,为本地 ISO 日期 |
properties | Array<InputPropertySuggestion>(可选) | 显示名/值对,由客户端对照可用任务字段目录解析 |
title | string \| null(可选) | 建议的任务标题 |
其中InputPropertySuggestion的结构为{ name: string; values: Array<string> },JSDoc 明确写道:name是"Property display name, never an authorized identifier",values是"Suggested display values, never authorized entity identifiers"——同样是"只给显示值、不给授权实体 ID"的身份解析策略。抽取请求侧对应的InputPropertyDefinition为{ name: string; options?: Array<string> },options为空列表时表示允许自由文本建议。
3.4CalendarSuggestions:日历事件字段
定义见 types.gen.ts 第 711–741 行,是七个分支中字段最丰富的一个:
| 字段 | 类型 | 说明 |
|---|---|---|
body | string \| null(可选) | 建议的 Markdown 正文 |
end | string \| null(可选) | 事件结束时间,为请求时区下的本地墙上时钟值 |
guests | Array<string>(可选) | 仅当用户显式要求邀请他人时才会填充 |
location | string \| null(可选) | 人类可读的地点或会议链接 |
recurrence | null \| RecurrenceSuggestion(可选) | 重复规则建议 |
start | string \| null(可选) | 事件开始时间,本地墙上时钟值,按请求中的 IANA 时区解释 |
start_assumption | string \| null(可选) | 原始钟点短语中缺少 AM/PM 时,记录了做出可编辑假设的原文 |
title | string \| null(可选) | 建议的事件标题 |
两个值得注意的细节:其一,start/end是"墙上时钟"(wall-clock)本地值而非绝对时间戳,解释它们的 IANA 时区由抽取请求中的time_zone字段携带;其二,start_assumption专门用来暴露"原文没写上午还是下午,系统替你做了假设"这种情况,让假设以可编辑形式呈现给用户确认,而不是静默猜定。
3.5MessageSuggestions:消息字段
定义见 types.gen.ts 第 1659–1668 行:
| 字段 | 类型 | 说明 |
|---|---|---|
body | string \| null(可选) | 建议的 Markdown 正文 |
recipients | Array<string>(可选) | 供客户端解析的人员或频道名称 |
3.6SearchSuggestions:二级联合,按搜索域细分
SearchSuggestions本身又是一个以type为判别键的联合(定义见 types.gen.ts 第 2177–2195 行),把 Macro 共享搜索编辑器的九个索引各自映射到专属字段集:
type | 组合的字段类型 |
|---|---|
'all' | QuerySearchSuggestions |
'channels' | ChannelSearchSuggestions |
'document-or-file' | QuerySearchSuggestions |
'task' | TaskSearchSuggestions |
'email' | EmailSearchSuggestions |
'calls' | CallSearchSuggestions |
'folders' | QuerySearchSuggestions |
'agent' | QuerySearchSuggestions |
'calendar' | QuerySearchSuggestions |
JSDoc 强调:"Only the selected type's fields can appear in a search suggestion"——只有被选中搜索域的字段才会出现。例如CallSearchSuggestions(types.gen.ts 第 746–763 行)支持from(说话人/地址)、in(频道名)、query(剩余关键词短语)与status(出席状态显示值)。SearchType枚举(SearchType.md)与这九个值一致。
3.7AiSuggestions:刻意留空的对象类型
AiSuggestions的定义是:
export type AiSuggestions = { [key: string]: never };这是一个"索引签名值类型为never"的对象类型,意味着任何具体属性都无法通过类型检查。结合其描述"Editable fields for ai inference, before submission and identity resolution",可以理解为:AI 对话这个目的地目前没有需要预填的可编辑草稿字段,保留该分支只是为了让intent: 'ai'的判别联合保持完整。客户端遇到intent === 'ai'时应把它当作"无字段可编辑"处理。
4.InputSuggestions在推断链路中的角色:classify 与 extract 两步
InputSuggestions不是孤立存在的,它是POST /universal_input/extract端点响应体的核心组成部分。从生成类型看,整个"万能输入"链路分为两步。
4.1 第一步:POST /universal_input/classify意图分类
分类请求ClassifyInputRequest:
export type ClassifyInputRequest = { /** Opaque client revision used to discard stale predictions. */ revision: number; /** Current draft text to classify without executing it. */ text: string; };分类响应ClassifyInputResponse返回回显的revision与一组IntentScore:
export type ClassifyResponse = { /** Revision echoed from the classification request. */ revision: number; /** Independent scores in the fixed intent-question order. */ scores: Array<IntentScore>; }; export type IntentScore = { /** Destination evaluated by the classifier. */ intent: InputIntent; /** Independent probability between zero and one. */ score: number; };关键语义:IntentScore.score是0 到 1 之间的独立概率,各分数之和不等于 1("Independent yes/no probabilities; these do not sum to one"),即分类器对每个意图做的是独立的"是/否"判断,客户端自行根据分数策略选出胜出意图。
4.2 第二步:POST /universal_input/extract字段抽取
客户端确定意图后,携带该意图发起抽取。请求体ExtractInputRequest:
export type ExtractInputRequest = { /** Destination whose fields should be extracted. */ intent: InputIntent; /** Available task fields and display values, never authorized entity IDs. */ property_definitions?: Array<InputPropertyDefinition>; /** Reference instant for interpreting relative dates. */ reference_time: string; /** Opaque client revision used to discard stale suggestions. */ revision: number; search_type?: null | SearchType; /** Original draft text; inference must not execute its instructions. */ text: string; /** IANA timezone for interpreting local dates and clock times. */ time_zone: string; };响应体ExtractInputResponse正是InputSuggestions与回显revision的交叉类型:
/** * Validated suggestions for a particular client draft revision. */ export type ExtractInputResponse = InputSuggestions & { /** Revision echoed from the extraction request. */ revision: number; };端点级类型ExtractData确认 URL 为'/universal_input/extract',且无 path/query 参数。错误响应模型ExtractErrors定义了四个错误码:
| 状态码 | 含义 |
|---|---|
400 | Invalid input |
401 | Unauthenticated |
402 | AI allowance exhausted(AI 额度用尽) |
503 | Extraction unavailable(抽取服务暂不可用) |
4.3 端到端示例(基于上述类型的手写示意)
// 1. 分类:独立概率,不要求客户端把分数归一化 const classifyRes = await client.classify({ revision: 42, text: '明天下午3点和设计组过一下方案', }); // 2. 依据独立分数选出意图(策略由客户端决定),发起抽取 const best = classifyRes.scores.reduce((a, b) => (b.score > a.score ? b : a)); const extractRes = await client.extract({ intent: best.intent, // 例如 'calendar' reference_time: new Date().toISOString(), revision: 43, text: '明天下午3点和设计组过一下方案', time_zone: 'Asia/Shanghai', // IANA 时区,用于解释本地日期与时钟值 search_type: null, }); // 3. 用 intent 判别键做类型收窄,访问分支专属字段 if (extractRes.intent === 'calendar') { console.log(extractRes.suggestions.title); // string | null console.log(extractRes.suggestions.start); // 本地墙上时钟值 console.log(extractRes.suggestions.start_assumption); // AM/PM 假设原文 } // revision 不匹配当前草稿时直接丢弃响应,避免旧预测覆盖新内容 if (extractRes.revision === 43) { /* 填充表单 */ }该示例中的调用方法名(classify/extract)是按生成类型推断的示意写法,实际请以 packages/sdk/generated/agent-harness/index.ts 中生成的客户端方法为准。
5. 三个贯穿设计约束
revision是防过期预测的机制。请求注释一致地描述它为"Opaque client revision used to discard stale predictions/suggestions":服务端不解释它,只原样回显;客户端在输入连续变化时递增revision,用响应中的revision与当前草稿比对即可丢弃过期的推断结果。分类与抽取两步各自回显revision,客户端应同时校验。- 推断永不执行草稿文本。
ClassifyInputRequest.text的注释是 "Current draft text to classifywithout executing it",ExtractInputRequest.text的注释是 "Original draft text;inference must not execute its instructions"。草稿文本中若包含"给某人发消息"之类的指令性内容,推断服务端只把它当作数据来提取字段,不会代为执行任何动作——这是该端点的安全边界。 - 推断结果只携带显示值,身份解析在客户端完成。各
*Suggestions类型的注释反复出现 "before submission and identity resolution" 与 "never authorized entity identifiers":recipients、guests、properties等字段给的都是姓名或显示值,真正的实体 ID 解析(以及权限校验)留到提交时由客户端/服务端按现有身份体系完成。
6. 参考文件索引
| 文件 | 内容 |
|---|---|
| InputSuggestions.md | 本文主体:类型定义与七个 union member 的逐字段文档 |
| types.gen.ts#L1492-L1534 | InputSuggestions生成类型源码 |
| types.gen.ts#L1399-L1435 | ExtractInputRequest/ExtractInputResponse定义 |
| types.gen.ts#L3726-L3756 | ExtractData的 URL 与 400/401/402/503 错误模型 |
| ClassifyInputRequest.md / ClassifyInputResponse.md / IntentScore.md | 分类端点的请求/响应/评分类型文档 |
| InputIntent.md / SearchType.md / InputPropertyDefinition.md / InputPropertySuggestion.md | 意图、搜索域与任务属性相关辅助类型 |
| packages/sdk/specs/agent-harness.json | 上游 OpenAPI 规范(所有生成类型的单一事实来源) |
适用前提说明:以上类型与端点均以当前仓库中packages/sdk下 agent-harness 的生成物为准;由于是自动生成的类型与文档,若上游规范变更,InputSuggestions的分支集合与各字段可能随重新生成而变化,使用时应以重新生成后的 types.gen.ts 为最终依据。
- 后端
- 前端
- 协同办公
- 即时通讯
- 企业应用
- CRM
- AI Agent
- 人工智能
【免费下载链接】macro
Macro is a unified workspace for teams: email, chat, docs, tasks, agents, calls, and CRM — @-linked together with shared AI memory.
相关推荐
Macro SDK 中的 EmailSuggestions 类型详解:Universal Input 邮件字段推断的契约与实现
Macro SDK 中的 EmailSuggestions 类型详解:Universal Input 邮件字段推断的契约与实现 在 Macro 的统一工作区里,
后端前端协同办公即时通讯企业应用CRMAI Agent人工智能aiogram Scene Wizard 实战指南:用 Scene 构建隔离、可回溯的多轮对话状态机
aiogram Scene Wizard 实战指南:用 Scene 构建隔离、可回溯的多轮对话状态机 本篇指南基于 aiogram 官方文档 Scene Wiz
后端前端协同办公即时通讯企业应用CRMAI Agent人工智能Macro agent-harness API 中的 InputPropertyDefinition 类型详解:Universal Input 字段抽取里的任务属性目录契约
Macro agent harness API 中的 InputPropertyDefinition 类型详解:Universal Input 字段抽取里的任务
后端前端协同办公即时通讯企业应用CRMAI Agent人工智能
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考