AI探索实践15 - Typescript开发AI应用7:实现Agent

大家好,我是feng,欢迎关注公众号和我一起探索。如果文章对你有所启发,请为我点赞、转发!

图片

一、回顾

复习一下之前介绍Typescript开发AI应用的相关知识点:

1.1 配置本地大模型

AI探索实践9 - Typescript开发AI应用1:不用Python!用前端也能开发一个本地运行的“ChatGPT”!icon-default.png?t=N7T8https://mp.csdn.net/mp_blog/creation/editor/136489515

 使用Ollama搭建和管理、运行本地大模型,使用Quasar、Vue3、LangChain等框架技术开发了一个类似ChatGPT的对话式AI应用实例。

1.2 大模型响应的流式输出与处理

AI探索实践10 - Typescript开发AI应用2:前端实现本地模型流式响应输出icon-default.png?t=N7T8https://mp.csdn.net/mp_blog/creation/editor/136522923LangChain提供了3种方式向大模型发送请求,分别是:

  1. invoke:单次请求响应类型。可获取一次请求的完整结果。
  2. batch:一次发送多个请求。可获取批量请求的完整结构。
  3. stream:单词请求、流式响应类型。可获取一次请求的流方式的响应数据数组。

我们可以根据不同的需求场景,采用不同的调用方式。

1.3 使用提示语模板

AI探索实践11 - Typescript开发AI应用3:Prompt Template (提示语模版) 功能icon-default.png?t=N7T8https://mp.csdn.net/mp_blog/creation/editor/136551418

了解提示语模板的作用,通过提示语模板功能,我们可以为用户发送的提示语,补充更多的上下文信息,从而得到更好的答案。我们可以在提示语中间指定模型的角色、明确模型要做的任务,以及说明需要识别用户的输入占位符等。

1.4 大模型输出的格式化

AI探索实践12 - Typescript开发AI应用4:大模型响应数据的格式化输出icon-default.png?t=N7T8https://mp.csdn.net/mp_blog/creation/editor/136570566我们可以对大模型的输出数据格式化为实际需要的类型,比如字符串、数组、json对象等。

1.5 抓取网页、加载和分割文档、向量存储与检索

AI探索实践13 - Typescript开发AI应用5:抓取网页、文档分割、向量存储与检索链语义检索的使用 【推荐】icon-default.png?t=N7T8https://mp.csdn.net/mp_blog/creation/editor/136588067 这篇博文比较重要,可视为一个RAG应用的基本实现示例。

1.6 为提示语模板附加对话历史

AI探索实践14 - Typescript开发AI应用6:将对话历史记录添加到上下文icon-default.png?t=N7T8https://mp.csdn.net/mp_blog/creation/editor/136641393了解如何使用LangChain的API来设置对话历史,主要有3种类型:

  1. AIMessage: 代表大模型消息
  2. HumanMessage:代表用户的消息
  3. SystemMessage:代表系统消息

本文将介绍另一个重要概念:Agent,它是实现企业AI应用的重要组成部分。

二、认识 Agent

2.1 从功能角度看Agent

Agent(智能体) = 一个设置了一些目标或任务,可以迭代运行的大型语言模型。这与大型语言模型(LLM)在像ChatGPT这样的工具中“通常”的使用方式不同。在ChatGPT中,你提出一个问题并获得一个答案作为回应。而Agent拥有复杂的工作流程,模型本质上可以自我对话,而无需人类驱动每一部分的交互。

这个定义有几个关键词:

  1. 任务:有明确的目的
  2. 迭代:能够循环执行某些逻辑或任务
  3. 大语言模型:agent的核心或者本质是大语言模型。
  4. 流程:按照一定的程序
  5. 自动:不需要人工干预

所以如果这个定义按照关键词来理解就是:一个能够解决明确问题的、按照一定流程执行的、不需要人工干预的、如有必要可以循环执行任务的大模型。

如果给这个定义加上:视觉、听觉和金属外壳,这不就是科幻电影中的人工智能的机器人嘛!

2.2 从开发角度看Agent

来看看LangChain上对Agent是如何定义的:Agents | 🦜️🔗 Langchain

The core idea of agents is to use a language model to choose a sequence of actions to take. In chains, a sequence of actions is hardcoded (in code). In agents, a language model is used as a reasoning engine to determine which actions to take and in which order.

中文含义就是:Agent的核心思想是使用语言模型来选择要采取的一系列操作。在链中,一系列操作被硬编码(在代码中)。在Agent中,语言模型被用作推理引擎来确定要采取哪些操作以及按什么顺序。

可以看出从开发角度和功能角度的定义是不同的。这个定义有2个关键词:

  1. 语言模型:个人理解指的是至少包含LLM的模型列表,因为只有LLM才具备推理能力。
  2. 系列操作:和ChatGPT等对话类大模型应用不同,Agent是可以进行“一系列操作”的。这说明Agent可以处理内部或外部的操作。内部操作,主要是大模型推理操作,以及后面对问题的响应。外部操作,是指可以调用外部资源的能力。比如抓取网页、加载文档、调用API获取某些能力等。

可以看出在LangChain中,链里面可以包含多个Agent即Agents,链把大模型抽象出来当做可重用的部分,每个Agent定义了一组操作(工具),由大模型根据“操作”的结果来决定下一步该执行哪一个“操作”。当大模型认为最终实现了任务目标时,代表了该angent执行完成。

Agents =  n * agent + 1 * 大模型

三、实现Agent

我将结合前面的知识,实现一个用于解答LCEL的Agent的示例。

3.1 定义模型和提示语:

const model = new ChatOllama({
  baseUrl: 'http://localhost:11434', // Default value
  model: 'qwen:4b',
  temperature: 0.7,
});

const prompt = ChatPromptTemplate.fromMessages([
  ('system', '你是一个对我非常有帮助的助理,你的名字叫做 Mask'),
  ('human', '{input}'),
  new MessagesPlaceholder('agent_scratchpad'),
]);

3.2 创建一个agent

import { createOpenAIFunctionsAgent, AgentExecutor } from 'langchain/agents';

// 定义一个agent可以调用的工具列表
const tools = [];
// 创建一个agent
const agent = await createOpenAIFunctionsAgent({
  llm: model,
  prompt,
  tools,
});
// 创建一个agent执行器
const agentExecutor = new AgentExecutor({
  agent,
  tools,
});

agent有多个类型,可以参考连接:  Agent Types | 🦜️🔗 Langchain 。

这里,我们使用OpenAIFunctions类型。

为了便于演示,我将创建一个node的控制台程序的逻辑,以便于我们在控制台下可以输入问题和获得LLM的响应。为此,我们需要安装:readline,控制台下执行:

yarn add readline

3.3 控制台应用逻辑

增加代码:


// 在控制台下获得用户的输入和程序的输出
const r1 = createInterface({
  input: process.stdin,
  output: process.stdout,
});

const askQuestion = () => {
  r1.question('User: ', async (input) => {
    // 如果用户输入 exit 则退出控制台应用程序
    if (input.toLocaleLowerCase() === 'exit') {
      r1.close();
      return;
    }

    // 调用 agent
    const response = await agentExecutor.invoke({
      input: input,
    });

    //
    console.log('Agent: ', response.output);
    // 回到问答状态
    askQuestion();
  });
};

// 执行
askQuestion();

我们先执行一下程序:

可以看到在提示语模板中,预定义了大模型的名字:Mask,大模型也能正确回答,但是当我问自己的名字时,大模型产生了“幻觉”。这是因为当前逻辑中,上下文中没有历史记录。

3.4 添加对话记录到上下文中

根据之前的介绍,我们可以轻松的将对话记录添加到上下文中。在提示语模板增加变量:chat_history

const prompt = ChatPromptTemplate.fromMessages([
  ('system', '你是一个对我非常有帮助的助理,你的名字叫做 Mask'),

  new MessagesPlaceholder('chat_history'),

  ('human', '{input}'),
  new MessagesPlaceholder('agent_scratchpad'),
]);

调整相关代码:

// 增加对话历史
const chatHistory = [];

const askQuestion = () => {
  r1.question('User: ', async (input) => {
    // 如果用户输入 exit 则退出控制台应用程序
    if (input.toLocaleLowerCase() === 'exit') {
      r1.close();
      return;
    }

    // 调用 agent
    const response = await agentExecutor.invoke({
      input: input,
      chat_history: chatHistory,
    });

    // LLM输出
    console.log('Agent: ', response.output);
    // 将用户输入,增加到对话记录中
    chatHistory.push(new HumanMessage(input));
    // 将模型响应,添加到对话记录中
    chatHistory.push(new AIMessage(response.output));

    // 回到问答状态
    askQuestion();
  });
};

运行结果:

可以看到,模型可以回答历史对话中的内容了。但是当我们问一个它不知道的问题时,当前的逻辑就无法解决了。此时,agent可以调用操作(工具 )的能力就有用武之地了。

3.5 创建检索链工具

利用之前摘取网页的知识,我们将抓取网页的检索链,创造一个工具,并添加到angent工具列表中:

import { CheerioWebBaseLoader } from 'langchain/document_loaders/web/cheerio';
import { RecursiveCharacterTextSplitter } from 'langchain/text_splitter';
import { OllamaEmbeddings } from '@langchain/community/embeddings/ollama';
import { MemoryVectorStore } from 'langchain/vectorstores/memory';
import { createRetrieverTool } from 'langchain/tools/retriever';

const loader = new CheerioWebBaseLoader(
  'https://js.langchain.com/docs/expression_language/',
);

const docs = await loader.load();

// 创建一个递归文本拆分器
const splitter = new RecursiveCharacterTextSplitter({
  chunkSize: 200,
  chunkOverlap: 20,
});

// 将抓取到的网页内容分割成更小的文档数组
const splitDocs = await splitter.splitDocuments(docs);
//console.log(splitDocs);

const embeddings = new OllamaEmbeddings({
  model: 'qwen:4b',
});

// 这里简单的使用内存向量数据库
const vectorStore = await MemoryVectorStore.fromDocuments(
  splitDocs,
  embeddings,
);

// 检索数据,设置相关性最高的数量:2个
const retriever = vectorStore.asRetriever({
  k: 2,
});

const retrieverTool = createRetrieverTool(retriever, {
  name: 'lecl_search',
  // 这描述,告诉LLM,遇到什么情况时使用这个工具
  description:
    //'使用这个工具来搜索有关于LangChain Expression Language (LCEL)的信息',
    'Use this tool when searching for informatioin about LangChain Expression Language (LCEL)',
});

//const searchTool = new TavilySearchResults();
// 定义一个agent可以调用的工具列表
const tools = [retrieverTool];

运行结果:

可以看到,LLM利用了agent的工具:检索链,来回答了用户的问题。 

四、总结

本文的Agent实例,是利用了之前介绍的几个技能:附加对话历史、文档加载、文档分割、向量数据库、检索链等作为工具,并附加到agent的工具列表中。当面大模型遇到问题推理到需要使用某个工具时,就会调用该工具来实现一定的业务。

其中的重点,则是创建agent工具,以及对这个工具的详细描述,以便大模型能够理解。

示例中检索链工具中文的描述,还不能让qwen:4b正确回答问题。推测应该是模型本身参数少,推理能力较弱,因此换成英文描述得到正确的结果。因此如果想获得好的结果,有两点需要注意:

  1. 大模型本身的能力
  2. 提示语(注释)需要尽可能的详细,以便于大模型的理解
  • 43
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值