经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 大数据/云/AI » 人工智能基础 » 查看文章
TypeChat源码分析:基于大语言模型的定制化 AI Agent 交互规范
来源:cnblogs  作者:10cl  时间:2023/8/2 9:18:13  对本文有异议

TypeChat源码分析:基于大语言模型的定制化 AI Agent 交互规范

image

本文深入介绍了微软最近发布的 TypeChat 项目,该项目允许开发者定义大语言模型返回的响应结构。通过分析源代码,探讨了 Prompt 的基本概念,为定制化开发互动式 AI Agent 提供便捷的解决方案。
文章着重介绍 TypeChat 的关键要素,例如集成不同的大语言模型、提高灵活性,并调整输出以适应特定场景,这对于在游戏中通过 AI Agent 实现多样交互至关重要。

在 TypeChat 中,先定义好 ChatGPT 的响应类型,即 Schema, 创建将自然语言请求翻译为特定类型的 JSON 对象的工具函数, 将函数列表和问题发送给GPT,
GPT根据函数定义,返回要执行的函数名和参数, 不同于 Function calling, 它使用 Typescript 类型来作为 Schema,要求 ChatGPT 返回符合这个类型定义的数据。

因为最近开发一个基于GPT的AI AGENT的游戏,不同的人在地图通过不断的和chatGPT定义角色的NPC聊天交互,然后从NPC那里得到不同的反馈,得到不一样的体验,对于交互的部分,我觉得 tyeChat 就可以很好的交互上的格式问题。

本文对typeChat 其中比较重要的点进行分析:

  1. 大模型对接的地方:目前只是支持了两种大模型,微软自己的Azure的和 OpenAI 的,比如还有很多的大模型如何接入的问题
  2. 灵活性的拓展:比如说目前 typeChat 对于类型的定义过于严格,可能要耗费大量的token 以及对于不需要那么严格的场景,比如聊天,只需要有几个关键的key是对的就可以了如何优化

基于 SourceCodeTrace 项目推崇的原则,本文代码块引用均有来源,SourceCodeTrace Project 帮助您在博客、文章记录的过程中,引入对应项目以及版本,行号等信息,让后续的读者,通过引用来源,能够进行更加深入的学习,在博客或文章中引入代码块时,尽量提供代码的来源信息。

核心架构

核心就是对话,校验,修复型对话,得到想要的结构。

  1. export function createJsonTranslator<T extends object>(model: TypeChatLanguageModel, schema: string, typeName: string): TypeChatJsonTranslator<T> {
  2. const validator = createJsonValidator<T>(schema, typeName);
  3. const typeChat: TypeChatJsonTranslator<T> = {
  4. model,
  5. validator,
  6. attemptRepair: true,
  7. stripNulls: false,
  8. createRequestPrompt,
  9. createRepairPrompt,
  10. translate
  11. };
  12. return typeChat;

/src/typechat.ts?#L65-L76

createJsonTranslator 函数是核心部分,它接受三个参数 model、schema 和 typeName,并返回一个包含几个方法和属性的对象 typeChat,
该对象用于将自然语言请求转换为指定类型的 JSON 对象。

  • model:是用于将自然语言请求翻译为 JSON 的大语言模型,目前是支持微软自己的Azure 和 Openai的
  • schema:是一个包含 JSON schema 的 TypeScript 源代码的字符串。
  • typeName:是在 schema 中指定的目标 JSON 类型的名称。

返回的 typeChat 对象包含以下几个属性和方法:

  • model:保存传入的语言模型。
  • validator:通过调用 createJsonValidator 函数,使用传入的 schema 和 typeName 创建一个 JSON 校验器,并将其保存在 validator 属性中。
  • attemptRepair:一个布尔值,表示在校验失败时是否尝试修复 JSON 对象。
  • stripNulls:一个布尔值,表示是否从最终的 JSON 对象中剥离空值(null)属性。
  • createRequestPrompt(request):一个函数,用于创建用户请求的 Prompt ,包含 JSON schema 和用户请求的内容。
  • createRepairPrompt(validationError):一个函数,检验格式不对的话,修复性的 Prompt ,再次请求。
  • translate(request):一个异步函数,用于将用户请求翻译为 JSON 对象。
    它使用语言模型 model 来翻译用户请求,并调用 JSON 校验器进行验证。如果验证成功,返回验证结果,否则根据 attemptRepair 的值决定是否尝试修复错误,最终返回修复后的 JSON 对象。

Prompt 的核心

  1. function createRequestPrompt(request: string) {
  2. return `You are a service that translates user requests into JSON objects of type "${validator.typeName}" according to the following TypeScript definitions:\n` +
  3. `\`\`\`\n${validator.schema}\`\`\`\n` +
  4. `The following is a user request:\n` +
  5. `"""\n${request}\n"""\n` +
  6. `The following is the user request translated into a JSON object with 2 spaces of indentation and no properties with the value undefined:\n`;
  7. }

/src/typechat.ts?#L78-L84

这里面的核心就是对 ChatGpt 做一个角色的定义, 定义 ChatGpt 作为一个处理JSON对象的服务,在一个就是 typescript 对对象类型的定义描述给 chatGpt 识别。

当 ChatGpt 回复之后,通过 validation 校验的类型错误,在给 chatGpt 说你的类型不对,具体错误是什么, 你需要在输出修改后的JSON对象:

  1. function createRepairPrompt(validationError: string) {
  2. return `The JSON object is invalid for the following reason:\n` +
  3. `"""\n${validationError}\n"""\n` +
  4. `The following is a revised JSON object:\n`;
  5. }

/src/typechat.ts?#L85-L90

通过这样的一次反馈得到最后需要的格式, 但是这个里面如果需要足够的稳定,还需要自行修改源码添加次数,以便达到自己的预期。

增加大模型接口

目前官网里面就支持了两种,微软自己的Azure的和 OpenAI 的ChatGpt,为了探索 TypeChat 核心概念与拓展性,为游戏开发定制 AI Agent 提供便利,还是需要处理这块代码实现不同大模型的对接需求:

  1. export function createLanguageModel(env: Record<string, string | undefined>): TypeChatLanguageModel {
  2. if (env.OPENAI_API_KEY) {
  3. const apiKey = env.OPENAI_API_KEY ?? missingEnvironmentVariable("OPENAI_API_KEY");
  4. const model = env.OPENAI_MODEL ?? missingEnvironmentVariable("OPENAI_MODEL");
  5. const endPoint = env.OPENAI_ENDPOINT ?? "https://api.openai.com/v1/chat/completions";
  6. const org = env.OPENAI_ORGANIZATION ?? "";
  7. return createOpenAILanguageModel(apiKey, model, endPoint, org);
  8. }
  9. if (env.AZURE_OPENAI_API_KEY) {
  10. const apiKey = env.AZURE_OPENAI_API_KEY ?? missingEnvironmentVariable("AZURE_OPENAI_API_KEY");
  11. const endPoint = env.AZURE_OPENAI_ENDPOINT ?? missingEnvironmentVariable("AZURE_OPENAI_ENDPOINT");
  12. return createAzureOpenAILanguageModel(apiKey, endPoint);
  13. }
  14. missingEnvironmentVariable("OPENAI_API_KEY or AZURE_OPENAI_API_KEY");
  15. }

/src/model.ts?#L41-L55

这里是两个模型公用的部分,基本的请求结构差不多,定义 Prompt 和 role, 然后得到 result.data.choices[0].message?.content 返回值。
这里可以修改返回的内容,以及在这里定义每次调用请求的大模型接口,可以通过这个地方,修改为自己定义的接口以及处理自己代码的逻辑。

  1. function createAxiosLanguageModel(url: string, config: object, defaultParams: Record<string, string>) {
  2. const client = axios.create(config);
  3. const model: TypeChatLanguageModel = {
  4. complete
  5. };
  6. return model;
  7. async function complete(prompt: string) {
  8. let retryCount = 0;
  9. const retryMaxAttempts = model.retryMaxAttempts ?? 3;
  10. const retryPauseMs = model.retryPauseMs ?? 1000;
  11. while (true) {
  12. const params = {
  13. ...defaultParams,
  14. messages: [{ role: "user", content: prompt }],
  15. temperature: 0,
  16. n: 1
  17. };
  18. const result = await client.post(url, params, { validateStatus: status => true });
  19. if (result.status === 200) {
  20. return success(result.data.choices[0].message?.content ?? "");
  21. }
  22. if (!isTransientHttpError(result.status) || retryCount >= retryMaxAttempts) {
  23. return error(`REST API error ${result.status}: ${result.statusText}`);
  24. }
  25. await sleep(retryPauseMs);
  26. retryCount++;
  27. }

/src/model.ts?#L89-L116

实际案例 prompt 解析

不通过 typechat, 基于 typechat的原理,你可以直接将下面的 《处理后的 prompt》直接粘贴到 chatGpt 也能得到同样的结果。
这里就是将 typechat 内提问的方式,通过中文的问答来实现的。

用户输入

我要两份大的,一份加意大利辣香肠,另一份多加酱汁。意大利辣肠加罗勒酱汁加加拿大培根。再加一份沙拉。麦
我把加拿大熏肉煎成五分熟。做希腊沙拉,不要红洋葱。给我两杯麦克和杰克,一杯内华达山脉。哦,再加一份不加红洋葱的沙拉。

处理后的 prompt

让 ChatGpt 为提供我们定义的 typescript 格式的 prompt为:
你是一个JSON转换服务,根据以下TypeScript定义将用户请求转换为“Order”类型的JSON对象:

  1. // an order from a restaurant that serves pizza, beer, and salad
  2. export type Order = {
  3. items: (OrderItem | UnknownText)[];
  4. };
  5. export type OrderItem = Pizza | Beer | Salad | NamedPizza;
  6. // Use this type for order items that match nothing else
  7. export interface UnknownText {
  8. itemType: 'unknown',
  9. text: string; // The text that wasn't understood
  10. }
  11. export type Pizza = {
  12. itemType: 'pizza';
  13. // default: large
  14. size?: 'small' | 'medium' | 'large' | 'extra large';
  15. // toppings requested (examples: pepperoni, arugula)
  16. addedToppings?: string[];
  17. // toppings requested to be removed (examples: fresh garlic, anchovies)
  18. removedToppings?: string[];
  19. // default: 1
  20. quantity?: number;
  21. // used if the requester references a pizza by name
  22. name?: "Hawaiian" | "Yeti" | "Pig In a Forest" | "Cherry Bomb";
  23. };
  24. export interface NamedPizza extends Pizza {
  25. }
  26. export type Beer = {
  27. itemType: 'beer';
  28. // examples: Mack and Jacks, Sierra Nevada Pale Ale, Miller Lite
  29. kind: string;
  30. // default: 1
  31. quantity?: number;
  32. };
  33. export const saladSize = ['half', 'whole'];
  34. export const saladStyle = ['Garden', 'Greek'];
  35. export type Salad = {
  36. itemType: 'salad';
  37. // default: half
  38. portion?: string;
  39. // default: Garden
  40. style?: string;
  41. // ingredients requested (examples: parmesan, croutons)
  42. addedIngredients?: string[];
  43. // ingredients requested to be removed (example: red onions)
  44. removedIngredients?: string[];
  45. // default: 1
  46. quantity?: number;
  47. };

以下是用户请求:
"""
我要两份大的,一份加意大利辣香肠,另一份多加酱汁。意大利辣肠加罗勒酱汁加加拿大培根。再加一份沙拉。麦
我把加拿大熏肉煎成五分熟。做希腊沙拉,不要红洋葱。给我两杯麦克和杰克,一杯内华达山脉。哦,再加一份不加红洋葱的沙拉。
"""
以下是转换成JSON对象的用户请求,其中包含2个缩进空间,没有值为undefined的属性:

ChatGPT 的回复

好的!以下是用户请求转换成的JSON对象,根据提供的TypeScript定义进行处理:

  1. {
  2. "items": [
  3. {
  4. "itemType": "pizza",
  5. "size": "large",
  6. "addedToppings": ["意大利辣香肠"],
  7. "quantity": 2
  8. },
  9. {
  10. "itemType": "pizza",
  11. "size": "large",
  12. "addedToppings": ["多加酱汁", "加拿大培根", "罗勒酱汁"],
  13. "quantity": 1
  14. },
  15. {
  16. "itemType": "salad",
  17. "portion": "whole",
  18. "style": "希腊",
  19. "removedIngredients": ["红洋葱"],
  20. "quantity": 1
  21. },
  22. {
  23. "itemType": "pizza",
  24. "size": "medium",
  25. "name": "Yeti",
  26. "addedToppings": ["加拿大熏肉煎成五分熟"],
  27. "quantity": 1
  28. },
  29. {
  30. "itemType": "salad",
  31. "portion": "whole",
  32. "style": "希腊",
  33. "removedIngredients": ["红洋葱"],
  34. "quantity": 1
  35. },
  36. {
  37. "itemType": "beer",
  38. "kind": "麦克和杰克",
  39. "quantity": 2
  40. },
  41. {
  42. "itemType": "beer",
  43. "kind": "内华达山脉",
  44. "quantity": 1
  45. }
  46. ]
  47. }

请注意,此转换是根据提供的TypeScript定义和用户请求进行的。用户请求中提到的披萨配料("意大利辣香肠"、"多加酱汁"、"加拿大培根"、"罗勒酱汁")和沙拉配料("加拿大熏肉煎成五分熟"、"红洋葱")已添加到JSON对象中的相应披萨和沙拉项目中。披萨和沙拉的尺寸和类型也按照用户请求进行了设置。啤酒项目也根据请求中的数量和类型进行了添加。

本文也在持续的更新中,如果你需要得到最新的更新,请访问: TypeChat源码分析:基于大语言模型的定制化 AI Agent 交互规范

原文链接:https://www.cnblogs.com/webers/p/ai_agent_typechat.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号