【Langchain Agent研究】SalesGPT项目介绍(四)

【Langchain Agent研究】SalesGPT项目介绍(三)-CSDN博客

  github地址:GitHub - jerry1900/SalesGPT: Context-aware AI Sales Agent to automate sales outreach.      

        上节课,我们主要介绍了SalesGPT的类属性和它最重要的类方法from_llm()。因为SalesGPT没有构造器方法,所以类方法from_llm()方法就是这个类的构造方法,所以这个类方法很重要。

        第二节课的时候我们讲过(【Langchain Agent研究】SalesGPT项目介绍(二)-CSDN博客),这个项目的运行代码是run.py,但是在我仔细研究项目的逻辑后发现,run.py的代码是有很明显的逻辑错误的,应该压根就跑不起来、或者会进死循环的,所以没有办法,我自己写了一个test方法来把这个项目跑起来,然后我把运行过程拆开,方便我们用debug来看一下代码运行过程中的中间变量。在介绍我们自己的test方法的过程中,我会顺便介绍SalesGPT其他几个重要的方法 step(), human_step(),_call(),determine_conversation_stage(),基本上掌握了这些方法就能够运行整个项目了。

run.py运行代码有逻辑问题

        首先跟大家说一下,我仔细研究了项目自带的run.py方法,感觉不太对头,就是它的这个方法里少了关键的调用determine_conversation_stage()方法,我们来看一下:

    sales_agent.seed_agent()

    print("=" * 10)
    cnt = 0
    while cnt != max_num_turns:
        cnt += 1
        if cnt == max_num_turns:
            print("Maximum number of turns reached - ending the conversation.")
            break
        sales_agent.step()

        # end conversation
        if "<END_OF_CALL>" in sales_agent.conversation_history[-1]:
            print("Sales Agent determined it is time to end the conversation.")
            break
        human_input = input("Your response: ")
        sales_agent.human_step(human_input)
        print("=" * 10)

        这块代码之前没有啥问题,但是在seed_agent()之后,从来没有调用过determine_conversation_stage()这个方法,这个在逻辑上是不对的,我们回忆一下我们第一课上讲的东西,整个业务逻辑是这样的:

        注意在用户输入之后、在Agent响应之前,每次都是要做阶段判断的,如果不判断对话阶段,是根本无法正确地和用户交互的,相当于整个逻辑链少了一个关键环节,这是一个很明显的错误,希望大家也能发现这个问题。

我们自己构造一个运行程序my_test.py

        我们自己尝试构造一个运行程序,去把这个项目跑起来,因为有很多问题只有把代码跑起来才能发现其中的问题:

import os

from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from salesgpt.agents import SalesGPT

load_dotenv()

print(os.getenv("OPENAI_API_KEY"))

llm = ChatOpenAI(
    temperature=0,
    openai_api_key = os.getenv("OPENAI_API_KEY"),
    base_url = os.getenv("OPENAI_BASE_URL")
)

        这里注意一下,我对项目用的ChatLiteLLM不太熟悉,不知道他怎么设置base_url的,因为我用的是国内openai节点,所以我索性不用ChatLiteLLM了,我直接用咱们以前项目用过的ChatOpenAI包装器,方便我修改base_url。

sales_agent = SalesGPT.from_llm(
            llm,
            verbose=True,
            use_tools=False,
            salesperson_name="Ted Lasso",
            salesperson_role="Sales Representative",
            company_name="Sleep Haven",
            company_business="""Sleep Haven 
                                    is a premium mattress company that provides
                                    customers with the most comfortable and
                                    supportive sleeping experience possible. 
                                    We offer a range of high-quality mattresses,
                                    pillows, and bedding accessories 
                                    that are designed to meet the unique 
                                    needs of our customers.""",
        )

sales_agent.seed_agent()
sales_agent.determine_conversation_stage()

        到这里,我们要注意一下,由于我换了模型,直接运行会报错:

        看报错位置和报错提示,大概能猜出来,由于我换了模型,ChatOpenAI这个模型缺少model这个参数,所以就报错了(ChatLiteLLM模型有这个参数)。因为我们之前研究过SalesGPT的构造方法:

def from_llm(cls, llm: ChatLiteLLM, verbose: bool = False, **kwargs) -> "SalesGPT":

        不难看出,这个model_name并非构造SalesGPT的必要参数,是放在kwargs里面的东西,所以干脆注销掉就好了。由于我是改的项目源代码,所以在这里要注释一下,免得以后忘了为啥要把这行代码注释掉。 我们注释掉之后再跑一下,我建议大家用pycharm的debug一步一步地跑,这样可以清晰地看到里面的运行过程、线程和参数:

        OK,到这里,项目开始跑起来了,这里看到已经开始构造StageAnalyzerChain了,我们继续往下看看,可以看到历史记录是空的,我们和openai的交互也成功了,也正确输出了:

prompt提示词调整 

        虽然项目跑起来了,但是仔细检查里面的过程,还是有问题:

        可以看到这里出了问题,我们第一次跑这个项目,之前是没有历史聊天记录的,但是在第一次做阶段判断的时候,直接stage分析的时候直接判定到了第二个阶段,这个是有问题的。解决方法有两个,一个是换更好用、逻辑推理能力更强的GPT-4模型,还有一个办法就是对项目自带的prompt提示词进行修改和优化如下:

 

Current Conversation stage is: {conversation_stage_id}

check conversation history step by step,if converssation history is null,output 1.

The answer needs to be one number only, no words.
Do not answer anything else nor add anything to you answer."""

# If there is no conversation history, output 1

         “If there is no conversation history, output 1”是原来项目里的提示词,感觉不太好用,我们用我自己写的提示词“check conversation history step by step,if converssation history is null,output 1.”,这里应用了提示词工程的小技巧(思维链),让大模型在检查这里的时候不要给我糊弄,好好看,我们再运行一下项目:

        可以看到,我们换了提示词之后,大模型终于把这块整明白了,阶段输出也正确了。

不断向agent提问,理解整个业务逻辑 ,查看中间过程

        我们下面可以不断地向用户提问,其实就是不断重复这个流程:

sales_agent.determine_conversation_stage()
sales_agent.step()
agent_output = sales_agent.conversation_history[-1]

human_input = input('say something')
sales_agent.human_step(human_input)

        先进行阶段判断,然后根据阶段判断agent先进行输出,然后用户再进行输出,你可以用pycharm的debug功能清晰看到中间过程:

        输出结果和日志在console里看,线程和参数在threads&variables里看: 

        在这里点开,你可以看到你代码构造的实例和变量,里面的东西太多了,大家自己点进去看吧,有助于你里面代码背后的东西。

 seed_agent()方法

        我们之前已经介绍了SalesGPT最重要的from_llm()类方法,这个方法同时也是这个类的构造方法,我们继续来看一下我们demo用到的SalesGPT的其他方法,注意,这些方法都不是类方法,都是实例方法,他们都是我们在实例化获得了一个sales_agent之后调用的。第一个是seed_agent()方法:

    @time_logger
    def seed_agent(self):
        # Step 1: seed the conversation
        self.current_conversation_stage = self.retrieve_conversation_stage("1")
        self.conversation_history = []

        这个方法的上面加了一个装饰器,用于计时的,代码里面的内容就是初始化一下,没啥好说的。

determine_conversation_stage()方法

        调用类的实例属性stage_analyzer_chain来进行阶段判断,输入的参数有三个如下,这里要对内容做一些格式上的调整,函数的输出就是把类的conversation_stage_id,current_conversation_stage这个两个值属性进行了对应的修改:

    @time_logger
    def determine_conversation_stage(self):
        self.conversation_stage_id = self.stage_analyzer_chain.run(
            conversation_history="\n".join(self.conversation_history).rstrip("\n"),
            conversation_stage_id=self.conversation_stage_id,
            conversation_stages="\n".join(
                [
                    str(key) + ": " + str(value)
                    for key, value in CONVERSATION_STAGES.items()
                ]
            ),
        )

        print(f"Conversation Stage ID: {self.conversation_stage_id}")
        self.current_conversation_stage = self.retrieve_conversation_stage(
            self.conversation_stage_id
        )

        print(f"Conversation Stage: {self.current_conversation_stage}")

step()方法和_call()方法

        step()方法里面做了一个简单判断,因为我们不需要stream的输出样式,所以就是去调用_call()方法,这个方法是一个类内部方法:

    @time_logger
    def step(self, stream: bool = False):
        """
        Args:
            stream (bool): whether or not return
            streaming generator object to manipulate streaming chunks in downstream applications.
        """
        if not stream:
            self._call(inputs={})
        else:
            return self._streaming_generator()

         _call()方法:

    def _call(self, inputs: Dict[str, Any]) -> Dict[str, Any]:
        """Run one step of the sales agent."""

        # override inputs temporarily
        inputs = {
            "input": "",
            "conversation_stage": self.current_conversation_stage,
            "conversation_history": "\n".join(self.conversation_history),
            "salesperson_name": self.salesperson_name,
            "salesperson_role": self.salesperson_role,
            "company_name": self.company_name,
            "company_business": self.company_business,
            "company_values": self.company_values,
            "conversation_purpose": self.conversation_purpose,
            "conversation_type": self.conversation_type,
        }

        # Generate agent's utterance
        if self.use_tools:
            ai_message = self.sales_agent_executor.invoke(inputs)
            output = ai_message["output"]
        else:
            ai_message = self.sales_conversation_utterance_chain.invoke(inputs)
            output = ai_message["text"]

        # Add agent's response to conversation history
        agent_name = self.salesperson_name
        output = agent_name + ": " + output
        if "<END_OF_TURN>" not in output:
            output += " <END_OF_TURN>"
        self.conversation_history.append(output)
        print(output.replace("<END_OF_TURN>", ""))
        return ai_message

        注意,这里的input是空的:

        因为系统输出只依赖于现在对话处于哪个阶段,并不依赖于用户输入的具体内容!这里困扰了我好久,我当时一直和我自己之前写的agent做比较,我还纳闷呢,为啥这个agent输入没有用户的Input呢?现在我明白了,用户的输入就是用来帮助进行阶段判断的,系统的输出只依赖于现在处于哪个阶段。

        继续往下看,如果使用了tools就用sales_agent_executor,否则就用sales_conversation_utterrance_chain,因此我们之前的demo运行都用的sales_conversation_utterrance_chain,下节课我们来用sales_agent_executor。

        # Generate agent's utterance
        if self.use_tools:
            ai_message = self.sales_agent_executor.invoke(inputs)
            output = ai_message["output"]
        else:
            ai_message = self.sales_conversation_utterance_chain.invoke(inputs)
            output = ai_message["text"]

                最后要把大模型给的输出结果包装一下,放到conversation_history里,ai_message作为函数的返回结果:

        # Add agent's response to conversation history
        agent_name = self.salesperson_name
        output = agent_name + ": " + output
        if "<END_OF_TURN>" not in output:
            output += " <END_OF_TURN>"
        self.conversation_history.append(output)
        print(output.replace("<END_OF_TURN>", ""))
        return ai_message

human_step()

        这个就更简单了,不说了。

    def human_step(self, human_input):
        # process human input
        human_input = "User: " + human_input + " <END_OF_TURN>"
        self.conversation_history.append(human_input)

        至此,我们就把如何把这个项目跑起来展示了一遍,我们对项目源码做了两处修改,大家注意要同样修改源码之后才能运行我们自己的代码,我贴一下整个my_test整体代码:

import os

from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from salesgpt.agents import SalesGPT

load_dotenv()

print(os.getenv("OPENAI_API_KEY"))

llm = ChatOpenAI(
    temperature=0,
    openai_api_key = os.getenv("OPENAI_API_KEY"),
    base_url = os.getenv("OPENAI_BASE_URL")
)

sales_agent = SalesGPT.from_llm(
            llm,
            verbose=True,
            use_tools=False,
            salesperson_name="Ted Lasso",
            salesperson_role="Sales Representative",
            company_name="Sleep Haven",
            company_business="""Sleep Haven 
                                    is a premium mattress company that provides
                                    customers with the most comfortable and
                                    supportive sleeping experience possible. 
                                    We offer a range of high-quality mattresses,
                                    pillows, and bedding accessories 
                                    that are designed to meet the unique 
                                    needs of our customers.""",
        )

sales_agent.seed_agent()
sales_agent.determine_conversation_stage()

sales_agent.step()
agent_output = sales_agent.conversation_history[-1]
assert agent_output is not None, "Agent output cannot be None."
assert isinstance(agent_output, str), "Agent output needs to be of type str"
assert len(agent_output) > 0, "Length of output needs to be greater than 0."

human_input = input('say something')
sales_agent.human_step(human_input)

sales_agent.determine_conversation_stage()
sales_agent.step()
agent_output = sales_agent.conversation_history[-1]

human_input = input('say something')
sales_agent.human_step(human_input)

sales_agent.determine_conversation_stage()
sales_agent.step()
agent_output = sales_agent.conversation_history[-1]

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

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

相关文章

电商API接口|大数据关键技术之数据采集发展趋势

在大数据和人工智能时代&#xff0c;数据之于人工智能的重要性不言而喻。今天&#xff0c;让我们一起聊聊数据采集相关的发展趋势。 本文从电商API接口数据采集场景、数据采集系统、数据采集技术方面阐述数据采集的发展趋势。 01 数据采集场景的发展趋势 作为大数据和人工智…

motplotlib图例案例1:通过多个legend完全控制图例显示顺序(指定按行排序 or 按列排序)

这个方法的核心&#xff0c;是手动的获得图中的handlers和labels&#xff0c;然后对它们进行切分和提取&#xff0c;最后分为几个legend进行显示。代码如下&#xff1a; 后来对下面的代码进行修改&#xff0c;通过handlers, labels get_legend_handles_labels(axs[axis])自动的…

线性规划单纯形法原理及实现

欢迎关注更多精彩 关注我&#xff0c;学习常用算法与数据结构&#xff0c;一题多解&#xff0c;降维打击。 本期话题&#xff1a;线性规划单纯形法原理及实现 标准化及单纯形方法 相关学习资料 https://www.bilibili.com/video/BV168411j7XL/?spm_id_from333.788&vd_so…

Linux进程概念 (下) 地址空间

前言 中篇讲了进程为什么要有优先级&#xff0c;以及环境变量和通过代码获得环境变量 本篇主要讲解什么是地址空间 &#xff0c; 地址空间是怎么设计的&#xff1f;为什么要有地址空间&#xff1f; 程序地址空间 先看下图 验证上图的正文代码至堆的地址是不是从低地址向高地…

强化学习(TD3)

TD3——Twin Delayed Deep Deterministic policy gradient 双延迟深度确定性策略梯度 TD3是DDPG的一个优化版本&#xff0c;旨在解决DDPG算法的高估问题 优化点&#xff1a; ①双重收集&#xff1a;采取两套critic收集&#xff0c;计算两者中较小的值&#xff0c;从而克制收…

【软考高级信息系统项目管理师--第十九章:项目绩效域】

&#x1f680; 作者 &#xff1a;“码上有前” &#x1f680; 文章简介 &#xff1a;软考高级–信息系统项目管理师 &#x1f680; 欢迎小伙伴们 点赞&#x1f44d;、收藏⭐、留言&#x1f4ac; 第十九章&#xff1a;项目绩效域 干系人绩效域预期目标绩效要点 团队绩效域预期目…

【Java】零基础蓝桥杯算法学习——动态规划例题

例题&#xff1a;2023年第十四届蓝桥杯Java软件开发B组E题 蜗牛 参考解答&#xff1a; 参考代码示例&#xff1a; import java.util.Scanner; public class Main {static int N 100010;static int[] arr new int[N];static int[] a new int[N]; //传送带的起始坐标static …

[杂记]mmdetection3.x中的数据流与基本流程详解(数据集读取, 数据增强, 训练)

之前跑了一下mmdetection 3.x自带的一些算法, 但是具体的代码细节总是看了就忘, 所以想做一些笔记, 方便初学者参考. 其实比较不能忍的是, 官网的文档还是空的… 这次想写其中的数据流是如何运作的, 包括从读取数据集的样本与真值, 到数据增强, 再到模型的forward当中. 0. MMDe…

打字侠,提供免费的五笔打字练习

在当今数字化时代&#xff0c;打字已成为生活和工作中不可或缺的技能之一。特别是在办公室环境中&#xff0c;快速准确地输入文字对提高工作效率至关重要。而对于许多中文输入法用户来说&#xff0c;五笔输入法因其高效和便捷而备受青睐。 然而&#xff0c;掌握五笔输入法并非…

JVM原理

一、java虚拟机的生命周期&#xff1a; Java虚拟机的生命周期 一个运行中的Java虚拟机有着一个清晰的任务&#xff1a;执行Java程序。程序开始执行时他才运行&#xff0c;程序结束时他就停止。你在同一台机器上运行三个程序&#xff0c;就会有三个运行中的Java虚拟机。 Java虚拟…

一休哥助手网页版如何使用

一休哥助手网页版可以使用GPT4提问了&#xff0c;具体操作流程如下&#xff1a; 1.登录网页版一休哥助手&#xff08;首次打开页面时&#xff0c;初始化久一点&#xff0c;请耐心等一下&#xff09; https://www.fudai.fun 2.登录后就可以使用GPT4了 3.你还可以自定义系统角色…

vtkBoarderWidget及图片坐标包含计算

开发环境&#xff1a; Windows 11 家庭中文版Microsoft Visual Studio Community 2019VTK-9.3.0.rc0vtk-example demo解决问题&#xff1a;移动图片到坐标轴的中心&#xff0c;创建一个vtkBoarderWidget控件&#xff0c;移动控件&#xff0c;计算控件与图片的包含关系 关键点…

K3s v1.26.0-rc.0-k3s1 部署Harbor私库权限配置

在K3s服务端配置 cat >> /etc/rancher/k3s/registries.yaml <<EOF mirrors: "harbor.baize-k3s.org": endpoint: - "https://harbor.baize-k3s.org" configs: "harbor.baize-k3s.org": auth: username: admin password: Harbor1…

LiveGBS流媒体平台GB/T28181常见问题-基础配置流媒体服务配置中本地|内网IP外网IP(可选)外网IP收流如何配置

LiveGBS常见问题基础配置流媒体服务配置中本地|内网IP外网IP外网IP收流如何配置&#xff1f; 1、流媒体服务配置2、播放提示none rtp data receive3、多网卡服务器4、收流端口配置5、端口区间可以如何配置6、搭建GB28181视频直播平台 1、流媒体服务配置 LiveGBS中基础配置-》流…

ssm在线学习平台-计算机毕业设计源码09650

目 录 摘要 1 绪论 1.1 选题背景及意义 1.2国内外现状分析 1.3论文结构与章节安排 2 在线学习平台系统分析 2.1 可行性分析 2.2 系统业务流程分析 2.3 系统功能分析 2.3.1 功能性分析 2.3.2 非功能性分析 2.4 系统用例分析 2.5本章小结 3 在线学习平台总体设计 …

HCIA-HarmonyOS设备开发认证V2.0-IOT硬件子系统-I2C

目录 一、 I2C 概述二、I2C 模块相关API三、接口调用实例四、I2C HDF驱动开发4.1、开发步骤(待续...) 坚持就有收获 一、 I2C 概述 I2C&#xff08;Inter Integrated Circuit&#xff09;集成电路间总线是由 Philips 公司开发的一种简单、双向二线制同步串行总线。I2C 以主从方…

Unity老项目Android 13支持

Unity老项目Android 13支持 前言 Google官方要求新、老app在一定时间要求内需要面向Android 12、Android 13构建&#xff0c;不然不给app过审。我们之前是面向Android API 30构建的&#xff0c;现在需要支持面向Android API 33构建。 https://developer.android.com/about/ver…

为什么2023年是AI视频的突破年,以及对2024年的预期#a16z

2023年所暴露的AI生成视频的各种问题&#xff0c;大部分被OpenAI发布的Sora解决了吗&#xff1f;以下为a16z发布的总结&#xff0c;在关键之处&#xff0c;我做了OpenAI Sora的对照备注。 推荐阅读&#xff0c;了解视频生成技术进展。 Why 2023 Was AI Video’s Breakout Year,…

怎么清理mac系统缓存系统垃圾文件 ?怎么清理mac系统DNS缓存

很多使用苹果电脑的用户都喜欢在同时运行多个软件&#xff0c;不过这样会导致在运行一些大型软件的时候出现不必要的卡顿现象&#xff0c;这时候我们就可以去清理下内存&#xff0c;不过很多人可能并不知道正确的清内存方式&#xff0c;下面就和小编一起来看看吧。 mac系统是一…

力扣94 二叉树的中序遍历 (Java版本) 递归、非递归

文章目录 题目描述递归解法非递归解法 题目描述 给定一个二叉树的根节点 root &#xff0c;返回 它的 中序 遍历 。 示例 1&#xff1a; 输入&#xff1a;root [1,null,2,3] 输出&#xff1a;[1,3,2] 示例 2&#xff1a; 输入&#xff1a;root [] 输出&#xff1a;[] 示…
最新文章