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

深度解析Spring AI:請求與響應機制的核心邏輯

開發 前端
在這次探討中,我們聚焦于Spring AI如何有效地發起請求并將響應信息傳遞給用戶。這一過程不僅是開發者與AI交互的橋梁,更是優化用戶體驗的關鍵。通過明確的請求結構和響應機制,Spring AI能夠靈活地處理各種用戶輸入,并根據上下文調整回答策略。

我們在前面的兩個章節中基本上對Spring Boot 3版本的新變化進行了全面的回顧,以確保在接下來研究Spring AI時能夠避免任何潛在的問題。今天,我們終于可以直接進入主題:Spring AI是如何發起請求并將信息返回給用戶的。

在接下來的內容中,我們將專注于這一過程,而流式回答和函數回調的相關內容我們可以在下次的講解中詳細探討。

開始解析

首先,對于還沒有項目的同學,請務必安裝所需的POM依賴項。請注意,JDK的版本要求為17。因此,你可以在IDEA中輕松下載和配置這個版本。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.3.1</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>
    <url/>
    <licenses>
        <license/>
    </licenses>
    <developers>
        <developer/>
    </developers>
    <scm>
        <connection/>
        <developerConnection/>
        <tag/>
        <url/>
    </scm>
    <properties>
        <java.version>17</java.version>
<!--        <spring-ai.version>1.1.0</spring-ai.version>-->
        <spring-ai.version>1.0.0-M2</spring-ai.version>
    </properties>
    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
            <version>4.1.0</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
<!--                <groupId>group.springframework.ai</groupId>-->
                <groupId>org.springframework.ai</groupId>
                <artifactId>spring-ai-bom</artifactId>
                <version>${spring-ai.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.graalvm.buildtools</groupId>
                <artifactId>native-maven-plugin</artifactId>
                <configuration>
                    <!-- imageName用于設置生成的二進制文件名稱 -->
                    <imageName>${project.artifactId}</imageName>
                    <!-- mainClass用于指定main方法類路徑 -->
                    <mainClass>com.example.demo.DemoApplication</mainClass>
                    <buildArgs>
                        --no-fallback
                    </buildArgs>
                </configuration>
                <executions>
                    <execution>
                        <id>build-native</id>
                        <goals>
                            <goal>compile-no-fork</goal>
                        </goals>
                        <phase>package</phase>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>

</project>

基本用法在之前的講解中已經覆蓋過,因此這里就不再詳細說明。為了更好地理解這一概念,我們將通過兩個具體的例子來進行演示。

第一個例子將展示阻塞回答的實現,而第二個例子則會涉及帶有上下文信息記憶的回答。這兩種方式將幫助我們更深入地了解如何在實際應用中靈活運用這些技術。

基本用法

這里將提供一個阻塞回答的用法示例,以便更好地理解其應用場景和具體實現方式。

@PostMapping("/ai")
ChatDataPO generationByText(@RequestParam("userInput")  String userInput) {
    String content = this.myChatClientWithSystem.prompt()
                .user(userInput)
                .call()
                .content();
    log.info("content: {}", content);
    ChatDataPO chatDataPO = ChatDataPO.builder().code("text").data(ChildData.builder().text(content).build()).build();;
    return chatDataPO;
}

在這個示例中,我們將展示如何實現一個等待 AI 完成回答的機制,并將結果直接返回給接口調用端。這一過程實際上非常簡單,您只需將問題傳遞給 user 參數即可。接下來,我們將進行源碼解析。

為了節省時間,我們不會詳細逐行分析中間過程的代碼,因為這可能會顯得冗長而復雜。相反,我們將直接聚焦于關鍵源碼,以便更高效地理解其核心邏輯和實現細節。

源碼解析——構建請求

我們現在直接進入 content 方法進行深入分析。在前面的步驟中,所有方法的參數調用主要是為了構建一個對象,為后續的操作做準備。而真正的核心調用邏輯則集中在 content 方法內部。

private ChatResponse doGetChatResponse(DefaultChatClientRequestSpec inputRequest, String formatParam) {

            Map<String, Object> context = new ConcurrentHashMap<>();
            context.putAll(inputRequest.getAdvisorParams());
            DefaultChatClientRequestSpec advisedRequest = DefaultChatClientRequestSpec.adviseOnRequest(inputRequest,
                    context);

            var processedUserText = StringUtils.hasText(formatParam)
                    ? advisedRequest.getUserText() + System.lineSeparator() + "{spring_ai_soc_format}"
                    : advisedRequest.getUserText();

            Map<String, Object> userParams = new HashMap<>(advisedRequest.getUserParams());
            if (StringUtils.hasText(formatParam)) {
                userParams.put("spring_ai_soc_format", formatParam);
            }

            var messages = new ArrayList<Message>(advisedRequest.getMessages());
            var textsAreValid = (StringUtils.hasText(processedUserText)
                    || StringUtils.hasText(advisedRequest.getSystemText()));
            if (textsAreValid) {
                if (StringUtils.hasText(advisedRequest.getSystemText())
                        || !advisedRequest.getSystemParams().isEmpty()) {
                    var systemMessage = new SystemMessage(
                            new PromptTemplate(advisedRequest.getSystemText(), advisedRequest.getSystemParams())
                                .render());
                    messages.add(systemMessage);
                }
                UserMessage userMessage = null;
                if (!CollectionUtils.isEmpty(userParams)) {
                    userMessage = new UserMessage(new PromptTemplate(processedUserText, userParams).render(),
                            advisedRequest.getMedia());
                }
                else {
                    userMessage = new UserMessage(processedUserText, advisedRequest.getMedia());
                }
                messages.add(userMessage);
            }

            if (advisedRequest.getChatOptions() instanceof FunctionCallingOptions functionCallingOptions) {
                if (!advisedRequest.getFunctionNames().isEmpty()) {
                    functionCallingOptions.setFunctions(new HashSet<>(advisedRequest.getFunctionNames()));
                }
                if (!advisedRequest.getFunctionCallbacks().isEmpty()) {
                    functionCallingOptions.setFunctionCallbacks(advisedRequest.getFunctionCallbacks());
                }
            }
            var prompt = new Prompt(messages, advisedRequest.getChatOptions());
            var chatResponse = this.chatModel.call(prompt);

            ChatResponse advisedResponse = chatResponse;
            // apply the advisors on response
            if (!CollectionUtils.isEmpty(inputRequest.getAdvisors())) {
                var currentAdvisors = new ArrayList<>(inputRequest.getAdvisors());
                for (RequestResponseAdvisor advisor : currentAdvisors) {
                    advisedResponse = advisor.adviseResponse(advisedResponse, context);
                }
            }

            return advisedResponse;
        }

這段代碼沒有任何注釋,確實令人感到意外,充分說明了Spring代碼的設計初衷——更多是為開發者所用,而非為人類閱讀。其核心思想是,能夠有效使用就足夠了。盡管這段代碼顯得簡潔明了,但其重要性不容忽視。所有的實現都非常精煉,沒有冗余的代碼,因此我決定不進行刪減,而是將其完整呈現出來。

為了幫助大家更好地理解其中的邏輯和結構,我將使用偽代碼來進行講解。

初始化上下文:創建一個空的上下文。

請求調整:請求調整的邏輯是基于上下文對輸入請求進行動態處理。首先,我們需要判斷請求對象是否已經被 advisor 包裝。如果需要那么我們將返回一個經過 advisor 包裝后的請求對象。

下面是相關的源碼實現,展示了這一邏輯的具體細節:

public static DefaultChatClientRequestSpec adviseOnRequest(DefaultChatClientRequestSpec inputRequest,
                Map<String, Object> context) {

//....此處省略一堆代碼
        var currentAdvisors = new ArrayList<>(inputRequest.advisors);
                for (RequestResponseAdvisor advisor : currentAdvisors) {
                    adviseRequest = advisor.adviseRequest(adviseRequest, context);
                }
                advisedRequest = new DefaultChatClientRequestSpec(adviseRequest.chatModel(), adviseRequest.userText(),
                        adviseRequest.userParams(), adviseRequest.systemText(), adviseRequest.systemParams(),
                        adviseRequest.functionCallbacks(), adviseRequest.messages(), adviseRequest.functionNames(),
                        adviseRequest.media(), adviseRequest.chatOptions(), adviseRequest.advisors(),
                        adviseRequest.advisorParams(), inputRequest.getObservationRegistry(),
                        inputRequest.getCustomObservationConvention());
            }

            return advisedRequest;
        }

在這里,我想詳細講解一下 advisor.adviseRequest(adviseRequest, context) 這一方法的功能和重要性。由于我們已經配置了增強類,比如引入了一個聊天記憶功能,該方法的作用就顯得尤為關鍵。具體來說,它負責對傳入的請求進行增強處理,以滿足特定的業務需求。

值得注意的是,這個增強請求的方法是與增強響應方法相對應的,它們通常成對出現。接下來,深入查看 adviseRequest 方法的具體實現:

String content = this.myChatClientWithSystem.prompt()
                .advisors(new MessageChatMemoryAdvisor(chatMemory))
                .user(userInput)
                .call()
                .content();

我們配置了 MessageChatMemoryAdvisor 類,其核心方法的具體實現是,在接收到相應的信息后,將該信息存儲到一個聊天記憶中。這樣一來,下一次處理請求時,就可以直接從聊天記憶中提取相關內容。

public AdvisedRequest adviseRequest(AdvisedRequest request, Map<String, Object> context) {

    //此處省略一堆代碼
    // 4. Add the new user input to the conversation memory.
    UserMessage userMessage = new UserMessage(request.userText(), request.media());
    this.getChatMemoryStore().add(this.doGetConversationId(context), userMessage);

    return advisedRequest;
}

處理用戶文本、構建用戶參數:需要依據 formatParam 方法來對用戶的輸入進行處理。具體而言,這個步驟不僅涉及到對用戶文本的格式化,還需要更新相應的用戶參數。

接下來,我們將展示具體的實現示例,以便更清晰地理解這一過程的操作細節:

.user(u -> u.text("""
                Generate the filmography for a random actor.
                {format}
              """)
            .param("format", converter.getFormat()))

上面的代碼段會將 {format} 替換為實際的格式化信息。除了用戶提供的參數外,系統信息中同樣包含了一些需要解析的參數,這些參數也必須在處理過程中正確地傳入。

構建消息列表:根據系統文本和用戶文本的有效性,構建消息的過程將兩者進行整合。我們可以將所有有效的消息添加到一個 List 集合中,以便于后續處理。此外,系統還會創建一個信息對象,用于保存這些消息的相關信息,以確保在需要時可以方便地訪問和管理它們。

是否有函數回調:如果有,則設置一下具體的函數。(下一章節細講)

生成聊天提示:創建一個提示new Prompt()對象并調用聊天模型api獲取返回信息。

返回增強:如果當前請求對象配置了 advisor,那么將會調用相應的增強方法。此外,系統會自動將對應的問答內容存儲到信息列表中,因此相應的信息也需要被一并記錄下來。

public ChatResponse adviseResponse(ChatResponse chatResponse, Map<String, Object> context) {

    List<Message> assistantMessages = chatResponse.getResults().stream().map(g -> (Message) g.getOutput()).toList();

    this.getChatMemoryStore().add(this.doGetConversationId(context), assistantMessages);

    return chatResponse;
}

返回結果:返回最終的聊天響應。

源碼解析——請求OpenAI

接下來,我們將詳細探討如何通過請求對象來調用 OpenAI 接口的具體過程。為此,我們將以 OpenAI 的源碼為基礎進行分析。如果您使用的是其他 AI 產品,那么在這一環節的流程將會有所不同,系統會根據具體的產品進行相應的跳轉。如圖所示:

圖片圖片

我們將對 OpenAI 的請求調用過程進行全面的解析,以深入理解其背后的機制和實現細節:

public ChatResponse call(Prompt prompt) {

    ChatCompletionRequest request = createRequest(prompt, false);

    ChatModelObservationContext observationContext = ChatModelObservationContext.builder()
        .prompt(prompt)
        .provider(OpenAiApiConstants.PROVIDER_NAME)
        .requestOptions(buildRequestOptions(request))
        .build();

    ChatResponse response = ChatModelObservationDocumentation.CHAT_MODEL_OPERATION
        .observation(this.observationConvention, DEFAULT_OBSERVATION_CONVENTION, () -> observationContext,
                this.observationRegistry)
        .observe(() -> {

            ResponseEntity<ChatCompletion> completionEntity = this.retryTemplate
                .execute(ctx -> this.openAiApi.chatCompletionEntity(request, getAdditionalHttpHeaders(prompt)));

            var chatCompletion = completionEntity.getBody();

            if (chatCompletion == null) {
                logger.warn("No chat completion returned for prompt: {}", prompt);
                return new ChatResponse(List.of());
            }

            List<Choice> choices = chatCompletion.choices();
            if (choices == null) {
                logger.warn("No choices returned for prompt: {}", prompt);
                return new ChatResponse(List.of());
            }

            List<Generation> generations = choices.stream().map(choice -> {
        // @formatter:off
                Map<String, Object> metadata = Map.of(
                        "id", chatCompletion.id() != null ? chatCompletion.id() : "",
                        "role", choice.message().role() != null ? choice.message().role().name() : "",
                        "index", choice.index(),
                        "finishReason", choice.finishReason() != null ? choice.finishReason().name() : "",
                        "refusal", StringUtils.hasText(choice.message().refusal()) ? choice.message().refusal() : "");
                // @formatter:on
                return buildGeneration(choice, metadata);
            }).toList();

            // Non function calling.
            RateLimit rateLimit = OpenAiResponseHeaderExtractor.extractAiResponseHeaders(completionEntity);

            ChatResponse chatResponse = new ChatResponse(generations, from(completionEntity.getBody(), rateLimit));

            observationContext.setResponse(chatResponse);

            return chatResponse;

        });

    if (response != null && isToolCall(response, Set.of(OpenAiApi.ChatCompletionFinishReason.TOOL_CALLS.name(),
            OpenAiApi.ChatCompletionFinishReason.STOP.name()))) {
        var toolCallConversation = handleToolCalls(prompt, response);
        // Recursively call the call method with the tool call message
        // conversation that contains the call responses.
        return this.call(new Prompt(toolCallConversation, prompt.getOptions()));
    }

    return response;
}

雖然這些內容都很有價值,刪減并不是一個好的選擇,但由于缺乏注釋,我們可能需要仔細分析。讓我們一起來看看這些信息,逐步理清其中的邏輯和要點。

createRequest 函數的主要作用是構建在實際調用 API 時所需的請求對象。由于不同服務提供商的接口設計各有特點,因此我們需要根據具體的 API 規范自行實現這一過程。例如,在調用 OpenAI 的接口時,我們需要構建特定的參數結構,這一過程大家應該已經非常熟悉。如下圖所示,我們可以看到構建請求時所需的各項參數及其格式。

圖片圖片

ChatModelObservationContext 主要用于配置與請求相關的其他限制和要求。這包括多個關鍵參數,例如本次請求的最大 token 數量限制、所使用的 OpenAI 問答模型的具體類型、以及請求的頻率限制等。如代碼所示:

private ChatOptions buildRequestOptions(OpenAiApi.ChatCompletionRequest request) {
    return ChatOptionsBuilder.builder()
        .withModel(request.model())
        .withFrequencyPenalty(request.frequencyPenalty())
        .withMaxTokens(request.maxTokens())
        .withPresencePenalty(request.presencePenalty())
        .withStopSequences(request.stop())
        .withTemperature(request.temperature())
        .withTopP(request.topP())
        .build();
}

剩下的 ChatResponse 大方法負責實際執行 API 請求并處理響應。在這一過程中,有幾個關鍵細節值得注意。

請求對象使用的是 retryTemplate,這是一個具有重試機制的請求 API 工具。它的設計旨在增強請求的可靠性,特別是在面對暫時性故障或網絡問題時,能夠自動進行重試,從而提高成功率。更為靈活的是,retryTemplate 允許用戶進行配置,以滿足不同應用場景的需求。

用戶可以根據實際需要調整重試次數、重試間隔時間以及其他相關參數,所有這些配置都可以通過 spring.ai.retry 這一前綴進行自定義設置。具體大家可以看這個類:

@AutoConfiguration
@ConditionalOnClass(RetryTemplate.class)
@EnableConfigurationProperties({ SpringAiRetryProperties.class })
public class SpringAiRetryAutoConfiguration {
  //此處省略一堆代碼
}

接著,如果 OpenAI 的接口正常返回響應,那么系統將開始格式化回答。在這一過程中,涉及到多個關鍵字段,這些字段對于程序員們而言應該都是相當熟悉的,尤其是那些有過接口對接經驗的開發者。

Map<String, Object> metadata = Map.of(
                            "id", chatCompletion.id() != null ? chatCompletion.id() : "",
                            "role", choice.message().role() != null ? choice.message().role().name() : "",
                            "index", choice.index(),
                            "finishReason", choice.finishReason() != null ? choice.finishReason().name() : "",
                            "refusal", StringUtils.hasText(choice.message().refusal()) ? choice.message().refusal() : "");

接著,在接收到所有返回參數后,系統將這些參數整合并返回給 response 對象。然而,在這一階段,我們又進行了一個重要的判斷,檢查是否為 isToolCall。這個判斷實際上涉及到函數回調的機制,這一部分的實現邏輯非常關鍵,但今天我們就不深入探討這個細節,留待下次再進行講解。

至此,整個調用流程已經圓滿完成。我們的接口順利而愉快地將處理后的信息返回給了調用端,確保了用戶請求的高效響應。

總結

在這次探討中,我們聚焦于Spring AI如何有效地發起請求并將響應信息傳遞給用戶。這一過程不僅是開發者與AI交互的橋梁,更是優化用戶體驗的關鍵。通過明確的請求結構和響應機制,Spring AI能夠靈活地處理各種用戶輸入,并根據上下文調整回答策略。

然后,我們深入分析了這一機制的核心,關注具體實現與業務邏輯。在此過程中,我們通過實例演示阻塞回答與帶上下文記憶的回答如何在實際應用中發揮作用。這樣的實操不僅能幫助我們更好地理解Spring AI的工作原理,也為將來深入探討流式回答和函數回調埋下了伏筆。

理解這一過程的背后邏輯,將為我們在日常開發中應用Spring AI提供有力支持。隨著技術的不斷進步,開發者們面臨的挑戰也在日益增加,但通過這種清晰的請求與響應架構,我們可以更從容地應對復雜性,實現更加智能化的解決方案。

責任編輯:武曉燕 來源: 靈墨AI探索室
相關推薦

2025-02-03 16:58:39

2023-11-08 09:49:19

Java實踐

2025-04-02 03:55:00

MCPAI智能體

2024-12-20 16:46:22

Spring三級緩存

2025-08-18 07:39:08

2024-08-01 17:14:53

2024-11-05 13:16:11

2011-08-02 18:07:03

iPhone 內省 Cocoa

2012-07-03 10:57:54

Hadoop核心機制

2025-10-15 10:15:01

2025-10-16 07:34:42

Spring工具類??API??

2010-11-25 09:37:14

MySQL查詢緩存機制

2025-03-26 11:30:40

2025-09-15 06:25:00

2024-07-11 08:17:00

2020-05-21 13:25:43

Spring組件架構

2016-10-09 14:41:40

Swift開發ARC

2011-06-22 16:50:09

Qt 進程 通信機制

2025-10-30 02:13:00

2024-04-16 14:57:51

人工智能深度學習
點贊
收藏

51CTO技術棧公眾號

久久www人成免费看片中文| 日本视频网站在线观看| 亚洲综合资源| 一区二区三区国产精品| 国产一区二区在线观看免费播放| 天天综合网久久综合网| 久久要要av| 精品成人佐山爱一区二区| 免费高清在线观看免费| 麻豆电影在线播放| 国产不卡一区视频| 国产精品电影网| 福利所第一导航| 国产欧美高清视频在线| 7777精品伊人久久久大香线蕉经典版下载 | 国产国产精品人在线视| 欧美日韩国产精品一区二区三区| 自拍偷拍精品| 欧美成人一区二区三区片免费| 欧美一区二区三区爽大粗免费| 欧美黄色激情| 久久久久久久久久美女| 99国产超薄丝袜足j在线观看 | 亚洲在线免费| 久久久精品2019中文字幕神马| 亚洲欧美日本一区| 国产一区2区在线观看| 91国产成人在线| 日本a在线免费观看| 国产在线69| 亚洲国产成人一区二区三区| 精品国产乱码一区二区三区四区| 国产精品一区二区三区在线免费观看| 美女久久一区| 91av国产在线| 亚洲精品午夜久久久久久久| 综合久久十次| 久久av在线看| 精品一区二区在线观看视频| 激情婷婷综合| 亚洲视频在线看| 久久精品国产亚洲av麻豆| 高潮久久久久久久久久久久久久 | 全色精品综合影院| 成人av电影在线观看| 91精品久久香蕉国产线看观看| 18国产免费视频| 日韩av一区二区三区四区| 51视频国产精品一区二区| 日本最新中文字幕| 亚洲黄色免费| 91国产中文字幕| 久久久久久久极品| 亚洲欧美视频| 国产成人jvid在线播放| 国产成人在线免费视频| 日韩午夜av在线| 97精品久久久| 91video| 久久久久国内| 国产精品视频色| 天堂网免费视频| 日本aⅴ免费视频一区二区三区| 欧美专区第一页| 亚洲中文字幕无码爆乳av| 日产国产高清一区二区三区| 国产日韩欧美视频在线| 国产男女猛烈无遮挡| 国产精品一级在线| 国产成人精品日本亚洲11| 欧美一级性视频| xnxx国产精品| 亚洲开发第一视频在线播放| 国产黄a三级三级三级av在线看 | 91日韩在线专区| 欧美一区二区三区精美影视| 国产黄在线看| 亚洲欧美国产三级| 欧美黑人经典片免费观看| 国偷自产一区二区免费视频| 欧美亚洲动漫另类| 国产美女视频免费看| 97视频一区| 亚洲欧美日韩图片| 99自拍偷拍视频| 午夜久久福利| 日本一本a高清免费不卡| 亚洲天堂2021av| 成人中文字幕在线| 日本电影一区二区三区| 国产原厂视频在线观看| 精品人伦一区二区三区蜜桃网站| 亚洲色图38p| 一区二区三区亚洲变态调教大结局 | 欧洲亚洲精品在线| 久久久久亚洲av片无码v| 另类尿喷潮videofree| 最新国产精品亚洲| 久久视频免费在线观看| 蜜臀av性久久久久蜜臀aⅴ流畅| 91免费看网站| 黄色网址在线播放| 一区二区三区欧美日| 老头吃奶性行交视频| 日韩在线网址| 曰本色欧美视频在线| 国产一级理论片| 日本美女一区二区三区| 国产高清在线一区二区| 日本在线www| 欧美午夜精品久久久久久久| 99热这里只有精品2| 免费欧美一区| 久久久久久亚洲| 国产一区二区三区三州| 久久午夜羞羞影院免费观看| 青青草视频国产| 国产精品99| 亚洲欧美综合v| 亚洲国产精一区二区三区性色| 麻豆极品一区二区三区| 欧美综合77777色婷婷| 好吊日av在线| 欧美一区二区三区播放老司机| 永久免费av无码网站性色av| 国产欧美日韩一级| 国产激情美女久久久久久吹潮| 又爽又大又黄a级毛片在线视频| 亚洲图片欧美综合| 免费高清视频在线观看| 日韩毛片视频| 国产精品成人播放| 色视频在线观看福利| 亚洲二区视频在线| 在线播放av网址| 亚洲精品网址| 成人午夜一级二级三级| 日本在线视频站| 欧美三日本三级三级在线播放| 国产夫妻性爱视频| 一区二区日本视频| 国产精品乱子乱xxxx| 欧美人与性动交α欧美精品济南到| 欧美精品日韩精品| 美国一级片在线观看| 麻豆一区二区三区| 亚洲欧美日韩另类精品一区二区三区 | 亚洲一区二区三区| 91欧美激情另类亚洲| 久做在线视频免费观看| 91精品久久久久久久91蜜桃| 91免费在线看片| 久久er99精品| 精品国产无码在线| 视频在线亚洲| 欧美—级a级欧美特级ar全黄| www.com在线观看| 亚洲亚洲人成综合网络| 强迫凌虐淫辱の牝奴在线观看| 日韩亚洲精品在线| 欧美精品一区在线发布| 欧美色网在线| 日韩在线欧美在线国产在线| 国产精品视频在线观看免费| 亚洲欧美国产毛片在线| 中国黄色片视频| 亚洲专区一区| 日韩成人av电影在线| 久久91超碰青草在哪里看| 久久精品精品电影网| 国产三级精品在线观看| 亚洲成人av一区| 妺妺窝人体色WWW精品| 久久99精品国产麻豆婷婷| 欧美日韩午夜爽爽| 午夜精品福利影院| 国产精品视频999| 色呦呦在线播放| 亚洲男人天堂网| 97人妻人人澡人人爽人人精品| 国产精品萝li| 熟妇高潮一区二区| 日韩成人av影视| 国产911在线观看| 偷拍亚洲精品| 91亚洲永久免费精品| 免费电影视频在线看| 亚洲精品自拍偷拍| 国产精品久久久久久久久久久久久久久久久久 | 性生活免费在线观看| 在线欧美福利| 亚洲精品成人自拍| 久久国产精品色av免费看| 国产日韩中文在线| 爱搞国产精品| 久久综合电影一区| 九一在线视频| 日韩欧美国产不卡| 国产在线观看第一页| 亚洲午夜一区二区| 丁香六月激情综合| 99久久精品国产精品久久| 中文字幕66页| 久久一综合视频| 强开小嫩苞一区二区三区网站| 亚洲8888| 国产成人一区二区三区免费看| 成人四虎影院| 欧美资源在线观看| 天堂8中文在线| 中文字幕一区二区三区电影| 亚洲av片在线观看| 日韩精品一区二区三区中文不卡| 无码人妻久久一区二区三区| 亚洲高清久久久| 久久噜噜色综合一区二区| 久久日韩精品一区二区五区| 中文写幕一区二区三区免费观成熟| 日韩在线一二三区| 国产91在线视频观看| 欧美激情1区| 在线看成人av电影| 欧美日韩国产免费观看视频| 久久国产精品亚洲va麻豆| 欧美a级大片在线| 国产日韩综合一区二区性色av| 惠美惠精品网| 欧美一级免费视频| 55av亚洲| 午夜精品久久久久久久白皮肤| 菠萝蜜视频国产在线播放| 中文字幕日韩精品在线| 国产高清视频在线| 亚洲精品中文字幕av| 日韩a级作爱片一二三区免费观看| 欧美xxxxxxxx| 国内爆初菊对白视频| 日韩欧美的一区| 亚洲第一页视频| 日韩一区二区中文字幕| 国产毛片毛片毛片毛片| 欧美日本在线一区| 91女人18毛片水多国产| 欧美久久久久久蜜桃| 中文字幕人成人乱码亚洲电影| 在线视频亚洲一区| 中文字幕乱码无码人妻系列蜜桃| 色婷婷综合激情| 日韩xxx视频| 欧美日韩美少妇| 亚洲图片视频小说| 91精品中文字幕一区二区三区| 亚洲中文一区二区三区| 在线成人av影院| av高清一区二区| 日韩精品一区二区三区中文精品| 精品人妻一区二区三区含羞草 | 欧美日韩国产综合新一区 | 国产婷婷精品| ww国产内射精品后入国产| 久久久久久夜| www.夜夜爽| 国产成人免费在线观看| 俄罗斯黄色录像| www国产亚洲精品久久麻豆| 成人在线一级片| 亚洲欧洲精品成人久久奇米网| 少妇人妻丰满做爰xxx| 一区二区三区视频在线看| 日韩乱码在线观看| 91福利小视频| 国产乱码精品一区二区三区精东| 日韩一区二区三区免费看| 日韩中文字幕观看| 国产亚洲一区二区精品| 九七电影韩国女主播在线观看| 久久久久九九九九| 日韩电影大全网站| 成人免费视频在线观看超级碰| 日韩中文字幕无砖| 美女一区视频| 国产精品毛片一区二区在线看| 黄色三级中文字幕| 久热国产精品| 日本泡妞xxxx免费视频软件| 久久综合色8888| 亚洲综合久久av一区二区三区| 一区二区三区精品在线观看| 欧美精品一二三四区| 欧美精品久久一区二区三区| 国精产品一品二品国精品69xx| 一本色道久久综合亚洲精品小说| 亚洲丝袜精品| 国产精品久久久久久av下载红粉 | www.成年人视频| 日韩专区一卡二卡| 9.1在线观看免费| 国产精品欧美经典| 久久午夜免费视频| 91精品国产入口在线| 欧洲天堂在线观看| 欧美疯狂性受xxxxx另类| av久久网站| 久久av免费观看| 欧美精品一级| 日本特黄a级片| 久久这里只有精品首页| 欧美成欧美va| 欧美网站大全在线观看| 天堂在线观看av| 久久在线免费视频| 国产在线|日韩| 久久66热这里只有精品| 一区二区三区在线观看免费| 久久久国产欧美| 99精品久久99久久久久| 欧美人妻精品一区二区免费看| 欧美日韩在线观看一区二区 | 欧美日韩导航| 97在线免费视频观看| 激情综合网天天干| 成人在线手机视频| 欧美日韩亚洲一区二区| 国产精品羞羞答答在线| 中文字幕日韩欧美在线| 成人激情综合| 免费日韩电影在线观看| 日韩视频精品在线观看| www.四虎精品| 亚洲综合网站在线观看| 国产日韩欧美视频在线观看| 日韩在线免费观看视频| 黄页免费欧美| 亚洲国产精品久久久久婷婷老年| 久久午夜电影| 丰满少妇一区二区| 欧美视频第一页| 青梅竹马是消防员在线| 26uuu另类亚洲欧美日本一| 老司机在线精品视频| 日韩精品―中文字幕| 99久久久无码国产精品| 日韩熟女精品一区二区三区| 亚洲国产精品电影| caoporn视频在线观看| 国产一区二区不卡视频在线观看| 伊人成人网在线看| 变态另类丨国产精品| 欧美色欧美亚洲高清在线视频| 亚洲色欧美另类| 欧美在线视频一区二区| 神马电影久久| 亚洲国产高清av| 最新国产の精品合集bt伙计| 一级黄色a视频| 久久久精品亚洲| www.神马久久| 91视频 -- 69xx| 国产欧美中文在线| 91亚洲国产成人久久精品麻豆| 日韩有码片在线观看| 麻豆视频久久| 久久久久久久久久网| 91老师片黄在线观看| 中文字幕av资源| 久久夜色撩人精品| 精品少妇一区| mm1313亚洲国产精品无码试看| 国产精品久久三区| 亚洲欧美另类一区| 人人澡人人澡人人看欧美| 欧美1级片网站| 女同性αv亚洲女同志| 欧美性猛交xxxx乱大交| 成年人在线看| www日韩av| 视频在线在亚洲| 久草综合在线视频| 亚洲精品二三区| 久久亚洲精品中文字幕| 国产精品va在线观看无码| 91毛片在线观看| 国产精品一区二区免费视频| 97碰在线观看| 日韩理论电影| 182在线视频| 欧美日本韩国一区二区三区视频| 中文字幕中文字幕在线中高清免费版| 国语精品中文字幕| 看片网站欧美日韩| 日韩成人高清视频| 精品国产一区二区三区久久狼黑人 | 亚洲黄色av| 蜜桃av免费观看| 亚洲成人网在线观看| 国产精品99| 黄色免费视频大全| 一区二区在线免费观看| 国产一级免费在线观看| 国产精品麻豆免费版|