当前位置: 首页 > news >正文

前端到AI,LangChain.Js(五)

学习地址:
学习小册

实战

基于前面的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可以直接给入每个类型的特点
在这里插入图片描述

接着创建一个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进行交互了。

在这里插入图片描述

在传统的编程里,我们需要控制用户的输入方式,输入顺序,输入格式等,比如很多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模拟丢硬币
在这里插入图片描述
然后将这个信息作为上下文交给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<void>((resolve) => {
    const intervalId = setInterval(() => {
      if (index < messageList.length) {
        console.log(messageList[index]);
        index++;
      } else {
        clearInterval(intervalId);
        resolve();
      }
    }, 1000);
  });

// 调用chain
  const llmResPromise = chainWithHistory.invoke(
    { input: "用户的问题是:" + input },
    { configurable: { sessionId: "no-used" } }
  );
  const [_, firstRes] = await Promise.all([printMessagesPromise, llmResPromise]);

// 输出占卦结果
  console.log('firstRes===', firstRes);

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

 // 开启新的一轮聊天
  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。
测试:
在这里插入图片描述

在这基础上提问
在这里插入图片描述
这是完整的流程,可以看到因为我们加入了 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),其表现出的智能效果更好。目前大多数代码辅助编程工具,都是基于通用大模型,使用大量的代码训练数据,微调而成。

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

相关文章:

  • DeepSeek V3 源码:从入门到放弃!
  • CInternetToolbar::_CommonHandleFileSysChange函数分析之CReBar::_IDToIndex函数的作用
  • Yocto Linux 量产 BSP 镜像定制
  • C++中避免重复虚函数的三大解决方案:以卡牌游戏开发为例
  • ArcGIS Pro:轻松制作地震动画,洞察灾害动态
  • Kubernetes全解析:从容器编排到云原生霸主
  • Arcgis中添加脚本工具箱
  • Java后端大厂高频面经——Java基础
  • *VulnHub-FristiLeaks:1.3暴力解法、细节解法,主打软硬都吃,隧道搭建、寻找exp、提权、只要你想没有做不到的姿势
  • ​‌fpassthru($stream)‌ 是 PHP 中的一个函数​
  • 加速科技Flex10K-L测试机:以硬核创新重塑显示驱动芯片测试新标杆!
  • Ubuntu 24.04 配置ODBC连接ORACLE 11G数据库
  • 从零构建企业级财务分析数仓 | Hive建模实战
  • 2025 ubuntu24.04系统安装docker
  • vue实现一个pdf在线预览,pdf选择文本并提取复制文字触发弹窗效果
  • 数据结构拓展:详解perror函数(C++)
  • 基于python实现的疫情数据可视化分析系统
  • 3. 前后端实现压缩包文件下载
  • 用R语言的XML库写一个采集图片的爬虫程序
  • 使用python进行数据分析需要安装的库
  • 花卉网站建设推广/交换链接适用于哪些网站
  • java网站建设/seo值是什么意思
  • 微信小程序报价单/张北网站seo
  • 网络编程就是做网站么/武汉楼市最新消息
  • 惠州网站建设/河南做网站的公司
  • 升腾d9116 做网站/深圳seo优化服务商