{"id":234,"date":"2026-01-10T07:45:08","date_gmt":"2026-01-10T07:45:08","guid":{"rendered":"https:\/\/hyc.eshachem.com\/program\/?p=234"},"modified":"2026-05-25T07:36:53","modified_gmt":"2026-05-25T07:36:53","slug":"%e4%bd%bf%e7%94%a8springboot%e8%88%87react%e4%b8%b2%e6%8e%a5gimini","status":"publish","type":"post","link":"https:\/\/hyc.eshachem.com\/program\/%e4%bd%bf%e7%94%a8springboot%e8%88%87react%e4%b8%b2%e6%8e%a5gimini\/","title":{"rendered":"\u4e32\u63a5Gemini"},"content":{"rendered":"\n<h1 class=\"wp-block-heading\" id=\"%e4%bd%bf%e7%94%a8server-action\">\u4f7f\u7528Server Action<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">\u5b89\u88dd\u6700\u65b0\u7684\u5b98\u65b9 Gemini SDK\uff1a<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">npm<\/span> <span class=\"hljs-selector-tag\">install<\/span> <span class=\"hljs-keyword\">@google<\/span>\/genai<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">\u5728\u5c08\u6848\u6839\u76ee\u9304\u7684 <code>.env<\/code> \u6a94\u6848\u4e2d\uff0c\u52a0\u5165 Gemini API Key\uff08\u81f3 Google AI Studio \u7533\u8acb\uff09\uff1a<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">GEMINI_API_KEY=your_actual_api_key_here<\/code><\/span><\/pre>\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">\u53ef\u4f7f\u7528\u7684\u6a21\u578b\u53ca\u63a5\u6cd5\u53ef\u53c3\u8003\u5b98\u65b9\u6587\u4ef6:<a href=\"https:\/\/ai.google.dev\/gemini-api\/docs\/models?hl=zh-tw\">https:\/\/ai.google.dev\/gemini-api\/docs\/models?hl=zh-tw<\/a><\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">server action<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-string\">\"use server\"<\/span>;\n\n<span class=\"hljs-keyword\">import<\/span> { GoogleGenAI } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@google\/genai\"<\/span>;\n\n<span class=\"hljs-comment\">\/\/ \u521d\u59cb\u5316 Google Gen AI \u7528\u6236\u7aef\uff08\u6703\u81ea\u52d5\u8b80\u53d6 process.env.GEMINI_API_KEY\uff09<\/span>\n<span class=\"hljs-keyword\">const<\/span> ai = <span class=\"hljs-keyword\">new<\/span> GoogleGenAI();\n\ninterface ChatMessage {\n  <span class=\"hljs-attr\">role<\/span>: <span class=\"hljs-string\">\"user\"<\/span> | <span class=\"hljs-string\">\"model\"<\/span>;\n  text: string;\n}\n\n<span class=\"hljs-comment\">\/**\n * \u8655\u7406\u8207 Gemini \u5c0d\u8a71\u7684 Server Action\n * <span class=\"hljs-doctag\">@param <\/span>userInput \u4f7f\u7528\u8005\u6700\u65b0\u8f38\u5165\u7684\u6587\u5b57\n * <span class=\"hljs-doctag\">@param <\/span>history \u904e\u53bb\u7684\u5c0d\u8a71\u6b77\u53f2\u7d00\u9304\uff0c\u683c\u5f0f\u5fc5\u9808\u7b26\u5408 Gemini \u8981\u6c42\n *\/<\/span>\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">async<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">chatWithGemini<\/span>(<span class=\"hljs-params\">userInput: string, history: ChatMessage[] = []<\/span>) <\/span>{\n  <span class=\"hljs-keyword\">if<\/span> (!userInput.trim()) {\n    <span class=\"hljs-keyword\">return<\/span> { <span class=\"hljs-attr\">success<\/span>: <span class=\"hljs-literal\">false<\/span>, <span class=\"hljs-attr\">error<\/span>: <span class=\"hljs-string\">\"\u8f38\u5165\u5167\u5bb9\u4e0d\u80fd\u70ba\u7a7a\"<\/span> };\n  }\n\n  <span class=\"hljs-keyword\">try<\/span> {\n    <span class=\"hljs-comment\">\/\/ 1. \u5c07\u524d\u7aef\u50b3\u5165\u7684\u6b77\u53f2\u7d00\u9304\u8f49\u63db\u70ba @google\/genai SDK \u6240\u9700\u7684 contents \u683c\u5f0f<\/span>\n    <span class=\"hljs-keyword\">const<\/span> contents = history.map(<span class=\"hljs-function\">(<span class=\"hljs-params\">msg<\/span>) =&gt;<\/span> ({\n      <span class=\"hljs-attr\">role<\/span>: msg.role,\n      <span class=\"hljs-attr\">parts<\/span>: [{ <span class=\"hljs-attr\">text<\/span>: msg.text }],\n    }));\n\n    <span class=\"hljs-comment\">\/\/ 2. \u5c07\u7576\u524d\u4f7f\u7528\u8005\u7684\u65b0\u8a0a\u606f\u52a0\u5165\u5c0d\u8a71\u5167\u5bb9\u4e2d<\/span>\n    contents.push({\n      <span class=\"hljs-attr\">role<\/span>: <span class=\"hljs-string\">\"user\"<\/span>,\n      <span class=\"hljs-attr\">parts<\/span>: [{ <span class=\"hljs-attr\">text<\/span>: userInput }],\n    });\n\n    <span class=\"hljs-comment\">\/\/ 3. \u547c\u53eb Gemini API \u7522\u751f\u56de\u61c9<\/span>\n    <span class=\"hljs-keyword\">const<\/span> response = <span class=\"hljs-keyword\">await<\/span> ai.models.generateContent({\n      <span class=\"hljs-attr\">model<\/span>: <span class=\"hljs-string\">\"gemini-2.5-flash\"<\/span>,\n      <span class=\"hljs-attr\">contents<\/span>: contents,\n      <span class=\"hljs-comment\">\/\/ \u9019\u88e1\u53ef\u4ee5\u9078\u64c7\u6027\u52a0\u5165 systemInstruction \u4f86\u898f\u7bc4 AI \u7684\u89d2\u8272\u8a2d\u5b9a<\/span>\n      <span class=\"hljs-attr\">config<\/span>: {\n        <span class=\"hljs-attr\">systemInstruction<\/span>: <span class=\"hljs-string\">\"\u4f60\u662f\u4e00\u4f4d\u5c08\u696d\u4e14\u89aa\u5207\u7684\u91ab\u7642\u7cfb\u7d71\u52a9\u7406\uff0c\u8acb\u7528\u7e41\u9ad4\u4e2d\u6587\u56de\u7b54\u3002\"<\/span>,\n        <span class=\"hljs-attr\">temperature<\/span>: <span class=\"hljs-number\">0.7<\/span>,\n      },\n    });\n\n    <span class=\"hljs-comment\">\/\/ 4. \u53d6\u5f97 AI \u56de\u50b3\u7684\u7d14\u6587\u5b57<\/span>\n    <span class=\"hljs-keyword\">const<\/span> replyText = response.text || <span class=\"hljs-string\">\"\u62b1\u6b49\uff0c\u6211\u66ab\u6642\u7121\u6cd5\u56de\u61c9\u3002\"<\/span>;\n\n    <span class=\"hljs-keyword\">return<\/span> {\n      <span class=\"hljs-attr\">success<\/span>: <span class=\"hljs-literal\">true<\/span>,\n      <span class=\"hljs-attr\">reply<\/span>: replyText,\n    };\n  } <span class=\"hljs-keyword\">catch<\/span> (error) {\n    <span class=\"hljs-built_in\">console<\/span>.error(<span class=\"hljs-string\">\"Gemini API \u547c\u53eb\u5931\u6557:\"<\/span>, error);\n    <span class=\"hljs-keyword\">return<\/span> {\n      <span class=\"hljs-attr\">success<\/span>: <span class=\"hljs-literal\">false<\/span>,\n      <span class=\"hljs-attr\">error<\/span>: error <span class=\"hljs-keyword\">instanceof<\/span> <span class=\"hljs-built_in\">Error<\/span> ? error.message : <span class=\"hljs-string\">\"\u9023\u7dda\u81f3 AI \u4f3a\u670d\u5668\u6642\u767c\u751f\u672a\u77e5\u932f\u8aa4\"<\/span>,\n    };\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h1 class=\"wp-block-heading\" id=\"%e4%bd%bf%e7%94%a8springboot%e8%88%87react\">\u4f7f\u7528SpringBoot\u8207React<\/h1>\n\n\n\n<h1 class=\"wp-block-heading\" id=\"google-ai-api\">Google AI API<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">\u5148\u53bb\u9019\u500b\u7db2\u5740\u53d6\u5f97API !<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/aistudio.google.com\">https:\/\/aistudio.google.com<\/a><\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"gemini-%e5%ae%98%e6%96%b9%e7%b5%90%e6%a7%8b\">Gemini \u5b98\u65b9\u7d50\u69cb<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Gemini API request \u683c\u5f0f\uff1a<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"JSON \/ JSON with Comments\" data-shcb-language-slug=\"json\"><span><code class=\"hljs language-json\">{\n  <span class=\"hljs-attr\">\"contents\"<\/span>: [\n    {\n      <span class=\"hljs-attr\">\"parts\"<\/span>: [\n        { <span class=\"hljs-attr\">\"text\"<\/span>: <span class=\"hljs-string\">\"Hello\"<\/span> }\n      ]\n    }\n  ]\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JSON \/ JSON with Comments<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">json<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">\u6240\u4ee5\u6211\u5011\u8981\u5c07\u4f7f\u7528\u8005\u7684\u8f38\u5165\uff0c\u52a0\u4e0aJSON Body\u548cHeader\u5305\u6210\u5b8c\u6574\u7684REST API\u683c\u5f0f\u50b3\u7d66gimini\uff0c\u4f5c\u70ba HTTP POST \u7684\u8acb\u6c42\u7269\u4ef6<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<h1 class=\"wp-block-heading\" id=\"springboot\">SpringBoot<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">\u5148\u5efa\u7acb\u4e00\u500b\u6a94\u6848\u5b58\u653eAPI\u91d1\u9470 <code>src\\main\\resources\\application.properties<\/code><\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">google.gemini.api.key=AIzaSyXXXXXXX...<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">\u63a5\u8457\u5efa\u7acb\u4e00\u500bservice\uff0c\u4e3b\u8981\u7684\u64cd\u4f5c\u90fd\u5728service\u4e0a !<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u64cd\u4f5c\u6d41\u7a0b:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u7a0b\u5f9e\u8a2d\u5b9a\u6a94\u8b80\u53d6 <strong>Gemini API Key<\/strong> (@Value)<\/li>\n\n\n\n<li>\u5c07\u4f7f\u7528\u8005\u8f38\u5165\u7684 <code>prompt<\/code> \u7d44\u6210 <strong>Gemini API \u8981\u6c42\u683c\u5f0f<\/strong><\/li>\n\n\n\n<li>\u900f\u904e <code>RestTemplate<\/code> \u547c\u53eb Google Gemini<\/li>\n\n\n\n<li>\u89e3\u6790\u56de\u50b3 JSON<\/li>\n\n\n\n<li>\u56de\u50b3 AI \u7522\u751f\u7684\u6587\u5b57\u7d50\u679c\uff08String\uff09<\/li>\n<\/ol>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">package demo.service;\n\n<span class=\"hljs-keyword\">import<\/span> org.springframework.beans.factory.annotation.Value;\n<span class=\"hljs-keyword\">import<\/span> org.springframework.stereotype.Service;\n<span class=\"hljs-keyword\">import<\/span> org.springframework.web.client.RestTemplate;\n<span class=\"hljs-keyword\">import<\/span> org.springframework.http.*;\n<span class=\"hljs-keyword\">import<\/span> java.util.*;\n\n@Service\npublic <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">AIService<\/span> <\/span>{\n\n    @Value(<span class=\"hljs-string\">\"${google.gemini.api.key}\"<\/span>)\n    private <span class=\"hljs-built_in\">String<\/span> apiKey;\n\n    private final <span class=\"hljs-built_in\">String<\/span> API_URL = <span class=\"hljs-string\">\"https:\/\/generativelanguage.googleapis.com\/v1beta\/models\/gemini-3-flash-preview:generateContent?key=\"<\/span>;\n\n    public <span class=\"hljs-built_in\">String<\/span> getAiResponse(<span class=\"hljs-built_in\">String<\/span> prompt) {\n        RestTemplate restTemplate = <span class=\"hljs-keyword\">new<\/span> RestTemplate();\n\n        <span class=\"hljs-comment\">\/\/ 1. \u8a2d\u5b9a Header<\/span>\n        HttpHeaders headers = <span class=\"hljs-keyword\">new<\/span> HttpHeaders();\n        headers.setContentType(MediaType.APPLICATION_JSON);\n\n        <span class=\"hljs-comment\">\/\/ 2. \u69cb\u5efa\u8acb\u6c42 Body<\/span>\n        <span class=\"hljs-built_in\">Map<\/span>&lt;<span class=\"hljs-built_in\">String<\/span>, <span class=\"hljs-built_in\">Object<\/span>&gt; textPart = <span class=\"hljs-built_in\">Map<\/span>.of(<span class=\"hljs-string\">\"text\"<\/span>, prompt);\n        <span class=\"hljs-built_in\">Map<\/span>&lt;<span class=\"hljs-built_in\">String<\/span>, <span class=\"hljs-built_in\">Object<\/span>&gt; parts = <span class=\"hljs-built_in\">Map<\/span>.of(<span class=\"hljs-string\">\"parts\"<\/span>, List.of(textPart));\n        <span class=\"hljs-built_in\">Map<\/span>&lt;<span class=\"hljs-built_in\">String<\/span>, <span class=\"hljs-built_in\">Object<\/span>&gt; contents = <span class=\"hljs-built_in\">Map<\/span>.of(<span class=\"hljs-string\">\"contents\"<\/span>, List.of(parts));\n\n        HttpEntity&lt;<span class=\"hljs-built_in\">Map<\/span>&lt;<span class=\"hljs-built_in\">String<\/span>, <span class=\"hljs-built_in\">Object<\/span>&gt;&gt; entity = <span class=\"hljs-keyword\">new<\/span> HttpEntity&lt;&gt;(contents, headers);\n\n        <span class=\"hljs-keyword\">try<\/span> {\n            <span class=\"hljs-comment\">\/\/ 3. \u767c\u9001\u8acb\u6c42<\/span>\n            ResponseEntity&lt;<span class=\"hljs-built_in\">Map<\/span>&gt; response = restTemplate.postForEntity(API_URL + apiKey, entity, <span class=\"hljs-built_in\">Map<\/span>.class); <span class=\"hljs-comment\">\/\/ \u547c\u53eb Gemini API<\/span>\n\n            <span class=\"hljs-comment\">\/\/ 4. \u89e3\u6790 Gemini \u7684 JSON (\u8def\u5f91: candidates[0].content.parts[0].text)<\/span>\n            <span class=\"hljs-built_in\">Map<\/span> body = response.getBody();\n            <span class=\"hljs-keyword\">if<\/span> (body != <span class=\"hljs-literal\">null<\/span> &amp;&amp; body.containsKey(<span class=\"hljs-string\">\"candidates\"<\/span>)) {\n                List candidates = (List) body.get(<span class=\"hljs-string\">\"candidates\"<\/span>);\n                <span class=\"hljs-built_in\">Map<\/span> firstCandidate = (<span class=\"hljs-built_in\">Map<\/span>) candidates.get(<span class=\"hljs-number\">0<\/span>);\n                <span class=\"hljs-built_in\">Map<\/span> content = (<span class=\"hljs-built_in\">Map<\/span>) firstCandidate.get(<span class=\"hljs-string\">\"content\"<\/span>);\n                List resParts = (List) content.get(<span class=\"hljs-string\">\"parts\"<\/span>);\n                <span class=\"hljs-built_in\">Map<\/span> firstPart = (<span class=\"hljs-built_in\">Map<\/span>) resParts.get(<span class=\"hljs-number\">0<\/span>);\n                <span class=\"hljs-keyword\">return<\/span> firstPart.get(<span class=\"hljs-string\">\"text\"<\/span>).toString();\n            }\n            <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-string\">\"AI \u56de\u50b3\u683c\u5f0f\u7570\u5e38\"<\/span>;\n        } <span class=\"hljs-keyword\">catch<\/span> (Exception e) {\n            e.printStackTrace();\n            <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-string\">\"AI \u670d\u52d9\u547c\u53eb\u5931\u6557: \"<\/span> + e.getMessage();\n        }\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h1 class=\"wp-block-heading\" id=\"react\">React<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">\u524d\u7aef\u8981\u8ca0\u8cac\u7684\u4e8b\u5c31\u6bd4\u8f03\u7c21\u55ae:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u7ba1\u7406<strong>\u5c0d\u8a71\u72c0\u614b\uff08 useState\uff09<\/strong><\/li>\n\n\n\n<li>\u63a5\u6536\u4f7f\u7528\u8005\u8f38\u5165\u4e26\u9001\u51fa\u81f3\u5f8c\u7aef ( fetch )<\/li>\n\n\n\n<li>\u63a5\u6536 AI \u56de\u61c9\u4e26\u66f4\u65b0\u5c0d\u8a71\u7d00\u9304<\/li>\n\n\n\n<li>\u63a7\u5236\u9001\u51fa\u884c\u70ba\uff08 useEffect \uff09<\/li>\n\n\n\n<li>\u8655\u7406\u975e\u540c\u6b65\u6d41\u7a0b\u8207\u932f\u8aa4\u72c0\u614b (async \/ await )<\/li>\n\n\n\n<li>\u7dad\u6301\u5c0d\u8a71\u53ef\u7528\u6027\uff08\u81ea\u52d5\u6372\u52d5 useEffect\uff09<\/li>\n<\/ol>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">import<\/span> React, { useState, useRef, useEffect } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'react'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { FaAngleRight, FaPaperPlane } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'react-icons\/fa'<\/span>;\n\ninterface Message {\n  <span class=\"hljs-attr\">role<\/span>: <span class=\"hljs-string\">'user'<\/span> | <span class=\"hljs-string\">'ai'<\/span>;\n  text: string;\n}\n\ninterface Props {\n  <span class=\"hljs-attr\">onClose<\/span>: <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> <span class=\"hljs-keyword\">void<\/span>;\n  medicalData: any;\n}\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">RightPanelContent<\/span>(<span class=\"hljs-params\">{\n  onClose,\n  medicalData\n}: Props<\/span>) <\/span>{\n  <span class=\"hljs-keyword\">const<\/span> [messages, setMessages] = useState&lt;Message[]&gt;([\n    {\n      <span class=\"hljs-attr\">role<\/span>: <span class=\"hljs-string\">'ai'<\/span>,\n      <span class=\"hljs-attr\">text<\/span>: <span class=\"hljs-string\">'\u4f60\u597d\uff01\u6211\u662f\u60a8\u7684\u91ab\u7642 AI \u52a9\u624b\uff0c\u5df2\u6e96\u5099\u597d\u5206\u6790\u75c5\u6b77\u8cc7\u6599\u3002\u8acb\u554f\u6709\u4ec0\u9ebc\u6211\u53ef\u4ee5\u5e6b\u60a8\u7684\uff1f'<\/span>\n    }\n  ]);\n\n  <span class=\"hljs-keyword\">const<\/span> [inputValue, setInputValue] = useState(<span class=\"hljs-string\">''<\/span>);\n  <span class=\"hljs-keyword\">const<\/span> [loading, setLoading] = useState(<span class=\"hljs-literal\">false<\/span>);\n  <span class=\"hljs-keyword\">const<\/span> scrollRef = useRef&lt;HTMLDivElement&gt;(<span class=\"hljs-literal\">null<\/span>);\n\n  <span class=\"hljs-comment\">\/\/ \u81ea\u52d5\u6372\u52d5\u5230\u6700\u5e95\u90e8<\/span>\n  useEffect(<span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n    <span class=\"hljs-keyword\">if<\/span> (scrollRef.current) {\n      scrollRef.current.scrollTop = scrollRef.current.scrollHeight;\n    }\n  }, [messages, loading]);\n\n  <span class=\"hljs-keyword\">const<\/span> handleSendMessage = <span class=\"hljs-keyword\">async<\/span> () =&gt; {\n    <span class=\"hljs-keyword\">if<\/span> (!inputValue.trim() || loading) <span class=\"hljs-keyword\">return<\/span>;\n\n    <span class=\"hljs-keyword\">const<\/span> userMessage = inputValue;\n    setMessages(<span class=\"hljs-function\"><span class=\"hljs-params\">prev<\/span> =&gt;<\/span> [...prev, { <span class=\"hljs-attr\">role<\/span>: <span class=\"hljs-string\">'user'<\/span>, <span class=\"hljs-attr\">text<\/span>: userMessage }]);\n    setInputValue(<span class=\"hljs-string\">''<\/span>);\n    setLoading(<span class=\"hljs-literal\">true<\/span>);\n\n    <span class=\"hljs-keyword\">try<\/span> { <span class=\"hljs-comment\">\/\/ inlineFetch\u7684\u5beb\u6cd5<\/span>\n      <span class=\"hljs-keyword\">const<\/span> response = <span class=\"hljs-keyword\">await<\/span> fetch(<span class=\"hljs-string\">'http:\/\/localhost:8080\/api\/ai\/chat'<\/span>, {\n        <span class=\"hljs-attr\">method<\/span>: <span class=\"hljs-string\">'POST'<\/span>,\n        <span class=\"hljs-attr\">headers<\/span>: { <span class=\"hljs-string\">'Content-Type'<\/span>: <span class=\"hljs-string\">'application\/json'<\/span> },\n        <span class=\"hljs-attr\">body<\/span>: <span class=\"hljs-built_in\">JSON<\/span>.stringify({\n          <span class=\"hljs-attr\">message<\/span>: userMessage,\n          <span class=\"hljs-attr\">context<\/span>: <span class=\"hljs-built_in\">JSON<\/span>.stringify(medicalData)\n        })\n      });\n\n      <span class=\"hljs-keyword\">const<\/span> result = <span class=\"hljs-keyword\">await<\/span> response.json();\n      setMessages(<span class=\"hljs-function\"><span class=\"hljs-params\">prev<\/span> =&gt;<\/span> [...prev, { <span class=\"hljs-attr\">role<\/span>: <span class=\"hljs-string\">'ai'<\/span>, <span class=\"hljs-attr\">text<\/span>: result.reply }]);\n    } <span class=\"hljs-keyword\">catch<\/span> (error) {\n      setMessages(<span class=\"hljs-function\"><span class=\"hljs-params\">prev<\/span> =&gt;<\/span> [\n        ...prev,\n        { <span class=\"hljs-attr\">role<\/span>: <span class=\"hljs-string\">'ai'<\/span>, <span class=\"hljs-attr\">text<\/span>: <span class=\"hljs-string\">'\u9023\u7dda\u5931\u6557\uff0c\u8acb\u6aa2\u67e5\u5f8c\u7aef\u670d\u52d9\u3002'<\/span> }\n      ]);\n    } <span class=\"hljs-keyword\">finally<\/span> {\n      setLoading(<span class=\"hljs-literal\">false<\/span>);\n    }\n  };\n\n  <span class=\"hljs-keyword\">return<\/span> (\n    <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n      {\/* Header *\/}\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span>&gt;<\/span>AI \u667a\u80fd\u5c0d\u8a71\u52a9\u624b<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">onClick<\/span>=<span class=\"hljs-string\">{onClose}<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">FaAngleRight<\/span> \/&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n      {\/* \u5c0d\u8a71\u5340 *\/}\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">ref<\/span>=<span class=\"hljs-string\">{scrollRef}<\/span>&gt;<\/span>\n        {messages.map((msg, index) =&gt; (\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">key<\/span>=<span class=\"hljs-string\">{index}<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">strong<\/span>&gt;<\/span>{msg.role === 'user' ? 'User' : 'AI'}:<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">strong<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span>&gt;<\/span>{msg.text}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n        ))}\n\n        {loading &amp;&amp; <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>AI \u6b63\u5728\u601d\u8003\u4e2d...<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>}\n      <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n      {\/* \u8f38\u5165\u5340 *\/}\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span>\n          <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"text\"<\/span>\n          <span class=\"hljs-attr\">placeholder<\/span>=<span class=\"hljs-string\">\"\u8acb\u8f38\u5165\u554f\u984c...\"<\/span>\n          <span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">{inputValue}<\/span>\n          <span class=\"hljs-attr\">onChange<\/span>=<span class=\"hljs-string\">{e<\/span> =&gt;<\/span> setInputValue(e.target.value)}\n          onKeyDown={e =&gt; e.key === 'Enter' &amp;&amp; handleSendMessage()}\n        \/&gt;\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">onClick<\/span>=<span class=\"hljs-string\">{handleSendMessage}<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">FaPaperPlane<\/span> \/&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span>\n  );\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>","protected":false},"excerpt":{"rendered":"<p>\u4f7f\u7528Server Action \u5b89\u88dd\u6700\u65b0\u7684\u5b98\u65b9 Gemini SDK\uff1a \u5728\u5c08\u6848\u6839\u76ee\u9304\u7684 .env \u6a94\u6848\u4e2d\uff0c\u52a0\u5165 Gemini API Key\uff08\u81f3 Google AI Studio \u7533\u8acb\uff09\uff1a \u53ef\u4f7f\u7528\u7684\u6a21\u578b\u53ca\u63a5\u6cd5\u53ef\u53c3\u8003\u5b98\u65b9\u6587\u4ef6:https:\/\/ai.google.dev\/gemini-api\/docs\/models?hl=zh-tw server action \u4f7f\u7528SpringBoot\u8207React Google AI API \u5148\u53bb\u9019\u500b\u7db2\u5740\u53d6\u5f97API ! https:\/\/aistudio.google.com Gemini \u5b98\u65b9\u7d50\u69cb Gemini API request \u683c\u5f0f\uff1a \u6240\u4ee5\u6211\u5011\u8981\u5c07\u4f7f\u7528\u8005\u7684\u8f38\u5165\uff0c\u52a0\u4e0aJSON Body\u548cHeader\u5305\u6210\u5b8c\u6574\u7684REST API\u683c\u5f0f\u50b3\u7d66gimini\uff0c\u4f5c\u70ba HTTP POST \u7684\u8acb\u6c42\u7269\u4ef6 SpringBoot \u5148\u5efa\u7acb\u4e00\u500b\u6a94\u6848\u5b58\u653eAPI\u91d1\u9470 src\\main\\resources\\application.properties \u63a5\u8457\u5efa\u7acb\u4e00\u500bservice\uff0c\u4e3b\u8981\u7684\u64cd\u4f5c\u90fd\u5728service\u4e0a ! \u64cd\u4f5c\u6d41\u7a0b: React \u524d\u7aef\u8981\u8ca0\u8cac\u7684\u4e8b\u5c31\u6bd4\u8f03\u7c21\u55ae:<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[39],"tags":[],"class_list":["post-234","post","type-post","status-publish","format-standard","hentry","category-back-end"],"views":4,"_links":{"self":[{"href":"https:\/\/hyc.eshachem.com\/program\/wp-json\/wp\/v2\/posts\/234","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/hyc.eshachem.com\/program\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/hyc.eshachem.com\/program\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/hyc.eshachem.com\/program\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/hyc.eshachem.com\/program\/wp-json\/wp\/v2\/comments?post=234"}],"version-history":[{"count":4,"href":"https:\/\/hyc.eshachem.com\/program\/wp-json\/wp\/v2\/posts\/234\/revisions"}],"predecessor-version":[{"id":367,"href":"https:\/\/hyc.eshachem.com\/program\/wp-json\/wp\/v2\/posts\/234\/revisions\/367"}],"wp:attachment":[{"href":"https:\/\/hyc.eshachem.com\/program\/wp-json\/wp\/v2\/media?parent=234"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/hyc.eshachem.com\/program\/wp-json\/wp\/v2\/categories?post=234"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/hyc.eshachem.com\/program\/wp-json\/wp\/v2\/tags?post=234"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}