前端到AI,LangChain.Js(五)

06-02 1399阅读

学习地址:

学习小册

实战

基于前面的RAG模块,可以通过构建本地存储向量数据库,本地存储聊天记录,部署成stream API,做一个chat bot。

Agents模块,可以通过tools进行数据标签和信息提取,通过RUnnableBranch构建复杂的chain等。

MBTI chat bot

做一个 MBTI 的 chatbot,让用户输入自己的 MBTI 类型和问题,然后由 LLM 根据这个性格去解答问题。

但不是使用传统的 GUI,而是让 LLM 去询问用户的 MBTI 和问题,多次对话,直到 LLM 认为收集到了足够的参数后,再调用对应的函数去回答用户的问题。

首先,先创建一个专业回答MBTI的chain

// 定义prompt
const prompt = ChatPromptTemplate.fromMessages([
  [
    "system",
    `你是一个共情能力非常强的心理医生,并且很了解MBTI(迈尔斯-布里格斯性格类型指标)的各种人格类型,
    你的任务是根据来访者的 MBTI 和问题,给出针对性的情感支持,你的回答要富有感情、有深度和充足的情感支持,引导来访者乐观积极面对问题`,
  ],
  [
    "human",
    "用户的 MBTI 类型是{type}, 这个类型的特点是{info}, 他的问题是{question}",
  ],
]);
//顺序调用
const mbtiChain = RunnableSequence.from([
  prompt,
  chatModel,
  new StringOutputParser(),
]);

这个chain主要就是根据用户的类型特点回答用户的问题

接着定义一个tools,这个tools负责调用mbtiChain

// 定义一个tools,里面调用mbtiChain去返回函数的内容
const mbtiTool = new DynamicStructuredTool({
  name: "get-mbti-chat",
  schema: z.object({
    type: z.enum(mbtiList).describe("用户的 MBTI 类型"),
    question: z.string().describe("用户的问题"),
  }),
  func: async ({ type, question }) => {
    const info = mbtiInfo[type];
    const res = await mbtiChain.invoke({ type, question, info });
    return res;
  },
  description: "根据用户的问题和 MBTI 类型,回答用户的问题",
});
const tools = [mbtiTool];

type和question是必填的信息,info可以直接给入每个类型的特点

前端到AI,LangChain.Js(五)

接着创建一个agent,该智能体主要用来与用户的沟通,获取用户的信息,判断用户的类型,然后调用tool去调用mbtiChain来回答用户的问题。

 const agentPrompt = await ChatPromptTemplate.fromMessages([
    [
   // 有足够信息的时候就去调用get-mbti-chat,让专业的mbti-chain来回答问题,没有的话就反复询问
      "system",
      "你是一个用户接待的 agent,通过自然语言询问用户的 MBTI 类型和问题,直到你有足够的信息调用 get-mbti-chat 来回答用户的问题,如果是通过调用 get-mbti-chat回答的问题,返回内容请包括 “这是来自专业的MBTI导师提供的建议”",
    ],
    new MessagesPlaceholder("history_message"),
    ["human", "{input}"],
    new MessagesPlaceholder("agent_scratchpad"),
  ]);
  const agent = await createOpenAIToolsAgent({
    llm: chatModel,
    tools,
    prompt: agentPrompt,
  });
  const agentExecutor = new AgentExecutor({
    agent,
    tools,
  });

首先定义一个prompt,表明他的主要功能,并且强化对get-mbti-chat调用的指示。

然后还要加上历史记忆功能。直接用内存history,然后用RunnableWithMessageHistory将chain包裹起来。

 // 创建hisotry
  const messageHistory = new ChatMessageHistory();
  // 包裹,使chain具有历史记忆功能
  const agentWithChatHistory = new RunnableWithMessageHistory({
    runnable: agentExecutor,
    getMessageHistory: () => messageHistory,
    inputMessagesKey: "input",
    historyMessagesKey: "history_message",
  });

这样这个agent就具有了历史记忆功能。

接着可以通过readline来通过cli交互。

 // 创建一个cli交互功能
  const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout,
  });
  function chat() {
    rl.question("User: ", async (input) => {
      if (input.toLowerCase() === "exit") {
        rl.close();
        return;
      }
      // 调用agent回答去返回
      const response = await agentWithChatHistory.invoke(
        {
          input,
        },
        {
          configurable: {
            sessionId: "no-used",
          },
        }
      );
      console.log("Agent: ", response.output);
      chat();
    });
  }
  console.log("请输入问题。 输入 exit 退出聊天。");
  chat();

至此,我们可以来跟agent进行交互了。

前端到AI,LangChain.Js(五)

在传统的编程里,我们需要控制用户的输入方式,输入顺序,输入格式等,比如很多MBTI问卷,就是GUI的方式。

而在llm中,这些不受限制,由LLM支持的基于语义理解推理引擎,用自然语言一步一步引导用户说出需求,通过自己对任务理解来创造流程引导用户。也就是LLM_UIZ

算命 bot

llm 本质上是一个语言模型,其基于预训练的模型,根据用户输入的 prompt 去生成最大概率的下一个字符(token)。换句话说,其最擅长的是“把话说圆”。 在前面的章节中,我们讲解了 llm 展现出来的是涌现的智能,他并不理解输出内容的意义,而是一个根据概率吐出 token 的机器。

六爻算卦

初爻 为 背字背 为 阴
二爻 为 字背字 为 阳
三爻 为 背背背 为 阴
您的首卦为 坎
四爻 为 字字字 为 阳
五爻 为 背背背 为 阴
六爻 为 字字背 为 阳
您的次卦为 震
六爻结果: 震坎  
卦名为:屯卦   
水雷屯(屯卦)起始维艰   
卦辞为:风刮乱丝不见头,颠三倒四犯忧愁,慢从款来左顺遂,急促反惹不自由   
算卦流程实现

八卦和八卦对应的信息是有真实答案的类别,一般不要让LLm自己生成,否则会出现一些不存在的卦象和解读。

所以,类似于 RAG 的思路,我们把标准的算卦流程和真实的八卦信息,由我们代码生成,并在后续 chat 中,直接嵌入到 llm 上下文中。

具体的实现过程就是把算卦流程编码化,写起来比较繁琐,但逻辑很简单。

模拟算卦流程

 const yaoName = ["初爻", "二爻", "三爻", "四爻", "五爻", "六爻"];
const guaDict = {
  阳阳阳: "乾",
  阴阴阴: "坤",
  阴阳阳: "兑",
  阳阴阳: "震",
  阳阳阴: "巽",
  阴阳阴: "坎",
  阳阴阴: "艮",
  阴阴阳: "离",
};
// 模拟算卦流程
function generateGua(): string[] {
  let yaoCount = 0;
  const messageList = [];
  const genYao = () => {
    const coinRes = Array.from({ length: 3 }, () =>
      Math.random() > 0.5 ? 1 : 0
    );
    const yinYang = coinRes.reduce((a, b) => a + b, 0) > 1.5 ? "阳" : "阴";
    const message = `${yaoName[yaoCount]} 为 ${coinRes
      .map((i) => (i > 0.5 ? "字" : "背"))
      .join("")} 为 ${yinYang}`;
    return {
      yinYang,
      message,
    };
  };
  const firstGuaYinYang = Array.from({ length: 3 }, () => {
    const { yinYang, message } = genYao();
    yaoCount++;
    messageList.push(message);
    return yinYang;
  });
  const firstGua = guaDict[firstGuaYinYang.join("")];
  messageList.push(`您的首卦为 ${firstGua}`);
  const secondGuaYinYang = Array.from({ length: 3 }, () => {
    const { yinYang, message } = genYao();
    yaoCount++;
    messageList.push(message);
    return yinYang;
  });
  const secondGua = guaDict[secondGuaYinYang.join("")];
  messageList.push(`您的次卦为 ${secondGua}`);
  const gua = secondGua + firstGua;
  const guaDesc = guaInfo[gua];
  const guaRes = `
  六爻结果: ${gua}  
  卦名为:${guaDesc.name}   
  ${guaDesc.des}   
  卦辞为:${guaDesc.sentence}   
    `;
  messageList.push(guaRes);
  return messageList;
}
// 生成一次算卦结果
const messageList = generateGua();

用Math.random模拟丢硬币

前端到AI,LangChain.Js(五)

然后将这个信息作为上下文交给LLM。

定义prompt

const guaMessage = messageList.map((message): ["ai", string] => [
    "ai",
    message,
  ]);
  // 成prompt
  const prompt = await ChatPromptTemplate.fromMessages([
    [
      "system",
      `你是一位出自中华六爻世家的卜卦专家,你的任务是根据卜卦者的问题和得到的卦象,为他们提供有益的建议。
你的解答应基于卦象的理解,同时也要尽可能地展现出乐观和积极的态度,引导卜卦者朝着积极的方向发展。
你的语言应该具有仙风道骨、雅致高贵的气质,以此来展现你的卜卦专家身份。`,
    ],
    ...guaMessage,
    new MessagesPlaceholder("history_message"), //方便插入聊天记录
    ["human", "{input}"],
  ]);

将上述算卦的流程转为AI:xxx,传入prompt里面

创建一个简单的chain,有历史记忆功能

//创建一个简单的chain
  const chain = prompt.pipe(chatModel).pipe(new StringOutputParser());
  const history = new ChatMessageHistory();
  const chainWithHistory = new RunnableWithMessageHistory({
    runnable: chain,
    getMessageHistory: (_sessionId) => history,
    inputMessagesKey: "input",
    historyMessagesKey: "history_message",
  });

最后通过cli直接交互

 const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout,
  });
  const question = util.promisify(rl.question).bind(rl);
// 等待用户输入
  const input = await question("告诉我你的疑问: ");
  let index = 0;
 // 一秒打印一次messageList的结果,模拟算卦
  const printMessagesPromise = new Promise((resolve) => {
    const intervalId = setInterval(() => {
      if (index  

输出占卦结果后,用户可能还有疑问,我们需要开启新的一轮聊天

 // 开启新的一轮聊天
  async function chat() {
    const input = await question("User: ");
    if (input.toLowerCase() === "exit") {
      rl.close();
      return;
    }
    const response = await chainWithHistory.invoke(
      { input },
      { configurable: { sessionId: "no-used" } }
    );
    console.log("AI: ", response);
    chat();
  }
  chat();

这样就完成了一个基础的算命bot。

测试:

前端到AI,LangChain.Js(五)

在这基础上提问

前端到AI,LangChain.Js(五)

这是完整的流程,可以看到因为我们加入了 chat history,llm 可以根据用户问题持续解读卦象。

从输出结果来看,llm 把话说圆的能力非常适合算卦的场景,他能有效的把用户的问题和随机生成的卦象挂钩,进行解读。并且我们在 prompt 指定其语言风格是 “仙风道骨、雅致高贵的气质”,只使用这简单的指令,就能控制 llm 的语言风格。

从代码量上看,LLm相关的代码只占很小一部分。所以LLM应用的核心不是LLM,而是用户场景和idea。

AI辅助编程基础

模型是什么?

“模型” 是指一个通过大量数据训练出来的智能系统,能够理解和生成自然语言,并完成多种任务

举个通俗易懂的例子,小孩学说话

1 数据收集:小孩从周围的人听到各种各样的句子和话语,相当于模型在训练过程中接受大量文本数据。

2 模式学习:小孩逐渐理解语言的结构,比如如何组成句子,单词的意思是什么,这就类似模型在训练过程中,识别语言模式和语义的过程。

3 生成语言:当小孩学会了足够多的单词和句子结构后,他可以尝试自己说话,表达想法,类似模型在接受输入后,输出一个合理的响应。

模型训练是什么?

通过海量的人类数据,让模型学会人类文字表达中的概率信息。

  • 1 数据收集 :收集大量的文本或代码数据,这些数据可以来自书籍、文章、代码库等多种来源
  • 2 数据预处理: 对收集到的数据进行清洗和整理,确保数据的质量和一致性。
  • 3 模型训练:利用高性能的计算资源(如 GPU),通过复杂的算法(如梯度下降)调整模型内部的参数,使其能够更准确地预测下一个词或代码片段,从而实现更接近人类语言的表达效果。

    通过这样的训练过程,模型逐渐掌握了语言和编程语言中的各种模式和规则。

    大模型是什么?

    LLM(large language Model)大型语言模型。

    在前面描述的模型原理中,模型只能在部分场景下展现出出色的补全能力,无法发挥更高级别的价值。

    而LLM就是模型的各个维度进行扩大,比如更大的数据集,更大的模型参数量,在“大力出奇迹”的思路下,大模型展现出相当的智能程度,称为“涌现的智能”,当数据和模型达到一定规模后,它表现出了类似智能的表象。

    有一个哲学实验可以帮助理解

    一个只说英语、对中文一窍不通的人被关在一间只有一个开口的封闭房间中。房间里有一本用英文写成的手册,指示该如何处理收到的中文信息及如何用中文作出相应回复。房外的人不断向房间内递进用中文写成的问题,房内的人便按照手册的说明,查找合适的指示,将相应的中文字符组合、形成答案,并将答案递出房间。 如果房内的人查询手册的速度飞快,手册涉及中文的所有应用情形,那么对于房间外的人来说,是否可以认为这个房间里的人懂中文?

    这个思维实验,与大模型十分相似。大模型基于概率原理,依靠巨大的模型规模和训练数据,展现出智能的表象。

    Fine-tune(微调)是什么?

    微调是指,在一个已经预训练的大模型基础上,针对特定任务或领域的进一步训练。例如,一个通用的语言模型可以通过微调,变成专门用于代码生成或特定编程语言的辅助编程。

    Fine-tune的核心在于,针对某个特殊场景,比如编程,应用在巨大通用数据集上训练的模型,再针对新的较小数据集(比如编程语言数据集,肯定小于人类通识),进行二次训练(fine-tune),其表现出的智能效果更好。目前大多数代码辅助编程工具,都是基于通用大模型,使用大量的代码训练数据,微调而成。

    如果用一句话粗略地总结大模型:

    它是一个基于概率和海量数据训练的模型,能够在部分任务中表现出近似甚至超越人类的智能,但这只是“涌现的智能”,而不是真正的智能。

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。

相关阅读

目录[+]

取消
微信二维码
微信二维码
支付宝二维码