当前位置: 首页 > article >正文

Agent:原理与快速构建 | 人工智能 | Langchain | Python ——学习笔记

视频链接:https://www.bilibili.com/video/BV1Hz421b7ag/?spm_id_from=333.788&vd_source=90787f5794c8e73cf358973d1de2e47f

# # 将.env的信息放到环境变量
# from dotenv import load_dotenv
# load_dotenv()


''' 第一节课程:使用openai调用智谱轻言问答示例''' 
from openai import OpenAI
client = OpenAI(
    api_key="你的key",
    base_url="https://open.bigmodel.cn/api/paas/v4/"
)

completion = client.chat.completions.create(
    model = 'glm-4',        # 使用的模型
    messages = [            # 对话列表
        {'role':'system','content':"你被用于抑制用户的购买欲望。当用户说要想买什么东西的时候,你需要提供理由让用户不要买。"},
        {'role':'user','content':"我正在考虑购买一个键盘,但我想抑制这个购买欲望。你能帮我列出一些理由,让我思考一下是否真的需要这个商品嘛?"}
    ], 
    max_tokens = 400,       # max_tokens用于控制模型输出的长度,取值范围取决于模型和输入,模型有可以处理的token总数,输入和输出不能超过这个数量。像'glm-4'最多token数量为128k
    temperature = 0.5       # temperature用于控制输出的随机性,数值越高随机性越大,每次都想得到一样的输出数值可以设置低一点
)

print(completion.choices[0].message.content)


# # ######################## 以下示例是一个应用完整的调用,主要由3部分组成:Prompt Template、Model、OutputParser######################################
from langchain_openai import ChatOpenAI
from langchain.prompts import ChatPromptTemplate

# Prompt Template: 用于将Prompt不变的地方固定住,需要变的地方留空。
prompt_template = ChatPromptTemplate.from_messages(
    [
        ('system',"你被用于抑制用户的购买欲望。当用户说要想买什么东西的时候,你需要提供理由让用户不要买。"),
        ('human',"我正在考虑购买一个{product},但我想抑制这个购买欲望。你能帮我列出一些理由,让我思考一下是否真的需要这个商品嘛?")
    ]
)
prompt_template.format(product='球鞋')

# Model:用于输入Prompt回答的模型
model = ChatOpenAI(
    model = 'glm-4',
    api_key="你的key",
    base_url="https://open.bigmodel.cn/api/paas/v4/",
    max_tokens = 500,       # max_tokens用于控制模型输出的长度
    temperature = 0.7
)

# OutputParser:进一步解析模型的输出变成所需要的样子,尤其是输出与需要的不一致时,解析过程可能会再次调用模型
def output_parser(output: str):
    parser_model = ChatOpenAI(
        model = 'glm-3-turbo',
        temperature = 0.2,
        api_key="你的key",
        base_url="https://open.bigmodel.cn/api/paas/v4/"
    )
    message = "你需要将传入的文本改写,尽可能更自然,这是你需要改写的文本:{text}"
    return parser_model.invoke(message.format(text=output))


# 其实在一开始的prompt中就要求回复自然的文本也是可以的。但在工程中,想获取稳定一致的输出,最好一个步骤只做一件事。
chain = prompt_template | model | output_parser     # 这里建立一个完整的调用链
answer = chain.invoke(input={'product':'裤子'})
print(answer.content)


# # # # 一个省钱助手 # # #
# chain = prompt_template | model | output_parser     # 这里建立一个完整的调用流程
# while True:
#     product = input("Hello! 马上要双十一,你最近又想买点啥?")
#     answer = chain.invoke(input={'product':product})
#     print(answer.content)



''' 第二节课程:langchain添加memory'''
from langchain.prompts import ChatPromptTemplate

prompt_template = ChatPromptTemplate.from_messages(
    [
        ('system',"You are a chatbot."),
        ('human',"{new_messages}")
    ]
)

print(prompt_template.invoke({'new_messages':'Hello'}).messages)


# # # 同样,传入历史消息也是留空一个位置
from langchain.prompts import MessagesPlaceholder

prompt_template = ChatPromptTemplate.from_messages(
    [
        ('system',"You are a chatbot."),
        # ('system', "你是一个聊天机器人"),
        MessagesPlaceholder(variable_name="chat_history"),
        ('human',"{new_messages}")
    ]
)

print(prompt_template.invoke({'chat_history':[('human','Hello'),('ai','Hi'),], 'new_messages':'How old are you?'}).messages)


from langchain_openai import ChatOpenAI

model = ChatOpenAI(
    model = 'glm-3-turbo',
    api_key="你的key",
    base_url="https://open.bigmodel.cn/api/paas/v4/",
    max_tokens = 500,       # max_tokens用于控制模型输出的长度
    temperature = 0.7
)

# # 添加memory步骤:首先用prompt_template和model组成chain,然后定义列表来存储对话记录
chain = prompt_template | model
chat_history = []

# 每次调用chain函数时,将传给模型的话和模型返回的话放入chat_history列表,下次调用的时候再作为历史信息传进去
def chat(new_messages):
    response = chain.invoke({'chat_history': chat_history, 'new_messages': new_messages})
    chat_history.extend([
        ('human', new_messages),
        ('ai', response.content)
        ])
    return response.content

print(chat('你好,我是ysn,今年72岁,已经退休了,正在学习AI知识'))
print(chat('请你阐述一下你了解关于我的个人信息'))


# 在实际应用中用langchain的component来实现,对开发一致性和扩展性都更好
from langchain_community.chat_message_histories import ChatMessageHistory

chat_history = ChatMessageHistory()

chat_history.add_user_message('你好,我是ysn,今年72岁,已经退休正在学习AI知识')
chat_history.add_ai_message('你好,我是AI')


from langchain_core.runnables.history import RunnableWithMessageHistory

chain_with_memory = RunnableWithMessageHistory(
    chain,
    lambda x: chat_history,
    input_messages_key = 'new_messages',
    history_messages_key = 'chat_history'
)

print(chain_with_memory.invoke({'new_messages': '请你阐述一下你了解关于我的个人信息'}, config={"configurable":{"session_id": "unused"}}).content)
print(chat_history.messages)


# 这个函数接受传入chain的东西然后修剪记忆,再将传入chain的东西返回去。
def trim_messages(chain_input):
    '''修剪记忆'''
    stored_messages = chat_history.messages
    # 当大于两条时就清空记忆保留最后两条
    if len(stored_messages) > 2:
        chat_history.clear()
        for message in stored_messages[-2:]:
            chat_history.add_message(message)
    return chain_input

chain_with_trimming = trim_messages | chain_with_memory

print(chain_with_trimming.invoke({'new_messages': '请你阐述一下你了解关于我的个人信息'}, config={"configurable":{"session_id": "unused"}}).content)
print(chat_history.messages)


def summarize_messages(chain_input):
    '''总结记忆,需要调用模型来总结'''
    stored_messages = chat_history.messages
    # 当信息数大于等于6条时进行总结
    if len(stored_messages)>=6:
        summarization_prompt = ChatPromptTemplate.from_messages(
            [
                MessagesPlaceholder(variable_name="chat_history"),
                ("user","将上述聊天信息提炼为一条摘要信息。尽可能多的包含具体细节。")
            ]
        )
        summarization_chain = summarization_prompt | model
        summary_message = summarization_chain.invoke({"chat_history": stored_messages})
        chat_history.clear()
        chat_history.add_message(summary_message)
    return chain_input

chain_with_summarization = summarize_messages | chain_with_memory

chat_history.clear()
chat_history.add_message(('human','你好,我是ysn,我今年72岁,已经退休正在学习AI知识'))
chat_history.add_message(('ai','你好,我是AI,请问有什么可以帮你'))
chat_history.add_message(('human','你觉得我适合做AI工程师嘛'))
chat_history.add_message(('ai','适合'))
chat_history.add_message(('human','为什么'))
chat_history.add_message(('ai','因为你在学习AI知识'))

print(chain_with_summarization.invoke({'new_messages': '你觉得我适合干什么'}, config={"configurable":{"session_id": "unused"}}).content)
print(chat_history.messages)



''' 第三节课程:Agent原理与快速构建'''
# agent 是一种结合了大模型推理能力和实际操作能力的智能体
# 通常我们使用模型是通过问答模式,比如我想实现数据库查询,模型可以告诉我一个Sql语句,然后需要自己粘贴这个语句到数据库中执行,来获取这个查询结果。
#  agent 可以接受我们的自然语言,直接修改数据库,还可以上网查找资料,回答最近发生的事情。

# 定义模型
from langchain_openai import ChatOpenAI

model = ChatOpenAI(
    model = 'glm-4-flash',      # 完全免费且响应速度很快
    openai_api_base = "https://open.bigmodel.cn/api/paas/v4/",
    api_key="你的key",
)

# 定义 tools    
from langchain_community.tools import DuckDuckGoSearchRun       # DuckDuckGo 搜索引擎免费

# 操作数据库的 tool
import pandas as pd
from pandasql import sqldf


def simulate_database_operation(sql):
	my_table = pd.DataFrame({
	    'name': ['Henry Myers', 'Martha Hawkins', 'Kelsey Lutz', 'Jonathan Fowler', 'Jonathan Young', 'Autumn Johnson', 'Kimberly Macias', 'Jared Mccormick', 'Casey Hoover', 'Erica Morse'],
	    'age': [60, 44, 54, 46, 76, 22, 69, 33, 23, 35],
	    'sex': ['F', 'M', 'M', 'F', 'F', 'M', 'M', 'F', 'F', 'M']
	})
	result = sqldf(sql)
	return result
# 正常的查询
print(simulate_database_operation('SELECT * FROM my_table WHERE age > 50'))
# # 错误的查询
# print(simulate_database_operation('SELECT * FROM my_table WHERE id > 50'))


# 如何把函数包装成tool? 只要加装饰器和docstring(即使用说明)即可
from langchain.tools import tool

@tool   
def simulate_database_operation(sql: str):
	'''根据sql语句操作数据库'''
	my_table = pd.DataFrame({
	    'name': ['Henry Myers', 'Martha Hawkins', 'Kelsey Lutz', 'Jonathan Fowler', 'Jonathan Young', 'Autumn Johnson', 'Kimberly Macias', 'Jared Mccormick', 'Casey Hoover', 'Erica Morse'],
	    'age': [60, 44, 54, 46, 76, 22, 69, 33, 23, 35],
	    'sex': ['F', 'M', 'M', 'F', 'F', 'M', 'M', 'F', 'F', 'M']
	})
	result = sqldf(sql)
	return result


# 模型调用 tool
tools = [DuckDuckGoSearchRun(), simulate_database_operation]
model_with_tools = model.bind_tools(tools)


from pprint import pprint as pp

# 不需要调用tool的问题
response = model_with_tools.invoke('印度的首都是哪里?')
pp(dict(response))

# 不需要调用tool的问题
search_response = model_with_tools.invoke('最近有什么新鲜事?搜索之后告诉我')
pp(dict(search_response))

fetch_response = model_with_tools.invoke('现在是北京时间几点?查询之后告诉我')
pp(dict(fetch_response))

# 需要操作数据库的问题
db_response = model_with_tools.invoke('帮我往数据库的my_table表中插入一条数据,name是张三,age是18,sex是male')
pp(dict(db_response))


######################### 手动实现agent ################################
# from icecream import ic

def call_tool(model_output, tools):
    tools_map = {tool.name.lower(): tool for tool in tools}
    tools_response = {}
    for tool in model_output.tool_calls:
        tool_name = tool['name']
        tool_args = tool['args']
        tool_instance = tools_map[tool_name]
        tool_response = tool_instance.invoke(*tool_args.values())
        tools_response[tool_name] = tool_response
    return tools_response


def manual_agent(query: str, model: ChatOpenAI, tools: list[tool]):
    '''①模型和工具绑定、②调用绑定的模型查询、③调用tool ④用tool_response和一开始的询问query获取最终的回答final_response'''
    model_with_tools = model.bind_tools(tools)
    model_output = model_with_tools.invoke(query)
    tool_response = call_tool(model_output, tools)
    final_response = model.invoke(
        f'original query: {query} \n\n\n tool response: {tool_response}',
    )
    return final_response


print(manual_agent("帮我查询数据库my_table表中有多少人年龄大于60", model, tools).content)


# 使用 langgraph 定义 agent
from langgraph.prebuilt import create_react_agent
agent = create_react_agent(model, tools)


from langchain_core.messages import HumanMessage

response = agent.invoke({'messages': [HumanMessage(content="帮我查询数据库my_table表中有多少人年龄大于60")]})
print(response)

response = agent.invoke({'messages': [HumanMessage(content="今天在北京的天气怎么样")]})
print(response)


http://www.kler.cn/news/310667.html

相关文章:

  • 使用 Fairseq 进行音频预训练:配置与实现
  • 设计模式之命令模式:从原理到实战,深入解析及源码应用
  • xml中SQL执行错误(使用另外一张表的两个字段,组装SQL的where查询条件)
  • 阿里巴巴搜索API返回值:电商市场竞争的新武器含
  • 动态规划---回文子串
  • 55 mysql 的登录认证流程
  • 掌握MATLAB中的数据类型转换技巧
  • 21. 什么是MyBatis中的N+1问题?如何解决?
  • qt信号与槽(自定义)
  • 手势识别-Yolov5模型-自制数据集训练
  • Kafka是如何保证数据的安全性、可靠性和分区的
  • 共享股东分红模式小程序开发
  • [数据集][目标检测]葡萄成熟度检测数据集VOC+YOLO格式1123张3类别
  • C HTML格式解析与生成之gumbo
  • python怎么输入整数
  • 万能小程序运营管理系统 _requestPost 任意文件读取漏洞复现
  • DAY20240911 VUE:解锁前端路由的奥秘:如何在单页应用中避免404困境?
  • 流量牵引技术与传统防火墙的区别
  • 在网络环境中怎么保护个人信息安全?
  • 土壤墒情测定仪的工作原理
  • 汽车软件开发之敏捷开发
  • Spring 源码解读:手动实现Spring事件机制
  • JSON.parseArray 内存溢出
  • 【第十一章:Sentosa_DSML社区版-机器学习分类】
  • Oracle数据库高级技术探秘:分区表管理与代码实战
  • Python 全栈系列271 微服务踩坑记
  • 数据库学习02——mysql清空表数据后 IBD 文件仍很大的解决方案
  • 面向开发者的LLM入门教程(学习笔记01)
  • 探索学习Python的最佳开发环境和编辑器
  • 家用燃气报警器-家庭可燃气体探测器-旭华智能