跳到主要内容
TypeChat

TypeScript 基本用法

TypeChat 目前是一个小型库,所以让我们看看一些基本用法来理解它。

import fs from "fs";
import path from "path";
import { createJsonTranslator, createLanguageModel } from "typechat";
import { processRequests } from "typechat/interactive";
import { createTypeScriptJsonValidator } from "typechat/ts";
import { SentimentResponse } from "./sentimentSchema";

// Create a model.
const model = createLanguageModel(process.env);

// Create a validator.
const schema = fs.readFileSync(path.join(__dirname, "sentimentSchema.ts"), "utf8");
const validator = createTypeScriptJsonValidator<SentimentResponse>(schema, "SentimentResponse");

// Create a translator.
const translator = createJsonTranslator(model, validator);

// Process requests interactively or from the input file specified on the command line
processRequests("😀> ", process.argv[2], async (request) => {
    const response = await translator.translate(request);
    if (!response.success) {
        console.log(response.message);
        return;
    }
    console.log(`The sentiment is ${response.data.sentiment}`);
});

提供模型

TypeChat 可以与任何语言模型一起使用。只要你能构造一个具有以下属性的对象

export interface TypeChatLanguageModel {
    /**
     * Optional property that specifies the maximum number of retry attempts (the default is 3).
     */
    retryMaxAttempts?: number;
    /**
     * Optional property that specifies the delay before retrying in milliseconds (the default is 1000ms).
     */
    retryPauseMs?: number;
    /**
     * Obtains a completion from the language model for the given prompt.
     * @param prompt The prompt string.
     */
    complete(prompt: string): Promise<Result<string>>;
}

那么你就可以尝试使用这样的模型来使用 TypeChat。

这里的关键是只需要 completecomplete 只是一个函数,它接受一个 string 并在一切顺利的情况下最终返回一个 string

为方便起见,TypeChat 提供了两个开箱即用的函数来连接 OpenAI API 和 Azure 的 OpenAI 服务。你可以直接调用它们。

export function createOpenAILanguageModel(apiKey: string, model: string, endPoint? string): TypeChatLanguageModel;

export function createAzureOpenAILanguageModel(apiKey: string, endPoint: string): TypeChatLanguageModel;

为了更方便,TypeChat 还提供了一个函数来推断你使用的是 OpenAI 还是 Azure OpenAI。

export function createLanguageModel(env: Record<string, string | undefined>): TypeChatLanguageModel

你可以填充你的环境变量,根据是设置了 OPENAI_API_KEY 还是 AZURE_OPENAI_API_KEY,你将获得相应类型的模型。

import dotenv from "dotenv";
dotenv.config(/*...*/);
import * as typechat from "typechat";
const model = typechat.createLanguageModel(process.env);

无论你决定如何构建模型,我们都建议将你的秘密令牌/API 密钥保存在 .env 文件中,并在 .gitignore 中指定 .env。你可以使用像 dotenv 这样的库来帮助加载这些。

加载模式

TypeChat 向语言模型描述类型,以帮助指导它们的响应。在这种情况下,我们使用的是 TypeScriptJsonValidator,它使用 TypeScript 编译器根据一组类型验证数据。这意味着我们将把我们期望返回的数据类型写入 .ts 文件。以下是我们的模式文件 sentimentSchema.ts 的样子

// The following is a schema definition for determining the sentiment of a some user input.

export interface SentimentResponse {
    sentiment: "negative" | "neutral" | "positive";  // The sentiment of the text
}

这也意味着我们需要手动原样加载一个输入 .ts 文件。

// Load up the type from our schema.
import type { SentimentResponse } from "./sentimentSchema";

// Load up the schema file contents.
const schema = fs.readFileSync(path.join(__dirname, "sentimentSchema.ts"), "utf8");

注意:此代码假定为 CommonJS 模块。如果你使用的是 ECMAScript 模块,你可以根据运行时的版本使用 import.meta.url 或通过 import.meta.dirname

这给某些类型的构建带来了一些复杂性,因为我们的输入文件需要被视为本地资产。实现这一点的一种方法是使用运行时或工具(如 ts-node)来导入文件以获取其类型,并读取文件内容。另一种方法是使用像 copyfiles 这样的实用程序将特定的模式文件移动到输出目录。如果你正在使用打包器,也可能存在将文件作为原始字符串导入的自定义方式。无论如何,我们的示例应该适用于前两种选项中的任何一种。

或者,如果需要,我们可以使用 Zod 和 ZodValidator 完全在内存中构建我们的模式,我们稍后会介绍。如果我们走这条路,我们的模式会是这样的。

import { z } from "zod";

export const SentimentResponse = z.object({
    sentiment: z.enum(["negative", "neutral", "positive"]).describe("The sentiment of the text")
});

export const SentimentSchema = {
    SentimentResponse
};

创建验证器

验证器实际上有两个作用:为语言模型生成文本模式,并确保任何数据都符合给定的形状。接口大致如下

/**
 * An object that represents a TypeScript schema for JSON objects.
 */
export interface TypeChatJsonValidator<T extends object> {
    /**
     * Return a string containing TypeScript source code for the validation schema.
     */
    getSchemaText(): string;
    /**
     * Return the name of the JSON object target type in the schema.
     */
    getTypeName(): string;
    /**
     * Validates the given JSON object according to the associated TypeScript schema. Returns a
     * `Success<T>` object containing the JSON object if validation was successful. Otherwise, returns
     * an `Error` object with a `message` property describing the error.
     * @param jsonText The JSON object to validate.
     * @returns The JSON object or an error message.
     */
    validate(jsonObject: object): Result<T>;
}

换句话说,这只是所有类型的文本、要响应的顶级类型的名称,以及一个验证函数,如果成功,它将返回输入的强类型视图。

TypeChat 提供了两个验证器。

TypeScriptJsonValidator

TypeScriptJsonValidator 作用于 TypeScript 文本文件。要创建一个,我们必须从 typechat/ts 导入 createTypeScriptJsonValidator

import { createTypeScriptJsonValidator } from "typechat/ts";

我们还需要实际从我们的模式中导入类型。

import { SentimentResponse } from "./sentimentSchema";

有了我们的模式文本和这个类型,我们就有足够的信息来创建一个验证器

const validator = createTypeScriptJsonValidator<SentimentResponse>(schema, "SentimentResponse");

我们提供了模式的文本和我们希望返回数据满足的类型名称。我们还必须提供类型参数 SentimentResponse 来解释我们期望的数据形状(尽管请注意,这有点像类型转换,并且不能保证)。

Zod 验证器

如果你选择使用 Zod 定义模式,可以使用 createZodJsonValidator 函数

import { createZodJsonValidator } from "typechat/zod";

Zod 验证器不需要源文件,而是需要一个 JavaScript 对象,将类型名称映射到 Zod 类型对象,例如以下示例中的 myObj

export const MyType = z.object(/*...*/);

export const MyOtherType = z.object(/*...*/);

export let myObj = {
    MyType,
    MyOtherType,
}

从上面看,那只是 SentimentSchema

export const SentimentSchema = {
    SentimentResponse
};

所以我们需要导入那个对象...

import { SentimentSchema } from "./sentimentSchema";

并将其以及我们预期的类型名称提供给 createZodJsonValidator

const validator = createZodJsonValidator(SentimentSchema, "SentimentResponse");

创建 JSON 转换器

TypeChatJsonTranslator 将这些结合在一起。

import { createJsonTranslator } from "typechat";

转换器既接受模型又接受验证器,并提供一种将一些用户输入转换为我们模式中的对象的方法。为此,它根据模式制作提示,联系模型,解析 JSON 数据,并尝试验证。可选地,如果验证失败,它将制作修复提示并重试。

const translator = createJsonTranslator(model, validator);

当我们准备好转换用户请求时,我们可以调用 translate 方法。

translator.translate("Hello world! 🙂");

我们稍后会回到这里。

创建提示

TypeChat 导出一个 processRequests 函数,可以轻松地进行 TypeChat 实验。我们需要从 typechat/interactive 导入它。

import { processRequests } from "typechat/interactive";

它要么创建一个交互式命令行提示,要么从文件中读取行。

typechat.processRequests("😀> ", process.argv[2], async (request) => {
    // ...
});

processRequests 接受 3 个参数。首先是提示前缀——这是用户在交互式场景中在自己的文本之前会看到的内容。你可以让它有趣一些。我们喜欢在这里使用表情符号。😄

接下来,我们接受一个文本文件名。输入字符串将按行从该文件中读取。如果文件名是 undefinedprocessRequests 将在标准输入上工作并提供一个交互式提示。使用 process.argv[2] 使我们的程序默认是交互式的,除非运行程序的人提供了输入文件作为命令行参数(例如 node ./dist/main.js inputFile.txt)。

最后,是请求处理程序。我们将在下一步填写它。

翻译请求

我们的处理程序每次被调用时都会收到一些用户输入(request 字符串)。是时候将该字符串传递给我们的 translator 对象了。

typechat.processRequests("😀> ", process.argv[2], async (request) => {
    const response = await translator.translate(request);
    if (!response.success) {
        console.log(response.message);
        return;
    }
    console.log(`The sentiment is ${response.data.sentiment}`);
});

我们正在对每个字符串调用 translate 方法并获得响应。如果出现问题,TypeChat 将重试请求,最多重试次数由我们 model 上的 retryMaxAttempts 指定。但是,如果初始请求以及所有重试都失败,response.success 将为 false,我们将能够获取一条 message 解释出了什么问题。

在理想情况下,response.success 将为 true,我们将能够访问我们类型良好的 data 属性!这将对应于我们在创建转换器对象时传入的类型(即 SentimentResponse)。

就是这样!你现在应该对 TypeChat 的 API 以及如何开始一个新项目有了基本的了解。🎉