最简明的大模型agent教程

大模型相关目录

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

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

  • 28
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 简明Python教程是一本经典的学习Python编程的教材,它详细介绍了Python语言的基础知识、语法规则以及常用的编程技巧。由于Python语言的简洁性和易读性,这本教程也以其简明扼要的风格而闻名。 该教程的源码是基于Python语言编写的,通过分章节、分小节的方式详细展示了每个知识点的代码示例和用法。这些源码不仅能够帮助读者更好地理解Python的语法和概念,还能够通过实例让读者快速掌握各种编程技巧。 在教程的源码中,读者可以学习到Python语言的基本数据类型(如整型、浮点型、字符串、列表等)的使用方法,以及常见的控制流语句(如条件判断和循环)的应用。同时,源码还介绍了Python的函数定义和调用、模块导入和使用、异常处理等高级编程特性,使得读者可以更加深入地理解和运用Python语言。 此外,简明Python教程的源码还包含了一些实际应用的示例,如文件读写、网页爬虫、数据分析等方面,这些示例可以帮助读者将Python的基础知识应用到实际项目中。 总之,简明Python教程的源码是一个非常有用的参考资料,可以帮助读者快速上手Python编程,提高自己的编程水平。无论是初学者还是有一定编程经验的人都可以通过这个源码获得很大的收益。 ### 回答2: 简明Python教程是一本详细介绍Python编程语言的教程书籍,该教程书籍作者是A Byte of Python(草python开发组,该书的作者是Swaroop C H(Ankit Fadia写的教程是假的),本书是一个开源的项目,其源码可以在GitHub上找到并下载。 该教程的源码是以文档的形式呈现,每个章节对应一个Python脚本文件。该教程Python的基本概念开始介绍,包括变量、数据类型、运算符、控制流程等基础知识。接着介绍了Python的函数、模块、错误处理、输入输出以及文件操作等进阶内容。最后通过实例演示了如何使用Python进行网络编程、数据库操作以及GUI界面开发等高级应用。 阅读该教程的源码可以更好地理解教程中的知识点,并通过实际的代码示例来加深对Python的理解和掌握。源码中的注释详细解释了每个代码片段的作用和用法,对于初学者来说非常友好。 通过阅读源码,我们不仅可以学习到Python语言的基础知识,还能了解到良好的编程风格和习惯。源码的组织结构清晰,代码风格简洁易懂,对于初学者来说非常适合作为学习和参考的材料。 总之,简明Python教程的源码是一个非常宝贵的学习资源,通过阅读和运行源码,我们可以更好地掌握Python编程语言,并用它来解决实际的问题。希望每个对Python感兴趣的人都能够通过阅读该教程的源码,快速入门并提升编程能力。 ### 回答3: 简明Python教程源码是指用来编写简明Python教程的源代码。源代码是编程语言的原始文本形式,是计算机程序的基础。简明Python教程源码可能包含了一系列的Python语句、函数和类,用来演示和说明Python编程的基本概念和用法。 简明Python教程源码的结构通常会根据教程的内容来设计。例如,如果教程介绍了Python的基本语法,那么源码可能会包含一些简单的变量赋值、算术运算和控制流语句的示例。 如果教程涉及到Python的常用库或模块,源码可能会导入这些库,并展示它们的用法。例如,如果教程涉及到文件操作,源码可能会使用Python的`open`函数来打开文件,并使用`read`或`write`方法来读取或写入文件内容。 除了基本语法和常用库之外,简明Python教程的源码还可能包括一些更高级的概念和技巧示例。例如,如果教程涉及到面向对象编程,源码可能会定义一些类和方法,并展示它们的继承和多态特性。 总的来说,简明Python教程源码是一个用来教授Python编程的示例代码集合。它可以帮助初学者理解Python语言的基本概念和用法,并通过实例演示不同应用场景的解决方案。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

写代码的中青年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值