实现自己的AI视频监控系统-第四章-基于langchain的AI大模型与智能体应用2
文章目录
- 前言
- 一、需求分析
- 二、需求拆解
- 具备友好的对话和问答能力
- 具备一定记忆能力
- 功能调用
- 三、实现与代码展示
- 总结
- 下期预告
前言
在上一小节,我们简单介绍了langchain的使用方式,包括模型调用、工具调用、智能体功能以及上下文记忆。本小节我们将深入系统直接进行需求的分析的和智能体的搭载。
一、需求分析
对于AI技术的使用,明确需求是重中之重,以大语言模型为例,我们的提示词准确且完善,所得到的结果会更好。回归到我们AI视频监控系统,我希望大模型助手可以实现以下基本功能:
- 具备友好的对话和问答能力
- 具备一定记忆能力
- 依据用户提示进行RTSP摄像头的拉流与管理。
- 依据用户提示进行AI模型的加载和推理。
- 依据用户提示进行分析视频流和原始视频流的切换
二、需求拆解
具备友好的对话和问答能力
- 这一条本身就是伪命题,我们采用LLM进行对话的时候就已经具备了基本的功能,我们只需要采用langchain进行正确模型的调用,即可实现。
具备一定记忆能力
- 我们依据实现了带有记忆的大模型和agent应用,我们需要聚焦于“记住哪些,忽略哪些,利用哪些”,实现更智能的助手应用。
功能调用
- 对于学习了上一小节的朋友或者是对LLM了解的朋友应该很清楚,最直观的方式就是基于大模型的function calling功能,让大模型依据我们的需要进行正确工具的调用,最终实现相关的需求
三、实现与代码展示
- 这里直接给出实现的代码
from langchain_core.messages import ChatMessage, HumanMessage, ToolMessage
from langgraph.checkpoint.memory import MemorySaver
from langgraph.graph import START, MessagesState, StateGraph
from langchain_ollama import ChatOllama
from langchain_core.tools import tool
import yamlcur_info = {"open_camera": [],"yaml_path":r"path\to\config.yaml" }
import json@tool
def check_info() -> dict:"""check global information"""global cur_inforeturn cur_info@tool
def open_stream(cameraId: int, rtsp: str) -> bool:"""pulling streaminput:cameraId : channel index(lobally Unique Identifier)rtsp : pulling stream address"""global cur_infoimport requestsdata = {"cameraId": cameraId,"rtsp": rtsp,}print(f"Opening stream: cameraId={cameraId}, rtsp={rtsp}")cur_info["open_camera"].append({"cameraId": cameraId, "rtsp": rtsp})return True@tool
def operation(type: str, **kwargs) -> str:"""type: operation type [open , close]**kwargs: 参数通过关键字传递open: 需要 cameraId 和 rtspclose: 需要 cameraId"""# llm调用存在参数传递的格式问题global cur_infotry:kwargs = kwargs["kwargs"]if isinstance(kwargs, list):return "failed: just open one camera at the same time"if type == "open":# 检查必需参数if 'cameraId' not in kwargs or 'rtsp' not in kwargs:return "failed: open operation requires cameraId and rtsp"camera_id = kwargs['cameraId']rtsp = kwargs['rtsp']for info in cur_info["open_camera"]:if info["cameraId"] == str(camera_id):return f"failed: camera {camera_id} has opened! please dont open again."if info["rtsp"] == rtsp:return f"failed: rtsp {rtsp} has opened with camera_id {info['cameraId']}!"cur_info["open_camera"].append({"cameraId": str(camera_id), "rtsp": rtsp})# 执行打开操作return f"success: opened camera {camera_id} with rtsp {rtsp}"elif type == "close":if 'cameraId' not in kwargs:return "failed: close operation requires cameraId"camera_id = kwargs['cameraId']# 执行关闭操作index = -1for cur_index, info in enumerate(cur_info["open_camera"]):if info["cameraId"] == str(camera_id):index = cur_indexbreakif index != -1:cur_info["open_camera"].pop(index)return f"success: closed camera {camera_id}"else:return f"failed: camera {camera_id} not exists"else:return "failed: operation type must be 'open' or 'close'"except Exception as e:return e@tool
def load_yaml_config(config_path='config.yaml'):"""加载 YAML 配置文件Args:config_path: YAML 配置文件路径Returns:解析后的配置字典,如果出错返回 None"""try:with open(config_path, 'r', encoding='utf-8') as file:config = yaml.safe_load(file)return configexcept FileNotFoundError:return f"配置文件 {config_path} 不存在"except yaml.YAMLError as e:return f"配置文件 {config_path} 格式错误: {e}"except Exception as e:return f"读取文件时发生未知错误: {e}"tools = [operation, check_info,load_yaml_config]# Define a new graph
workflow = StateGraph(state_schema=MessagesState)
llm = ChatOllama(base_url="path/to/ollama", # 添加了协议和端口model="model_name",validate_model_on_init=True,temperature=0.0,num_predict=1024,
).bind_tools(tools)# Define the function that calls the model
def call_model(state: MessagesState):response = llm.invoke(state["messages"])return {"messages": response}# Define the (single) node in the graph
workflow.add_edge(START, "model")
workflow.add_node("model", call_model)# Add memory
memory = MemorySaver()
app = workflow.compile(checkpointer=memory)# 使用固定的thread_id来保持对话上下文
config = {"configurable": {"thread_id": "terminal_chat_session"}}print("欢迎使用聊天机器人! 输入 'quit' 或 'exit' 退出程序。")while True:# try:# 获取用户输入user_input = input("\n用户: ").strip()# 检查退出条件if user_input.lower() in ['quit', 'exit', 'bye']:print("再见!")breakif not user_input:continue# 创建输入消息input_messages = [HumanMessage(user_input)]# 调用模型output = app.invoke({"messages": input_messages}, config)# 检查是否有工具调用while len(output["messages"]) > 0 and hasattr(output["messages"][-1], 'tool_calls') and len(output["messages"][-1].tool_calls) > 0:# 处理工具调用for tool_call in output["messages"][-1].tool_calls:# try:# 选择并执行工具selected_tool = \{"operation": operation, "check_info": check_info,"load_yaml_config":load_yaml_config}[tool_call["name"].lower()]print(f"调用工具: {tool_call['name']} 参数: {tool_call['args']}")tool_output = selected_tool.invoke(tool_call["args"])print(f"工具返回: {tool_output}")# 添加工具响应消息input_messages.append(ToolMessage(str(tool_output), tool_call_id=tool_call["id"]))# except Exception as e:# print(f"工具执行错误: {e}")# input_messages.append(ToolMessage(f"Error: {str(e)}", tool_call_id=tool_call["id"]))# 再次调用模型处理工具结果output = app.invoke({"messages": input_messages}, config)# 显示最终回复if len(output["messages"]) > 0:last_message = output["messages"][-1]if hasattr(last_message, 'content'):print(f"\n助手: {last_message.content}")else:print("\n助手: (无文本回复)")else:print("\n助手: (无回复)")#-------------------------------------------------------------------------------------------------------# 结果展示
欢迎使用聊天机器人! 输入 'quit' 或 'exit' 退出程序。**用户**: 你好,我是AI视频监控系统,请问你是谁?**助手**: 您好!我是阿里云开发的助手Qwen。我在这里可以帮助您解答问题、编写代码或者完成各种任务。请问您需要什么帮助呢?如果您是AI视频监控系统的管理员或操作员,我可以协助您进行摄像头的操作和配置检查等任务。请告诉我具体的需求吧。**用户**: 你好,请问我是谁?**助手**: 您好!根据之前的对话内容,您似乎是AI视频监控系统的一部分或者是该系统的使用者。请问您需要了解更具体的哪方面的信息呢?如果有关于摄像头操作或配置的问题,我可以帮助解答和执行相关任务。请详细说明您的需求。**用户**: 好的,请你根据系统配置信息,打开对应的摄像头
调用工具: check_info 参数: {}
工具返回: {'open_camera': [], 'yaml_path': 'langchain_test\\config.yaml'}
调用工具: load_yaml_config 参数: {'config_path': '/path/to/config.yaml'}
工具返回: 配置文件 /path/to/config.yaml 不存在**助手**: 看起来提供的配置路径 `/path/to/config.yaml` 并不存在。请提供正确的 YAML 配置文件路径,以便我可以从中获取摄像头的 ID 和 RTSP 地址等信息来打开对应的摄像头。您可以再次尝试并提供一个有效的路径吗?如果不确定具体路径,请告知我您希望操作的具体摄像头名称或其他标识符,我会尽力帮助您找到正确的配置。**用户**: 好的,我的配置路径在./config.yaml,请你读取内容并显示
调用工具: load_yaml_config 参数: {'config_path': './config.yaml'}
工具返回: {'rtsp': ['rtsp://camera1', 'rtsp://camera2', 'rtsp://camera3', 'rtsp://camera4', 'rtsp://camera5', 'rtsp://camera6', 'rtsp://camera7', 'rtsp://camera8', 'rtsp://camera9', 'rtsp://camera10']}**助手**: 我已经读取了配置文件 `./config.yaml` 并获取到了摄像头的 RTSP 地址列表。现在请告诉我您希望打开哪个具体的摄像头,以便我可以继续进行操作。例如,如果您想打开第一个摄像头(RTSP地址为 `rtsp://192.168.10.207:554/camera1`),请告知我对应的摄像头 ID 或名称。如果配置文件中有定义,请提供相应的标识符。如果没有明确的标识符,您可以直接告诉我您想打开哪个位置或编号的摄像头。请问您希望操作哪一个摄像头呢?**用户**: 当前一共有多少摄像头信息?助手: 根据读取到的配置文件内容,总共有 10 个摄像头的信息,它们的 RTSP 地址如下:- `rtsp://camera1`
- `rtsp://camera2`
- `rtsp://camera3`
- `rtsp://camera4`
- `rtsp://camera5`
- `rtsp://camera6`
- `rtsp://camera7`
- `rtsp://camera8`
- `rtsp://camera9`
- `rtsp://camera10`请问您想操作其中的哪一个摄像头呢?请提供具体的摄像头 ID 或名称。**用户**: 请你帮我打开这10个摄像头的视频流
调用工具: operation 参数: {'kwargs': {'cameraId': 1, 'rtsp': 'rtsp://camera1'}, 'type': 'open'}
工具返回: success: opened camera 1 with rtsp rtsp://camera1
调用工具: operation 参数: {'kwargs': {'cameraId': 2, 'rtsp': 'rtsp://camera2'}, 'type': 'open'}
工具返回: success: opened camera 2 with rtsp rtsp://camera2
调用工具: operation 参数: {'kwargs': {'cameraId': 3, 'rtsp': 'rtsp://camera3'}, 'type': 'open'}
工具返回: success: opened camera 3 with rtsp rtsp://camera3
调用工具: operation 参数: {'kwargs': {'cameraId': 4, 'rtsp': 'rtsp://camera4'}, 'type': 'open'}
工具返回: success: opened camera 4 with rtsp rtsp://camera4
调用工具: operation 参数: {'kwargs': {'cameraId': 5, 'rtsp': 'rtsp://camera5'}, 'type': 'open'}
工具返回: success: opened camera 5 with rtsp rtsp://camera5
调用工具: operation 参数: {'kwargs': {'cameraId': 6, 'rtsp': 'rtsp://camera6'}, 'type': 'open'}
工具返回: success: opened camera 6 with rtsp rtsp://camera6
调用工具: operation 参数: {'kwargs': {'cameraId': 7, 'rtsp': 'rtsp://camera7'}, 'type': 'open'}
工具返回: success: opened camera 7 with rtsp rtsp://camera7
调用工具: operation 参数: {'kwargs': {'cameraId': 8, 'rtsp': 'rtsp://camera8'}, 'type': 'open'}
工具返回: success: opened camera 8 with rtsp rtsp://camera8
调用工具: operation 参数: {'kwargs': {'cameraId': 9, 'rtsp': 'rtsp://camera9'}, 'type': 'open'}
工具返回: success: opened camera 9 with rtsp rtsp://camera9
调用工具: operation 参数: {'kwargs': {'cameraId': 10, 'rtsp': 'rtsp://camera10'}, 'type': 'open'}
工具返回: success: opened camera 10 with rtsp rtsp://camera10**助手**: 所有 10 个摄像头的视频流都已经成功打开。每个摄像头都通过其对应的 RTSP 流地址连接到了指定的服务器。如果您需要进一步的帮助或有其他请求,请告诉我!
- 执行流程图如下所示:
- 流程甘特图如下所示:
总结
以上,我们已经实现了一个基本的AI大模型助手,具备上文理解、记忆以及工具的调用能力,下一节,我们将设计一个完备的客户端界面,并融入更加完善的AI助手系统,以实现最终的应用客户端。
下期预告
- AI视频监控系统客户端设计