{"id":255,"date":"2025-11-30T08:14:08","date_gmt":"2025-11-30T00:14:08","guid":{"rendered":"http:\/\/52.76.12.94\/?p=255"},"modified":"2026-01-31T09:01:46","modified_gmt":"2026-01-31T01:01:46","slug":"fastapi-%e9%a9%b1%e5%8a%a8%e7%9a%84-amazon-bedrock-%e4%bb%a3%e7%90%86%e6%9c%8d%e5%8a%a1%ef%bc%9a%e6%9e%b6%e6%9e%84%e8%ae%be%e8%ae%a1%e4%b8%8e%e5%ae%9e%e7%8e%b0","status":"publish","type":"post","link":"https:\/\/flycloud.io\/en\/2025\/11\/30\/fastapi-%e9%a9%b1%e5%8a%a8%e7%9a%84-amazon-bedrock-%e4%bb%a3%e7%90%86%e6%9c%8d%e5%8a%a1%ef%bc%9a%e6%9e%b6%e6%9e%84%e8%ae%be%e8%ae%a1%e4%b8%8e%e5%ae%9e%e7%8e%b0\/","title":{"rendered":"FastAPI \u9a71\u52a8\u7684 Amazon Bedrock \u4ee3\u7406\u670d\u52a1\uff1a\u67b6\u6784\u8bbe\u8ba1\u4e0e\u5b9e\u73b0"},"content":{"rendered":"<p><\/p>\n\n\n\n<p>\u672c\u6587\u5c06\u4ecb\u7ecd\u4e00\u4e2a\u57fa\u4e8e FastAPI \u7684\u8f7b\u91cf\u7ea7\u4ee3\u7406\u670d\u52a1\uff0c\u5b83\u5c06 Amazon Bedrock \u7684\u5f3a\u5927\u529f\u80fd\u5c01\u88c5\u5728\u7b80\u6d01\u7684 HTTP API \u80cc\u540e\u3002\u6211\u4eec\u5c06\u6df1\u5165\u63a2\u8ba8 FastAPI \u7684\u6838\u5fc3\u539f\u7406\u4e0e\u4f18\u52bf\uff0c\u5e76\u8be6\u7ec6\u8bf4\u660e\u4e3a\u4ec0\u4e48\u9009\u62e9 FastAPI \u4f5c\u4e3a\u6b64\u9879\u76ee\u7684 Web \u6846\u67b6\u3002<\/p>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"\u4e00fastapi-\u6846\u67b6\u73b0\u4ee3-python-web-\u5f00\u53d1\u7684\u4f18\u9009\u65b9\u6848\">\u4e00\u3001FastAPI \u6846\u67b6\uff1a\u73b0\u4ee3 Python Web \u5f00\u53d1\u7684\u4f18\u9009\u65b9\u6848<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"11-fastapi-\u7684\u6838\u5fc3\u539f\u7406\">1.1 FastAPI \u7684\u6838\u5fc3\u539f\u7406<\/h3>\n\n\n\n<p>FastAPI \u662f\u4e00\u4e2a\u73b0\u4ee3\u3001\u9ad8\u6027\u80fd\u7684 Python Web \u6846\u67b6\uff0c\u57fa\u4e8e\u4ee5\u4e0b\u5173\u952e\u6280\u672f\u6784\u5efa\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Starlette<\/strong>\uff1a\u63d0\u4f9b ASGI \u652f\u6301\u548c Web \u6846\u67b6\u57fa\u7840\u529f\u80fd<\/li>\n\n\n\n<li><strong>Pydantic<\/strong>\uff1a\u63d0\u4f9b\u6570\u636e\u9a8c\u8bc1\u548c\u5e8f\u5217\u5316<\/li>\n\n\n\n<li><strong>\u7c7b\u578b\u63d0\u793a<\/strong>\uff1a\u5229\u7528 Python 3.6+ \u7684\u7c7b\u578b\u6ce8\u89e3\u5b9e\u73b0\u81ea\u52a8\u6570\u636e\u9a8c\u8bc1\u548c\u6587\u6863\u751f\u6210<\/li>\n<\/ul>\n\n\n\n<p>FastAPI \u7684\u6838\u5fc3\u5de5\u4f5c\u539f\u7406\u53ef\u4ee5\u6982\u62ec\u4e3a\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># FastAPI \u81ea\u52a8\u5904\u7406\u8bf7\u6c42-\u54cd\u5e94\u5faa\u73af\n# 1. \u63a5\u6536 HTTP \u8bf7\u6c42\n# 2. \u9a8c\u8bc1\u8bf7\u6c42\u6570\u636e\uff08\u57fa\u4e8e Pydantic \u6a21\u578b\uff09\n# 3. \u6267\u884c\u4e1a\u52a1\u903b\u8f91\n# 4. \u5e8f\u5217\u5316\u54cd\u5e94\u6570\u636e\n# 5. \u8fd4\u56de HTTP \u54cd\u5e94\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"12-fastapi-\u7684\u6838\u5fc3\u529f\u80fd\u7279\u6027\">1.2 FastAPI \u7684\u6838\u5fc3\u529f\u80fd\u7279\u6027<\/h3>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"121-\u81ea\u52a8-api-\u6587\u6863\u751f\u6210\">1.2.1 \u81ea\u52a8 API \u6587\u6863\u751f\u6210<\/h4>\n\n\n\n<p>FastAPI \u6700\u5f3a\u5927\u7684\u7279\u6027\u4e4b\u4e00\u662f\u80fd\u591f\u81ea\u52a8\u751f\u6210 OpenAPI \u89c4\u8303\u7684\u4ea4\u4e92\u5f0f\u6587\u6863\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># \u8bbf\u95ee \/docs \u8def\u5f84\u5373\u53ef\u83b7\u5f97 Swagger UI \u754c\u9762\n# \u8bbf\u95ee \/redoc \u8def\u5f84\u53ef\u83b7\u5f97 ReDoc \u754c\u9762\n<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"122-\u7c7b\u578b\u5b89\u5168\u4e0e\u81ea\u52a8\u9a8c\u8bc1\">1.2.2 \u7c7b\u578b\u5b89\u5168\u4e0e\u81ea\u52a8\u9a8c\u8bc1<\/h4>\n\n\n\n<p>\u5229\u7528 Python \u7c7b\u578b\u63d0\u793a\uff0cFastAPI \u5728\u8fd0\u884c\u65f6\u81ea\u52a8\u9a8c\u8bc1\u8bf7\u6c42\u548c\u54cd\u5e94\u6570\u636e\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>from pydantic import BaseModel\nfrom typing import List, Optional\n\nclass ChatRequest(BaseModel):\n    mode: str = \"chat\"\n    model: Optional&#91;str] = None\n    payload: dict\n    stream: bool = False\n<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"123-\u5f02\u6b65\u9ad8\u6027\u80fd\">1.2.3 \u5f02\u6b65\u9ad8\u6027\u80fd<\/h4>\n\n\n\n<p>\u57fa\u4e8e asyncio \u548c ASGI\uff0cFastAPI \u80fd\u591f\u5904\u7406\u5927\u91cf\u5e76\u53d1\u8bf7\u6c42\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># \u652f\u6301 async\/await \u8bed\u6cd5\nasync def handle_request(request: Request):\n    # \u975e\u963b\u585e IO \u64cd\u4f5c\n    result = await external_service.call()\n    return result\n<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"124-\u4f9d\u8d56\u6ce8\u5165\u7cfb\u7edf\">1.2.4 \u4f9d\u8d56\u6ce8\u5165\u7cfb\u7edf<\/h4>\n\n\n\n<p>FastAPI \u63d0\u4f9b\u4f18\u96c5\u7684\u4f9d\u8d56\u6ce8\u5165\u673a\u5236\uff0c\u4fbf\u4e8e\u7ba1\u7406\u5171\u4eab\u8d44\u6e90\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># \u5171\u4eab Bedrock \u5ba2\u6237\u7aef\u5b9e\u4f8b\ndef get_bedrock_client() -&gt; boto3.client:\n    # \u5355\u4f8b\u6a21\u5f0f\u521b\u5efa\u5ba2\u6237\u7aef\n    pass\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"\u4e8c\u4e3a\u4ec0\u4e48\u9009\u62e9-fastapi-\u4f5c\u4e3a-bedrock-\u4ee3\u7406\u6846\u67b6\">\u4e8c\u3001\u4e3a\u4ec0\u4e48\u9009\u62e9 FastAPI \u4f5c\u4e3a Bedrock \u4ee3\u7406\u6846\u67b6<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"21-\u6027\u80fd\u8003\u91cf\u5e94\u5bf9-ai-\u63a8\u7406\u7684\u9ad8\u5ef6\u8fdf\u573a\u666f\">2.1 \u6027\u80fd\u8003\u91cf\uff1a\u5e94\u5bf9 AI \u63a8\u7406\u7684\u9ad8\u5ef6\u8fdf\u573a\u666f<\/h3>\n\n\n\n<p>Amazon Bedrock \u8c03\u7528\u901a\u5e38\u5b58\u5728\u8f83\u9ad8\u5ef6\u8fdf\uff08100-2000ms\uff09\uff0c\u4f7f\u7528 FastAPI \u7684\u5f02\u6b65\u7279\u6027\u53ef\u4ee5\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>\u907f\u514d\u8bf7\u6c42\u963b\u585e<\/strong>\uff1a\u5f53\u4e00\u4e2a\u8bf7\u6c42\u5728\u7b49\u5f85 Bedrock \u54cd\u5e94\u65f6\uff0c\u670d\u52a1\u5668\u53ef\u4ee5\u5904\u7406\u5176\u4ed6\u8bf7\u6c42<\/li>\n\n\n\n<li><strong>\u652f\u6301\u9ad8\u5e76\u53d1<\/strong>\uff1a\u5355\u8fdb\u7a0b\u53ef\u5904\u7406\u6570\u767e\u4e2a\u5e76\u53d1\u8fde\u63a5<\/li>\n\n\n\n<li><strong>\u8d44\u6e90\u9ad8\u6548\u5229\u7528<\/strong>\uff1a\u975e\u963b\u585e IO \u51cf\u5c11\u7ebf\u7a0b\u4e0a\u4e0b\u6587\u5207\u6362\u5f00\u9500<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code># \u5728\u4f20\u7edf\u540c\u6b65\u6846\u67b6\u4e2d\n# \u6bcf\u4e2a\u8bf7\u6c42\u5360\u7528\u4e00\u4e2a\u7ebf\u7a0b\uff0c\u7ebf\u7a0b\u6c60\u8017\u5c3d\u540e\u65e0\u6cd5\u5904\u7406\u65b0\u8bf7\u6c42\n\n# \u5728 FastAPI \u5f02\u6b65\u6a21\u5f0f\u4e2d\n# \u5355\u4e2a\u7ebf\u7a0b\u53ef\u5904\u7406\u591a\u4e2a\u5e76\u53d1\u8bf7\u6c42\uff0c\u901a\u8fc7\u4e8b\u4ef6\u5faa\u73af\u8c03\u5ea6\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"22-\u5f00\u53d1\u6548\u7387\u5feb\u901f\u8fed\u4ee3-ai-\u5e94\u7528\">2.2 \u5f00\u53d1\u6548\u7387\uff1a\u5feb\u901f\u8fed\u4ee3 AI \u5e94\u7528<\/h3>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"221-\u81ea\u52a8\u751f\u6210\u7684-api-\u6587\u6863\">2.2.1 \u81ea\u52a8\u751f\u6210\u7684 API \u6587\u6863<\/h4>\n\n\n\n<p>\u56e2\u961f\u6210\u5458\u53ef\u4ee5\u76f4\u63a5\u901a\u8fc7\u6d4f\u89c8\u5668\u6d4b\u8bd5 API\uff0c\u65e0\u9700\u989d\u5916\u5de5\u5177\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># \u542f\u52a8\u670d\u52a1\u540e\u8bbf\u95ee http:\/\/localhost:8000\/docs\n# \u5373\u53ef\u770b\u5230\u5b8c\u6574\u7684 API \u6587\u6863\u548c\u6d4b\u8bd5\u754c\u9762\n<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"222-\u7c7b\u578b\u5b89\u5168\u51cf\u5c11\u9519\u8bef\">2.2.2 \u7c7b\u578b\u5b89\u5168\u51cf\u5c11\u9519\u8bef<\/h4>\n\n\n\n<p>\u5728\u6784\u5efa\u590d\u6742\u7684 AI \u8bf7\u6c42\u8d1f\u8f7d\u65f6\uff0c\u7c7b\u578b\u63d0\u793a\u80fd\u53ca\u65e9\u53d1\u73b0\u9519\u8bef\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># \u9519\u8bef\u793a\u4f8b\uff1a\u5c06\u5b57\u7b26\u4e32\u4f20\u5165\u9700\u8981\u5b57\u5178\u7684\u5b57\u6bb5\npayload = \"text content\"  # \u7c7b\u578b\u9519\u8bef\u5c06\u5728\u5f00\u53d1\u65f6\u88ab\u53d1\u73b0\n\n# \u6b63\u786e\u793a\u4f8b\npayload = {\"messages\": &#91;{\"role\": \"user\", \"content\": \"text\"}]}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"23-\u751f\u6001\u7cfb\u7edf\u96c6\u6210\">2.3 \u751f\u6001\u7cfb\u7edf\u96c6\u6210<\/h3>\n\n\n\n<p>FastAPI \u4e0e\u73b0\u4ee3 Python \u5de5\u5177\u94fe\u65e0\u7f1d\u96c6\u6210\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>boto3<\/strong>\uff1aAWS SDK \u7684\u5f02\u6b65\u517c\u5bb9<\/li>\n\n\n\n<li><strong>Pydantic<\/strong>\uff1a\u4e0e Bedrock \u7684 JSON \u8bf7\u6c42\/\u54cd\u5e94\u5b8c\u7f8e\u5339\u914d<\/li>\n\n\n\n<li><strong>pytest<\/strong>\uff1a\u652f\u6301\u5f02\u6b65\u6d4b\u8bd5<\/li>\n\n\n\n<li><strong>Docker<\/strong>\uff1a\u8f7b\u91cf\u5316\u5bb9\u5668\u90e8\u7f72<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"24-\u9879\u76ee\u957f\u671f\u53ef\u7ef4\u62a4\u6027\">2.4 \u9879\u76ee\u957f\u671f\u53ef\u7ef4\u62a4\u6027<\/h3>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"241-\u6e05\u6670\u7684\u4ee3\u7801\u7ed3\u6784\">2.4.1 \u6e05\u6670\u7684\u4ee3\u7801\u7ed3\u6784<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>api\/          # HTTP \u5c42\uff1a\u8def\u7531\u3001\u8bf7\u6c42\u5904\u7406\n\u251c\u2500\u2500 main.py          # FastAPI \u5e94\u7528\u5b9e\u4f8b\n\u251c\u2500\u2500 chat_handler.py  # \u8bf7\u6c42\u8def\u7531\u548c\u7f16\u6392\n\u2514\u2500\u2500 response.py      # \u54cd\u5e94\u8f85\u52a9\u51fd\u6570\n\ncore\/         # \u6838\u5fc3\u5c42\uff1a\u914d\u7f6e\u3001\u5ba2\u6237\u7aef\n\u251c\u2500\u2500 settings.py      # \u96c6\u4e2d\u914d\u7f6e\u7ba1\u7406\n\u2514\u2500\u2500 bedrock_client.py # \u5171\u4eab Bedrock \u5ba2\u6237\u7aef\n\nservices\/     # \u670d\u52a1\u5c42\uff1a\u4e1a\u52a1\u903b\u8f91\n\u251c\u2500\u2500 chat_service.py      # \u804a\u5929\u670d\u52a1\n\u2514\u2500\u2500 embedding_service.py # \u5d4c\u5165\u670d\u52a1\n<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"242-\u914d\u7f6e\u96c6\u4e2d\u7ba1\u7406\">2.4.2 \u914d\u7f6e\u96c6\u4e2d\u7ba1\u7406<\/h4>\n\n\n\n<p>\u6240\u6709\u914d\u7f6e\u96c6\u4e2d\u5728&nbsp;<code>core\/settings.py<\/code>\uff0c\u4fbf\u4e8e\u7ba1\u7406\u548c\u4fee\u6539\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@dataclass\nclass AppSettings:\n    aws_region: str = \"us-east-1\"\n    bedrock_model_id: str = \"anthropic.claude-3-sonnet-20240229-v1:0\"\n    # \u4fee\u6539\u4e00\u5904\uff0c\u5168\u5c40\u751f\u6548\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"\u4e09bedrock-\u4ee3\u7406\u670d\u52a1\u6838\u5fc3\u67b6\u6784\">\u4e09\u3001Bedrock \u4ee3\u7406\u670d\u52a1\u6838\u5fc3\u67b6\u6784<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"31-\u6574\u4f53\u67b6\u6784\u6982\u89c8\">3.1 \u6574\u4f53\u67b6\u6784\u6982\u89c8<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510     HTTP     \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510    AWS SDK     \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502   \u60a8\u7684\u5e94\u7528      \u2502 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2192 \u2502  FastAPI \u4ee3\u7406   \u2502 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2192 \u2502 Amazon Bedrock  \u2502\n\u2502 (Web\/\u79fb\u52a8\/API)  \u2502   \/chat      \u2502  (\u672c\u670d\u52a1)       \u2502 boto3        \u2502  (AI \u670d\u52a1)      \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518              \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518              \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n        \u2191                                \u2191\n        \u2502 \u65e0\u9700 AWS SDK                    \u2502 \u7edf\u4e00\u9519\u8bef\u5904\u7406\n        \u2502 \u65e0\u9700\u7ba1\u7406\u51ed\u636e                    \u2502 \u8fde\u63a5\u6c60\u590d\u7528\n        \u2502 \u7b80\u5355 HTTP                       \u2502 \u81ea\u52a8\u91cd\u8bd5\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"32-fastapi-\u5e94\u7528\u6838\u5fc3\u7ed3\u6784\">3.2 FastAPI \u5e94\u7528\u6838\u5fc3\u7ed3\u6784<\/h3>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"321-\u5e94\u7528\u5165\u53e3-apimainpy\">3.2.1 \u5e94\u7528\u5165\u53e3 (api\/main.py)<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>from fastapi import FastAPI, Request\nfrom fastapi.middleware.cors import CORSMiddleware\n\napp = FastAPI(\n    title=\"Amazon Bedrock \u4ee3\u7406\u670d\u52a1\",\n    description=\"\u7edf\u4e00\u7684 HTTP API \u63a5\u53e3\uff0c\u7528\u4e8e\u8bbf\u95ee Bedrock \u7684\u804a\u5929\u548c\u5d4c\u5165\u529f\u80fd\",\n    version=\"1.0.0\"\n)\n\n# CORS \u914d\u7f6e\uff0c\u652f\u6301\u524d\u7aef\u8de8\u57df\u8c03\u7528\napp.add_middleware(\n    CORSMiddleware,\n    allow_origins=&#91;\"*\"],  # \u751f\u4ea7\u73af\u5883\u5e94\u914d\u7f6e\u5177\u4f53\u57df\u540d\n    allow_credentials=True,\n    allow_methods=&#91;\"*\"],\n    allow_headers=&#91;\"*\"],\n)\n\n@app.post(\"\/chat\")\nasync def chat(request: Request):\n    \"\"\"\u7edf\u4e00\u7684\u804a\u5929\u548c\u5d4c\u5165\u63a5\u53e3\"\"\"\n    body = await request.json()\n    return await handle_chat_request(body)\n\n@app.get(\"\/health\")\nasync def health():\n    \"\"\"\u5065\u5eb7\u68c0\u67e5\u7aef\u70b9\"\"\"\n    return {\"status\": \"ok\", \"service\": \"bedrock-proxy\"}\n<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"322-\u8bf7\u6c42\u5904\u7406\u6838\u5fc3-apichat_handlerpy\">3.2.2 \u8bf7\u6c42\u5904\u7406\u6838\u5fc3 (api\/chat_handler.py)<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>async def handle_chat_request(body: Dict&#91;str, Any]) -&gt; JSONResponse | StreamingResponse:\n    \"\"\"\n    \u4e3b\u8981\u8def\u7531\u903b\u8f91\uff1a\n    1. \u5224\u65ad\u6a21\u5f0f\uff1achat\uff08\u804a\u5929\uff09\u8fd8\u662f embed\uff08\u5d4c\u5165\uff09\n    2. \u5224\u65ad\u54cd\u5e94\u65b9\u5f0f\uff1a\u6d41\u5f0f\u8fd8\u662f\u975e\u6d41\u5f0f\n    3. \u8c03\u7528\u76f8\u5e94\u7684\u670d\u52a1\n    4. \u8fd4\u56de\u683c\u5f0f\u5316\u7684\u54cd\u5e94\n    \"\"\"\n    mode = body.get(\"mode\", \"chat\")\n\n    if mode == \"embed\":\n        # \u5904\u7406\u5d4c\u5165\u8bf7\u6c42\n        return await handle_embedding(body)\n    else:\n        # \u5904\u7406\u804a\u5929\u8bf7\u6c42\n        stream = body.get(\"stream\", False)\n        if stream:\n            return await handle_streaming_chat(body)\n        else:\n            return await handle_sync_chat(body)\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"33-bedrock-\u5ba2\u6237\u7aef\u7ba1\u7406-corebedrock_clientpy\">3.3 Bedrock \u5ba2\u6237\u7aef\u7ba1\u7406 (core\/bedrock_client.py)<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>def get_bedrock_client() -&gt; boto3.client:\n    \"\"\"\n    \u521b\u5efa\u5e76\u590d\u7528 Bedrock \u5ba2\u6237\u7aef\u5b9e\u4f8b\n\n    \u5173\u952e\u914d\u7f6e\uff1a\n    - max_pool_connections: \u8fde\u63a5\u6c60\u5927\u5c0f\uff08\u9ed8\u8ba4 50\uff09\n    - retries: \u81ea\u9002\u5e94\u91cd\u8bd5\u7b56\u7565\n    - region: AWS \u533a\u57df\n    \"\"\"\n    global _bedrock_client\n\n    if _bedrock_client is None:\n        settings = AppSettings.instance()\n        config = Config(\n            region_name=settings.aws_region,\n            max_pool_connections=settings.max_pool_connections,\n            retries={\n                \"max_attempts\": settings.max_retry_attempts,\n                \"mode\": \"adaptive\"\n            }\n        )\n        _bedrock_client = boto3.client(\n            \"bedrock-runtime\",\n            config=config\n        )\n\n    return _bedrock_client\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"34-\u804a\u5929\u670d\u52a1\u5b9e\u73b0-serviceschat_servicepy\">3.4 \u804a\u5929\u670d\u52a1\u5b9e\u73b0 (services\/chat_service.py)<\/h3>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"341-\u540c\u6b65\u804a\u5929\u6a21\u5f0f\">3.4.1 \u540c\u6b65\u804a\u5929\u6a21\u5f0f<\/h4>\n\n\n\n<p>\u9002\u7528\u4e8e\u4e0d\u9700\u8981\u5b9e\u65f6\u54cd\u5e94\u7684\u573a\u666f\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>def chat_sync(self, body: dict) -&gt; dict:\n    \"\"\"\n    \u540c\u6b65\u804a\u5929\u6d41\u7a0b\uff1a\n    1. \u51c6\u5907\u8bf7\u6c42\u8d1f\u8f7d\n    2. \u8c03\u7528 invoke_model\n    3. \u89e3\u6790\u54cd\u5e94\n    4. \u8fd4\u56de\u5b8c\u6574\u7ed3\u679c\n    \"\"\"\n    response = self.client.invoke_model(**invoke_kwargs)\n    response_body = json.loads(response&#91;\"body\"].read())\n\n    return {\n        \"answer\": self._extract_text(response_body),\n        \"usage\": response_body.get(\"usage\", {}),\n        \"model\": model_id\n    }\n<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"342-\u6d41\u5f0f\u804a\u5929\u6a21\u5f0f\">3.4.2 \u6d41\u5f0f\u804a\u5929\u6a21\u5f0f<\/h4>\n\n\n\n<p>\u9002\u7528\u4e8e\u9700\u8981\u5b9e\u65f6\u663e\u793a\u751f\u6210\u5185\u5bb9\u7684\u573a\u666f\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>def chat_stream(self, prepared: tuple):\n    \"\"\"\n    \u6d41\u5f0f\u804a\u5929\u6d41\u7a0b\uff1a\n    1. \u51c6\u5907\u8bf7\u6c42\u8d1f\u8f7d\n    2. \u8c03\u7528 invoke_model_with_response_stream\n    3. \u751f\u6210\u5668\u9010\u5757\u8fd4\u56de\u5185\u5bb9\n    4. \u5ba2\u6237\u7aef\u5b9e\u65f6\u63a5\u6536\u5e76\u663e\u793a\n    \"\"\"\n    response = self.client.invoke_model_with_response_stream(**invoke_kwargs)\n\n    def generate():\n        for event in response&#91;\"body\"]:\n            chunk = json.loads(event&#91;\"chunk\"]&#91;\"bytes\"])\n            yield {\n                \"text\": chunk.get(\"text\", \"\"),\n                \"is_final\": False\n            }\n\n    return generate()\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"35-\u5d4c\u5165\u670d\u52a1\u5b9e\u73b0-servicesembedding_servicepy\">3.5 \u5d4c\u5165\u670d\u52a1\u5b9e\u73b0 (services\/embedding_service.py)<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>def embed(self, text: str, model_id: Optional&#91;str] = None) -&gt; dict:\n    \"\"\"\n    \u6587\u672c\u5d4c\u5165\u6d41\u7a0b\uff1a\n    1. \u6784\u5efa\u8bf7\u6c42\u8d1f\u8f7d\uff08\u652f\u6301 Titan \u548c Cohere\uff09\n    2. \u8c03\u7528 invoke_model\n    3. \u89e3\u6790\u5d4c\u5165\u5411\u91cf\n    4. \u8fd4\u56de\u6807\u51c6\u5316\u683c\u5f0f\n    \"\"\"\n    model = model_id or self.settings.bedrock_embed_model_id\n    payload = self._build_payload(text, model)\n\n    response = self.client.invoke_model(\n        modelId=model,\n        body=json.dumps(payload),\n        contentType=\"application\/json\",\n        accept=\"application\/json\"\n    )\n\n    body = json.loads(response&#91;\"body\"].read())\n    embedding = self._parse_embedding(body, model)\n\n    return {\n        \"embedding\": embedding,\n        \"dimensions\": len(embedding),\n        \"model\": model\n    }\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"\u56db\u914d\u7f6e\u7ba1\u7406\u6700\u4f73\u5b9e\u8df5\">\u56db\u3001\u914d\u7f6e\u7ba1\u7406\u6700\u4f73\u5b9e\u8df5<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"41-\u73af\u5883\u53d8\u91cf\u9a71\u52a8\u7684\u914d\u7f6e\">4.1 \u73af\u5883\u53d8\u91cf\u9a71\u52a8\u7684\u914d\u7f6e<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code># core\/settings.py\n@dataclass\nclass AppSettings:\n    # AWS \u914d\u7f6e\n    aws_region: str = os.getenv(\"AWS_REGION\", \"us-east-1\")\n    bedrock_model_id: str = os.getenv(\n        \"BEDROCK_MODEL_ID\",\n        \"anthropic.claude-3-sonnet-20240229-v1:0\"\n    )\n\n    # Guardrails \u914d\u7f6e\n    guardrail_id: Optional&#91;str] = os.getenv(\"BEDROCK_GUARDRAIL_ID\")\n    guardrail_version: str = os.getenv(\"BEDROCK_GUARDRAIL_VERSION\", \"1\")\n\n    # HTTP \u8c03\u4f18\n    max_pool_connections: int = int(os.getenv(\"MAX_POOL_CONNECTIONS\", \"50\"))\n    max_retry_attempts: int = int(os.getenv(\"MAX_RETRY_ATTEMPTS\", \"5\"))\n\n# \u5355\u4f8b\u6a21\u5f0f\uff0c\u5168\u5c40\u590d\u7528\n_settings = None\n\n@classmethod\ndef instance(cls) -&gt; \"AppSettings\":\n    if cls._settings is None:\n        cls._settings = cls()\n    return cls._settings\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"42-env-\u6587\u4ef6\u793a\u4f8b\">4.2 .env \u6587\u4ef6\u793a\u4f8b<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code># AWS \u914d\u7f6e\nAWS_REGION=us-east-1\nAWS_ACCESS_KEY_ID=your_access_key\nAWS_SECRET_ACCESS_KEY=your_secret_key\n\n# Bedrock \u6a21\u578b\u914d\u7f6e\nBEDROCK_MODEL_ID=anthropic.claude-3-sonnet-20240229-v1:0\nBEDROCK_EMBED_MODEL_ID=amazon.titan-embed-text-v2:0\n\n# Guardrails\uff08\u53ef\u9009\uff09\nBEDROCK_GUARDRAIL_ID=gr-xxxxxxxx\nBEDROCK_GUARDRAIL_VERSION=1\n\n# CORS \u914d\u7f6e\nALLOWED_ORIGINS=http:\/\/localhost:3000,https:\/\/your-app.com\n\n# \u6027\u80fd\u8c03\u4f18\nMAX_POOL_CONNECTIONS=50\nMAX_RETRY_ATTEMPTS=5\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"\u4e94\u90e8\u7f72\u4e0e\u4f7f\u7528\">\u4e94\u3001\u90e8\u7f72\u4e0e\u4f7f\u7528<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"51-\u672c\u5730\u5f00\u53d1\u73af\u5883\">5.1 \u672c\u5730\u5f00\u53d1\u73af\u5883<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code># 1. \u514b\u9686\u9879\u76ee\ngit clone https:\/\/github.com\/loaynaser3\/bedrock-proxy.git\ncd bedrock-proxy\n\n# 2. \u521b\u5efa\u865a\u62df\u73af\u5883\npython -m venv venv\nsource venv\/bin\/activate  # Linux\/Mac\n# \u6216 venv\\Scripts\\activate  # Windows\n\n# 3. \u5b89\u88c5\u4f9d\u8d56\npip install -r requirements.txt\n\n# 4. \u914d\u7f6e AWS \u51ed\u636e\n# \u65b9\u5f0f 1: \u4f7f\u7528 .env \u6587\u4ef6\ncp .env.example .env\n# \u7f16\u8f91 .env \u6587\u4ef6\u586b\u5199 AWS \u51ed\u636e\n\n# \u65b9\u5f0f 2: \u4f7f\u7528 AWS CLI\naws configure\n\n# 5. \u542f\u52a8\u670d\u52a1\nuvicorn api.main:app --host 0.0.0.0 --port 8000 --reload\n\n# 6. \u8bbf\u95ee\u6587\u6863\n# \u6d4f\u89c8\u5668\u6253\u5f00: http:\/\/localhost:8000\/docs\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"52-docker-\u90e8\u7f72\">5.2 Docker \u90e8\u7f72<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code># Dockerfile\nFROM python:3.12-slim\n\nWORKDIR \/app\n\nCOPY requirements.txt .\nRUN pip install --no-cache-dir -r requirements.txt\n\nCOPY . .\n\nEXPOSE 8000\n\nCMD &#91;\"uvicorn\", \"api.main:app\", \"--host\", \"0.0.0.0\", \"--port\", \"8000\"]\n<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code># \u6784\u5efa\u955c\u50cf\ndocker build -t bedrock-proxy .\n\n# \u8fd0\u884c\u5bb9\u5668\ndocker run -d \\\n  --name bedrock-proxy \\\n  -p 8000:8000 \\\n  --env-file .env \\\n  bedrock-proxy\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"53-kubernetes-\u90e8\u7f72\u793a\u4f8b\">5.3 Kubernetes \u90e8\u7f72\u793a\u4f8b<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code># k8s-deployment.yaml\napiVersion: apps\/v1\nkind: Deployment\nmetadata:\n  name: bedrock-proxy\nspec:\n  replicas: 3\n  selector:\n    matchLabels:\n      app: bedrock-proxy\n  template:\n    metadata:\n      labels:\n        app: bedrock-proxy\n    spec:\n      containers:\n      - name: proxy\n        image: your-registry\/bedrock-proxy:latest\n        ports:\n        - containerPort: 8000\n        env:\n        - name: AWS_REGION\n          value: \"us-east-1\"\n        - name: BEDROCK_MODEL_ID\n          value: \"anthropic.claude-3-sonnet-20240229-v1:0\"\n        resources:\n          requests:\n            memory: \"256Mi\"\n            cpu: \"250m\"\n          limits:\n            memory: \"512Mi\"\n            cpu: \"500m\"\n---\napiVersion: v1\nkind: Service\nmetadata:\n  name: bedrock-proxy-service\nspec:\n  selector:\n    app: bedrock-proxy\n  ports:\n  - port: 80\n    targetPort: 8000\n  type: LoadBalancer\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"\u516dapi-\u4f7f\u7528\u793a\u4f8b\">\u516d\u3001API \u4f7f\u7528\u793a\u4f8b<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"61-\u804a\u5929\u8865\u5168\u540c\u6b65\u6a21\u5f0f\">6.1 \u804a\u5929\u8865\u5168\uff08\u540c\u6b65\u6a21\u5f0f\uff09<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>import requests\n\nresponse = requests.post(\n    \"http:\/\/localhost:8000\/chat\",\n    json={\n        \"mode\": \"chat\",\n        \"model\": \"anthropic.claude-3-sonnet-20240229-v1:0\",\n        \"payload\": {\n            \"anthropic_version\": \"bedrock-2023-05-31\",\n            \"messages\": &#91;\n                {\n                    \"role\": \"user\",\n                    \"content\": &#91;\n                        {\"type\": \"text\", \"text\": \"\u89e3\u91ca DevOps \u6982\u5ff5\"}\n                    ]\n                }\n            ],\n            \"max_tokens\": 256\n        }\n    }\n)\n\nresult = response.json()\nprint(f\"\u56de\u7b54: {result&#91;'answer']}\")\nprint(f\"\u7528\u91cf: {result&#91;'usage']}\")\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"62-\u6d41\u5f0f\u804a\u5929\u5b9e\u65f6\u54cd\u5e94\">6.2 \u6d41\u5f0f\u804a\u5929\uff08\u5b9e\u65f6\u54cd\u5e94\uff09<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>import requests\nimport json\n\nresponse = requests.post(\n    \"http:\/\/localhost:8000\/chat\",\n    json={\n        \"mode\": \"chat\",\n        \"stream\": True,\n        \"model\": \"anthropic.claude-3-sonnet-20240229-v1:0\",\n        \"payload\": {\n            \"anthropic_version\": \"bedrock-2023-05-31\",\n            \"messages\": &#91;\n                {\n                    \"role\": \"user\",\n                    \"content\": \"\u5199\u4e00\u6bb5\u5173\u4e8e\u673a\u5668\u5b66\u4e60\u7684\u4ecb\u7ecd\"\n                }\n            ],\n            \"max_tokens\": 512\n        }\n    },\n    stream=True\n)\n\n# \u9010\u5757\u63a5\u6536\u5e76\u663e\u793a\nfor line in response.iter_lines():\n    if line:\n        chunk = json.loads(line)\n        if \"text\" in chunk:\n            print(chunk&#91;\"text\"], end=\"\", flush=True)\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"63-\u6587\u672c\u5d4c\u5165\">6.3 \u6587\u672c\u5d4c\u5165<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>import requests\n\nresponse = requests.post(\n    \"http:\/\/localhost:8000\/chat\",\n    json={\n        \"mode\": \"embed\",\n        \"model\": \"amazon.titan-embed-text-v2:0\",\n        \"text\": \"\u8fd9\u662f\u4e00\u4e2a\u6d4b\u8bd5\u53e5\u5b50\"\n    }\n)\n\nresult = response.json()\nprint(f\"\u5d4c\u5165\u7ef4\u5ea6: {result&#91;'dimensions']}\")\nprint(f\"\u524d5\u4e2a\u503c: {result&#91;'embedding']&#91;:5]}\")\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"\u4e03\u4ea7\u54c1\u7ea7\u589e\u5f3a\u5efa\u8bae\">\u4e03\u3001\u4ea7\u54c1\u7ea7\u589e\u5f3a\u5efa\u8bae<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"71-\u8ba4\u8bc1\u4e0e\u6388\u6743\">7.1 \u8ba4\u8bc1\u4e0e\u6388\u6743<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code># \u6dfb\u52a0 API Key \u8ba4\u8bc1\nfrom fastapi.security import HTTPBearer, HTTPAuthorizationCredentials\n\nsecurity = HTTPBearer()\n\nasync def verify_api_key(credentials: HTTPAuthorizationCredentials = Depends(security)):\n    if credentials.credentials not in VALID_API_KEYS:\n        raise HTTPException(status_code=401, detail=\"Invalid API Key\")\n    return credentials.credentials\n\n@app.post(\"\/chat\")\nasync def chat(request: Request, api_key: str = Depends(verify_api_key)):\n    # \u4e1a\u52a1\u903b\u8f91\n    pass\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"72-\u901f\u7387\u9650\u5236\">7.2 \u901f\u7387\u9650\u5236<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>from fastapi_limiter import FastAPILimiter\nfrom fastapi_limiter.depends import RateLimiter\n\n@app.post(\"\/chat\", dependencies=&#91;Depends(RateLimiter(times=10, seconds=60))])\nasync def chat(request: Request):\n    # \u6bcf\u4e2a\u7528\u6237\u6bcf\u5206\u949f\u6700\u591a 10 \u6b21\u8bf7\u6c42\n    pass\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"73-\u8bf7\u6c42\u65e5\u5fd7\u4e0e\u76d1\u63a7\">7.3 \u8bf7\u6c42\u65e5\u5fd7\u4e0e\u76d1\u63a7<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>import logging\nfrom datetime import datetime\n\nlogging.basicConfig(\n    level=logging.INFO,\n    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'\n)\n\nlogger = logging.getLogger(__name__)\n\n@app.middleware(\"http\")\nasync def log_requests(request: Request, call_next):\n    start_time = datetime.now()\n    response = await call_next(request)\n    duration = (datetime.now() - start_time).total_seconds()\n\n    logger.info(\n        f\"{request.method} {request.url.path} \"\n        f\"Status: {response.status_code} \"\n        f\"Duration: {duration:.3f}s\"\n    )\n    return response\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"74-\u7f13\u5b58\u7b56\u7565\">7.4 \u7f13\u5b58\u7b56\u7565<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>from functools import lru_cache\n\n@lru_cache(maxsize=128)\ndef get_embedding_cached(text: str, model: str):\n    # \u7f13\u5b58\u9891\u7e41\u8bf7\u6c42\u7684\u5d4c\u5165\u7ed3\u679c\n    return embedding_service.embed(text, model)\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"75-\u8d1f\u8f7d\u5747\u8861\u4e0e\u9ad8\u53ef\u7528\">7.5 \u8d1f\u8f7d\u5747\u8861\u4e0e\u9ad8\u53ef\u7528<\/h3>\n\n\n\n<p>\u5728\u751f\u4ea7\u73af\u5883\u5efa\u8bae\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u90e8\u7f72\u591a\u4e2a\u5b9e\u4f8b<\/strong>\uff1a\u4f7f\u7528 Kubernetes \u6216 ECS \u90e8\u7f72 3+ \u5b9e\u4f8b<\/li>\n\n\n\n<li><strong>\u914d\u7f6e\u5065\u5eb7\u68c0\u67e5<\/strong>\uff1a\u5b9a\u671f\u63a2\u6d4b \/health \u7aef\u70b9<\/li>\n\n\n\n<li><strong>\u81ea\u52a8\u6269\u7f29\u5bb9<\/strong>\uff1a\u6839\u636e CPU\/\u5185\u5b58\u6307\u6807\u81ea\u52a8\u8c03\u6574\u5b9e\u4f8b\u6570<\/li>\n\n\n\n<li><strong>\u4f7f\u7528 ALB<\/strong>\uff1aAWS Application Load Balancer \u5206\u53d1\u6d41\u91cf<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"\u516b\u4e3a\u4ec0\u4e48\u9009\u62e9\u4ee3\u7406\u67b6\u6784\">\u516b\u3001\u4e3a\u4ec0\u4e48\u9009\u62e9\u4ee3\u7406\u67b6\u6784<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"81-\u5b89\u5168\u6027\u4f18\u52bf\">8.1 \u5b89\u5168\u6027\u4f18\u52bf<\/h3>\n\n\n\n<p><strong>\u4f20\u7edf\u76f4\u63a5\u8bbf\u95ee\u6a21\u5f0f\uff1a<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\u524d\u7aef\/\u79fb\u52a8\u7aef \u2192 \u66b4\u9732 AWS \u51ed\u636e \u2192 \u76f4\u63a5\u8c03\u7528 Bedrock\n\u274c \u51ed\u636e\u6cc4\u9732\u98ce\u9669\n\u274c \u65e0\u6cd5\u63a7\u5236\u8bbf\u95ee\u6743\u9650\n\u274c \u96be\u4ee5\u5ba1\u8ba1\u4f7f\u7528\u91cf\n<\/code><\/pre>\n\n\n\n<p><strong>\u4ee3\u7406\u6a21\u5f0f\uff1a<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\u524d\u7aef\/\u79fb\u52a8\u7aef \u2192 HTTP \u8c03\u7528 \u2192 FastAPI \u4ee3\u7406 \u2192 \u5b89\u5168\u51ed\u636e \u2192 Bedrock\n\u2705 \u51ed\u636e\u4e0d\u66b4\u9732\n\u2705 \u53ef\u6dfb\u52a0\u81ea\u5b9a\u4e49\u8ba4\u8bc1\n\u2705 \u5b8c\u6574\u4f7f\u7528\u65e5\u5fd7\n\u2705 \u53ef\u5b9e\u65bd\u901f\u7387\u9650\u5236\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"82-\u5f00\u53d1\u6548\u7387\u63d0\u5347\">8.2 \u5f00\u53d1\u6548\u7387\u63d0\u5347<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u5bf9\u6bd4\u9879<\/th><th>\u76f4\u63a5\u8c03\u7528 Bedrock<\/th><th>\u4f7f\u7528\u4ee3\u7406\u670d\u52a1<\/th><\/tr><\/thead><tbody><tr><td>\u524d\u7aef\u4ee3\u7801\u590d\u6742\u5ea6<\/td><td>\u9700\u8981 AWS SDK\uff0c\u5904\u7406\u8ba4\u8bc1<\/td><td>\u7b80\u5355 HTTP \u8bf7\u6c42<\/td><\/tr><tr><td>\u591a\u5e94\u7528\u7ef4\u62a4<\/td><td>\u5404\u5e94\u7528\u72ec\u7acb\u914d\u7f6e<\/td><td>\u7edf\u4e00\u914d\u7f6e<\/td><\/tr><tr><td>\u6a21\u578b\u5207\u6362<\/td><td>\u6bcf\u4e2a\u5e94\u7528\u4fee\u6539\u4ee3\u7801<\/td><td>\u4fee\u6539\u4ee3\u7406\u914d\u7f6e<\/td><\/tr><tr><td>\u9519\u8bef\u5904\u7406<\/td><td>\u5404\u81ea\u5b9e\u73b0<\/td><td>\u7edf\u4e00\u5904\u7406<\/td><\/tr><tr><td>\u8fde\u63a5\u6c60\u7ba1\u7406<\/td><td>\u5404\u81ea\u5b9e\u73b0<\/td><td>\u96c6\u4e2d\u4f18\u5316<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"83-\u53ef\u6269\u5c55\u6027\u8bbe\u8ba1\">8.3 \u53ef\u6269\u5c55\u6027\u8bbe\u8ba1<\/h3>\n\n\n\n<p>\u4ee3\u7406\u67b6\u6784\u4e3a future enhancement \u63d0\u4f9b\u4e86\u6269\u5c55\u70b9\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># \u793a\u4f8b\uff1a\u6dfb\u52a0\u81ea\u5b9a\u4e49\u5de5\u5177\u8c03\u7528\nclass CustomToolService:\n    def __init__(self, bedrock_client):\n        self.client = bedrock_client\n\n    def register_tool(self, name: str, function: Callable):\n        # \u6ce8\u518c\u81ea\u5b9a\u4e49\u5de5\u5177\n        pass\n\n    def execute_tool_call(self, tool_call: dict):\n        # \u6267\u884c\u5de5\u5177\u8c03\u7528\n        pass\n\n# \u96c6\u6210\u5230 ChatService\nchat_service = ChatService(bedrock_client, settings)\nchat_service.tool_executor = CustomToolService(bedrock_client)\n<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code># \u793a\u4f8b\uff1a\u6dfb\u52a0\u8bf7\u6c42\u62e6\u622a\u5668\nclass RequestInterceptor:\n    async def before_request(self, request: dict):\n        # \u65e5\u5fd7\u8bb0\u5f55\n        # \u5185\u5bb9\u5ba1\u6838\n        # \u683c\u5f0f\u8f6c\u6362\n        pass\n\n    async def after_response(self, response: dict):\n        # \u7ed3\u679c\u7f13\u5b58\n        # \u540e\u5904\u7406\n        pass\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"\u4e5d\u6027\u80fd\u4f18\u5316\u6280\u5de7\">\u4e5d\u3001\u6027\u80fd\u4f18\u5316\u6280\u5de7<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"91-\u8fde\u63a5\u6c60\u8c03\u4f18\">9.1 \u8fde\u63a5\u6c60\u8c03\u4f18<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code># core\/bedrock_client.py\nconfig = Config(\n    region_name=settings.aws_region,\n    max_pool_connections=100,  # \u6839\u636e\u5e76\u53d1\u91cf\u8c03\u6574\n    retries={\n        \"max_attempts\": 3,\n        \"mode\": \"adaptive\"  # \u81ea\u9002\u5e94\u91cd\u8bd5\u7b56\u7565\n    }\n)\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"92-\u6d41\u5f0f\u54cd\u5e94\u4f18\u5316\">9.2 \u6d41\u5f0f\u54cd\u5e94\u4f18\u5316<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code># \u8bbe\u7f6e\u5408\u9002\u7684\u5206\u5757\u5927\u5c0f\n@app.post(\"\/chat\")\nasync def chat(request: Request):\n    response = await chat_service.chat_stream(prepared)\n\n    # \u4f7f\u7528 StreamingResponse \u51cf\u5c11\u5185\u5b58\u5360\u7528\n    return StreamingResponse(\n        response,\n        media_type=\"application\/x-ndjson\",\n        headers={\n            \"Cache-Control\": \"no-cache\",\n            \"X-Accel-Buffering\": \"no\"  # \u7981\u7528\u4ee3\u7406\u7f13\u51b2\n        }\n    )\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"93-\u6a21\u578b\u9884\u70ed\">9.3 \u6a21\u578b\u9884\u70ed<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code># \u5e94\u7528\u542f\u52a8\u65f6\u9884\u70ed\u8fde\u63a5\n@app.on_event(\"startup\")\nasync def startup_event():\n    # \u521d\u59cb\u5316 Bedrock \u5ba2\u6237\u7aef\n    _ = get_bedrock_client()\n\n    # \u53ef\u9009\uff1a\u53d1\u9001\u4e00\u4e2a\u6d4b\u8bd5\u8bf7\u6c42\u9884\u70ed\u6a21\u578b\n    logger.info(\"Bedrock \u4ee3\u7406\u670d\u52a1\u542f\u52a8\u5b8c\u6210\")\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"\u5341\u603b\u7ed3\">\u5341\u3001\u603b\u7ed3<\/h2>\n\n\n\n<p>FastAPI \u4f5c\u4e3a\u73b0\u4ee3\u5316 Python Web \u6846\u67b6\uff0c\u4e3a\u6784\u5efa Amazon Bedrock \u4ee3\u7406\u670d\u52a1\u63d0\u4f9b\u4e86\u7406\u60f3\u7684 foundation\uff1a<\/p>\n\n\n\n<p><strong>\u6280\u672f\u4f18\u52bf\uff1a<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>\u9ad8\u6027\u80fd<\/strong>\uff1a\u5f02\u6b65 IO \u652f\u6301\u9ad8\u5e76\u53d1\uff0c\u9002\u5408 AI \u63a8\u7406\u573a\u666f<\/li>\n\n\n\n<li><strong>\u81ea\u52a8\u6587\u6863<\/strong>\uff1aSwagger UI \u964d\u4f4e\u56e2\u961f\u534f\u4f5c\u6210\u672c<\/li>\n\n\n\n<li><strong>\u7c7b\u578b\u5b89\u5168<\/strong>\uff1aPydantic \u6a21\u578b\u786e\u4fdd\u6570\u636e\u5b8c\u6574\u6027<\/li>\n\n\n\n<li><strong>\u6613\u4e8e\u6269\u5c55<\/strong>\uff1a\u4e2d\u95f4\u4ef6\u548c\u4f9d\u8d56\u6ce8\u5165\u652f\u6301\u4e1a\u52a1\u9700\u6c42\u6f14\u53d8<\/li>\n\n\n\n<li><strong>\u8f7b\u91cf\u7ea7<\/strong>\uff1a\u6700\u5c0f\u5316\u8fd0\u884c\u65f6\u5f00\u9500<\/li>\n<\/ul>\n\n\n\n<p><strong>\u4e1a\u52a1\u4ef7\u503c\uff1a<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>\u5b89\u5168\u6027<\/strong>\uff1aAWS \u51ed\u636e\u4e0d\u518d\u66b4\u9732\u7ed9\u524d\u7aef<\/li>\n\n\n\n<li><strong>\u4e00\u81f4\u6027<\/strong>\uff1a\u7edf\u4e00\u6a21\u578b\u914d\u7f6e\u548c Guardrails \u7b56\u7565<\/li>\n\n\n\n<li><strong>\u53ef\u89c2\u6d4b\u6027<\/strong>\uff1a\u96c6\u4e2d\u65e5\u5fd7\u548c\u76d1\u63a7<\/li>\n\n\n\n<li><strong>\u7b80\u5316\u5f00\u53d1<\/strong>\uff1a\u5ba2\u6237\u7aef\u65e0\u9700 AWS SDK \u77e5\u8bc6<\/li>\n<\/ul>\n\n\n\n<p>\u901a\u8fc7\u672c\u6587\u4ecb\u7ecd\u7684\u67b6\u6784\uff0c\u5f00\u53d1\u56e2\u961f\u53ef\u4ee5\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u5feb\u901f\u96c6\u6210<\/strong>\uff1a\u4efb\u4f55\u652f\u6301 HTTP \u7684\u8bed\u8a00\/\u6846\u67b6\u5747\u53ef\u8c03\u7528<\/li>\n\n\n\n<li><strong>\u4e13\u6ce8\u4e1a\u52a1<\/strong>\uff1a\u5c4f\u853d AWS SDK \u5b9e\u73b0\u7ec6\u8282<\/li>\n\n\n\n<li><strong>\u7075\u6d3b\u6f14\u8fdb<\/strong>\uff1a\u5728\u4ee3\u7406\u5c42\u6dfb\u52a0\u65b0\u529f\u80fd\u4e0d\u5f71\u54cd\u5ba2\u6237\u7aef<\/li>\n\n\n\n<li><strong>\u751f\u4ea7\u5c31\u7eea<\/strong>\uff1a\u6613\u4e8e\u6269\u5c55\u8ba4\u8bc1\u3001\u76d1\u63a7\u3001\u7f13\u5b58\u7b49\u4f01\u4e1a\u7ea7\u7279\u6027<\/li>\n<\/ol>\n\n\n\n<p>\u65e0\u8bba\u60a8\u662f\u6784\u5efa AI \u9a71\u52a8\u7684 Web \u5e94\u7528\u3001\u79fb\u52a8\u5e94\u7528\u8fd8\u662f\u5fae\u670d\u52a1\u67b6\u6784\uff0c\u8fd9\u4e2a FastAPI + Amazon Bedrock \u7684\u4ee3\u7406\u6a21\u5f0f\u90fd\u80fd\u663e\u8457\u63d0\u5347\u5f00\u53d1\u6548\u7387\u548c\u7cfb\u7edf\u53ef\u7ef4\u62a4\u6027\u3002<\/p>","protected":false},"excerpt":{"rendered":"<p>\u672c\u6587\u5c06\u4ecb\u7ecd\u4e00\u4e2a\u57fa\u4e8e FastAPI \u7684\u8f7b\u91cf\u7ea7\u4ee3\u7406\u670d\u52a1\uff0c\u5b83\u5c06 Amazon Bedrock \u7684\u5f3a\u5927\u529f\u80fd\u5c01\u88c5\u5728\u7b80\u6d01\u7684 HTTP API \u80cc\u540e\u3002\u6211\u4eec\u5c06\u6df1\u5165\u63a2\u8ba8 FastAPI \u7684\u6838\u5fc3\u539f\u7406\u4e0e\u4f18\u52bf\uff0c\u5e76\u8be6\u7ec6\u8bf4\u660e\u4e3a\u4ec0\u4e48\u9009\u62e9 FastAPI \u4f5c\u4e3a\u6b64\u9879\u76ee\u7684 Web \u6846\u67b6\u3002 \u4e00\u3001FastAPI \u6846\u67b6\uff1a\u73b0\u4ee3 Python Web \u5f00\u53d1\u7684\u4f18\u9009\u65b9\u6848 1.1 FastAPI \u7684\u6838\u5fc3\u539f\u7406 FastAPI \u662f\u4e00\u4e2a\u73b0\u4ee3\u3001\u9ad8\u6027\u80fd\u7684 &hellip;<\/p>","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5,4],"tags":[],"class_list":["post-255","post","type-post","status-publish","format-standard","hentry","category-ai","category-aws"],"_links":{"self":[{"href":"https:\/\/flycloud.io\/en\/wp-json\/wp\/v2\/posts\/255","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/flycloud.io\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/flycloud.io\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/flycloud.io\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/flycloud.io\/en\/wp-json\/wp\/v2\/comments?post=255"}],"version-history":[{"count":1,"href":"https:\/\/flycloud.io\/en\/wp-json\/wp\/v2\/posts\/255\/revisions"}],"predecessor-version":[{"id":256,"href":"https:\/\/flycloud.io\/en\/wp-json\/wp\/v2\/posts\/255\/revisions\/256"}],"wp:attachment":[{"href":"https:\/\/flycloud.io\/en\/wp-json\/wp\/v2\/media?parent=255"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/flycloud.io\/en\/wp-json\/wp\/v2\/categories?post=255"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/flycloud.io\/en\/wp-json\/wp\/v2\/tags?post=255"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}