最简明的大模型agent教程

大模型相关目录

大模型,包括部署微调prompt/Agent应用开发、知识库增强、数据库增强、知识图谱增强、自然语言处理、多模态等大模型应用开发内容
从0起步,扬帆起航。

  1. 大模型应用向开发路径及一点个人思考
  2. 大模型应用开发实用开源项目汇总
  3. 大模型问答项目问答性能评估方法
  4. 大模型数据侧总结
  5. 大模型token等基本概念及参数和内存的关系
  6. 大模型应用开发-华为大模型生态规划
  7. 从零开始的LLaMA-Factory的指令增量微调
  8. 基于实体抽取-SMC-语义向量的大模型能力评估通用算法(附代码)
  9. 基于Langchain-chatchat的向量库构建及检索(附代码)
  10. 一文教你成为合格的Prompt工程师
  11. 最简明的大模型agent教程

文章目录

  • 大模型相关目录
  • 一、Agent简介
  • 二、langchain-chatchat下的Agent开发
  • 三、langchain的Agent开发


一、Agent简介

大模型Agent是结合了大规模神经网络模型和自主计算实体的技术,它具备强大的表达、学习和交互能力,能够在无人干预的情况下,根据环境信息自主决策和控制行为。

简单而言之,agent是增强大模型能力的技术方案路径。主要包括:工具、工具选择方案,大模型工具应用3个部分。运行大体流程:

1用户给出一个任务(Prompt) -> 2思考(Thought) -> 3行动(Action) -> 4观察(Observation)
在这里插入图片描述

二、langchain-chatchat下的Agent开发

更详细地流程可参考GitHub wiki介绍

于tools_select.py预设新增的工具、工具描述等信息
在这里插入图片描述

于下述路径下新增同名新工具py文件,并进行工具内容定义。

在这里插入图片描述
工具样例如下:

from pydantic import BaseModel, Field
import requests
from configs.kb_config import SENIVERSE_API_KEY


def weather(location: str, api_key: str):
    url = f"https://api.seniverse.com/v3/weather/now.json?key={api_key}&location={location}&language=zh-Hans&unit=c"
    response = requests.get(url)
    if response.status_code == 200:
        data = response.json()
        weather = {
            "temperature": data["results"][0]["now"]["temperature"],
            "description": data["results"][0]["now"]["text"],
        }
        return weather
    else:
        raise Exception(
            f"Failed to retrieve weather: {response.status_code}")


def weathercheck(location: str):
    return weather(location, SENIVERSE_API_KEY)


class WeatherInput(BaseModel):
    location: str = Field(description="City name,include city and county")

但我们仍不知道大模型、langchain框架选择工具、使用工具、进行输出等过程的调度原理,可参考如下代码:

from langchain.utilities import ArxivAPIWrapper
from langchain_experimental.tools import PythonAstREPLTool

from typing import Dict, Tuple
import os
import json

arxiv = ArxivAPIWrapper()
python=PythonAstREPLTool()

class ElectricityBillTool:
    def run(self, name, start_date, end_date):
        # 假设这里执行了某些操作来查询电费
        # 为了简化,我们直接返回一条测试信息
        return f"电费查询结果:姓名:{name}, 期间:{start_date}{end_date}, 电费:100元"

def tool_wrapper_for_model(tool, expects_kwargs=True):
    def tool_(args_json):
        args = json.loads(args_json)
        if expects_kwargs:
            return tool.run(**args)  # 使用 **args 将字典展开为关键字参数
        else:
            # 如果 run 方法期望位置参数,我们假设所有参数都聚合在一个叫 'query' 的键下
            return tool.run(args['query'])  # 使用位置参数调用
    return tool_


electricity_bill_tool = ElectricityBillTool()


# 以下是给模型看的工具描述:
TOOLS = [
    {
        'name_for_human':
            'arxiv',
        'name_for_model':
            'Arxiv',
        'description_for_model':
            'A wrapper around Arxiv.org Useful for when you need to answer questions about Physics, Mathematics, Computer Science, Quantitative Biology, Quantitative Finance, Statistics, Electrical Engineering, and Economics from scientific articles on arxiv.org.',
        'parameters': [{
            "name": "query",
            "type": "string",
            "description": "the document id of arxiv to search",
            'required': True
        }], 
        'tool_api': tool_wrapper_for_model(arxiv, expects_kwargs=False)
    },
    {
        'name_for_human':
            'python',
        'name_for_model':
            'python',
        'description_for_model':
            "A Python shell. Use this to execute python commands. When using this tool, sometimes output is abbreviated - Make sure it does not look abbreviated before using it in your answer. "
            "Don't add comments to your python code.",
        'parameters': [{
            "name": "query",
            "type": "string",
            "description": "a valid python command.",
            'required': True
        }],
        'tool_api': tool_wrapper_for_model(python, expects_kwargs=False)
    },
    {
    'name_for_human': 'electricity_bill',
    'name_for_model': 'ElectricityBill',
    'description_for_model': '查询电费工具,根据姓名、开始时间和结束时间查询电费。',
    'parameters': [
        {"name": "name", "type": "string", "description": "姓名", 'required': True},
        {"name": "start_date", "type": "string", "description": "开始时间", 'required': True},
        {"name": "end_date", "type": "string", "description": "结束时间", 'required': True}
    ],
    'tool_api': tool_wrapper_for_model(electricity_bill_tool, expects_kwargs=True)
    }

]

TOOL_DESC = """{name_for_model}: Call this tool to interact with the {name_for_human} API. What is the {name_for_human} API useful for? {description_for_model} Parameters: {parameters} Format the arguments as a JSON object."""

REACT_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!

Question: {query}"""


def build_planning_prompt(TOOLS, query):
    tool_descs = []
    tool_names = []
    for info in TOOLS:
        tool_descs.append(
            TOOL_DESC.format(
                name_for_model=info['name_for_model'],
                name_for_human=info['name_for_human'],
                description_for_model=info['description_for_model'],
                parameters=json.dumps(
                    info['parameters'], ensure_ascii=False),
            )
        )
        tool_names.append(info['name_for_model'])
    tool_descs = '\n\n'.join(tool_descs)
    tool_names = ','.join(tool_names)

    prompt = REACT_PROMPT.format(tool_descs=tool_descs, tool_names=tool_names, query=query)
    return prompt


def parse_latest_plugin_call(text: str) -> Tuple[str, str]:
    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`,
            # then it is likely that `Observation` is ommited by the LLM,
            # because the output text may have discarded the stop word.
            text = text.rstrip() + '\nObservation:'  # Add it back.
            k = text.rfind('\nObservation:')
    if 0 <= i < j < k:
        plugin_name = text[i + len('\nAction:'):j].strip()
        plugin_args = text[j + len('\nAction Input:'):k].strip()
        return plugin_name, plugin_args
    return '', ''

def use_api(tools, response):
    use_toolname, action_input = parse_latest_plugin_call(response)
    if use_toolname == "":
        return "no tool founds"

    used_tool_meta = list(filter(lambda x: x["name_for_model"] == use_toolname, tools))
    if len(used_tool_meta) == 0:
        return "no tool founds"
    
    api_output = used_tool_meta[0]["tool_api"](action_input)
    return api_output

def get_model_response(prompt, stop):
    from openai import OpenAI
    client = OpenAI()

    completion = client.chat.completions.create(
        model="gpt-3.5-turbo",
        messages=[
            {"role": "user", "content": prompt}
        ],
        stream=False,
        stop = stop,
    )
    response = completion.choices[0].message.content
    return response

def main(query, choose_tools):
    prompt = build_planning_prompt(choose_tools, query) # 组织prompt
    stop = ["Observation:", "Observation:\n"]
    print(prompt)
    response = get_model_response(prompt, stop)


    while "Final Answer:" not in response: # 出现final Answer时结束
        api_output = use_api(choose_tools, response) # 抽取入参并执行api
        api_output = str(api_output) # 部分api工具返回结果非字符串格式需进行转化后输出
        if "no tool founds" == api_output:
            break
        print("\033[32m" + response + "\033[0m" + "\033[34m" + ' ' + api_output + "\033[0m")
        prompt = prompt + response + ' ' + api_output # 合并api输出
        response = get_model_response(prompt, stop)

    print("\033[32m" + response + "\033[0m")

if __name__ == "__main__":
    query = "查一下张三2024年1月的电费" # 所提问题
    choose_tools = TOOLS # 选择备选工具
    print("=" * 10)
    main(query, choose_tools)

三、langchain的Agent开发

主控:

prompt = get_tools_agent_prompt(query)
agent = get_tools_agent()
response = agent.run(prompt)

get_tools_agent_prompt:

def get_tools_agent_prompt(query, history=None):
        from datetime import datetime

        current_time = datetime.now()

        prompt = (
                f"""
            如果查询未指定年/月,请参考当前时刻:{current_time}。

            请用中文思考及回复。

            优先考虑使用工具解决问题,当工具无法解决时,尝试根据经验直接回答。

            最终回复时若内容较多需要注意保持可读性,添加合理的换行。

            下面是用户问题:

            """
            
                + query
            )
        return prompt

get_tools_agent

def get_tools_agent():
    llm = get_llm()
    tools = get_tools()
    agent = initialize_agent(
            tools, 
            llm, 
            agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, 
            verbose=True, 
            max_iterations=5,
            handle_parsing_errors=True,
        )
    return agent
# 获取llm
def get_llm():
        
    api_key = os.getenv("PROXY_API_KEY")
    api_url = os.getenv("PROXY_SERVER_URL")
    api_url = api_url.split('/chat')[0]
    model = os.getenv("PROXYLLM_BACKEND")

    if "openai" in api_url:
        llm = ChatOpenAI(temperature=0,model=model,openai_api_base=api_url,openai_api_key=api_key,)
    elif "bigmodel" in api_url:
        llm = ChatZhipuAI(temperature=0.01,api_key=api_key,model="glm-4",)
    elif "dashscope" in api_url:
        from langchain_community.chat_models.tongyi import ChatTongyi
        llm = ChatTongyi(model="qwen-max",top_p=0.01,streaming=True,dashscope_api_key=api_key)

    return llm
def get_tools():
    llm=get_llm()
    tools = [
        Tool(
            name="查询负荷数据及同比环比",
            func=calculate_growth, 
            description="""
            当你想要查询某地负荷(最大值、最小值、平均值),查询最大/最小值发生时间,或者查询同比或环比变化的时候很有用,该工具返回某地负荷数据及同比变化率。
            输入应该包括地区(公司)名(如果未指定,则默认为直供区)、时间(仅允许YYYY,YYYY-QQ,YYYY-MM,YYYY-MM-DD,YYYY-节气五种格式,也即年、年-季度、年-月、年-月-日,年-节气)、最大/最小/平均(若未明确提及,默认为平均)、同比/环比(可以不输入),以|分隔。
            查询某一年时,仅输入年份即可。
            例如:上海|2023|最大|环比、广州|2023-Q2|最小|环比、深圳|2021-07|平均、北京|2023-05-01|最大。
            """,
        )
    ]
    tools.append(
        Tool(
            name="查询负荷新高(低)",
            func=find_record_breaking_loads, 
            description="""
            当你想要查询某地负荷几创新高或几创新低的时候很有用,该工具返回创下新高/低的次数,及对应的详细信息。
            输入应该包括地区(公司)名(如果未指定,则默认为直供区)、基础时间(仅允许YYYY,YYYY-QQ,YYYY-MM,YYYY-MM-DD,YYYY-节气五种格式,也即年、年-季度、年-月、年-月-日,年-节气)、对比时间(如未指定,默认与基础时间的前一年做对比,时间类型同前者)、高/低,以|分隔。
            例如:上海|2023-Q1|2022-Q1|高、广州|2023-寒露|2021-寒露|低、深圳|2023|2021|高。
            """,
        ),
    )
    return tools
def calculate_growth(context):
    # 读取CSV文件
    # df = pd.read_csv('./data/load.csv')

    # 分割输入参数
    params = context.split("|")

    # 确保至少有三个参数
    if len(params) < 3:
        return "至少需要三个参数(region, time_period, compare_type)。"

    # 提取参数,如果缺少第四个参数,则默认为"同比"
    elif len(params) == 3:
        region, time_period, compare_type = params[:3]
        growth_type = None
    elif len(params) == 4:
        region, time_period, compare_type, growth_type = context.split("|")
    else:
        return "您输入的参数数量不匹配,请仔细核对。"
def find_record_breaking_loads(context):
    # 分割输入参数
    params = context.split("|")

    if len(params) not in [3, 4]:
        return "您输入的参数数量不匹配,请仔细核对。"

    region = params[0]
    current_time_period = params[1]
    high_low = params[-1]
    # compare_time_period = params[2] if len(params) == 4 else None
    compare_time_period = params[2] if len(params) == 4 else str(int(params[1][:4])-1) + params[1][4:]


    # 筛选指定地区的数据
    df_region = df[df['公司'] == region]

    # 解析当前时间段
    try:
        current_period_parsed = parse_time_period(df, current_time_period)
    except ValueError as e:
        return f"当前时间段解析错误:{e}"

    # 解析对比时间段,如果有
    if compare_time_period:
        try:
            compare_period_parsed = parse_time_period(df, compare_time_period)
        except ValueError as e:
            return f"对比时间段解析错误:{e}"
    else:
        compare_period_parsed = None

    # 根据时间段获取数据
    if isinstance(current_period_parsed, tuple):
        current_start_date, current_end_date = current_period_parsed
        df_current = df_region[(df_region['时间'] >= current_start_date) & (df_region['时间'] < current_end_date)]

        if compare_period_parsed:
            compare_start_date, compare_end_date = compare_period_parsed
            df_compare = df_region[(df_region['时间'] >= compare_start_date) & (df_region['时间'] < compare_end_date)]
        else:
            # 默认比较去年同期
            year = int(current_start_date.split('-')[0])
            previous_year = year - 1
            df_compare = df_region[(df_region['时间'] >= f"{previous_year}-{current_start_date[5:]}") & (df_region['时间'] < f"{previous_year}-{current_end_date[5:]}")]

    # 比较负荷值
    if high_low == '高':
        reference_load = df_compare['负荷'].max()
    else:
        reference_load = df_compare['负荷'].min()

    # 计算创纪录的次数
    if high_low == '高':
        df_records = df_current[df_current['负荷'] > reference_load]
    else:
        df_records = df_current[df_current['负荷'] < reference_load]

    # 结果处理
    if not df_records.empty:
        record_count = len(df_records)
        top_record = df_records.sort_values(by="负荷", ascending=(high_low != '高')).iloc[0]
        return f"{region}{current_time_period}相较{compare_time_period}共有{record_count}次创下新{high_low},其中最{high_low}负荷为{top_record['负荷']}MW,时间为{top_record['时间']}。"
    else:
        return f"{region}{current_time_period}相较{compare_time_period}未创新{high_low}。"

最后介绍一下 AgentType.ZERO_SHOT_REACT_DESCRIPTION

首先介绍一些常见的关键词:

ReAct:由单词“Reason”和“Act”组合而成,前者对应于推理,即大模型的通用文本逻辑判断能力,或者说是对问题进行思考和拆解的能力;后者对应于行动,即具备专业知识的特定领域精确回答能力,或者说是调用外部工具的能力。ReAct顾名思义就是把思考和行动相结合,通过二者的依次迭代执行完成任务。
Zero-shot:零样本,或者说是无记忆。在运行时,只考虑与当前代理的一次交互,不保留对话历史。
Conversational:引入了对话历史,因为有记忆了,所以需要初始化代理时引入memory参数。其一个缺点是可能无法执行复杂的Tool调用任务。
Chat:常规情况下以OpenAI方式初始化LLM,此类Agent可以以ChatOpenAI方式初始化模型。前者是更通用的接口,用于与不同类型的语言模型进行交互,可以与各种LLM模型集成。ChatOpenAI接口是对其的高级封装,更专注于对话式交互。

在这里插入图片描述
在这里插入图片描述

以上内容转自:https://wangjn.blog.csdn.net/article/details/134806188

ZERO_SHOT_REACT_DESCRIPTION通常使用
其他的都作妖

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

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

相关文章

关闭Elasticsearch built-in security features are not enabled

禁用Kibana安全提示&#xff08;Elasticsearch built-in security features are not enabled&#xff09; Kibana提示#! Elasticsearch built-in security features are not enabled. Without authentication, your cluster could be accessible to anyone. See https://www.e…

基于SSM+Jsp+Mysql的KTV点歌系统

基于SSMJspMysql的KTV点歌系统 基于SSMJspMysql的KTV点歌系统的设计与实现 开发语言&#xff1a;Java框架&#xff1a;ssm技术&#xff1a;JSPJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工…

Redisson分布式锁(WatchDog分析,浅浅看下源码)

带大家简单了解下Redisson的看门狗机制&#xff0c;这个面试中也比较常见。 目录 WatchDog&#xff08;看门狗&#xff09;机制开启WatchDog&#xff08;看门狗&#xff09;浅看下源码 WatchDog&#xff08;看门狗&#xff09;机制 Redisson看门狗机制是用于解决在业务运行时间…

弱网测试利器 - Charles工具实战分享!

一&#xff1a;弱网测试要点 二&#xff1a;利用抓包工具charles进行弱网设置&#xff0c;适用PC端和移动端&#xff08;IOS&#xff0f;Android&#xff09; 1、以charles 4.5.6版本为例&#xff0c;打开Proxy->Throttle Settings 2、打开Throttle Settings&#xff0c;界面…

蓝桥杯练习06给网页化个妆

给页面化个妆 介绍 各个网站都拥有登录页面&#xff0c;设计一个界面美观的登录页面&#xff0c;会给用户带来视觉上的享受。本题中我们要完成一个登录页面的布局。 准备 开始答题前&#xff0c;需要先打开本题的项目代码文件夹&#xff0c;目录结构如下&#xff1a; 其中&…

A7680C 4G模块连接MQTT服务器

AT\r\n检查模块正常工作 返回 OK ATCPIN?\r\n检查SIM是否正常 返回&#xff1a; [20:01:31.561]接收←ATCPIN? CPIN: READY OK ATCGREG? //检查网络注册注册状态 返回 [20:02:21.042]接收←ATCGREG?…

28 OpenCV 轮廓周围绘制图形

文章目录 approxPolyDP 轮廓周围绘制矩形boundingRectminAreaRect绘制圆和椭圆示例 approxPolyDP 轮廓周围绘制矩形 approxPolyDP(InputArray curve, OutputArray approxCurve, double epsilon, bool closed)curve&#xff1a;输入点集&#xff0c;二维点向量的集合appro…

干货!Python正则表达式之匹配分组和其他功能函数

1.匹配分组 ()&#xff1a;表示一个整体&#xff0c;表示分组&#xff0c;然后捕获 2.匹配分组实例 # 导入re模块 import re ​ tel "0755-98776754" ​ # 定义正则表达式 pattern "(\\d{4})-(\\d{8})" result re.search(pattern, tel) print(result…

offer选择:创业公司 VS 大厂外包

面试拿到两个offer&#xff0c;一个是规模只有几十人的初创小公司&#xff0c;另一个是大厂外包岗位。都是功能测试&#xff0c;两者薪水待遇也差不多&#xff0c;该如何选择?更有利于之后的职业发展...... 这是一个比较典型的问题&#xff0c;对于要转行的同学或者是刚入行没…

Leetcode 200. 岛屿数量

心路历程&#xff1a; 在没有看图论这一章之前看这道题没什么直接的思路&#xff0c;在看完图论之后&#xff0c;学着使用DFS和BFS去套用解决。第一次自己做的时候还是遇到了很多小问题。整体思路很流畅&#xff0c;但是需要处理的细节第一次没怎么处理好&#xff0c;花了很多…

VC++ error C1001: 内部编译器错误 c\error.h”,第 1291 行) 原因和解决

原因是使用模板时实现方法没写分号 #include <iostream>template <class T> class A { public:A() {};~A() {};void GetName() {return}; };int main(int argc, char* argv[]) {return 0; }

Linux:执行命令的命令eval与Bash解析命令的方式

相关阅读 Linuxhttps://blog.csdn.net/weixin_45791458/category_12234591.html?spm1001.2014.3001.5482 eval命令用于接收参数&#xff0c;并将这些参数作为一行命令执行&#xff0c;这也许会使人困惑&#xff0c;为什么我不能直接执行命令而需要使用eval命令间接执行呢&…

【题目】【网络系统管理】2022 年全国职业院校技能大赛 网络系统管理赛项 模块 A:网络构建

2022 年全国职业院校技能大赛 网络系统管理赛项 模块 A&#xff1a;网络构建 目录 考试说明 … 3 任务描述 … 3 任务清单 … 3 &#xff08;一&#xff09;基础配置 … 3 &#xff08;二&#xff09;有线网络配置 … 4 &#xff08;三&#xff09;无线网络配置 … 5 &…

仿muduo库实现one thread one loop式并发服务器

文章目录 一、项目简介 二、项目整体认识 2、1 HTTP服务器 2、2 Reactor模型 三、预备知识 3、1 C11 中的 bind 3、2 简单的秒级定时任务实现 3、3 正则库的简单使用 3、4 通用类型any类型的实现 四、服务器功能模块划分与实现 4、1 Buffer模块 4、2 Socket模块 4、3 Channel模…

数据结构--树(二叉树)

定义 树的结点 如上图A的结点为2&#xff0c;B的结点为1&#xff0c;树的结点就是最多的那个&#xff0c;这棵树的结点就是3. 树的存储结构 树的存储结构可以是多样的 typedef struct BiTNode /* 结点结构 */ {DATATYPE data; /* 结点数据 */struct BiTNode *lchild,*rchi…

算法打卡Day14

今日任务&#xff1a; 1&#xff09;104.二叉树的最大深度 2&#xff09;559.n叉树的最大深度 3&#xff09;111.二叉树的最小深度 4&#xff09;222.完全二叉树的节点个数 104.二叉树的最大深度 题目链接&#xff1a;104. 二叉树的最大深度 - 力扣&#xff08;LeetCode&#…

视频讲解|基于非对称纳什谈判的多微网电能共享运行优化策略

1 主要内容 该讲解视频对应的程序链接为基于非对称纳什谈判的多微网电能共享运行优化策略_吴锦领&#xff0c;主要内容是对《基于非对称纳什谈判的多微网电能共享运行优化策略》的matlab复现&#xff0c;解决的是微网间基于非对称纳什谈判的P2P电能交易共享问题&#xff0c;基…

js生成笛卡尔集合

let arr[[黑, 金, 白],[16G, 32G],[电信, 移动, 联通], ]let listarr.reduce((a, b) > { return a.flatMap(x > b.map(y > [...x, y]))}, [[]] )console.log(list)生成结果

20240322,结构类型,枚举,

一&#xff0c;枚举 1.1 常量符号化 程序中用符号表达数字&#xff0c;增加程序的可读性&#xff1f; #include<stdio.h>//能跑&#xff0c;但是报错不推荐将字符串转为CHAR** const int red0; const int yellow1; const int green2; //为撒在前面&#xff1f; int…

移动硬盘加Type-C接口充电:革新存储与充电体验

随着科技的飞速发展&#xff0c;电子设备的功能和性能也在不断提升。近年来&#xff0c;移动硬盘作为数据存储的重要工具&#xff0c;其接口和充电方式的革新也备受关注。特别是Type-C接口的普及&#xff0c;为移动硬盘带来了前所未有的便利。本文将深入探讨移动硬盘加入Type-C…