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)