精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

構建 Autonomous AI Agent |函數調用(Function Calling) 技術實例探索 原創 精華

發布于 2024-5-23 10:35
瀏覽
1收藏

編者按: 大語言模型擁有令人驚嘆的語言理解和生成能力,卻也存在自主決策、與外部系統交互等方面的不足。函數調用(Function Calling)技術的出現,正是為解決這一難題而生的創新方案,它賦予了大語言模型更強的自主能力和與外部世界連接的能力,成為實現真正智能自主 Agent 的關鍵一環。

本期我們精心為各位讀者伙伴呈現一篇詳實的搭建技術教程,全面介紹了如何利用函數調用技術構建 Autonomous AI Agents 。作者從函數調用(Function Calling)的工作原理和應用場景出發,通過構建一個旅游服務助手的實例,層層遞進地講解了整個系統的設計思路、技術細節和代碼實現。

希望通過閱讀本文,各位讀者伙伴能獲得啟發,為開發更智能、更人性化的 AI Agents 尋得更寬廣的光明大道。

作者 | Julian Yip

編譯 | 岳揚

函數調用并非一個新鮮概念。早在 2023 年 7 月,  OpenAI 就為其 GPT 模型引入了這一功能,現在這一功能也被其他競爭對手采用。比如,谷歌的 Gemini API 最近也開始支持函數調用, Anthropic 也在將其整合到 Claude 中。函數調用(譯者注:Function Calling,允許模型通過調用特定的函數來執行某些復雜任務。)已經成為大語言模型(LLMs)的關鍵功能之一,能夠顯著增強大模型應用能力。因此,學習這項技術是極其有意義的。

基于此,我打算撰寫一篇詳細的教程,內容重點為基礎介紹(因為這類教程已經很多了)之外的內容。本教程將專注于實際應用上,展示如何構建一個 fully autonomous AI agent(譯者注:能夠獨立運行和做出決策的、不需要人為干預的 AI agent 。),并將其與 Streamlit 集成來實現類似 ChatGPT 的 Web 交互界面。雖然本教程使用 OpenAI 進行演示,但本文內容同樣適用于其他支持函數調用的大語言模型,例如 Gemini。

01 函數調用(Function Calling)的用途有哪些?

Function Calling 這一技術讓開發者能夠定義函數(也被稱為工具(tools),可以將其視為模型要執行的操作,如進行數學運算或下訂單),并讓模型智能地選擇并輸出一個包含調用這些函數所需參數的 JSON 對象。簡單來說,這一技術具備以下功能:

  • 自主決策(Autonomous decision making):模型能夠智能地選擇所需工具來回答問題。
  • 可靠地解析過程(Reliable parsing):響應一般以 JSON 格式呈現,而非更典型的對話式響應(dialogue-like response)。乍看之下似乎沒什么,但正是這種技術使得 LLM 能夠通過結構化輸入( structured inputs)連接到外部系統,比如通過 API 進行交互。

這種技術為人們帶來了各種各樣的新機遇、新機會:

  • Autonomous AI assistants:機器人不僅可以回答用戶咨詢的問題,還能與內部系統(譯者注:企業內部用于處理內部業務流程、數據管理、客戶關系等任務的系統)交互,處理客戶下訂單和退貨等任務。
  • Personal research assistants:比方說,當我們需要制定旅行計劃時,可以請這些助理在互聯網搜索內容、爬取內容、比較內容,并將結果匯總到 Excel 中。
  • IoT voice commands:模型可以根據檢測到的用戶意圖來控制設備或給出操作建議,例如調節空調溫度。

02 函數調用功能的運行流程

參考 Gemini 的函數調用文檔[1],函數調用功能的運行流程如下,OpenAI 中此功能的工作原理基本相同:

構建 Autonomous AI Agent |函數調用(Function Calling) 技術實例探索-AI.x社區

圖片來源:Gemini 的函數調用文檔[1]

1. 用戶向應用程序發出提示詞(prompt)

2. 應用程序會傳遞用戶提供的提示詞和函數聲明(Function Declaration(s)),即對模型所需工具的描述信息

3. 根據函數聲明,模型會給出工具選取建議和相關的請求參數。注意,模型僅會輸出建議的工具和請求參數,并不會實際調用函數

4. & 5. 應用程序根據模型響應調用相關 API

6. & 7. 將 API 的響應內容再次輸入模型,生成人類可讀的內容

8. 應用程序將最終響應返回給用戶,然后再次回到第 1 步,如此循環往復

上述的介紹內容可能看起來有些許復雜,接下來將通過實例詳細解釋該概念。

03 該 Agents 的整體設計和總體架構

在深入講解具體代碼之前,先簡要介紹一下本文介紹的這個 Agents 的整體設計和總體架構。

3.1 Solution:旅游服務助手

在本文,我們將為外出旅游的酒店顧客構建一個旅游服務助手,該產品可以使用以下工具(這些工具使得該服務助手能夠訪問外部應用程序)。

  • get_items 和 purchase_item:通過 API 連接到數據庫中的產品目錄(product catalog),這兩個工具分別用于獲取商品列表和進行商品購買
  • rag_pipeline_func:通過檢索增強生成(RAG)連接到存儲和管理文檔數據的存儲系統,以便從非結構化文本中獲取相關信息,例如酒店的宣傳冊

構建 Autonomous AI Agent |函數調用(Function Calling) 技術實例探索-AI.x社區

3.2 相關技術棧

  • 嵌入模型(Embedding model):all-MiniLM-L6-v2[2]
  • 向量數據庫(Vector Database):Haystack 的 InMemoryDocumentStore[3]
  • 大語言模型(LLM):通過 OpenRouter 訪問 GPT-4 Turbo[4]。只要支持函數調用,稍作代碼修改即可使用其他大語言模型(如 Gemini)。
  • LLM 框架:使用 Haystack[5],因為它易于使用,文檔詳盡,并且在 pipeline 的構建方面比較透明。本教程實際上是對該框架使用教程[6]的擴展。

現在開始介紹吧!

04 使用上述技術棧構建一個 Agent 樣例

4.1 前期準備工作

前往 Github[7] 克隆本項目代碼。以下內容可以在 Notebook ??function_calling_demo?? 中找到。

請創建并激活一個虛擬環境,然后運行 pip install -r requirements.txt 安裝所需的包。

4.2 項目初始化

首先連接 OpenRouter。如果有 OpenAI API 密鑰,也可以使用原始的 ??OpenAIChatGenerator??? 而不重寫覆蓋 ??api_base_url?? 參數。

import os
from dotenv import load_dotenv
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack.utils import Secret
from haystack.dataclasses import ChatMessage
from haystack.components.generators.utils import print_streaming_chunk

# Set your API key as environment variable before executing this
load_dotenv()
OPENROUTER_API_KEY = os.environ.get('OPENROUTER_API_KEY')

chat_generator = OpenAIChatGenerator(api_key=Secret.from_env_var("OPENROUTER_API_KEY"),
  api_base_url="https://openrouter.ai/api/v1",
  model="openai/gpt-4-turbo-preview",
        streaming_callback=print_streaming_chunk)

接下來,我們測試 chat_generator 是否能成功調用。

chat_generator.run(messages=[ChatMessage.from_user("Return this text: 'test'")])

---------- The response should look like this ----------
{'replies': [ChatMessage(content="'test'", role=<ChatRole.ASSISTANT: 'assistant'>, name=None, meta={'model': 'openai/gpt-4-turbo-preview', 'index': 0, 'finish_reason': 'stop', 'usage': {}})]}

4.3 步驟 1:選擇使用合適的數據存儲方案

在此,我們將在應用程序和兩個數據源(data sources)之間建立連接:用于非結構化文本的文檔存儲系統(Document store),以及通過 API 連接的應用程序數據庫(application database via API)。

使用 Pipeline 給文檔編制索引

需要給系統提供文本樣本(sample texts),以供模型進行檢索增強生成(RAG)。這些文本將被轉換為嵌入(embeddings),并使用將文檔數據存儲在內存中的數據存儲方案。

from haystack import Pipeline, Document
from haystack.document_stores.in_memory import InMemoryDocumentStore
from haystack.components.writers import DocumentWriter
from haystack.components.embedders import SentenceTransformersDocumentEmbedder

# Sample documents
documents = [
    Document(content="Coffee shop opens at 9am and closes at 5pm."),
    Document(content="Gym room opens at 6am and closes at 10pm.")
]

# Create the document store
document_store = InMemoryDocumentStore()

# Create a pipeline to turn the texts into embeddings and store them in the document store
indexing_pipeline = Pipeline()
indexing_pipeline.add_component(
 "doc_embedder", SentenceTransformersDocumentEmbedder(model="sentence-transformers/all-MiniLM-L6-v2")
)
indexing_pipeline.add_component("doc_writer", DocumentWriter(document_store=document_store))

indexing_pipeline.connect("doc_embedder.documents", "doc_writer.documents")

indexing_pipeline.run({"doc_embedder": {"documents": documents}})

上述程序的輸出結果應該與輸入的示例文檔數據保持一致:

{'doc_writer': {'documents_written': 2}}

啟動 API 服務進程

在 db_api.py 文件中創建一個用 Flask 框架構建的 API 服務,用于連接 SQLite 數據庫。請在終端運行 python db_api.py,啟動該服務。

構建 Autonomous AI Agent |函數調用(Function Calling) 技術實例探索-AI.x社區

如果服務成功執行,終端將顯示圖中所示的信息

我注意到在 db_api.py 中預置一些初始的基礎數據。

構建 Autonomous AI Agent |函數調用(Function Calling) 技術實例探索-AI.x社區

數據庫中的數據樣本

4.4 步驟 2:定義函數(Define the functions)

這一步是在準備真正的函數,以供模型在之后的函數調用(Function Calling)步驟中調用執行。(如 02 節 “函數調用功能的運行流程” 中所述的步驟 4-5)

RAG 函數(RAG function)

其中之一就是 RAG 函數 ??rag_pipeline_func??。這個函數的作用是讓模型能夠搜索之前存儲在文檔存儲中的文本內容,并基于搜索結果提供答案。它首先使用 Haystack 這個框架。將 RAG (檢索增強生成)的檢索過程定義為一個 pipeline 。

from haystack.components.embedders import SentenceTransformersTextEmbedder
from haystack.components.retrievers.in_memory import InMemoryEmbeddingRetriever
from haystack.components.builders import PromptBuilder
from haystack.components.generators import OpenAIGenerator

template = """
Answer the questions based on the given context.

Context:
{% for document in documents %}
    {{ document.content }}
{% endfor %}
Question: {{ question }}
Answer:
"""
rag_pipe = Pipeline()
rag_pipe.add_component("embedder", SentenceTransformersTextEmbedder(model="sentence-transformers/all-MiniLM-L6-v2"))
rag_pipe.add_component("retriever", InMemoryEmbeddingRetriever(document_store=document_store))
rag_pipe.add_component("prompt_builder", PromptBuilder(template=template))
# Note to llm: We are using OpenAIGenerator, not the OpenAIChatGenerator, because the latter only accepts List[str] as input and cannot accept prompt_builder's str output
rag_pipe.add_component("llm", OpenAIGenerator(api_key=Secret.from_env_var("OPENROUTER_API_KEY"),
  api_base_url="https://openrouter.ai/api/v1",
  model="openai/gpt-4-turbo-preview"))

rag_pipe.connect("embedder.embedding", "retriever.query_embedding")
rag_pipe.connect("retriever", "prompt_builder.documents")
rag_pipe.connect("prompt_builder", "llm")

測試函數功能是否正常工作。

query = “When does the coffee shop open?”
rag_pipe.run({"embedder": {"text": query}, "prompt_builder": {"question": query}})

rag_pipeline_func 函數在被模型調用執行后,應該會產生如下輸出。請注意,模型給出的回答來自于我們之前提供的樣本文檔數據。

{'llm': {'replies': ['The coffee shop opens at 9am.'],
 'meta': [{'model': 'openai/gpt-4-turbo-preview',
 'index': 0,
 'finish_reason': 'stop',
 'usage': {'completion_tokens': 9,
 'prompt_tokens': 60,
 'total_tokens': 69,
 'total_cost': 0.00087}}]}}

然后,我們可以將 rag_pipe 轉化為一個函數,在需要時調用 rag_pipeline_func(query) 獲取基于 query 的答案,而不會返回其他的中間細節信息。

def rag_pipeline_func(query: str):
    result = rag_pipe.run({"embedder": {"text": query}, "prompt_builder": {"question": query}})

 return {"reply": result["llm"]["replies"][0]}

定義與數據庫進行交互的 API

在此處,我們定義與數據庫進行交互的 ??get_items??? 函數和 ??purchase_itemfunctions?? 函數。

# Flask's default local URL, change it if necessary
db_base_url = 'http://127.0.0.1:5000'

# Use requests to get the data from the database
import requests
import json

# get_categories is supplied as part of the prompt, it is not used as a tool
def get_categories():
    response = requests.get(f'{db_base_url}/category')
    data = response.json()
 return data

def get_items(ids=None,categories=None):
    params = {
 'id': ids,
 'category': categories,
 }
    response = requests.get(f'{db_base_url}/item', params=params)
    data = response.json()
 return data

def purchase_item(id,quantity):

    headers = {
 'Content-type':'application/json', 
 'Accept':'application/json'
 }

    data = {
 'id': id,
 'quantity': quantity,
 }
    response = requests.post(f'{db_base_url}/item/purchase', json=data, headers=headers)
 return response.json()

定義工具函數列表

現在我們已經成功完成函數的定義,接下來需要讓模型識別并了解如何使用這些函數,為此我們需要為這些函數提供一些描述說明內容。

由于我們在此處使用的是 OpenAI,所以需要按照 OpenAI 要求的格式[8]來描述這些 tools(函數)。

tools = [
 {
 "type": "function",
 "function": {
 "name": "get_items",
 "description": "Get a list of items from the database",
 "parameters": {
 "type": "object",
 "properties": {
 "ids": {
 "type": "string",
 "description": "Comma separated list of item ids to fetch",
 },
 "categories": {
 "type": "string",
 "description": "Comma separated list of item categories to fetch",
 },
 },
 "required": [],
 },
 }
 },
 {
 "type": "function",
 "function": {
 "name": "purchase_item",
 "description": "Purchase a particular item",
 "parameters": {
 "type": "object",
 "properties": {
 "id": {
 "type": "string",
 "description": "The given product ID, product name is not accepted here. Please obtain the product ID from the database first.",
 },
 "quantity": {
 "type": "integer",
 "description": "Number of items to purchase",
 },
 },
 "required": [],
 },
 }
 },
 {
 "type": "function",
 "function": {
 "name": "rag_pipeline_func",
 "description": "Get information from hotel brochure",
 "parameters": {
 "type": "object",
 "properties": {
 "query": {
 "type": "string",
 "description": "The query to use in the search. Infer this from the user's message. It should be a question or a statement",
 }
 },
 "required": ["query"],
 },
 },
 }
]

4.5 步驟 3:將所有系統組件整合在一起

我們現在已經準備好了測試函數調用(Function Calling)功能所需的所有系統組件!這一步驟我們需要做以下幾件事:

  1. 為模型提供初始提示詞(prompt),并為其提供上下文
  2. 提供樣例用戶消息,模擬真實用戶的 query 或需求
  3. 將之前定義的工具函數列表(tool list)作為 ??tools?? 參數傳遞給 chat generator (譯者注:生成對話式回復的語言模型或 AI 系統),這是最關鍵的一步。

# 1. Initial prompt
context = f"""You are an assistant to tourists visiting a hotel.
You have access to a database of items (which includes {get_categories()}) that tourists can buy, you also have access to the hotel's brochure.
If the tourist's question cannot be answered from the database, you can refer to the brochure.
If the tourist's question cannot be answered from the brochure, you can ask the tourist to ask the hotel staff.
"""
messages = [
    ChatMessage.from_system(context),
 # 2. Sample message from user
    ChatMessage.from_user("Can I buy a coffee?"),
 ]

# 3. Passing the tools list and invoke the chat generator
response = chat_generator.run(messages=messages, generation_kwargs= {"tools": tools})
response

---------- Response ----------
{'replies': [ChatMessage(content='[{"index": 0, "id": "call_AkTWoiJzx5uJSgKW0WAI1yBB", "function": {"arguments": "{\"categories\":\"Food and beverages\"}", "name": "get_items"}, "type": "function"}]', role=<ChatRole.ASSISTANT: 'assistant'>, name=None, meta={'model': 'openai/gpt-4-turbo-preview', 'index': 0, 'finish_reason': 'tool_calls', 'usage': {}})]}

現在讓我們來檢查一下模型響應內容。

需要注意的是,函數調用(Function Calling)所返回的內容,不僅包括模型選擇調用的函數本身,還應該包括為調用該函數所傳入的參數。

function_call = json.loads(response["replies"][0].content)[0]
function_name = function_call["function"]["name"]
function_args = json.loads(function_call["function"]["arguments"])
print("Function Name:", function_name)
print("Function Arguments:", function_args)

---------- Response ----------
Function Name: get_items
Function Arguments: {‘categories’: ‘Food and beverages’}

當模型遇到另一個新問題時,會分析該問題,結合它已有的上下文信息,評估哪一個可用的工具函數最能夠幫助回答這個問題。

# Another question
messages.append(ChatMessage.from_user("Where's the coffee shop?"))

# Invoke the chat generator, and passing the tools list
response = chat_generator.run(messages=messages, generation_kwargs= {"tools": tools})
function_call = json.loads(response["replies"][0].content)[0]
function_name = function_call["function"]["name"]
function_args = json.loads(function_call["function"]["arguments"])
print("Function Name:", function_name)
print("Function Arguments:", function_args)

---------- Response ----------
Function Name: rag_pipeline_func
Function Arguments: {'query': "Where's the coffee shop?"}

請再次注意,這一步驟實際上還沒有真正調用執行任何函數,真正執行函數調用,將是我們接下來這個步驟要做的。

調用函數

這一步驟,我們需要將參數輸入所選函數:

## Find the correspoding function and call it with the given arguments
available_functions = {"get_items": get_items, "purchase_item": purchase_item,"rag_pipeline_func": rag_pipeline_func}
function_to_call = available_functions[function_name]
function_response = function_to_call(**function_args)
print("Function Response:", function_response)

---------- Response ----------
Function Response: {'reply': 'The provided context does not specify a physical location for the coffee shop, only its operating hours. Therefore, I cannot determine where the coffee shop is located based on the given information.'}

然后,我們可以將來自 ??rag_pipeline_func??? 的模型響應結果,作為上下文信息附加到 ??messages?? 變量中,從而讓模型基于這個附加的上下文,生成最終的答復。

messages.append(ChatMessage.from_function(content=json.dumps(function_response), name=function_name))
response = chat_generator.run(messages=messages)
response_msg = response["replies"][0]

print(response_msg.content)

---------- Response ----------
For the location of the coffee shop within the hotel, I recommend asking the hotel staff directly. They will be able to guide you to it accurately.

現在已經完成了一個完整的用戶與 AI 的對話循環!

4.6 步驟 4:將其轉化為實時交互式對話(interactive chat)系統

上面的代碼展示了函數調用是如何實現的,但我們想更進一步,將其轉化為實時交互式對話(interactive chat)系統。

在本節,我展示了兩種實現方式:

  1. 較為原始的 input() 方法,將對話內容打印到 notebook 中。
  2. 通過 Streamlit 進行渲染,提供類似 ChatGPT 的 UI 體驗。

input() loop

這部分代碼是從 Haystack 的教程[9]中復制過來的,我們可以通過它快速測試模型。請注意:該應用程序是為了演示函數調用(Function Calling)這一概念而創建的,并非意味著此應用程序的健壯性完美,例如:支持同時對多個項目進行排序、無幻覺等。

import json
from haystack.dataclasses import ChatMessage, ChatRole

response = None
messages = [
    ChatMessage.from_system(context)
]

while True:
 # if OpenAI response is a tool call
 if response and response["replies"][0].meta["finish_reason"] == "tool_calls":
        function_calls = json.loads(response["replies"][0].content)

 for function_call in function_calls:
 ## Parse function calling information
            function_name = function_call["function"]["name"]
            function_args = json.loads(function_call["function"]["arguments"])

 ## Find the correspoding function and call it with the given arguments
            function_to_call = available_functions[function_name]
            function_response = function_to_call(**function_args)

 ## Append function response to the messages list using `ChatMessage.from_function`
            messages.append(ChatMessage.from_function(content=json.dumps(function_response), name=function_name))

 # Regular Conversation
 else:
 # Append assistant messages to the messages list
 if not messages[-1].is_from(ChatRole.SYSTEM):
            messages.append(response["replies"][0])

        user_input = input("ENTER YOUR MESSAGE ?? INFO: Type 'exit' or 'quit' to stop\n")
 if user_input.lower() == "exit" or user_input.lower() == "quit":
 break
 else:
            messages.append(ChatMessage.from_user(user_input))

    response = chat_generator.run(messages=messages, generation_kwargs={"tools": tools})

構建 Autonomous AI Agent |函數調用(Function Calling) 技術實例探索-AI.x社區

在集成開發環境中運行交互式聊天 App

盡管基本的交互方式也可以運行使用,但擁有一個更加美觀友好的用戶界面會讓用戶體驗更加出色。

Streamlit 界面

Streamlit 能夠將 Python 腳本和 Web 開發技術優雅地結合,轉化為可共享使用的 Web 服務應用,為這個函數調用交互式應用程序構建了一個全新的 Web 界面。上述代碼已被改編成一個 Streamlit 應用,位于代碼倉庫的 streamlit 文件夾中。

我們可以通過以下步驟運行該應用:

  1. 如果還未運行,請使用 ??python db_api.py?? 啟動 API 服務器。
  2. 將 ??OPENROUTER_API_KEY??? 設置為環境變量,例如在 Linux 上或使用 ??git bash??? 時,執行 ??export OPENROUTER_API_KEY='@替換為您的API密鑰'??。
  3. 在終端中進入 streamlit 文件夾,目錄切換命令為 ??cd streamlit??。
  4. 運行 ??streamlit run app.py?? 啟動 Streamlit。瀏覽器應該會自動創建一個新的標簽頁,運行該應用程序。

基本上我想介紹的內容就是這些了!真心希望大家能夠喜歡這篇文章。

構建 Autonomous AI Agent |函數調用(Function Calling) 技術實例探索-AI.x社區

Streamlit UI

Thanks for reading!

Julian Yip

Multi-Cloud Data Architect | Azure, GCP, Databricks Certified | ML and MLOps Practitioner

END

參考資料

[1]??https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/function-calling??

[2]??https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2??

[3]??https://docs.haystack.deepset.ai/docs/inmemorydocumentstore??

[4]??https://openrouter.ai/models/openai/gpt-4-1106-preview??

[5]??https://haystack.deepset.ai/??

[6]??https://haystack.deepset.ai/tutorials/40_building_chat_application_with_function_calling??

[7]??https://github.com/yip-kl/llm_function_calling_demo??

[8]??https://cookbook.openai.com/examples/function_calling_with_an_openapi_spec??

[9]??https://haystack.deepset.ai/tutorials/40_building_chat_application_with_function_calling??

本文經原作者授權,由 Baihai IDP 編譯。如需轉載譯文,請聯系獲取授權。

原文鏈接:

??https://towardsdatascience.com/build-autonomous-ai-agents-with-function-calling-0bb483753975??


?著作權歸作者所有,如需轉載,請注明出處,否則將追究法律責任
1
收藏 1
回復
舉報
回復
相關推薦
谁有免费的黄色网址| 日本韩国欧美在线观看| 国产区精品在线| 亚洲一本视频| 中文字幕一区电影| 日本一区二区免费视频| 精品3atv在线视频| 亚洲精选一二三| 欧美日韩亚洲在线| 不卡视频免费在线观看| 久久精品五月| 久久999免费视频| 成年人免费观看视频网站| 99精品国产九九国产精品| 精品久久久久久久中文字幕 | 国产69精品99久久久久久宅男| 亚洲第一香蕉网| 欧美激情三级| 欧美在线观看视频一区二区| 日韩极品视频在线观看| 日韩黄色影片| 国产成人自拍高清视频在线免费播放| 日韩免费视频在线观看| 激情综合五月网| 日韩欧美视频| 亚洲欧美中文日韩在线v日本| 日韩不卡的av| 精品国产黄a∨片高清在线| 亚洲一区中文日韩| 一区在线电影| 国产永久免费高清在线观看| 成人综合婷婷国产精品久久免费| 国产精品成久久久久三级| 久久久.www| 日韩三级在线| 亚洲性猛交xxxxwww| 中国黄色片视频| 久久久久毛片免费观看| 在线观看不卡一区| 成人免费观看视频在线观看| 国产啊啊啊视频在线观看| 中文字幕中文乱码欧美一区二区| 青青草原成人| 手机福利在线| 99免费精品视频| 国产高清精品一区二区| 99久久夜色精品国产亚洲| 久久国产精品99久久久久久老狼 | 蜜臀av.com| 亚洲xxxxxx| 国产校园另类小说区| 久久riav| 午夜小视频免费| av中文字幕亚洲| 精品日本一区二区三区在线观看| av资源免费看| 国产伦精品一区二区三区免费迷 | 欧美成年网站| 日韩视频免费直播| 熟妇女人妻丰满少妇中文字幕| 久久不卡日韩美女| 欧美日韩免费观看一区二区三区| 又色又爽又高潮免费视频国产| 日韩电影免费观| 一本大道综合伊人精品热热 | 久久精品国产99久久6| 国产精品久久91| 国产黄网在线观看| 美女在线视频一区| 91精品久久久久久久久青青| 夜夜躁狠狠躁日日躁av| 精品一区二区三区免费| 91系列在线播放| 性生交大片免费看女人按摩| 丁香亚洲综合激情啪啪综合| 国产欧美一区二区在线播放| 无码精品黑人一区二区三区 | www.九色在线| 欧美性感美女h网站在线观看免费| 欧美视频在线免费播放| 伊人网在线播放| 在线精品视频免费播放| 亚欧美在线观看| 国产亚洲亚洲国产一二区| 日韩欧美一区中文| 亚洲一级av无码毛片精品| 神马午夜久久| 日韩在线视频线视频免费网站| 国产一区二区三区在线视频观看| 欧美日韩一区二区国产| 欧美性视频精品| 国产精品国产精品国产| 国产白丝精品91爽爽久久| 精品国产日本| 91青青在线视频| 亚洲影院久久精品| 欧美污视频网站| 国产视频一区二区在线播放| 亚洲精品美女久久 | 亚洲一区二区三区视频在线| 鲁一鲁一鲁一鲁一澡| 欧美一级做a| 亚洲第一精品福利| 亚洲不卡的av| 在线播放日韩| 国产免费一区视频观看免费 | 不卡大黄网站免费看| 日本不卡免费新一二三区| 好吊日视频在线观看| 舔着乳尖日韩一区| 色呦色呦色精品| 香蕉视频一区| 欧美人在线视频| 婷婷激情五月综合| 丁香婷婷综合五月| 一区二区三区av在线| 久草免费在线视频| 日韩欧美一二三| 久久免费手机视频| 国产日韩一区| 99视频在线播放| 北岛玲日韩精品一区二区三区| 亚洲一区二区三区激情| 免费一区二区三区在线观看| 日韩高清电影免费| 欧美国产日本在线| 国产精品怡红院| 国产清纯白嫩初高生在线观看91| 美脚丝袜脚交一区二区| 亚洲一区有码| 色午夜这里只有精品| 在线观看免费国产视频| 国产v日产∨综合v精品视频| 黄色免费高清视频| 欧美va视频| 亚洲精品视频二区| 欧美一区二区三区四| 国产91精品露脸国语对白| 在线视频欧美一区| av亚洲一区| 亚洲男人av在线| 国产成人无码精品久久久久| 国产成人综合网| 免费cad大片在线观看| 欧美videos粗暴| 中文字幕亚洲欧美| 中文字幕人妻一区二区在线视频| 久久奇米777| 国产男女无遮挡| 奇米影视777在线欧美电影观看| 久久久久国产精品免费| 国产成人毛毛毛片| 一区二区三区视频在线观看| 久久精品一二三四| 欧美午夜一区| 超碰97在线资源| 欧美日韩经典丝袜| 精品久久久久一区二区国产| 久久久久97国产| 国产激情视频一区二区在线观看 | 精品一区二区成人精品| 亚洲精品不卡| 亚洲三级电影| 欧美大尺度激情区在线播放| va婷婷在线免费观看| 亚洲最大的成人av| 六十路息与子猛烈交尾| 校园激情久久| 日本一区二区三区四区高清视频 | 欧美特黄一级片| 国产一区二区免费视频| 91传媒免费视频| 精品欧美午夜寂寞影院| 日本午夜人人精品| 9i精品一二三区| 91精品国产综合久久久久久久| 91麻豆精品成人一区二区| 国产精品一级黄| 国产v片免费观看| 国产精品中文字幕亚洲欧美| 国产精选久久久久久| av在线免费观看网址| 精品国产凹凸成av人导航| 欧美精品二区三区| 国产精品视频免费看| 日本女人黄色片| 99亚洲一区二区| 四虎影视永久免费在线观看一区二区三区| 成人在线中文| 久久久久久尹人网香蕉| 欧美色图另类| 欧美一卡二卡三卡四卡| 日韩三级小视频| 欧美激情一区三区| xxxx国产视频| 天堂精品中文字幕在线| 特色特色大片在线| 婷婷精品在线观看| 亚洲一区二区三区四区在线播放| 黄色影院在线看| 中文字幕精品久久久久| 成人爽a毛片一区二区| 色综合久久久久综合体桃花网| 少妇高潮在线观看| 99国产精品久| 在线成人免费av| 日本成人在线电影网| 18禁裸男晨勃露j毛免费观看| 精品视频免费| 精品蜜桃传媒| av日韩一区| 国产精品草莓在线免费观看| 色女人在线视频| 在线播放国产一区中文字幕剧情欧美 | 国产精品地址| 一区二区三区四区免费视频| 日本福利一区| 99久久综合狠狠综合久久止| 日韩制服诱惑| 7777免费精品视频| 午夜成年人在线免费视频| 在线播放日韩欧美| 天天射天天色天天干| 欧美一区二区三区成人| 樱花视频在线免费观看| 精品久久久久久中文字幕一区奶水 | 蜜臀一区二区三区精品免费视频| 一本久道久久综合婷婷鲸鱼| 正在播放久久| 欧美色蜜桃97| 欧美日韩国产免费一区二区三区| 国产suv精品一区二区四区视频| 91久久夜色精品国产网站| 欧美成人黑人| 欧美亚洲在线视频| cao在线视频| 欧美黄色性视频| 成人免费观看视频大全| 神马久久久久久| 91在线视频免费看| 一本色道久久综合狠狠躁篇怎么玩 | 亚洲一区二区精品在线观看| 精品久久影视| 天堂av一区二区| 精品国产99| 欧洲在线视频一区| 一区二区美女| 欧美一区1区三区3区公司| 国产成人精品一区二区免费看京| 九9re精品视频在线观看re6| 久久99国产精品久久99大师| 国产精品美女久久久久av福利| 一区二区三区四区高清视频 | 亚洲一级免费在线观看| 免费视频一区二区| 香港日本韩国三级网站| 蜜桃在线一区二区三区| 伊人影院综合在线| 九九热在线视频观看这里只有精品| www.日本一区| 国产伦精品一区二区三区免费 | 2021久久国产精品不只是精品| 亚洲精品中文字幕在线播放| 久久亚洲一区二区三区四区| 色欲AV无码精品一区二区久久| 国产色产综合色产在线视频| 国产aaaaaaaaa| 亚洲品质自拍视频| 欧美黄色免费观看| 精品国产福利在线| 无码人妻aⅴ一区二区三区有奶水| 色8久久精品久久久久久蜜| 波多野结衣家庭主妇| 欧美军同video69gay| www.天堂在线| 精品视频在线导航| 午夜激情在线观看| 久久久久久久久久亚洲| 新版的欧美在线视频| 国产成人福利网站| 成人国产精品一区二区网站| 国产99在线免费| 国产精品一区二区av日韩在线| 亚洲五月六月| 亚洲人成高清| 国内自拍视频网| 国产一区二区视频在线播放| 亚洲中文字幕无码一区| 国产欧美日韩精品一区| 麻豆chinese极品少妇| 色综合久久中文字幕| 国产精品怡红院| 亚洲欧美综合精品久久成人| 久cao在线| 欧亚精品中文字幕| 人人爱人人干婷婷丁香亚洲| 六月婷婷久久| 在线一区免费| 国产精品免费观看久久| 国产又粗又猛又爽又黄91精品| 日本三级日本三级日本三级极| 国产日产精品1区| 欧美日韩免费做爰视频| 色婷婷av一区二区三区大白胸| 国产片在线播放| 亚洲午夜未删减在线观看 | 欧美午夜电影在线观看| 日本www.色| av中文字幕在线不卡| 国产视频精品免费| 色婷婷激情综合| 国模私拍视频在线| www国产91| 最近高清中文在线字幕在线观看1| 91青草视频久久| 国产一区二区精品久| 久久99久久99精品| 精品一区二区三区免费观看| 美女洗澡无遮挡| 午夜精品久久久久久久| 国产成人精品一区二区无码呦| 这里只有精品视频在线| 中文字幕在线直播| 国精产品一区二区| 最新国产精品久久久| 成人亚洲免费视频| 国产日韩成人精品| 国产亚洲欧美在线精品| 精品国产91久久久久久久妲己| 免费av在线| 国产精品免费久久久久久| 亚洲三级网页| 欧美日韩黄色一级片| 不卡欧美aaaaa| 国产一级片久久| 日韩欧美一级二级三级| av在线免费网址| 亚洲v日韩v综合v精品v| 我不卡影院28| 91视频福利网| 亚洲精品美腿丝袜| 99国产精品欲| 久久香蕉国产线看观看av| 福利视频亚洲| 一本一道久久a久久精品综合| 欧美aa在线视频| 亚洲精品视频网址| 欧美揉bbbbb揉bbbbb| 超碰97在线免费观看| 国产成人在线一区| 欧美日韩精品在线一区| 美女黄色片视频| 国产精品日日摸夜夜摸av| 中文字幕欧美色图| 这里只有精品视频| 中文字幕成人| 粉嫩av一区二区三区天美传媒 | 日韩av在线看免费观看| 色综合久久综合| 91官网在线| 成人亚洲欧美一区二区三区| 88国产精品视频一区二区三区| 午夜免费视频网站| 亚洲一区在线电影| 天堂中文在线资| 国产精品久久久久久久天堂| 欧美aaaa视频| 免费看三级黄色片| 午夜精品久久久久久| 国产h在线观看| 成人两性免费视频| 国产精品激情电影| theav精尽人亡av| 欧美午夜理伦三级在线观看| 黄网页免费在线观看| 成人欧美一区二区三区黑人免费| 中文精品视频| 极品蜜桃臀肥臀-x88av| 91精品视频网| av影视在线看| 日韩精品一区二区三区四区五区| 黑人精品欧美一区二区蜜桃| 日韩av在线播| 日韩在线播放视频| 全国精品免费看| 伊人成人222| 午夜伦欧美伦电影理论片| 在线观看h片| 国产日本一区二区三区| 蜜臀a∨国产成人精品| 国产精品第72页| 日韩在线免费高清视频| 欧美wwwsss9999| 亚洲色图偷拍视频| 一本一本久久a久久精品综合麻豆 一本一道波多野结衣一区二区 | 日韩一级二级| 黄色大片中文字幕| 亚洲婷婷在线视频| 激情小视频在线观看| 国产精品国模大尺度私拍|