精品欧美一区二区三区在线观看 _久久久久国色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
回復
舉報
回復
相關推薦
亚洲欧美网站| 精品久久97| 亚洲黄色性网站| 国产精品污www一区二区三区| 九九热在线视频播放| 激情综合网站| 日韩一区二区免费高清| 欧美日韩激情视频在线观看| av基地在线| 成人午夜视频免费看| 国产精品久久999| 欧美精品99久久久| 久久99高清| 欧美变态凌虐bdsm| 亚洲人辣妹窥探嘘嘘| xxxcom在线观看| 中文字幕成人在线观看| 国产精品美女xx| 97人妻精品一区二区三区软件 | 久久99精品一区二区三区| 欧美老肥婆性猛交视频| www.中文字幕av| 日韩一区二区三区色| 色综合激情五月| 99热这里只有精品免费| av福利在线播放| 99麻豆久久久国产精品免费| 91视频国产精品| 国产熟妇一区二区三区四区| 国产一区日韩一区| 日韩中文字幕在线免费观看| 一本色道综合久久欧美日韩精品 | 欧美一区二区三区精品| 久久久久狠狠高潮亚洲精品| 国产区美女在线| 亚洲男人的天堂av| 亚洲日本精品国产第一区| 欧美日韩激情视频一区二区三区| 国产伦理精品不卡| 国产裸体写真av一区二区| 国产一级精品视频| 亚洲人成高清| 国语自产精品视频在免费| 好吊日在线视频| 91嫩草亚洲精品| 在线视频免费一区二区| 国产肥白大熟妇bbbb视频| 久久夜色电影| 亚洲国产精品va在看黑人| 91精品国产三级| 99久久久国产| 欧美精品三级日韩久久| 成人日韩在线视频| 久久人体av| 欧美精品久久久久久久多人混战| 一级黄色香蕉视频| 国产一区二区精品调教| 欧美中文字幕一二三区视频| 欧美自拍小视频| 日韩欧美精品一区二区综合视频| 欧美系列亚洲系列| 污污网站免费观看| vam成人资源在线观看| 91.com在线观看| 天天干天天色天天干| 成人精品国产亚洲| 精品日韩美女的视频高清| 高清欧美精品xxxxx| 啊啊啊久久久| 色综合天天综合在线视频| 黄色三级视频片| 国产精品伊人| 日韩午夜激情av| 天天插天天射天天干| 免费精品国产| 日韩性xxxx爱| 欧美成人片在线观看| 亚洲久久成人| 国产精品九九久久久久久久| 国产一区二区在线视频观看| 国产盗摄女厕一区二区三区| 国产一区二区视频在线免费观看| 欧美偷拍视频| 中文字幕一区在线观看| youjizz.com在线观看| 国产伦理精品| 欧美性色综合网| 少妇性l交大片7724com| 日韩精选在线| 视频在线观看一区二区| 九九热视频精品| 久久久久国产一区二区| 成人性生交xxxxx网站| 欧美一级特黄aaaaaa大片在线观看| 97久久久精品综合88久久| 亚洲aⅴ天堂av在线电影软件| 超碰公开在线| 欧美性高潮在线| 久久久久xxxx| 日本午夜精品| www.久久久久| 少妇一级淫片免费放中国| 热久久国产精品| 99视频免费观看| 精品av中文字幕在线毛片| 国产精品超碰97尤物18| 你真棒插曲来救救我在线观看| 精品欧美一区二区三区在线观看 | 日韩电影免费| 亚洲欧美一区二区三区极速播放 | 成人亚洲网站| 亚洲国产欧美一区| 国产高清视频免费在线观看| 制服诱惑一区二区| 91视频-88av| 成人在线二区| 婷婷中文字幕综合| 亚洲黄色片免费看| 日韩av大片| 91av在线播放| 丰满大乳国产精品| 中文字幕中文字幕一区| 麻豆传传媒久久久爱| 一区二区三区视频免费视频观看网站 | 精品av一区二区| 欧美激情综合亚洲一二区| 一道本无吗一区| 国产婷婷色一区二区三区| 成人精品视频在线播放| 麻豆精品国产| 一区二区三区国产在线观看| 日本天堂网在线| 99re热这里只有精品免费视频| 300部国产真实乱| 欧美男女视频| 色偷偷噜噜噜亚洲男人| 久草视频在线免费| 久久亚洲综合av| 好吊妞无缓冲视频观看| 豆花视频一区二区| 国模极品一区二区三区| 丰满人妻一区二区三区无码av | 国产成+人+日韩+欧美+亚洲| 中文精品视频一区二区在线观看| 欧美××××黑人××性爽| 亚洲精品一区av在线播放| 91视频免费网址| 99久久久无码国产精品| 欧美变态另类刺激| 玖玖玖免费嫩草在线影院一区| 久久久久久久久久国产| 亚洲美女福利视频| 亚洲一区二区三区在线| 岛国精品一区二区三区| 国产一区美女| 九色综合日本| 波多野结衣亚洲| 亚洲欧美在线磁力| 人妻中文字幕一区二区三区| 中文字幕精品在线不卡| 尤物国产在线观看| 影音先锋日韩精品| www.成人av| 日韩大片免费观看| 国产午夜精品一区二区三区| 中文在线免费观看| 一区在线中文字幕| 亚洲图片欧美另类| 亚洲欧美卡通另类91av| 色综合影院在线观看| 亚洲精品66| 欧美激情亚洲另类| 欧洲亚洲精品视频| 欧美日韩午夜精品| 欧美人与禽zozzo禽性配| 99精品热视频| 91网址在线播放| 婷婷综合伊人| 国产日韩一区二区| 91国内外精品自在线播放| 日韩中文字幕在线播放| 国产草草影院ccyycom| 天天操天天色综合| 一本色道久久88| 成人国产精品免费网站| 午夜精品久久久内射近拍高清| 成人羞羞网站| 春色成人在线视频| 欧美精选视频一区二区| 久久躁日日躁aaaaxxxx| 完全免费av在线播放| 亚洲视频日韩精品| 国产视频手机在线观看| 精品国产91久久久久久| 影音先锋男人看片资源| 成人午夜在线播放| 最新天堂中文在线| 亚洲色诱最新| 永久免费看av| 精品国产不卡| 精品一区久久久久久| 日韩欧国产精品一区综合无码| 久久久久久久亚洲精品| аⅴ资源新版在线天堂| 亚洲成色www8888| 一区二区精品视频在线观看| 福利视频一区二区| 日本a级片视频| 国产精品色眯眯| 无码人妻aⅴ一区二区三区| 国产一区二区久久| 亚洲人成色77777| 精品电影一区| 一区二区精品视频| 国内成人自拍| 久久av一区二区三区亚洲| 99视频有精品高清视频| 国产精品扒开腿做爽爽爽男男 | 极品美乳网红视频免费在线观看| 日韩欧美国产一区二区在线播放| 亚洲男人天堂网址| 无吗不卡中文字幕| 精品无码一区二区三区电影桃花 | 亚洲精品久久久久国产| 精品久久国产视频| 欧美日韩中字一区| 免费看毛片网站| 午夜精彩视频在线观看不卡| 91porn在线视频| 亚洲欧美自拍偷拍| 国产极品视频在线观看| 国产欧美日本一区二区三区| 午夜久久久久久久| av在线一区二区三区| www.黄色网| 国产成人啪午夜精品网站男同| 性欧美1819| 美女国产一区二区三区| 好男人www社区| 视频一区二区欧美| mm1313亚洲国产精品无码试看| 亚洲一区二区三区高清| 色综合久久久久无码专区| 国产精品激情| 日韩一级性生活片| 在线成人黄色| 浮妇高潮喷白浆视频| 亚洲麻豆一区| 成年人视频观看| 久久精品网址| 亚洲成色www.777999| 高清中文字幕mv的电影| 美女精品一区| 妞干网在线免费视频| 久久精品国语| 黄色av免费在线播放| 日本va欧美va欧美va精品| 在线观看亚洲色图| 精品亚洲欧美一区| 欧美日韩一区二区区| 盗摄精品av一区二区三区| 尤物网站在线观看| 久久综合九色综合欧美就去吻| 欧美精品黑人猛交高潮| 91蜜桃婷婷狠狠久久综合9色| aa片在线观看视频在线播放| 欧美国产视频在线| 中日韩一级黄色片| 夜夜嗨av一区二区三区网页| 国产精品2020| 色哟哟国产精品| 一级黄色录像大片| 日韩欧美亚洲一区二区| 少妇高潮一区二区三区99小说 | 国产欧美日韩丝袜精品一区| 日韩欧乱色一区二区三区在线| 亚洲伊人久久综合| 欧美日韩一区二区三区四区不卡 | 中国黄色a级片| 欧美国产激情二区三区| 日韩在线不卡av| 亚洲成人在线网站| 亚洲欧美偷拍视频| 欧美精品日韩精品| 日本免费网站在线观看| 国产亚洲激情在线| 在线heyzo| 国产高清在线不卡| 亚洲一二三区视频| 日韩精品国内| 欧美午夜影院| 国产男女无遮挡| 极品尤物av久久免费看| 天天躁日日躁狠狠躁av| 亚洲国产精品精华液2区45| 久草视频手机在线观看| 91久久精品网| 午夜精品在线播放| 国产午夜精品一区理论片飘花 | 欧美少妇另类| 欧美高清无遮挡| 国产精品66| 欧美福利一区二区三区| 欧美在线网站| 在线免费观看av的网站| 成人免费看黄yyy456| 国产成人在线网址| 色诱视频网站一区| 黄片毛片在线看| 中文字幕精品一区久久久久| 9999在线视频| 亚洲jizzjizz日本少妇| 精品久久美女| 能在线观看的av| 国产不卡视频在线播放| 妖精视频在线观看免费| 欧美视频中文在线看| 成人黄色免费视频| 久久精品国产一区二区电影| 日本精品另类| 欧美三级网色| 亚洲影院一区| 国产人妻黑人一区二区三区| 亚洲视频免费观看| 91午夜交换视频| 中文字幕免费国产精品| 黑人巨大精品| 久久福利电影| 亚洲精品影视| 亚洲香蕉中文网| 一区二区高清视频在线观看| 国产一区二区三区中文字幕| 在线看福利67194| 影视一区二区三区| 欧美一区二区三区精美影视| 国产一区二区三区久久久久久久久| 又色又爽又黄18网站| 综合亚洲深深色噜噜狠狠网站| 中文字幕激情视频| 在线播放日韩专区| 99精品国自产在线| 亚洲精品成人久久久998| 日韩电影在线一区二区三区| 中文字幕在线1| 色噜噜狠狠一区二区三区果冻| 精品美女视频在线观看免费软件| 91av视频导航| 女人av一区| 国产av人人夜夜澡人人爽| 日本一区二区在线不卡| 中文人妻熟女乱又乱精品| 中文字幕国产亚洲| www.91精品| 欧美做受777cos| 成人一区二区视频| 国产精品老女人| 国产丝袜视频一区| 电影亚洲精品噜噜在线观看| 日韩精品另类天天更新| 青青青伊人色综合久久| 情侣偷拍对白清晰饥渴难耐| 91精品国产综合久久精品app| 搞黄网站在线观看| 国产精品午夜av在线| 久久久久网站| 综合 欧美 亚洲日本| 日韩一区二区三区视频| 国精产品一区一区三区mba下载| 国产在线一区二区三区欧美 | 一区二区三区四区五区视频在线观看| 在线观看黄色国产| 美女av一区二区三区| 国产精品毛片av| 成人在线免费播放视频| 日韩美女久久久| 香蕉国产在线视频| 国产欧美韩国高清| 午夜精品久久久久99热蜜桃导演| 午夜福利三级理论电影| 色婷婷综合激情| 国产剧情在线| 精品伦精品一区二区三区视频| 日韩高清国产一区在线| 中文字幕亚洲欧美日韩| 日韩成人在线网站| 欧洲亚洲精品久久久久| 成人性免费视频| 欧美高清在线视频| 亚洲国产av一区二区| 国产成人精品久久二区二区| 欧美日本不卡高清| 韩国三级hd中文字幕| 日韩欧美高清dvd碟片| 欧美黑人粗大| 一本大道东京热无码aⅴ| 久久久久久影视| 亚洲精品久久久久久动漫器材一区 | 日韩av图片| 成人黄色大片在线观看| 一级黄色大毛片|