Perslis 无障碍
05 / 获取密钥

你不安装我们的运行时,你调用它。

你的应用用我们发放的密钥调用 TinkySpeak 接口。你无需持有我们的代码,无需分发模型,而符号、声音与界面完全属于你。

坦白现状

试点阶段,人工发放。

目前没有自助开通密钥的控制台。你告诉我们你在做什么,我们发放密钥,并与你一起推进部署。如果你更希望把引擎运行在自己的基础设施里,而不是调用我们的服务,来信时说明即可——那是一次关于条款的沟通,而不是一个下载链接。

接入步骤

六步,从头到尾。

步骤你要做的
1 申请密钥告诉我们你在做什么、为谁而做。我们会为试点发放密钥,并与你讨论部署方式——目前还不是自助开通的控制台。
2 把密钥留在服务端存入 TINKYSPEAK_API_TOKEN,以 Authorization: Bearer … 发送。切勿把密钥打包进应用——任何人都能读出来。当你服务多位用户时,账号与会话归属由你的后端负责。
3 每场对话开一个会话会话承载语言、沟通设置与记忆。在对话持续期间保留它的 id,结束时删除。
4 跑通这五步hear → 渲染 → select → speak → confirm。这就是对话的全部接口,无论使用者是用触摸、开关还是眼动来选择,流程都一样。
5 接上你自己的符号运行时绝不获取或绘制素材。你把选项映射到自己的符号库并返回描述对象;未匹配的选项保留表情符号。
6 需要时再接上摄像头从你自己的摄像头发送画面,同样的磁贴就会返回——关于使用者眼前的菜单、货架或物品。
密钥

它该放在哪里,不该放在哪里。

# Your server, not the device in someone's hands.
export TINKYSPEAK_API_TOKEN="the key we issue you"

密钥标识的是你的应用,而不是某个人。来自浏览器的请求必须来自你登记的来源;非回环地址必须使用令牌。运行时不记录任何对话请求体。

开启会话

每场对话一个。

POST /v1/sessions
Authorization: Bearer $TINKYSPEAK_API_TOKEN
Content-Type: application/json

{ "language": "en", "partnerLanguage": "en",
  "profile": { "age": 7, "level": "sentence", "choices": 6 } }

→ { "id": "SESSION_ID", "choices": [], ... }
主循环

五次调用,中间那一步由你的应用掌控。

// 1 — what the other person said
const state = await api.hear({ text: 'Would you like tea or coffee?' });
renderYourTiles(state.choices);        // six complete sentences

// 2 — the person picks a tile in YOUR interface
const selected = await api.select({ choiceId: tappedTileId });

// 3 — turn the draft into a delivery request
const pending = await api.speak({ draftId: selected.draft.id });

// 4 — YOUR speech engine says it, in your voice, on your device
await yourSpeechEngine(pending.speech.text, pending.speech.language);

// 5 — tell the runtime what actually happened
await api.confirmSpeech({ speechId: pending.speech.id, outcome: 'spoken' });

每个 id 只能使用一次,因此过期的点击无法送出昨天的句子。并发操作返回 409 session_busy。送出失败时用 failed 或 cancelled 回报——绝不要盲目重试。

你自己的符号库

词语由我们提出,图像由你决定。

一个选项包含词语与身份。它看起来是什么样,只有你的应用能决定,因为只有你的应用了解这个人、他的词汇,以及他早已认得的那套图像。

// Every choice arrives with an id, a label, a sentence and an emoji.
// You decide what it looks like.
function yourArtworkFor(choice) {
  const hit = myLibrary.lookup(choice.label, choice.sentence);
  if (!hit) return null;                       // null keeps the emoji fallback
  return { kind: 'symbol', library: 'my-aac-library', id: hit.id, alt: hit.alt };
  // or: { kind: 'image', uri: '/my-art/water.png', alt: 'My water cup' }
}

返回 null 就保留表情符号。对整句生成内容做匹配并不是通用符号词典——请提供语言感知的查找,并让它诚实地失败,而不是显示错误的图像。更换素材绝不会改变被说出的词语。

图像模型

需要时给它一双眼睛。

视觉与对话模型分开配置,因此你可以加入摄像头功能,而不改变词语的生成方式。

// A photo from YOUR camera or photo picker, as base64.
const state = await api.scan({
  images: [{ mimeType: 'image/jpeg', data: base64Frame }],
  mode: 'menu',            // auto | object | menu | scene
});
renderYourTiles(state.choices);          // tiles about what is in the picture
state.vision.categories;                 // the whole catalog, paged
state.vision.objects;                    // { name, box, bodyPart }

每次扫描最多八张图,单张 5 MiB。检测框是图像的比例值,原点在左上角。价格只是展示数据,绝不构成购买。识别可能出错,因此警告与原始文本都会返回给你的复核界面;读取不完整时会失败,而不是编造菜单。

申请 API 密钥先试一试
继续阅读全部命令