Typescript 之父(微软)在 7月 21 号发布了一个有趣的项目—— [Typechat](https://github.com/microsoft/TypeChat)
。旨在使用 AI 来连接自然语言和应用的 Schema / API。说白话就是使用 Typescript 类型信息来约束 ChatGPT 输出内容的结构。
我们早已见证过 ChatGPT 的强大,如果想要对接到我们已有的软件系统,通常会要求它输出 JSON 这类形式化、结构化的数据。如果你调教过 ChatGPT 就会发现, 它的输出结果往往没那么靠谱。为了让它输出符合要求的内容,我们需要给出足够的上下文信息和示例,并且这个调教过程也比较玄学。
本文就来看看 Typechat 是如何让 ChatGPT 输出符合需求的内容
扮演一个 linux 终端
扮演一个 Javascript 执行器
扮演 Typescript
这能说明 ChatGPT 的预训练集中包含了丰富的编程语言相关的内容。
众所周知, ChatGPT 生成的内容存在一定的随机性和不稳定性,很难一步到位。读者们作为开发者我们经常使用它来生成代码,应该能够体会到。
这个问题怎么解决呢?大概有以下几个方向
最后是平常心,开放地对待, AI 不是无所不能的,我们可能用尽的所有技巧, 也可能无法令人满意的答案。
如果我们想要让 AI 连接到其他生态,比如连接到软件系统、控制硬件设备、实现各种自动化流程,在现在这个阶段,我们需要让 ChatGPT 输出结构化的数据,比如 JSON、XML、或者其他常见的 DSL。
就像我们开头说的 “ Typechat 旨在使用 AI 来连接自然语言和应用的 Schema / API”, 结合上面的流程图理解,你应该就能体会到这句话的意思。AI
在这里就是一个连接者
,让用户可以使用自然语言
和我们的应用系统
进行交互,AI 在这里的责任就是将自然语言
翻译为我们应用系统能够处理的 DSL
。
ChatGPT 已经具备这样的能力:
绘制 mermaid 流程图
输出 JSON
如果你要求输出更复杂的数据结构,则需要使用 Few-shot Prompt 等手段,在受限的 Token 范围内,给 ChatGPT足够的案例和上下文信息。
除此之外,OpenAI 官方在 613 版本的 GPT 3.5 和 4 带来了函数调用
的能力(Function Calling), 可以帮助开发者通过 API 方式实现类似于 ChatGPT 插件的数据交互能力。让开发者可以使用 JSON Schema 来描述你的函数接口,GPT 会根据用户的输入,决定调用哪个函数,并组装符合 Schema 要求的 JSON 数据。
以下是 OpenAI 的官方示例:
# 🔴 函数接口定义
functions = [
{
"name": "get_current_weather",
"description": "Get the current weather in a given location",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. San Francisco, CA",
},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]},
},
"required": ["location"],
},
}
]
# 🔴 用户输入
messages = [{"role": "user", "content": "What's the weather like in Boston?"}]
# 调用
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo-0613",
messages=messages,
functions=functions,
function_call="auto", # auto is default, but we'll be explicit
)
response_message = response["choices"][0]["message"]
# 🔴 GPT 会告诉你最终需要调用哪个函数以及它的参数,也可能不会调用任何东西
if response_message.get("function_call"):
gpt-3.5-turbo-0613、gpt-4-0613 针对 Function calling 这种场景做了微调,实际上这些 ‘函数’ 也是注入到 system
prompt 里面,同样会占用请求的 Token。在旧的版本理论上也可以实现类似的效果。我会在后续的文章中专门介绍 Function calling。
实际上,Function Calling 还是不完美,比如无法保证严格按照我们给定的 JSON Schema 输出,不支持复杂的 JSON Schema,缺乏灵活性等等。现在我们开始介绍本文的主角 —— typechat
TypeChat 是微软刚发布一个有趣的项目,不同于 Function calling, 它使用 Typescript 类型来作为 「Schema」,要求 ChatGPT 返回符合这个类型定义的数据。
在 Typechat
中,先定义好 ChatGPT 的响应类型,即 Schema
, 例如:
type Response = {
items: Item[];
};
type Item = {
name: string;
quantity: number;
size?: string;
notes?: string;
}
要求 ChatGPT 返回 JSON 格式,并符合上述的 Response 类型。接着输入用户需求:
Could I get a blueberry muffin and a grande latte?
最后 ChatGPT 返回结果:
{
"items": [
{
"name": "blueberry muffin",
"quantity": 1
},
{
"name": "latte",
"quantity": 1,
"size": "grande"
}
]
}
那么它是怎么工作的?我们在上一节对 ChatGPT 的能力做了大概的分析,你可以将它们结合起来想想:
Typechat 就是运用了上述思路:
它的 Prompt 非常简单。 请求的 Prompt:
function createRequestPrompt(request: string) {
return `You are a service that translates user requests into JSON objects of type "${validator.typeName}" according to the following TypeScript definitions:\n` +
`\`\`\`\n${validator.schema}\`\`\`\n` +
`The following is a user request:\n` +
`"""\n${request}\n"""\n` +
`The following is the user request translated into a JSON object with 2 spaces of indentation and no properties with the value undefined:\n`;
}
纠错 Prompt:
function createRepairPrompt(validationError: string) {
return `The JSON object is invalid for the following reason:\n` +
`"""\n${validationError}\n"""\n` +
`The following is a revised JSON object:\n`;
}
翻译流程:
async function translate(request: string) {
let prompt = typeChat.createRequestPrompt(request);
let attemptRepair = typeChat.attemptRepair;
while (true) {
const response = await model.complete(prompt);
if (!response.success) {
return response;
}
const responseText = response.data;
const startIndex = responseText.indexOf("{");
const endIndex = responseText.lastIndexOf("}");
if (!(startIndex >= 0 && endIndex > startIndex)) {
return error(`Response is not JSON:\n${responseText}`);
}
const jsonText = responseText.slice(startIndex, endIndex + 1);
// 🔴 类型检查
const validation = validator.validate(jsonText);
if (validation.success) {
return validation;
}
if (!attemptRepair) {
return error(`JSON validation failed: ${validation.message}\n${jsonText}`);
}
// 🔴 修复
prompt += `${responseText}\n${typeChat.createRepairPrompt(validation.message)}`;
attemptRepair = false;
}
}
Typechat 与 Function calling 对比:
它们都改变不了 ChatGPT 的特性,结果依然不一定是可靠的。目前 Typechat 只有一轮纠正,其实际的效果、Token 消耗量等还有待验证。
上面我们简单介绍了 ChatGPT 的一些特性和缺陷。接着引入了 Typechat,它给我们提供了一个较新的思路:使用 Typescript 类型来定义 ChatGPT 的输出结构,然后通过 Typescript 来验证输出结果,循环纠正 ChatGPT。
本文的要点: