大语言模型实战——最小化agent

1. agent是什么

大模型拥有语言理解和推理能力后,就相当于拥有了大脑,要让模型发挥更大的潜力,就需要给它安装上手臂,让它拥有行动的能力。

而Agent就是一个将语言模型和外部工具结合起来的智能体,它使用语言模型的推理能力做出决策,再调用外部工具来完成具体的行动,并将行动结果反馈给语言模型,这样语言模型可以通过行动的结果来做出进一步的决策,直到得出结果(工作流程如下图所示)。

由上可知,一个智能体系统最少由以下几部分组成:

  1. 语言模型
  2. 工具集
  3. Agent

本文将动手搭建一个最小化的agent,下面将分别就这几部分进行展开。

2. 语言模型

首先需要一个具有functionCalling能力的语言模型,来理解用户问题,并针对问题进行思考和规划行动方案。这里使用qwen:7b作为我们的 Agent 模型。

这里和前面一篇文章RAG所用的语言模型相似。

class OllamaChat:
    def __init__(self, model: str = "qwen") -> None:
        self.model = model

	def _build_messages(self, prompt: str, content: str):
	 	……
	 	
    def chat(self, prompt: str, history: List[Dict], content: str) -> str:
        ……

2.1 构造提示词

将用户的问题、历史聊天记录和系统提示词,按照语言模型的格式要求,拼成一个完整的提示词。

    def _build_messages(self, prompt: str, history: List[dict], system_prompt: str):
        messages = [{"role": "system", "content": system_prompt}]
        for item in history:
            messages.append({"role": "user", "content": item["prompt"]})
            messages.append({"role": "assistant", "content": item["response"]})

        messages.append({"role": "user", "content": prompt})
        print(f"prompt messages: {messages}")
        return messages

2.2 聊天对话

这里与前面RAG实现的相同,详情参考搭建纯本地迷你版RAG。

def chat(self, prompt: str, history: List[dict], meta_instruction:str ='') -> str:
import ollama
    response = ollama.chat(
        model=self.model,
        messages=self._build_messages(prompt, history, meta_instruction),
        stream=True
    )
    final_response = ''
    for chunk in response:
        if type(chunk) == "str":
            chunk = to_json(chunk)
        if 'content' in chunk.get('message', {}):
            final_response += chunk['message']['content']
        
    history.append({"prompt": prompt, "response": final_response})
    return final_response, history

2. 工具

工具包括两部分信息,工具的实现和工具的使用描述。

2.1 工具封装

这里实现一个最简单的本地时间函数来作为大语言模型可以调用的工具。

def current_time():
     """获取本地时间信息,返回yyyy-MM-dd HH:mm:ss格式"""
     timestamp = time.time()
     # 将时间戳转换为本地时间
     time_tuple = time.localtime(timestamp)
     return time.strftime("%Y-%m-%d %H:%M:%S", time_tuple)

2.2 工具描述

封装好工具实现后,我们需要对它进行一些描述,目的是让大语言模型知道什么时候调用此工具以及如何调用此工具。具体包括如下信息:

  • name_for_model: 用以给程序识别的工具标识。
  • name_for_human:人类可以理解的工具名称。
  • description_for_model:功能描述,工具能用来做什么。
  • parameters:工具需要的参数。
tool_config = [
	{
	    'name_for_human': '当前系统时间查询',
	    'name_for_model': 'current_time',
	    'description_for_model': '当前系统时间查询是一个简单的工具,用于获取系统本地当前的时间信息。',
	    'parameters': []
	}
]

3. Agent

Agent是核心类,通过提示词和一定的逻辑,将外部工具整合进大语言模型推理决策的流程中,最终完成用户交给的任务。它有以下核心方法:

  • build_system_input: 构造系统提示词
  • parse_latest_plugin_call: 解析大语言模型需要调用的工具信息
  • call_plugin: 调用工具
  • text_completion:对外提供给用户调用的主方法,负责将其它三个方法的功能串联成一个自动解决问题的业务流程。
class Agent:
    def __init__(self, model: str = '') -> None:
        self.system_prompt = self.build_system_input()
        self.model = OllamaChat(model)

    def build_system_input(self):
        ……
    
    def parse_latest_plugin_call(self, text):
        ……
    
    def call_plugin(self, plugin_name, plugin_args):
       	……

    def text_completion(self, text, history=[], max_loops=5):
       	……

3.1 构造system-prompt

作用:根据提示词来告诉大模型可以凋用哪些工具,并且以什么样的方式输出。

Answer the following questions as best you can. You have access to the following tools:

{tool_descs}

Use the following format:

Question: the input question you must answer
Thought: you should always think about what to do
Action: the action to take, should be one of [{tool_names}]
Action Input: the input to the action
Observation: the result of the action
... (this Thought/Action/Action Input/Observation can be repeated zero or more times)
Thought: I now know the final answer
Final Answer: the final answer to the original input question

Begin!

这里的thought->Action->Action Input->Observation结构是一种典型的Reasoning(推理) 和 Action(行动)的思想,旨在使模型能够基于观察到的信息进行推理,然后采取适当行动,从而实现更高级的应用。

工具可能会有多个,我们先为每个工具定义一个给语言模型的通用描述模板,这里就简单的将工具标识、工具描述、工具参数三者以一定的格式接起来。如下:

TOOL_DESC = """{name_for_model}:  {description_for_model} Parameters: {parameters} Format the arguments as a JSON object."""

上面的系统提示词中,有tool_descs和tool_names两个占位符,我们需要用前面定义好的工具作替换:

def build_system_input(self):
    tool_descs, tool_names = [], []
    for item in tool.toolConfig:
        tool_descs.append(TOOL_DESC.format(**item))
        tool_names.append(item['name_for_model'])
    tool_descs = '\n\n'.join(tool_descs)
    tool_names = ','.join(tool_names)
    sys_prompt = REACT_PROMPT.format(tool_descs=tool_descs, tool_names=tool_names)
    return sys_prompt

这样,就能得到一个能用并完整的系统提示词,剩下的只需要用户提问题即可。

3.2 解析工具信息

LLM返回的response中可能带有工具调用信息,我们需要从中查找并解析出要调用的工具和参数。

def parse_latest_plugin_call(self, text):
    plugin_name, plugin_args = '', ''
    i = text.rfind('\nAction:')
    j = text.rfind('\nAction Input:')
    k = text.rfind('\nObservation:')
    if 0 <= i < j:  # If the text has `Action` and `Action input`,
        if k < j:  # but does not contain `Observation`,
            text = text.rstrip() + '\nObservation:'  # Add it back.
        k = text.rfind('\nObservation:')
        plugin_name = text[i + len('\nAction:') : j].strip()
        plugin_args = text[j + len('\nAction Input:') : k].strip()
        text = text[:k]
    return plugin_name, plugin_args, text

3.3 调用工具

这里只有一个工具,直接根据plugin_name调用即可。

def call_plugin(self, plugin_name, plugin_args):
   	1tool.current_time(**plugin_args)
    elif plugin_name == 'local_file_search':
        return '\nObservation:' + tool.local_file_search(**plugin_args)

3.4 主方法

流程为:

  1. 先和模型进行第一次交互,返回一个response。
  2. 解析response中要调用的工具信息,如果不需要工具,直接返回。
  3. 否则,调用工具,并将工具返回的结果拼接模型第一次的输出上,目的是为了给模型提供前一步的上下文。
  4. 和模型进行第二次交互,语言模型根据上下文以及工具调用返回的信息来生成最终的结果。
def text_completion(self, text, history=[], max_loops=5):
    text = "\nQuestion:" + text
    response, his = self.model.chat(text, history, self.system_prompt)
    plugin_name, plugin_args, response = self.parse_latest_plugin_call(response)
    if not plugin_name:
        return response, his
    
    response += self.call_plugin(plugin_name, plugin_args)
    response, his = self.model.chat(response, history, self.system_prompt)
    return response, his

4. 运行流程

启动方式:创建agent并使用agent向大语言模型下一个任务。

agent = Agent('qwen')
response, _ = agent.text_completion(text='告诉我当前系统的本地准确时间?', history=[])
print(response)

这里将详细描述下agent与大语言模型的交互过程。

1)第一次chat
用户prompt:

Question:告诉我当前系统的本地准确时间?

系统提示词:

Answer the following questions as best you can. You have access to the following tools:

current_time:  当前系统时间查询是一个简单的工具,用于获取系统本地当前的时间信息。 Parameters: [] Format the arguments as a JSON object.

Use the following format:

Question: the input question you must answer
Thought: you should always think about what to do
Action: the action to take, should be one of [google_search,current_time,local_file_search]
Action Input: the input to the action
Observation: the result of the action
... (this Thought/Action/Action Input/Observation can be repeated zero or more times)
Thought: I now know the final answer
Final Answer: the final answer to the original input question

Begin!

语言模型的response:

Thought: 我应该使用哪个工具来获取当前系统的本地准确时间?
Action: current_time
Action Input: {}

2)工具调用
调用本地方法parse_latest_plugin_call来解析response,得到的工具信息:

plugin_name:current_time
args: {}

调用工具方法tool.current_time拿到本地时间:2024-05-24 23:02:49

3)第二次chat

将本地时间拼接成Observation得到第二次chat用户提示词输入:

Thought: 我应该使用哪个工具来获取当前系统的本地准确时间?
Action: current_time
Action Input: {}
Observation:2024-05-24 23:02:49

第二次chat的系统提示词和第一次相同,这里省略。

第二次chat的response:

Thought: 我现在可以作答了。
Final Answer: 当前系统时间是 2024-05-24 23:02:49

从第二次chat的response中得到了用户问题的答案。

这样就完成了一个最小化agent,这里主要是演示了下FunctionCalling的调用过程,它是扩展语言模型能力的关键。

作为扩展,我们可以根据需要添加多个tool,例如:

  • 搜索本地文件
  • 获取文件内容

并且可以修改agent的流程,来支持需要多次调用不同tool的复杂任务,相应的也需要更长的上下文和能力更强的语言模型,有兴趣可以尝试下。

参考资料

  1. tiny-universe
  2. 搭建纯本地迷你版RAG

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/639899.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

安全设计 | Microsoft 威胁建模工具Threat Modeling Tool安装及使用详解(文末附样例)

1. 概览 微软威胁建模工具&#xff08;Threat Modeling Tool&#xff09;是 Microsoft 安全开发生命周期 (SDL&#xff0c;Security Develop LifeCycle) 的核心要素。 当潜在安全问题处于无需花费过多成本即可相对容易解决的阶段&#xff0c;软件架构师可以使用威胁建模工具提…

人工智能再现大脑细胞导航的活动模式

人工智能再现大脑细胞导航的活动模式 李升伟 编译 深度学习算法可自发模拟特殊神经元的活动&#xff0c;这种神经元活动可以告诉我们在空间的位置。 大鼠使用被称为网格细胞的大脑细胞帮助它们导航&#xff0c;人工智能程序已经可以再现这种能力。 科学家已经使用人工智能来再…

【STM32+k210项目】基于AI技术智能语音台灯的设计(完整工程资料源码)

视频演示 基于AI技术智能语音台灯的设计 前言&#xff1a; 随着社会的快速发展&#xff0c;人们对家用电器智能化程度的要求越来越高。不管是对于学生人群还是对于工作加班者&#xff0c;台灯是每家每户必不可少的工具&#xff0c;长期处于光线太强或者过弱的环境中学习和一系列…

用户账户的权限管理

用户账户的权限管理 用户账号&#xff1a; 1、超级用户 &#xff1a;管理员账号 root 默认对本机拥有最高权限的账户&#xff0c;在系统中唯一。 2、普通用户&#xff1a;一般由管理员创建&#xff0c;拥有的权限是受限制的&#xff0c;一般只在自己的家目录中拥有完整的权限…

每日练习之排序——链表的合并;完全背包—— 兑换零钱

链表的合并 题目描述 运行代码 #include<iostream> #include<algorithm> using namespace std; int main() { int a[31];for(int i 1;i < 30;i)cin>>a[i];sort(a 1,a 1 30);for(int i 1;i < 30;i)cout<<a[i]<<" ";cout&…

走进创新高地,探索职业未来——记大学生参观刺掌信息科技学习活动

在这个向阳生长&#xff0c;充满活力的5月&#xff0c;一群来自江苏大学的充满朝气与求知欲的大学生们来我司参观学习&#xff0c;他们带着对网络安全的热爱和职业生涯的憧憬&#xff0c;走进我们的企业&#xff0c;开始了探索之旅。 交流会上&#xff0c;江苏刺掌信息科技有限…

Python使用multiprocessing实现多进程

大家好&#xff0c;当我们工作中涉及到处理大量数据、并行计算或并发任务时&#xff0c;Python的multiprocessing模块是一个强大而实用的工具。通过它&#xff0c;我们可以轻松地利用多核处理器的优势&#xff0c;将任务分配给多个进程并同时执行&#xff0c;从而提高程序的性能…

CTFHUB技能树——SSRF(二)

目录 上传文件 ​FastCGI协议 Redis协议 上传文件 题目描述&#xff1a;这次需要上传一个文件到flag.php了.祝你好运 index.php与上题一样&#xff0c;使用POST请求的方法向flag.php传递参数 //flag.php页面源码 <?phperror_reporting(0);if($_SERVER["REMOTE_ADDR&…

“从根到叶:使用决策树导航数据”

目录 一、说明 二、什么是决策树&#xff1f; 三、基本概念&#xff1a; 四、工作原理&#xff1a; 五、分类原理分析 5.1 信息熵&#xff1a; 5.2 信息增益&#xff1a; 5.3 基尼杂质&#xff1a; 5.4 基尼系数和熵的区别&#xff1a; 六、对于回归决策树&#xff1a; 6.1 均方…

OR-Oncology Research 肿瘤学研究

文章目录 一、期刊简介二、征稿信息三、期刊表现四、投稿须知五、投稿咨询 一、期刊简介 Oncology Research以临床前和临床癌症治疗为特色&#xff0c;发表了高质量的同行评审研究&#xff0c;有助于在分子生物学、细胞生物学、生物化学、生物物理学、遗传学、生物学、内分泌学…

128天的创意之旅:从初心到成就,我的博客创作纪念日回顾

文章目录 &#x1f680;机缘&#xff1a;初心的种子——回望创作之旅的启航&#x1f308;收获&#xff1a;成长的果实——128天创作之旅的宝贵馈赠❤️日常&#xff1a;创作与生活的交织&#x1f44a;成就&#xff1a;代码的艺术&#x1f6b2;憧憬&#xff1a;未来的蓝图 &…

【RabbitMQ】SpringAMQP--消息转换器

SpringAMQP–消息转换器 测试发送Object类型消息 1.声明队列 Configuration public class FanoutConfig {Beanpublic Queue objectQueue(){return new Queue("object.queue");} }运行消费者后&#xff1a; 2.发送消息 RunWith(SpringRunner.class) SpringBootTes…

01.爬虫---初识网络爬虫

01.初识网络爬虫 1.什么是网络爬虫2.网络爬虫的类型3.网络爬虫的工作原理4.网络爬虫的应用场景5.网络爬虫的挑战与应对策略6.爬虫的合法性总结 1.什么是网络爬虫 网络爬虫&#xff0c;亦称网络蜘蛛或网络机器人&#xff0c;是一种能够自动地、系统地浏览和收集互联网上信息的程…

物联网应用开发--STM32与机智云通信(ESP8266 Wi-Fi+手机APP+LED+蜂鸣器+SHT20温湿度传感器)

实现目标 1、熟悉机智云平台&#xff0c;会下载APP 2、熟悉新云平台创建产品&#xff0c;项目虚拟调试 3、掌握云平台生成MCU代码&#xff0c;并移植。机智云透传固件的下载 4、具体目标&#xff1a;&#xff08;1&#xff09;注册机智云平台&#xff1b;&#xff08;2&…

数据结构~~二叉树-堆

目录 一、基本概念 树的概念 二叉树-堆的概念 二、堆的结构 三、堆排序 向上调整建堆 向下调整建堆 四、TOP-K 五、完整代码 六、总结 一、基本概念 树的概念 树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09;个有限结点组成一个具有层次关…

在ubuntu中查询与某脚本或某设备相关的进程,ps,pgrep,lsof,fuser,pstree,htop命令的使用指南

一、查询与脚本有关的进程 1. 用ps命令 在 Ubuntu 系统中&#xff0c;如果你想查询与特定 Python 脚本 abc.py 相关的线程&#xff0c;你可以使用 ps 命令和 grep 命令结合来查找。ps 命令用于显示当前运行的进程状态&#xff0c;而 grep 命令可以帮助你过滤出包含指定字符串…

(六)DockerCompose安装与配置

DockerCompose简介 Compose 项目是 Docker 官方的开源项目&#xff0c;负责实现对 Docker 容器集群的快速编排。使用前面介绍的Dockerfile我们很容易定义一个单独的应用容器。然而在日常开发工作中&#xff0c;经常会碰到需要多个容器相互配合来完成某项任务的情况。例如要实现…

Docker Desktop安装和如何在WSL2中使用Docker

最近在使用WSL的过程中&#xff0c;想使用docker遇到了一些问题&#xff0c;在WSL中安装Linux版本的docker&#xff0c;启动镜像之后不能从Windows机器的端口映射出来&#xff0c;查了一圈之后&#xff0c;发现应该使用Docker Desktop软件&#xff0c;下面是安装和使用的方式 …

error1310 写入文件时发生错误,请确认您是否有访问权限 也可能出现error 1304 :写入文件时出错

一般错误提示如下 error1310 Error writing to file 错误 1310 &#xff1a;写入文件时出错&#xff1a;请确认您有权访问该目录&#xff0c; error1304 Error writing to file 错误 1304 &#xff1a;写入文件时出错&#xff1a;请确认您有权访问该目录 1.首先我们退出所…

揭秘齿轮加工工艺的选用原则:精准打造高效传动的秘密武器

在机械制造领域&#xff0c;齿轮作为传动系统中的重要组成部分&#xff0c;其加工工艺的选择至关重要。不同的齿轮加工工艺会影响齿轮的精度、耐用性和效率。本文将通过递进式结构&#xff0c;深入探讨齿轮加工工艺的选用原则&#xff0c;带您了解如何精准打造高效传动的秘密武…