[{"data":1,"prerenderedAt":3416},["ShallowReactive",2],{"blog-decrease-coding-agent-illusion":3,"related-decrease-coding-agent-illusion":261},{"id":4,"title":5,"author":6,"body":7,"category":246,"date":247,"description":248,"draft":249,"extension":250,"image":251,"meta":252,"navigation":253,"path":254,"seo":255,"stem":256,"tags":257,"__hash__":260},"blog/blog/decrease-coding-agent-illusion.md","如何降低 Coding Agent的幻覺？","Ting Zhang",{"type":8,"value":9,"toc":231},"minimark",[10,14,18,21,26,35,42,45,49,52,65,68,71,74,77,87,90,96,101,105,108,118,123,129,132,136,143,154,162,166,173,179,187,190,197,203,206,212,215,220],[11,12,13],"h1",{"id":13},"前言",[15,16,17],"p",{},"在做公司 POC 的過程，因為需要用到 Strands Agent & AWS AgentCore 一些比較新的SDK，實在有太多次都遇到 AI Gen 完以後發現 Syntax Highlight 沒有提示，看一下 Source Code or Docs 發現根本沒有這個 function / attr 的狀況，嘗試了幾個提升 LLM 準確率的方法",[15,19,20],{},"基本上解決思路都是讓 Agent 可以有個參照對象，提升正確率",[22,23,25],"h2",{"id":24},"context7","Context7",[15,27,28],{},[29,30,34],"a",{"href":31,"rel":32},"https://github.com/upstash/context7",[33],"nofollow","GitHub - upstash/context7: Context7 MCP Server -- Up-to-date code documentation for LLMs and AI code editors",[15,36,37],{},[29,38,41],{"href":39,"rel":40},"https://context7.com/",[33],"Context7 - Up-to-date documentation for LLMs and AI code editors",[15,43,44],{},"Context7 是一個 MCP Tool, 可以自動查詢最新文檔",[46,47,48],"h3",{"id":48},"文檔來源",[15,50,51],{},"任何人都可以到官網去發一個 Add Library Request，這邊可以看到目前正在進行 Parsing & Crawling 的目標文檔，我嘗試把我自己的repo丟上去也可以跑，並且有辦法在 Dashboard 搜尋到",[15,53,54,59,62],{},[55,56],"img",{"alt":57,"src":58},"","/images/blog/decrease-coding-agent-illusion/image1.png",[55,60],{"alt":57,"src":61},"/images/blog/decrease-coding-agent-illusion/image2.png",[55,63],{"alt":57,"src":64},"/images/blog/decrease-coding-agent-illusion/image3.png",[46,66,67],{"id":67},"使用",[15,69,70],{},"可以選擇自架 MCP Server or 使用官方服務",[15,72,73],{},"原本因為方便所以直接使用官方服務(安裝時需填入 API key)，一個月限制 1000 次 query，實際使用發現他好像每天都會重置 1000 次的 quota，基本上用不完。",[15,75,76],{},"你可以選擇加敘述在會自動放到 Context 的地方 (ex: cursor rule, claude.md, agent.md, etc.)",[78,79,84],"pre",{"className":80,"code":82,"language":83},[81],"language-text","Always use Context7 MCP when I need library/API documentation, code generation, setup or configuration steps without me having to explicitly ask.\n","text",[85,86,82],"code",{"__ignoreMap":57},[15,88,89],{},"或者加在 LLM 來回的地方",[78,91,94],{"className":92,"code":93,"language":83},[81],"幫我寫一個 strands agent graph pattern example, 記得遵照文檔教學執行/必須使用 context 7...\n",[85,95,93],{"__ignoreMap":57},[15,97,98],{},[55,99],{"alt":57,"src":100},"/images/blog/decrease-coding-agent-illusion/image4.png",[46,102,104],{"id":103},"how-it-works","How it works?",[15,106,107],{},"使用兩個 MCP Tools 來實現",[109,110,111,115],"ul",{},[112,113,114],"li",{},"Search",[112,116,117],{},"Get Context",[119,120,122],"h4",{"id":121},"context-usage","Context Usage",[78,124,127],{"className":125,"code":126,"language":83},[81],"MCP tools · /mcp                                                                                                                                                   \n     └ mcp__context7__resolve-library-id: 499 tokens                                                                                                                    \n     └ mcp__context7__query-docs: 408 tokens             \n",[85,128,126],{"__ignoreMap":57},[15,130,131],{},"可以看到 context7在 context 使用上算是非常輕量的 MCP Tool",[46,133,135],{"id":134},"context7-作為-skill-管理工具","Context7 作為 Skill 管理工具",[15,137,138],{},[29,139,142],{"href":140,"rel":141},"https://context7.com/docs/skills",[33],"Skills - Context7 MCP",[109,144,145,148,151],{},[112,146,147],{},"搜尋功能",[112,149,150],{},"號稱有濾除有安全疑慮的 Skill (不安全的 script / prompt injection…)",[112,152,153],{},"可以自動偵測本機上有安裝的 Coding LLM 然後安裝進去 (但是看起來不支援 Kiro🥲)",[15,155,156,159],{},[55,157],{"alt":57,"src":158},"/images/blog/decrease-coding-agent-illusion/image5.png",[55,160],{"alt":57,"src":161},"/images/blog/decrease-coding-agent-illusion/image6.png",[22,163,165],{"id":164},"skill","Skill",[15,167,168],{},[29,169,172],{"href":170,"rel":171},"https://platform.claude.com/docs/en/agents-and-tools/agent-skills/overview",[33],"Agent Skills",[174,175,176],"blockquote",{},[15,177,178],{},"大家應該都知道的東西，網路上很多基本介紹這邊不多贅述，我的用法是把docs repo 直接放到 Reference 內，讓 LLM 去產生 SKILL.md",[109,180,181,184],{},[112,182,183],{},"讓 LLM 知道有這個 skill",[112,185,186],{},"不一定每個 sdk 都有 docs repo, 所以也順便有個 index 可以讓 LLM 快速的查詢文檔路徑",[46,188,189],{"id":189},"範例",[15,191,192],{},[29,193,196],{"href":194,"rel":195},"https://github.com/strands-agents/docs",[33],"GitHub - strands-agents/docs: Documentation for the Strands Agents SDK. A model-driven approach to building AI agents in just a few lines of code.",[78,198,201],{"className":199,"code":200,"language":83},[81],"# ~/.claude/skills/strands-agents-docs/SKILL.md\n---\nname: strands-agents-docs\ndescription: Access comprehensive Strands Agents documentation to help build, deploy, and operate AI agents using the Strands Agents SDK\n---\n# Strands Agents Documentation Skill\nThis skill provides access to the complete Strands Agents documentation, enabling code agents to quickly locate relevant guides, examples, and API references for building AI agents with the Strands Agents SDK.\n## Instructions\nWhen working with Strands Agents SDK, use this skill to:\n1. Locate relevant documentation files based on the task or concept\n2. Reference specific guides for implementation patterns\n3. Find deployment and production operation best practices\n4. Access model provider configurations and examples\n5. Understand multi-agent patterns and tool development\nSearch for documentation by topic or concept, and reference the appropriate markdown file path below for detailed information.\n## Documentation Index\n### Quickstart Guides\n- `/Users/ting/.claude/skills/strands-agents-docs/references/docs/user-guide/quickstart.md` - Complete quickstart guide showing how to create your first Strands agent with tools and model providers\n- `/Users/ting/.claude/skills/strands-agents-docs/references/docs/user-guide/quickstart/overview.md` - Overview of getting started with Strands Agents\n=== 下略 ===\n",[85,202,200],{"__ignoreMap":57},[46,204,122],{"id":205},"context-usage-1",[78,207,210],{"className":208,"code":209,"language":83},[81],"Skills · /skills\n User\n └ bedrock-agentcore-sdk: 38 tokens                                                                                                                                 \n └ strands-agents-docs: 36 tokens\n",[85,211,209],{"__ignoreMap":57},[22,213,214],{"id":214},"其他",[174,216,217],{},[15,218,219],{},"基本上都是“LLM已經拉了一坨沒辦法跑的 Code”的時候，已經參與Debug了，是不理想的狀況",[109,221,222,225,228],{},[112,223,224],{},"指出 site-packages 內的原始碼路徑給 LLM, 讓他參照使用",[112,226,227],{},"複製文檔直接喂到嘴邊",[112,229,230],{},"搭配其他 AI Tools (Perplexity, Deepwiki)",{"title":57,"searchDepth":232,"depth":232,"links":233},2,[234,241,245],{"id":24,"depth":232,"text":25,"children":235},[236,238,239,240],{"id":48,"depth":237,"text":48},3,{"id":67,"depth":237,"text":67},{"id":103,"depth":237,"text":104},{"id":134,"depth":237,"text":135},{"id":164,"depth":232,"text":165,"children":242},[243,244],{"id":189,"depth":237,"text":189},{"id":205,"depth":237,"text":122},{"id":214,"depth":232,"text":214},"技術","2026-01-26","介紹透過 context7 & agent skills 來提升 Coding agent one shot 機率的方法",false,"md","/images/blog/decrease-coding-agent-illusion/banner.png",{},true,"/blog/decrease-coding-agent-illusion",{"title":5,"description":248},"blog/decrease-coding-agent-illusion",[258,259],"Tooling","Development","m_26ovdfaN8lhTUGpa0GYVOveveCALhEyBRKj-NqpLs",[262,1666,2009],{"id":263,"title":264,"author":6,"body":265,"category":246,"date":1656,"description":1657,"draft":249,"extension":250,"image":57,"meta":1658,"navigation":253,"path":1659,"seo":1660,"stem":1661,"tags":1662,"__hash__":1665},"blog/blog/uv-and-ruff.md","uv and Ruff: 最佳的 Python 工具鏈",{"type":8,"value":266,"toc":1638},[267,271,276,279,287,291,322,326,333,355,362,373,377,652,655,660,666,671,677,680,683,691,695,717,719,723,770,772,775,778,803,806,810,853,857,958,962,1037,1044,1051,1341,1345,1365,1369,1381,1385,1444,1448,1474,1478,1606,1608,1611,1634],[11,268,270],{"id":269},"這篇文章的用處","這篇文章的用處？",[174,272,273],{},[15,274,275],{},"若遇過 format on save 很慢 / 裝環境很麻煩 / 套件衝突，那這篇文章可能對你有用",[11,277,278],{"id":278},"uv",[174,280,281,284],{},[15,282,283],{},"uv 是一個極速的 Python 套件管理器和專案管理工具，被設計為 pip、pip-tools、pipx、poetry、pyenv、virtualenv 等工具的統一替代方案。",[15,285,286],{},"uv 的核心理念是提供一個快速、可靠且易用的 Python 工具鏈。",[22,288,290],{"id":289},"uv-的主要特點","uv 的主要特點",[109,292,293,299,312,319],{},[112,294,295],{},[296,297,298],"strong",{},"速度極快",[112,300,301,304],{},[296,302,303],{},"統一的工作流程",[109,305,306,309],{},[112,307,308],{},"整合了套件安裝、虛擬環境管理、專案初始化等功能",[112,310,311],{},"一個工具解決多個需求，簡化開發工作流程",[112,313,314,315,318],{},"確保所有環境部署時依賴相同 (",[85,316,317],{},"uv.lock",")",[112,320,321],{},"方便切換 python 版本，以及在設定檔中鎖定 python 版本",[22,323,325],{"id":324},"requirementstxt-vs-pyprojecttoml","requirements.txt vs pyproject.toml",[174,327,328],{},[15,329,330],{},[296,331,332],{},"pyproject.toml 是官方標準",[109,334,335,338,341,344,347],{},[112,336,337],{},"PEP 518、PEP 621 等官方標準定義的專案配置格式",[112,339,340],{},"Python 生態系統的統一標準，未來發展方向",[112,342,343],{},"被所有現代工具（pip、uv、poetry、setuptools）支援",[112,345,346],{},"可以根據開發、打包、部署等環境分別配置",[112,348,349,350],{},"有 support 的工具都可以統一配置\n",[109,351,352],{},[112,353,354],{},"例如等等要介紹的 ruff",[174,356,357],{},[15,358,359],{},[296,360,361],{},"requirements.txt 是非正式慣例",[109,363,364,367,370],{},[112,365,366],{},"只是社群約定俗成的做法",[112,368,369],{},"沒有正式規範，格式相對簡陋",[112,371,372],{},"逐漸被新工具取代",[46,374,376],{"id":375},"範例-pyprojecttoml","範例 pyproject.toml",[78,378,382],{"className":379,"code":380,"language":381,"meta":57,"style":57},"language-toml shiki shiki-themes github-light github-dark","[project]\nname = \"my-awesome-project\"\nversion = \"1.0.0\"\ndescription = \"A fantastic Python project\"\nauthors = [{name = \"Your Name\", email = \"you@example.com\"}]\nlicense = {text = \"MIT\"}\nreadme = \"README.md\"\nkeywords = [\"python\", \"awesome\"]\nclassifiers = [\n    \"Development Status :: 4 - Beta\",\n    \"Programming Language :: Python :: 3.11\",\n]\n\n# 依賴管理\ndependencies = [\n    \"requests>=2.28.0\",\n    \"click>=8.0.0\",\n]\n\n# 開發依賴\n[project.optional-dependencies]\ndev = [\n    \"pytest>=7.0.0\",\n    \"ruff>=0.1.0\",\n    \"mypy>=1.0.0\",\n]\ntest = [\n    \"pytest-cov>=4.0.0\",\n    \"pytest-mock>=3.10.0\",\n]\n\n# 工具配置\n[tool.ruff]\nline-length = 88\ntarget-version = \"py311\"\n\n[tool.ruff.lint]\nselect = [\"E\", \"F\", \"UP\", \"B\", \"SIM\", \"I\"]\n\n[tool.pytest.ini_options]\ntestpaths = [\"tests\"]\npython_files = [\"test_*.py\"]\n\n[build-system]\nrequires = [\"setuptools>=61.0\"]\nbuild-backend = \"setuptools.build_meta\"\n","toml",[85,383,384,392,397,402,408,414,420,426,432,438,444,450,456,462,468,474,480,486,491,496,502,508,514,520,526,532,537,543,549,555,560,565,571,577,583,589,594,600,606,611,617,623,629,634,640,646],{"__ignoreMap":57},[385,386,389],"span",{"class":387,"line":388},"line",1,[385,390,391],{},"[project]\n",[385,393,394],{"class":387,"line":232},[385,395,396],{},"name = \"my-awesome-project\"\n",[385,398,399],{"class":387,"line":237},[385,400,401],{},"version = \"1.0.0\"\n",[385,403,405],{"class":387,"line":404},4,[385,406,407],{},"description = \"A fantastic Python project\"\n",[385,409,411],{"class":387,"line":410},5,[385,412,413],{},"authors = [{name = \"Your Name\", email = \"you@example.com\"}]\n",[385,415,417],{"class":387,"line":416},6,[385,418,419],{},"license = {text = \"MIT\"}\n",[385,421,423],{"class":387,"line":422},7,[385,424,425],{},"readme = \"README.md\"\n",[385,427,429],{"class":387,"line":428},8,[385,430,431],{},"keywords = [\"python\", \"awesome\"]\n",[385,433,435],{"class":387,"line":434},9,[385,436,437],{},"classifiers = [\n",[385,439,441],{"class":387,"line":440},10,[385,442,443],{},"    \"Development Status :: 4 - Beta\",\n",[385,445,447],{"class":387,"line":446},11,[385,448,449],{},"    \"Programming Language :: Python :: 3.11\",\n",[385,451,453],{"class":387,"line":452},12,[385,454,455],{},"]\n",[385,457,459],{"class":387,"line":458},13,[385,460,461],{"emptyLinePlaceholder":253},"\n",[385,463,465],{"class":387,"line":464},14,[385,466,467],{},"# 依賴管理\n",[385,469,471],{"class":387,"line":470},15,[385,472,473],{},"dependencies = [\n",[385,475,477],{"class":387,"line":476},16,[385,478,479],{},"    \"requests>=2.28.0\",\n",[385,481,483],{"class":387,"line":482},17,[385,484,485],{},"    \"click>=8.0.0\",\n",[385,487,489],{"class":387,"line":488},18,[385,490,455],{},[385,492,494],{"class":387,"line":493},19,[385,495,461],{"emptyLinePlaceholder":253},[385,497,499],{"class":387,"line":498},20,[385,500,501],{},"# 開發依賴\n",[385,503,505],{"class":387,"line":504},21,[385,506,507],{},"[project.optional-dependencies]\n",[385,509,511],{"class":387,"line":510},22,[385,512,513],{},"dev = [\n",[385,515,517],{"class":387,"line":516},23,[385,518,519],{},"    \"pytest>=7.0.0\",\n",[385,521,523],{"class":387,"line":522},24,[385,524,525],{},"    \"ruff>=0.1.0\",\n",[385,527,529],{"class":387,"line":528},25,[385,530,531],{},"    \"mypy>=1.0.0\",\n",[385,533,535],{"class":387,"line":534},26,[385,536,455],{},[385,538,540],{"class":387,"line":539},27,[385,541,542],{},"test = [\n",[385,544,546],{"class":387,"line":545},28,[385,547,548],{},"    \"pytest-cov>=4.0.0\",\n",[385,550,552],{"class":387,"line":551},29,[385,553,554],{},"    \"pytest-mock>=3.10.0\",\n",[385,556,558],{"class":387,"line":557},30,[385,559,455],{},[385,561,563],{"class":387,"line":562},31,[385,564,461],{"emptyLinePlaceholder":253},[385,566,568],{"class":387,"line":567},32,[385,569,570],{},"# 工具配置\n",[385,572,574],{"class":387,"line":573},33,[385,575,576],{},"[tool.ruff]\n",[385,578,580],{"class":387,"line":579},34,[385,581,582],{},"line-length = 88\n",[385,584,586],{"class":387,"line":585},35,[385,587,588],{},"target-version = \"py311\"\n",[385,590,592],{"class":387,"line":591},36,[385,593,461],{"emptyLinePlaceholder":253},[385,595,597],{"class":387,"line":596},37,[385,598,599],{},"[tool.ruff.lint]\n",[385,601,603],{"class":387,"line":602},38,[385,604,605],{},"select = [\"E\", \"F\", \"UP\", \"B\", \"SIM\", \"I\"]\n",[385,607,609],{"class":387,"line":608},39,[385,610,461],{"emptyLinePlaceholder":253},[385,612,614],{"class":387,"line":613},40,[385,615,616],{},"[tool.pytest.ini_options]\n",[385,618,620],{"class":387,"line":619},41,[385,621,622],{},"testpaths = [\"tests\"]\n",[385,624,626],{"class":387,"line":625},42,[385,627,628],{},"python_files = [\"test_*.py\"]\n",[385,630,632],{"class":387,"line":631},43,[385,633,461],{"emptyLinePlaceholder":253},[385,635,637],{"class":387,"line":636},44,[385,638,639],{},"[build-system]\n",[385,641,643],{"class":387,"line":642},45,[385,644,645],{},"requires = [\"setuptools>=61.0\"]\n",[385,647,649],{"class":387,"line":648},46,[385,650,651],{},"build-backend = \"setuptools.build_meta\"\n",[46,653,654],{"id":654},"專案檔案架構比較",[15,656,657],{},[296,658,659],{},"使用 requirements.txt 的專案結構",[78,661,664],{"className":662,"code":663,"language":83},[81],"my-project/\n├── requirements.txt\n├── requirements-dev.txt\n├── requirements-test.txt\n├── setup.py        # setuptools 配置\n├── setup.cfg\n├── pytest.ini      # pytest 配置\n├── mypy.ini        # mypy 配置\n├── .coveragerc     # coverage 配置\n└── pyproject.toml  # 只給 ruff 用\n",[85,665,663],{"__ignoreMap":57},[15,667,668],{},[296,669,670],{},"使用 pyproject.toml 的專案結構",[78,672,675],{"className":673,"code":674,"language":83},[81],"my-project/\n├── pyproject.toml  # 所有配置都在這裡\n├── uv.lock         # 版本鎖定檔案\n└── src/\n",[85,676,674],{"__ignoreMap":57},[678,679],"hr",{},[11,681,682],{"id":682},"ruff",[174,684,685,688],{},[15,686,687],{},"ruff 是一個極速的 Python linter 和 code formatter，用 Rust 編寫，可以替代 Flake8、isort、Black 等多個工具。",[15,689,690],{},"它的目標是提供最快速、最全面的 Python 程式碼品質檢查和格式化解決方案。",[22,692,694],{"id":693},"ruff-的主要特點","ruff 的主要特點",[109,696,697,699,712],{},[112,698,298],{},[112,700,701,704],{},[296,702,703],{},"豐富的規則集",[109,705,706,709],{},[112,707,708],{},"支援 800+ 個 lint 規則",[112,710,711],{},"整合了 Flake8、isort、pycodestyle、pyflakes 等工具的規則",[112,713,714],{},[296,715,716],{},"無需配置即可使用",[678,718],{},[11,720,722],{"id":721},"為什麼要使用-uv-和-ruff","為什麼要使用 uv 和 ruff？",[109,724,725,741,747,764],{},[112,726,727,730,731],{},[296,728,729],{},"即時程式碼檢查","：ruff 的極速檢查讓程式碼品質控制變得無縫\n",[109,732,733],{},[112,734,735,736],{},"尤其公司專案目前很多軟體動輒幾千行（較少抽象與依功能分割檔案），使用傳統程式碼檢查與 format 工具需要接近 1 分鐘或甚至更多\n",[109,737,738],{},[112,739,740],{},"想像你每次 Ctrl + S 都要等 1 分鐘…",[112,742,743,746],{},[296,744,745],{},"穩定的工具鏈","：目前 uv 與 ruff 算是在各自領域（套件管理與 lint check / format）統一江湖的存在，未來不太會遇到需要再更換的情況",[112,748,749,750],{},"為了未來可能需要統一產品版本鋪路，使用 Git 協作的情況會越來越多\n",[109,751,752,758],{},[112,753,754,757],{},[296,755,756],{},"一致的環境","：uv 確保所有團隊成員使用相同的依賴版本",[112,759,760,763],{},[296,761,762],{},"統一的程式碼風格","：ruff 自動化程式碼風格檢查和格式化",[112,765,766,769],{},[296,767,768],{},"更快的 CI/CD","：極速的執行效能減少構建時間，現在也有使用 GKE 的專案，可以減少 runner 開銷",[678,771],{},[11,773,774],{"id":774},"遷移範例",[15,776,777],{},"假設原本使用 requirements.txt + 很多 lint error",[109,779,780],{},[112,781,782,783],{},"lint error 例如：\n",[109,784,785,790,793,796],{},[112,786,787],{},[85,788,789],{},"if a == None",[112,791,792],{},"assign var or import but never used",[112,794,795],{},"assign a python keyword as a var name",[112,797,798,799,802],{},"use ",[85,800,801],{},"format"," instead of f-string",[15,804,805],{},"有遇到任何問題找 AI 處理，或是看文檔的 best practices",[22,807,809],{"id":808},"_1-安裝與驗證","1. 安裝與驗證",[78,811,815],{"className":812,"code":813,"language":814,"meta":57,"style":57},"language-bash shiki shiki-themes github-light github-dark","# 安裝 uv\npip install uv\n\n# 驗證（否則要看一下文檔，根據作業系統有不同安裝方式）\nuv --version\n","bash",[85,816,817,823,836,840,845],{"__ignoreMap":57},[385,818,819],{"class":387,"line":388},[385,820,822],{"class":821},"sJ8bj","# 安裝 uv\n",[385,824,825,829,833],{"class":387,"line":232},[385,826,828],{"class":827},"sScJk","pip",[385,830,832],{"class":831},"sZZnC"," install",[385,834,835],{"class":831}," uv\n",[385,837,838],{"class":387,"line":237},[385,839,461],{"emptyLinePlaceholder":253},[385,841,842],{"class":387,"line":404},[385,843,844],{"class":821},"# 驗證（否則要看一下文檔，根據作業系統有不同安裝方式）\n",[385,846,847,849],{"class":387,"line":410},[385,848,278],{"class":827},[385,850,852],{"class":851},"sj4cs"," --version\n",[22,854,856],{"id":855},"_2-init-專案與安裝依賴","2. init 專案與安裝依賴",[78,858,860],{"className":812,"code":859,"language":814,"meta":57,"style":57},"# 移到專案根目錄\ncd {your_project}\n\n# init 專案\nuv init --no-readme\n\n# 從現有 requirements.txt 遷移\nuv add -r requirements.txt\n\n# 手動新增依賴 (if needed)\nuv add requests numpy\n\n# 安裝 ruff\nuv add --dev ruff\n",[85,861,862,867,875,879,884,894,898,903,916,920,925,937,941,946],{"__ignoreMap":57},[385,863,864],{"class":387,"line":388},[385,865,866],{"class":821},"# 移到專案根目錄\n",[385,868,869,872],{"class":387,"line":232},[385,870,871],{"class":851},"cd",[385,873,874],{"class":831}," {your_project}\n",[385,876,877],{"class":387,"line":237},[385,878,461],{"emptyLinePlaceholder":253},[385,880,881],{"class":387,"line":404},[385,882,883],{"class":821},"# init 專案\n",[385,885,886,888,891],{"class":387,"line":410},[385,887,278],{"class":827},[385,889,890],{"class":831}," init",[385,892,893],{"class":851}," --no-readme\n",[385,895,896],{"class":387,"line":416},[385,897,461],{"emptyLinePlaceholder":253},[385,899,900],{"class":387,"line":422},[385,901,902],{"class":821},"# 從現有 requirements.txt 遷移\n",[385,904,905,907,910,913],{"class":387,"line":428},[385,906,278],{"class":827},[385,908,909],{"class":831}," add",[385,911,912],{"class":851}," -r",[385,914,915],{"class":831}," requirements.txt\n",[385,917,918],{"class":387,"line":434},[385,919,461],{"emptyLinePlaceholder":253},[385,921,922],{"class":387,"line":440},[385,923,924],{"class":821},"# 手動新增依賴 (if needed)\n",[385,926,927,929,931,934],{"class":387,"line":446},[385,928,278],{"class":827},[385,930,909],{"class":831},[385,932,933],{"class":831}," requests",[385,935,936],{"class":831}," numpy\n",[385,938,939],{"class":387,"line":452},[385,940,461],{"emptyLinePlaceholder":253},[385,942,943],{"class":387,"line":458},[385,944,945],{"class":821},"# 安裝 ruff\n",[385,947,948,950,952,955],{"class":387,"line":464},[385,949,278],{"class":827},[385,951,909],{"class":831},[385,953,954],{"class":851}," --dev",[385,956,957],{"class":831}," ruff\n",[22,959,961],{"id":960},"_3-設定-python-版本","3. 設定 python 版本",[78,963,965],{"className":812,"code":964,"language":814,"meta":57,"style":57},"# 查看可用的 Python 版本\nuv python list\n\n# 範例輸出：\n# cpython-3.14.0b4-macos-aarch64-none                 \u003Cdownload available>\n# cpython-3.14.0b4+freethreaded-macos-aarch64-none    \u003Cdownload available>\n# cpython-3.13.5-macos-aarch64-none                   /opt/homebrew/bin/python3.13\n# cpython-3.13.5-macos-aarch64-none                   /opt/homebrew/bin/python3\n# cpython-3.13.5-macos-aarch64-none                   \u003Cdownload available>\n\n# 設定專案使用的 Python 版本（例如 3.10）\nuv python pin 3.10\n",[85,966,967,972,982,986,991,996,1001,1006,1011,1016,1020,1025],{"__ignoreMap":57},[385,968,969],{"class":387,"line":388},[385,970,971],{"class":821},"# 查看可用的 Python 版本\n",[385,973,974,976,979],{"class":387,"line":232},[385,975,278],{"class":827},[385,977,978],{"class":831}," python",[385,980,981],{"class":831}," list\n",[385,983,984],{"class":387,"line":237},[385,985,461],{"emptyLinePlaceholder":253},[385,987,988],{"class":387,"line":404},[385,989,990],{"class":821},"# 範例輸出：\n",[385,992,993],{"class":387,"line":410},[385,994,995],{"class":821},"# cpython-3.14.0b4-macos-aarch64-none                 \u003Cdownload available>\n",[385,997,998],{"class":387,"line":416},[385,999,1000],{"class":821},"# cpython-3.14.0b4+freethreaded-macos-aarch64-none    \u003Cdownload available>\n",[385,1002,1003],{"class":387,"line":422},[385,1004,1005],{"class":821},"# cpython-3.13.5-macos-aarch64-none                   /opt/homebrew/bin/python3.13\n",[385,1007,1008],{"class":387,"line":428},[385,1009,1010],{"class":821},"# cpython-3.13.5-macos-aarch64-none                   /opt/homebrew/bin/python3\n",[385,1012,1013],{"class":387,"line":434},[385,1014,1015],{"class":821},"# cpython-3.13.5-macos-aarch64-none                   \u003Cdownload available>\n",[385,1017,1018],{"class":387,"line":440},[385,1019,461],{"emptyLinePlaceholder":253},[385,1021,1022],{"class":387,"line":446},[385,1023,1024],{"class":821},"# 設定專案使用的 Python 版本（例如 3.10）\n",[385,1026,1027,1029,1031,1034],{"class":387,"line":452},[385,1028,278],{"class":827},[385,1030,978],{"class":831},[385,1032,1033],{"class":831}," pin",[385,1035,1036],{"class":851}," 3.10\n",[22,1038,1040,1041],{"id":1039},"_4-設定-pyprojecttoml","4. 設定 ",[85,1042,1043],{},"pyproject.toml",[15,1045,1046,1047,1050],{},"下面只是範例，基本上只需要注意 ",[85,1048,1049],{},"tool.ruff"," 相關的部分",[78,1052,1054],{"className":379,"code":1053,"language":381,"meta":57,"style":57},"[project]\nname = \"my-project\"\nversion = \"0.1.0\"\ndescription = \"Add your description here\"\ndependencies = [\n    \"numpy>=1.24.0\",\n    \"requests>=2.31.0\",\n]\n\n[tool.uv]\ndev-dependencies = [\n    \"black>=23.7.0\",\n    \"flake8>=6.0.0\",\n    \"pytest>=7.4.0\",\n    \"ruff>=0.1.6\",\n]\n\n[build-system]\nrequires = [\"hatchling\"]\nbuild-backend = \"hatchling.build\"\n\n# 加上 ruff 相關設置\n[tool.ruff]\nline-length = 88\ntarget-version = \"py310\"  # 改成你的 python 版本\n\n[tool.ruff.lint]\nselect = [\n    \"E\",   # pycodestyle errors\n    \"W\",   # pycodestyle warnings\n    \"F\",   # pyflakes\n    \"I\",   # isort\n    \"C\",   # flake8-comprehensions\n    \"B\",   # flake8-bugbear\n    \"UP\",  # pyupgrade\n]\nignore = [\n    \"E501\",  # line too long, handled by black\n    \"B008\",  # do not perform function calls in argument defaults\n    \"C901\",  # too complex\n    \"W191\",  # indentation contains tabs\n]\nexclude = [\n    \"tests/*\",\n    \"venv/*\",\n    \"*/site-packages/*\",\n    \"*/static/*\",\n    \"wsgi.py\",\n]\nfixable = [\"ALL\"]\nunfixable = []\n\n[tool.ruff.lint.per-file-ignores]\n\"tests/**/*\" = [\"S101\"]  # 測試中允許 assert\n\n[tool.ruff.format]\nquote-style = \"double\"\nindent-style = \"space\"\n",[85,1055,1056,1060,1065,1070,1075,1079,1084,1089,1093,1097,1102,1107,1112,1117,1122,1127,1131,1135,1139,1144,1149,1153,1158,1162,1166,1171,1175,1179,1184,1189,1194,1199,1204,1209,1214,1219,1223,1228,1233,1238,1243,1248,1252,1257,1262,1267,1272,1278,1284,1289,1295,1301,1306,1312,1318,1323,1329,1335],{"__ignoreMap":57},[385,1057,1058],{"class":387,"line":388},[385,1059,391],{},[385,1061,1062],{"class":387,"line":232},[385,1063,1064],{},"name = \"my-project\"\n",[385,1066,1067],{"class":387,"line":237},[385,1068,1069],{},"version = \"0.1.0\"\n",[385,1071,1072],{"class":387,"line":404},[385,1073,1074],{},"description = \"Add your description here\"\n",[385,1076,1077],{"class":387,"line":410},[385,1078,473],{},[385,1080,1081],{"class":387,"line":416},[385,1082,1083],{},"    \"numpy>=1.24.0\",\n",[385,1085,1086],{"class":387,"line":422},[385,1087,1088],{},"    \"requests>=2.31.0\",\n",[385,1090,1091],{"class":387,"line":428},[385,1092,455],{},[385,1094,1095],{"class":387,"line":434},[385,1096,461],{"emptyLinePlaceholder":253},[385,1098,1099],{"class":387,"line":440},[385,1100,1101],{},"[tool.uv]\n",[385,1103,1104],{"class":387,"line":446},[385,1105,1106],{},"dev-dependencies = [\n",[385,1108,1109],{"class":387,"line":452},[385,1110,1111],{},"    \"black>=23.7.0\",\n",[385,1113,1114],{"class":387,"line":458},[385,1115,1116],{},"    \"flake8>=6.0.0\",\n",[385,1118,1119],{"class":387,"line":464},[385,1120,1121],{},"    \"pytest>=7.4.0\",\n",[385,1123,1124],{"class":387,"line":470},[385,1125,1126],{},"    \"ruff>=0.1.6\",\n",[385,1128,1129],{"class":387,"line":476},[385,1130,455],{},[385,1132,1133],{"class":387,"line":482},[385,1134,461],{"emptyLinePlaceholder":253},[385,1136,1137],{"class":387,"line":488},[385,1138,639],{},[385,1140,1141],{"class":387,"line":493},[385,1142,1143],{},"requires = [\"hatchling\"]\n",[385,1145,1146],{"class":387,"line":498},[385,1147,1148],{},"build-backend = \"hatchling.build\"\n",[385,1150,1151],{"class":387,"line":504},[385,1152,461],{"emptyLinePlaceholder":253},[385,1154,1155],{"class":387,"line":510},[385,1156,1157],{},"# 加上 ruff 相關設置\n",[385,1159,1160],{"class":387,"line":516},[385,1161,576],{},[385,1163,1164],{"class":387,"line":522},[385,1165,582],{},[385,1167,1168],{"class":387,"line":528},[385,1169,1170],{},"target-version = \"py310\"  # 改成你的 python 版本\n",[385,1172,1173],{"class":387,"line":534},[385,1174,461],{"emptyLinePlaceholder":253},[385,1176,1177],{"class":387,"line":539},[385,1178,599],{},[385,1180,1181],{"class":387,"line":545},[385,1182,1183],{},"select = [\n",[385,1185,1186],{"class":387,"line":551},[385,1187,1188],{},"    \"E\",   # pycodestyle errors\n",[385,1190,1191],{"class":387,"line":557},[385,1192,1193],{},"    \"W\",   # pycodestyle warnings\n",[385,1195,1196],{"class":387,"line":562},[385,1197,1198],{},"    \"F\",   # pyflakes\n",[385,1200,1201],{"class":387,"line":567},[385,1202,1203],{},"    \"I\",   # isort\n",[385,1205,1206],{"class":387,"line":573},[385,1207,1208],{},"    \"C\",   # flake8-comprehensions\n",[385,1210,1211],{"class":387,"line":579},[385,1212,1213],{},"    \"B\",   # flake8-bugbear\n",[385,1215,1216],{"class":387,"line":585},[385,1217,1218],{},"    \"UP\",  # pyupgrade\n",[385,1220,1221],{"class":387,"line":591},[385,1222,455],{},[385,1224,1225],{"class":387,"line":596},[385,1226,1227],{},"ignore = [\n",[385,1229,1230],{"class":387,"line":602},[385,1231,1232],{},"    \"E501\",  # line too long, handled by black\n",[385,1234,1235],{"class":387,"line":608},[385,1236,1237],{},"    \"B008\",  # do not perform function calls in argument defaults\n",[385,1239,1240],{"class":387,"line":613},[385,1241,1242],{},"    \"C901\",  # too complex\n",[385,1244,1245],{"class":387,"line":619},[385,1246,1247],{},"    \"W191\",  # indentation contains tabs\n",[385,1249,1250],{"class":387,"line":625},[385,1251,455],{},[385,1253,1254],{"class":387,"line":631},[385,1255,1256],{},"exclude = [\n",[385,1258,1259],{"class":387,"line":636},[385,1260,1261],{},"    \"tests/*\",\n",[385,1263,1264],{"class":387,"line":642},[385,1265,1266],{},"    \"venv/*\",\n",[385,1268,1269],{"class":387,"line":648},[385,1270,1271],{},"    \"*/site-packages/*\",\n",[385,1273,1275],{"class":387,"line":1274},47,[385,1276,1277],{},"    \"*/static/*\",\n",[385,1279,1281],{"class":387,"line":1280},48,[385,1282,1283],{},"    \"wsgi.py\",\n",[385,1285,1287],{"class":387,"line":1286},49,[385,1288,455],{},[385,1290,1292],{"class":387,"line":1291},50,[385,1293,1294],{},"fixable = [\"ALL\"]\n",[385,1296,1298],{"class":387,"line":1297},51,[385,1299,1300],{},"unfixable = []\n",[385,1302,1304],{"class":387,"line":1303},52,[385,1305,461],{"emptyLinePlaceholder":253},[385,1307,1309],{"class":387,"line":1308},53,[385,1310,1311],{},"[tool.ruff.lint.per-file-ignores]\n",[385,1313,1315],{"class":387,"line":1314},54,[385,1316,1317],{},"\"tests/**/*\" = [\"S101\"]  # 測試中允許 assert\n",[385,1319,1321],{"class":387,"line":1320},55,[385,1322,461],{"emptyLinePlaceholder":253},[385,1324,1326],{"class":387,"line":1325},56,[385,1327,1328],{},"[tool.ruff.format]\n",[385,1330,1332],{"class":387,"line":1331},57,[385,1333,1334],{},"quote-style = \"double\"\n",[385,1336,1338],{"class":387,"line":1337},58,[385,1339,1340],{},"indent-style = \"space\"\n",[22,1342,1344],{"id":1343},"_5-安裝依賴","5. 安裝依賴",[78,1346,1348],{"className":812,"code":1347,"language":814,"meta":57,"style":57},"# 這會產生 uv.lock，第一次加上 --frozen 就好\nuv sync --frozen\n",[85,1349,1350,1355],{"__ignoreMap":57},[385,1351,1352],{"class":387,"line":388},[385,1353,1354],{"class":821},"# 這會產生 uv.lock，第一次加上 --frozen 就好\n",[385,1356,1357,1359,1362],{"class":387,"line":232},[385,1358,278],{"class":827},[385,1360,1361],{"class":831}," sync",[385,1363,1364],{"class":851}," --frozen\n",[22,1366,1368],{"id":1367},"_6-ide-設置如果你使用-vs-code","6. IDE 設置（如果你使用 VS Code）",[1370,1371,1372,1375,1378],"ol",{},[112,1373,1374],{},"安裝 VS Code extensions（如果你使用 VS Code，可以去找一下 ruff 的 extension，可以使用 IDE 的修復輔助）",[112,1376,1377],{},"Ctrl + Shift + P：>Format Document With… 將 ruff 設為 default formatter",[112,1379,1380],{},"確保 format on save = true",[22,1382,1384],{"id":1383},"_7-清理現有代碼","7. 清理現有代碼",[78,1386,1388],{"className":812,"code":1387,"language":814,"meta":57,"style":57},"# 先看有幾個錯誤，以及可以使用 --fix 自動修復的項目\nruff check\n\n# 自動修復\nruff check --fix\n\n# 接下來開始清理 code style 問題\n# 啟用 hot reload，可以快速修復已存在的錯誤\nruff check --watch\n",[85,1389,1390,1395,1402,1406,1411,1421,1425,1430,1435],{"__ignoreMap":57},[385,1391,1392],{"class":387,"line":388},[385,1393,1394],{"class":821},"# 先看有幾個錯誤，以及可以使用 --fix 自動修復的項目\n",[385,1396,1397,1399],{"class":387,"line":232},[385,1398,682],{"class":827},[385,1400,1401],{"class":831}," check\n",[385,1403,1404],{"class":387,"line":237},[385,1405,461],{"emptyLinePlaceholder":253},[385,1407,1408],{"class":387,"line":404},[385,1409,1410],{"class":821},"# 自動修復\n",[385,1412,1413,1415,1418],{"class":387,"line":410},[385,1414,682],{"class":827},[385,1416,1417],{"class":831}," check",[385,1419,1420],{"class":851}," --fix\n",[385,1422,1423],{"class":387,"line":416},[385,1424,461],{"emptyLinePlaceholder":253},[385,1426,1427],{"class":387,"line":422},[385,1428,1429],{"class":821},"# 接下來開始清理 code style 問題\n",[385,1431,1432],{"class":387,"line":428},[385,1433,1434],{"class":821},"# 啟用 hot reload，可以快速修復已存在的錯誤\n",[385,1436,1437,1439,1441],{"class":387,"line":434},[385,1438,682],{"class":827},[385,1440,1417],{"class":831},[385,1442,1443],{"class":851}," --watch\n",[22,1445,1447],{"id":1446},"_8-執行專案","8. 執行專案",[78,1449,1451],{"className":812,"code":1450,"language":814,"meta":57,"style":57},"uv run pytest\nuv run python src/main.py\n",[85,1452,1453,1463],{"__ignoreMap":57},[385,1454,1455,1457,1460],{"class":387,"line":388},[385,1456,278],{"class":827},[385,1458,1459],{"class":831}," run",[385,1461,1462],{"class":831}," pytest\n",[385,1464,1465,1467,1469,1471],{"class":387,"line":232},[385,1466,278],{"class":827},[385,1468,1459],{"class":831},[385,1470,978],{"class":831},[385,1472,1473],{"class":831}," src/main.py\n",[22,1475,1477],{"id":1476},"_9-dockerfile-範例","9. Dockerfile 範例",[78,1479,1483],{"className":1480,"code":1481,"language":1482,"meta":57,"style":57},"language-dockerfile shiki shiki-themes github-light github-dark","# 使用官方 Python 基礎映像\nFROM python:3.11-slim\n\n# 設定工作目錄\nWORKDIR /app\n\n# 安裝 uv\nCOPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /usr/local/bin/\n\n# 複製依賴文件\nCOPY pyproject.toml uv.lock ./\n\n# 安裝依賴（不包含開發依賴）\nRUN uv sync --frozen --no-dev\n\n# 複製應用程式代碼\nCOPY . .\n\n# 設定 Python 路徑\nENV PATH=\"/app/.venv/bin:$PATH\"\n\n# 暴露端口（根據你的應用程式調整）\nEXPOSE 8000\n\n# 執行應用程式\nCMD [\"python\", \"src/main.py\"]\n","dockerfile",[85,1484,1485,1490,1495,1499,1504,1509,1513,1517,1522,1526,1531,1536,1540,1545,1550,1554,1559,1564,1568,1573,1578,1582,1587,1592,1596,1601],{"__ignoreMap":57},[385,1486,1487],{"class":387,"line":388},[385,1488,1489],{},"# 使用官方 Python 基礎映像\n",[385,1491,1492],{"class":387,"line":232},[385,1493,1494],{},"FROM python:3.11-slim\n",[385,1496,1497],{"class":387,"line":237},[385,1498,461],{"emptyLinePlaceholder":253},[385,1500,1501],{"class":387,"line":404},[385,1502,1503],{},"# 設定工作目錄\n",[385,1505,1506],{"class":387,"line":410},[385,1507,1508],{},"WORKDIR /app\n",[385,1510,1511],{"class":387,"line":416},[385,1512,461],{"emptyLinePlaceholder":253},[385,1514,1515],{"class":387,"line":422},[385,1516,822],{},[385,1518,1519],{"class":387,"line":428},[385,1520,1521],{},"COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /usr/local/bin/\n",[385,1523,1524],{"class":387,"line":434},[385,1525,461],{"emptyLinePlaceholder":253},[385,1527,1528],{"class":387,"line":440},[385,1529,1530],{},"# 複製依賴文件\n",[385,1532,1533],{"class":387,"line":446},[385,1534,1535],{},"COPY pyproject.toml uv.lock ./\n",[385,1537,1538],{"class":387,"line":452},[385,1539,461],{"emptyLinePlaceholder":253},[385,1541,1542],{"class":387,"line":458},[385,1543,1544],{},"# 安裝依賴（不包含開發依賴）\n",[385,1546,1547],{"class":387,"line":464},[385,1548,1549],{},"RUN uv sync --frozen --no-dev\n",[385,1551,1552],{"class":387,"line":470},[385,1553,461],{"emptyLinePlaceholder":253},[385,1555,1556],{"class":387,"line":476},[385,1557,1558],{},"# 複製應用程式代碼\n",[385,1560,1561],{"class":387,"line":482},[385,1562,1563],{},"COPY . .\n",[385,1565,1566],{"class":387,"line":488},[385,1567,461],{"emptyLinePlaceholder":253},[385,1569,1570],{"class":387,"line":493},[385,1571,1572],{},"# 設定 Python 路徑\n",[385,1574,1575],{"class":387,"line":498},[385,1576,1577],{},"ENV PATH=\"/app/.venv/bin:$PATH\"\n",[385,1579,1580],{"class":387,"line":504},[385,1581,461],{"emptyLinePlaceholder":253},[385,1583,1584],{"class":387,"line":510},[385,1585,1586],{},"# 暴露端口（根據你的應用程式調整）\n",[385,1588,1589],{"class":387,"line":516},[385,1590,1591],{},"EXPOSE 8000\n",[385,1593,1594],{"class":387,"line":522},[385,1595,461],{"emptyLinePlaceholder":253},[385,1597,1598],{"class":387,"line":528},[385,1599,1600],{},"# 執行應用程式\n",[385,1602,1603],{"class":387,"line":534},[385,1604,1605],{},"CMD [\"python\", \"src/main.py\"]\n",[678,1607],{},[11,1609,1610],{"id":1610},"參考資料",[109,1612,1613,1620,1627],{},[112,1614,1615],{},[29,1616,1619],{"href":1617,"rel":1618},"https://astral.sh/",[33],"Astral: High-performance Python tooling",[112,1621,1622],{},[29,1623,1626],{"href":1624,"rel":1625},"https://docs.astral.sh/uv/",[33],"uv Documentation",[112,1628,1629],{},[29,1630,1633],{"href":1631,"rel":1632},"https://docs.astral.sh/ruff/",[33],"Ruff Documentation",[1635,1636,1637],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":57,"searchDepth":232,"depth":232,"links":1639},[1640,1641,1645,1646,1647,1648,1649,1651,1652,1653,1654,1655],{"id":289,"depth":232,"text":290},{"id":324,"depth":232,"text":325,"children":1642},[1643,1644],{"id":375,"depth":237,"text":376},{"id":654,"depth":237,"text":654},{"id":693,"depth":232,"text":694},{"id":808,"depth":232,"text":809},{"id":855,"depth":232,"text":856},{"id":960,"depth":232,"text":961},{"id":1039,"depth":232,"text":1650},"4. 設定 pyproject.toml",{"id":1343,"depth":232,"text":1344},{"id":1367,"depth":232,"text":1368},{"id":1383,"depth":232,"text":1384},{"id":1446,"depth":232,"text":1447},{"id":1476,"depth":232,"text":1477},"2025-10-24","介紹 uv 和 Ruff，兩個最佳的 Python 工具鏈，以及如何遷移到 uv 和 Ruff。",{},"/blog/uv-and-ruff",{"title":264,"description":1657},"blog/uv-and-ruff",[1663,258,259,278,1664],"Python","Ruff","-zRvt4hekmdP0cJ82m1OICrMfHIMFKCtweqM3DQ743s",{"id":1667,"title":1668,"author":6,"body":1669,"category":246,"date":1993,"description":1994,"draft":249,"extension":250,"image":1995,"meta":1996,"navigation":253,"path":1997,"seo":1998,"stem":1999,"tags":2000,"__hash__":2008},"blog/blog/20260304-troublemaker.md","關於我一天在公司 AWS 開發帳號花掉 14000 鎂的那回事",{"type":8,"value":1670,"toc":1975},[1671,1678,1681,1685,1692,1695,1704,1713,1720,1723,1737,1740,1745,1748,1751,1758,1761,1764,1767,1771,1774,1778,1781,1786,1795,1807,1810,1818,1823,1826,1829,1834,1837,1844,1849,1853,1856,1867,1871,1874,1881,1888,1891,1894,1897,1900,1903,1906,1909,1912,1916,1919,1922,1928,1932,1935,1946,1950,1953,1960,1963,1966,1969,1972],[15,1672,1673,1674,1677],{},"身為工程師，你可能聽過同事不小心把 Production 資料庫砍了、或是忘記關 EC2 多燒了幾百塊。但你有聽過",[296,1675,1676],{},"用雲服務一天燒掉 14,000 美金","的嗎？",[15,1679,1680],{},"沒錯，這件事發生在我身上...嗎？",[22,1682,1684],{"id":1683},"tldr","TL;DR",[15,1686,1687,1688,1691],{},"在公司開發帳號 survey AWS Bedrock AgentCore Policy 功能，試用了 Cedar Policy Generator。幾天後帳單突然出現一筆 ",[296,1689,1690],{},"$14,000+ USD 的單日費用","。經過 CloudTrail 排查確認是 Cedar Generator 觸發的異常計費，最終 AWS 承認是 Bedrock 端的計費 bug，修復並校正了帳單。",[22,1693,1694],{"id":1694},"事發經過",[15,1696,1697,1698,1703],{},"3/4 我因為看到 ",[29,1699,1702],{"href":1700,"rel":1701},"https://aws.amazon.com/bedrock/agentcore/",[33],"AgentCore"," 新 Feature - Policy 剛上線，所以需要 Survey 一下。",[15,1705,1706,1707,1712],{},"AgentCore Policy 讓你可以用 ",[29,1708,1711],{"href":1709,"rel":1710},"https://www.cedarpolicy.com/",[33],"Cedar"," 語言來定義 Agent 的授權策略，控制 Agent 能存取哪些工具和資源。Cedar 是 AWS 開源的授權語言，語法可讀性很高，設計上是要讓非工程師也能看得懂的那種。",[15,1714,1715,1716,1719],{},"而 AgentCore 很貼心地提供了一個 ",[296,1717,1718],{},"Natural Language Policy Generator","——你用自然語言描述你要的權限規則，它就幫你生成對應的 Cedar Policy。聽起來很 User-friendly 對吧？",[15,1721,1722],{},"我就照著文件走了一遍流程：",[1370,1724,1725,1728,1731,1734],{},[112,1726,1727],{},"建了一個 Policy Engine",[112,1729,1730],{},"試了幾次 Cedar Policy Generator（用自然語言描述轉成 Cedar）",[112,1732,1733],{},"確認功能可以正常運作",[112,1735,1736],{},"收工，覺得這功能還不錯",[15,1738,1739],{},"整個過程大概就一兩個小時，正常的 survey 流程。",[15,1741,1742],{},[296,1743,1744],{},"然後就沒有然後了——直到五天後。",[22,1746,1747],{"id":1747},"帳單爆炸",[15,1749,1750],{},"後來請假看完中華隊在 WBC 的比賽，打贏韓國那場實在很感動，回來上班的第一天，3/9 下午，Slack突然被狂 tag，我被我們公司的 Billing Manager & Team Lead 問為何會有一筆費用產生，我也趕緊地打開CE，一個讓我愣住的數字：",[174,1752,1753],{},[15,1754,1755],{},[296,1756,1757],{},"3/4 單日Agentcore產生了 $14,000+ USD的費用",[15,1759,1760],{},"我先是以為自己眼花了。重新整理頁面，數字還在那裡。",[15,1762,1763],{},"完了... 哪裡搞錯了吧？",[22,1765,1766],{"id":1766},"排查過程",[46,1768,1770],{"id":1769},"step-1先開-support-ticket","Step 1：先開 Support Ticket",[15,1772,1773],{},"不管三七二十一，先開 AWS Support Ticket 回報異常帳單。把時間範圍、帳號資訊、異常金額都附上去，讓 Support 那邊開始調查。",[46,1775,1777],{"id":1776},"step-2自己也同步排查","Step 2：自己也同步排查",[15,1779,1780],{},"等 Support 回覆的同時，我也開始自己排查紀錄。",[15,1782,1783],{},[296,1784,1785],{},"確認 AgentCore Policy 的收費機制",[15,1787,1788,1789,1794],{},"根據 ",[29,1790,1793],{"href":1791,"rel":1792},"https://aws.amazon.com/bedrock/agentcore/pricing/",[33],"AgentCore 定價頁面","，Policy 的計費主要是：",[109,1796,1797,1804],{},[112,1798,1799,1800,1803],{},"Cedar Policy Generator：",[296,1801,1802],{},"按 input token 數量計費","（每 1,000 tokens）",[112,1805,1806],{},"Policy Engine 的 Authorization 請求：按請求數計費",[15,1808,1809],{},"稍微思考一下：",[1370,1811,1812,1815],{},[112,1813,1814],{},"我當天的使用量根本不可能撐到這個金額。就算我瘋狂打 Generator 也打不出 $14,000，同事跟我說 Billing 那邊顯示 Policy 使用了 109 1M Tokens (接近 1.1 億，我甚至不知道一天要怎麼用掉那麼多 Tokens...)。",[112,1816,1817],{},"我的 Policy Engine 並沒有 Attach Gateway，所以也沒有真正的使用 Policy 這個 Feature。",[15,1819,1820],{},[296,1821,1822],{},"確認 Runtime 沒有產生額外費用",[15,1824,1825],{},"AgentCore Runtime 是按 CPU 和記憶體的秒級消耗來計費的。我需要確保沒有遺留的 Runtime 在背景持續運作，或者跟 Gateway 掛鉤產生連鎖費用。",[15,1827,1828],{},"檢查結果：沒有任何遺留資源在跑。",[15,1830,1831],{},[296,1832,1833],{},"CloudTrail",[15,1835,1836],{},"我拉出了那段時間的 CloudTrail 紀錄，逐筆檢查跟 AgentCore 相關的 API 呼叫。",[15,1838,1839,1840,1843],{},"最終鎖定是 ",[85,1841,1842],{},"StartPolicyGeneration"," 這個 API 呼叫。從紀錄上看，我確實只有呼叫了3次，請求量完全不合理對應到那個帳單金額。",[15,1845,1846],{},[296,1847,1848],{},"結論：應該不是我的使用量有問題，是計費那邊有問題。",[46,1850,1852],{"id":1851},"step-3跟-support-同步","Step 3：跟 Support 同步",[15,1854,1855],{},"我把 CloudTrail 的排查結果整理好，回覆到 Support Ticket 上。附上了：",[109,1857,1858,1861,1864],{},[112,1859,1860],{},"明確的 API 呼叫時間和次數",[112,1862,1863],{},"計費金額的不合理性說明",[112,1865,1866],{},"我這邊已經確認沒有遺留資源",[46,1868,1870],{"id":1869},"step-4aws-確認是計費-bug","Step 4：AWS 確認是計費 Bug",[15,1872,1873],{},"AWS Support 將 case 轉給了 Bedrock 團隊。Bedrock 團隊調查後確認：",[174,1875,1876],{},[15,1877,1878],{},[296,1879,1880],{},"Cedar Policy Generator 存在計費問題，導致實際計費金額遠超正常使用量應有的費用。",[15,1882,1883,1884,1887],{},"他們修復了這個計費 bug，並且",[296,1885,1886],{},"校正了帳單","。",[15,1889,1890],{},"結案。",[22,1892,1893],{"id":1893},"心理狀態",[15,1895,1896],{},"看到 $14,000 這個數字的時候，腦袋裡跑過各種最壞的劇本——會不會要自己賠？這是我好幾個月的薪水總和，光想就覺得可怕。",[15,1898,1899],{},"即使理性上告訴自己先查原因再說，甚至基本上確定不是我這邊的問題，但焦慮感還是壓不住。",[15,1901,1902],{},"這幾天嚴重睡眠不足，每天躺在床上腦袋還在轉「到底是哪裡出問題」。腸胃也跟著出狀況，一直拉肚子。壓力對身體的影響比我想像中來得直接。",[15,1904,1905],{},"最後當 CloudTrail 的證據越來越明確指向計費問題，Support 也確認 Bedrock 團隊發現問題並正在進行修復，我的心情才開始慢慢平復。",[22,1907,1908],{"id":1908},"事後反思",[15,1910,1911],{},"雖然這次最後證實是 AWS 的計費 bug，但整個事件讓我學到了很多。",[46,1913,1915],{"id":1914},"_1-工作紀錄真的很重要","1. 工作紀錄真的很重要",[15,1917,1918],{},"當下會感到慌張的原因，很大一部分來自 Context 不足，一來我不確定 Agentcore Policy 是如何計費，為何可以用到那麼貴？二來我不知道我5天前具體做了什麼操作。",[15,1920,1921],{},"如果我當初 survey 的時候沒有留下操作紀錄，排查的時候會更加困難。CloudTrail 能幫你查到 API 呼叫，但你自己當時在做什麼、為什麼做，這些 context 只有你自己知道。",[15,1923,1924,1927],{},[296,1925,1926],{},"養成習慣：每次操作不熟的雲服務時，簡單記錄一下你做了什麼。"," 不需要多詳細，一個簡單的筆記或是 Slack 訊息就夠了。關鍵時刻這些紀錄可以救你一命。",[46,1929,1931],{"id":1930},"_2-使用前搞懂計費方式","2. 使用前搞懂計費方式",[15,1933,1934],{},"這聽起來像廢話，但真的很多人（包括我）在 survey 新服務的時候會直接跳進去玩，不會先仔細看定價頁面。",[109,1936,1937,1940,1943],{},[112,1938,1939],{},"開始 survey 前，先看過定價頁面",[112,1941,1942],{},"特別注意按量計費的服務，搞清楚「量」是怎麼定義的",[112,1944,1945],{},"開發帳號最好設定 Budget Alert，超過閾值自動通知",[46,1947,1949],{"id":1948},"_3-先了解公司的處理流程","3. 先了解公司的處理流程",[15,1951,1952],{},"事發的時候，我其實不太確定公司對這種事情的態度和處理方式。後來跟主管報告時，主管第一句話就是：",[174,1954,1955],{},[15,1956,1957],{},[296,1958,1959],{},"「不可能叫你賠的，先搞清楚狀況就好。」",[15,1961,1962],{},"這句話讓我放下了很大的心理負擔。每間公司的文化不同，但我想大部分公司都不會因為合理操作導致的意外費用而讓員工賠償。",[22,1964,1965],{"id":1965},"結語",[15,1967,1968],{},"回頭看這件事，蠻慶幸最後不是我的問題，也慶幸公司的主管很 Nice",[15,1970,1971],{},"這次經歷讓我建立起了面對雲端成本異常的 SOP。以前覺得帳單管理是 FinOps 團隊的事，現在覺得每個會碰到雲服務的工程師都應該有基本的成本意識。",[15,1973,1974],{},"題外話，這讓我想到我大學時期第一次買股票的時候，我買了3股台積電花了大概 2000塊吧，當時每天看著損益正負三四百塊就能很影響我的心情，影響我做事與學習的效率，直到今天可能每天的損益就是一個月的薪水，我還是照常做著自己的事情。我想有了這次經驗，肯定能讓我在未來遇到類似的事情的時候心態更穩健吧。",{"title":57,"searchDepth":232,"depth":232,"links":1976},[1977,1978,1979,1980,1986,1987,1992],{"id":1683,"depth":232,"text":1684},{"id":1694,"depth":232,"text":1694},{"id":1747,"depth":232,"text":1747},{"id":1766,"depth":232,"text":1766,"children":1981},[1982,1983,1984,1985],{"id":1769,"depth":237,"text":1770},{"id":1776,"depth":237,"text":1777},{"id":1851,"depth":237,"text":1852},{"id":1869,"depth":237,"text":1870},{"id":1893,"depth":232,"text":1893},{"id":1908,"depth":232,"text":1908,"children":1988},[1989,1990,1991],{"id":1914,"depth":237,"text":1915},{"id":1930,"depth":237,"text":1931},{"id":1948,"depth":237,"text":1949},{"id":1965,"depth":232,"text":1965},"2026-03-18","Survey AWS AgentCore Policy完，過了幾天以後才發現帳單噴了14000...","/images/blog/20260304-troublemaker/banner.png",{},"/blog/20260304-troublemaker",{"title":1668,"description":1994},"blog/20260304-troublemaker",[2001,2002,2003,2004,2005,2006,2007],"AWS","雲端","成本控管","經驗分享","踩雷","DevOps","雲服務","6QyxVCpq8RjfovhyvoS0butlFVU7bQKJ1pfTGyL4M1Q",{"id":2010,"title":2011,"author":6,"body":2012,"category":246,"date":1656,"description":3407,"draft":249,"extension":250,"image":57,"meta":3408,"navigation":253,"path":3409,"seo":3410,"stem":3411,"tags":3412,"__hash__":3415},"blog/blog/concurrency.md","Python 並發處理方法",{"type":8,"value":2013,"toc":3386},[2014,2016,2019,2022,2025,2029,2076,2080,2115,2119,2152,2156,2174,2177,2325,2328,2965,2968,3214,3218,3224,3242,3246,3251,3269,3272,3277,3281,3284,3288,3291,3305,3309,3312,3358,3360,3383],[22,2015,270],{"id":269},[15,2017,2018],{},"若專案內有遇到效能瓶頸，這可能可以幫助你加速。",[15,2020,2021],{},"Python 提供了幾種並發方式，每種方式適用於不同類型的任務。",[22,2023,2024],{"id":2024},"各方式介紹",[46,2026,2028],{"id":2027},"_1-多線程multithreading","1. 多線程（Multithreading）",[109,2030,2031,2037,2058,2064,2070],{},[112,2032,2033,2036],{},[296,2034,2035],{},"特點","：由於 GIL 的存在，多線程無法實現真正的並行，線程間由解釋器切換執行。",[112,2038,2039,2042,2043,2046,2047],{},[296,2040,2041],{},"適用場景","：適合 ",[296,2044,2045],{},"I/O 綁定任務","，如：\n",[109,2048,2049,2052,2055],{},[112,2050,2051],{},"API 呼叫",[112,2053,2054],{},"檔案讀寫",[112,2056,2057],{},"使用者輸入等待",[112,2059,2060,2063],{},[296,2061,2062],{},"限制","：在 CPU 綁定任務（如計算 Fibonacci 數列）中，效能與單線程迴圈相差無幾。",[112,2065,2066,2069],{},[296,2067,2068],{},"優點","：線程間資料共享簡單，記憶體開銷低。",[112,2071,2072,2075],{},[296,2073,2074],{},"缺點","：受 GIL 限制，無法利用多核心。",[46,2077,2079],{"id":2078},"_2-多進程multiprocessing","2. 多進程（Multiprocessing）",[109,2081,2082,2087,2105,2110],{},[112,2083,2084,2086],{},[296,2085,2035],{},"：每個進程擁有獨立的 Python 解釋器和 GIL，因此可以在不同 CPU 核心上實現真正的並行。",[112,2088,2089,2042,2091,2046,2094],{},[296,2090,2041],{},[296,2092,2093],{},"CPU 綁定任務",[109,2095,2096,2099,2102],{},[112,2097,2098],{},"數學運算",[112,2100,2101],{},"圖像處理",[112,2103,2104],{},"日誌分析",[112,2106,2107,2109],{},[296,2108,2068],{},"：繞過 GIL，實現真並行，速度較快。",[112,2111,2112,2114],{},[296,2113,2074],{},"：記憶體使用量高，進程間資料共享較複雜（需使用管道或共享記憶體）。",[46,2116,2118],{"id":2117},"_3-asyncio","3. Asyncio",[109,2120,2121,2126,2142,2147],{},[112,2122,2123,2125],{},[296,2124,2035],{},"：使用協程（coroutines）實現非阻塞的並發，適合 I/O 綁定任務。",[112,2127,2128,2130,2131],{},[296,2129,2041],{},"：\n",[109,2132,2133,2136,2139],{},[112,2134,2135],{},"網路請求",[112,2137,2138],{},"檔案 I/O",[112,2140,2141],{},"其他等待外部資源的任務",[112,2143,2144,2146],{},[296,2145,2068],{},"：輕量，記憶體使用效率高。",[112,2148,2149,2151],{},[296,2150,2074],{},"：不適合 CPU 密集型任務，因為它並非真正的並行。",[46,2153,2155],{"id":2154},"_4-第三方庫","4. 第三方庫",[109,2157,2158,2164,2169],{},[112,2159,2160,2163],{},[296,2161,2162],{},"例子","：Cython、NumPy、Pandas、PyTorch 等。",[112,2165,2166,2168],{},[296,2167,2035],{},"：這些庫通常使用 C 語言實現，部分操作可繞過 GIL，提供高效能。",[112,2170,2171,2173],{},[296,2172,2041],{},"：特定計算密集型任務（如矩陣運算）。",[22,2175,2176],{"id":2176},"特性比較表",[2178,2179,2180,2202],"table",{},[2181,2182,2183],"thead",{},[2184,2185,2186,2190,2193,2196,2199],"tr",{},[2187,2188,2189],"th",{},"特性",[2187,2191,2192],{},"Multi-threading",[2187,2194,2195],{},"Multi-processing",[2187,2197,2198],{},"AsyncIO",[2187,2200,2201],{},"Disabled GIL",[2203,2204,2205,2223,2240,2258,2275,2291,2306],"tbody",{},[2184,2206,2207,2213,2216,2219,2221],{},[2208,2209,2210],"td",{},[296,2211,2212],{},"並行",[2208,2214,2215],{},"❌",[2208,2217,2218],{},"✅",[2208,2220,2215],{},[2208,2222,2218],{},[2184,2224,2225,2230,2233,2236,2238],{},[2208,2226,2227],{},[296,2228,2229],{},"記憶體共享",[2208,2231,2232],{},"容易",[2208,2234,2235],{},"困難",[2208,2237,2232],{},[2208,2239,2232],{},[2184,2241,2242,2247,2250,2253,2255],{},[2208,2243,2244],{},[296,2245,2246],{},"記憶體使用",[2208,2248,2249],{},"低",[2208,2251,2252],{},"高",[2208,2254,2249],{},[2208,2256,2257],{},"中等",[2184,2259,2260,2265,2268,2271,2273],{},[2208,2261,2262],{},[296,2263,2264],{},"CPU 密集型",[2208,2266,2267],{},"差",[2208,2269,2270],{},"好",[2208,2272,2267],{},[2208,2274,2270],{},[2184,2276,2277,2282,2284,2286,2289],{},[2208,2278,2279],{},[296,2280,2281],{},"I/O 密集型",[2208,2283,2270],{},[2208,2285,2257],{},[2208,2287,2288],{},"最佳",[2208,2290,2270],{},[2184,2292,2293,2298,2300,2302,2304],{},[2208,2294,2295],{},[296,2296,2297],{},"實作複雜度",[2208,2299,2257],{},[2208,2301,2252],{},[2208,2303,2257],{},[2208,2305,2252],{},[2184,2307,2308,2313,2316,2319,2322],{},[2208,2309,2310],{},[296,2311,2312],{},"競爭條件風險",[2208,2314,2315],{},"低（GIL保護）",[2208,2317,2318],{},"低（隔離）",[2208,2320,2321],{},"無（單執行緒）",[2208,2323,2324],{},"高（需手動處理）",[22,2326,2327],{"id":2327},"範例程式碼",[78,2329,2333],{"className":2330,"code":2331,"language":2332,"meta":57,"style":57},"language-python shiki shiki-themes github-light github-dark","import time\nimport threading\nimport multiprocessing\nimport asyncio\nimport sys\nfrom concurrent.futures import ThreadPoolExecutor\nfrom functools import wraps\n\n# 計時裝飾器，用於測量執行時間\ndef timing_decorator(func):\n    @wraps(func)\n    def wrapper(*args, **kwargs):\n        start = time.time()\n        result = func(*args, **kwargs)\n        end = time.time()\n        print(f\"{func.__name__} 執行時間: {end - start:.4f} 秒\")\n        return result\n    return wrapper\n\n# CPU 密集型任務：計算 Fibonacci 數列\ndef fib(n):\n    if n \u003C= 1:\n        return n\n    return fib(n - 1) + fib(n - 2)\n\n# I/O 綁定任務：模擬網路請求（全局函數）\ndef sync_io_task():\n    time.sleep(1)  # 模擬 1 秒的 I/O 延遲\n\n# 異步 I/O 任務\nasync def io_bound_task():\n    await asyncio.sleep(1)  # 模擬 1 秒的 I/O 延遲\n    return \"I/O 任務完成\"\n\n# 多線程實現\n@timing_decorator\ndef run_multithreading(n_tasks, task_type=\"cpu\"):\n    threads = []\n    if task_type == \"cpu\":\n        for _ in range(n_tasks):\n            t = threading.Thread(target=fib, args=(35,))\n            threads.append(t)\n            t.start()\n        for t in threads:\n            t.join()\n    else:  # I/O 任務\n        for _ in range(n_tasks):\n            t = threading.Thread(target=sync_io_task)\n            threads.append(t)\n            t.start()\n        for t in threads:\n            t.join()\n\n# 多進程實現\n@timing_decorator\ndef run_multiprocessing(n_tasks, task_type=\"cpu\"):\n    processes = []\n    if task_type == \"cpu\":\n        for _ in range(n_tasks):\n            p = multiprocessing.Process(target=fib, args=(35,))\n            processes.append(p)\n            p.start()\n        for p in processes:\n            p.join()\n    else:  # I/O 任務\n        for _ in range(n_tasks):\n            p = multiprocessing.Process(target=sync_io_task)\n            processes.append(p)\n            p.start()\n        for p in processes:\n            p.join()\n\n# Asyncio 實現\n@timing_decorator\nasync def run_asyncio(n_tasks):\n    tasks = [io_bound_task() for _ in range(n_tasks)]\n    await asyncio.gather(*tasks)\n\n# 禁用 GIL 的多線程實現（需 Python 3.13 --disable-gil）\n@timing_decorator\ndef run_nogil_multithreading(n_tasks, task_type=\"cpu\"):\n    with ThreadPoolExecutor(max_workers=n_tasks) as executor:\n        if task_type == \"cpu\":\n            futures = [executor.submit(fib, 35) for _ in range(n_tasks)]\n        else:  # I/O 任務\n            futures = [executor.submit(sync_io_task) for _ in range(n_tasks)]\n        for future in futures:\n            future.result()\n\n# 主程式\nif __name__ == \"__main__\":\n    n_tasks = 4  # 任務數量\n\n    print(\"=== CPU 密集型任務 (計算 Fibonacci 數列) ===\")\n    print(\"多線程 (Multithreading):\")\n    run_multithreading(n_tasks, task_type=\"cpu\")\n\n    print(\"\\n多進程 (Multiprocessing):\")\n    run_multiprocessing(n_tasks, task_type=\"cpu\")\n\n    if sys.version_info >= (3, 13) and hasattr(sys, \"disable_gil\"):\n        print(\"\\n禁用 GIL 的多線程:\")\n        run_nogil_multithreading(n_tasks, task_type=\"cpu\")\n    else:\n        print(\"\\n禁用 GIL 的多線程: 需要 Python 3.13 並啟用 --disable-gil\")\n\n    print(\"\\n=== I/O 綁定任務 (模擬網路請求) ===\")\n    print(\"多線程 (Multithreading):\")\n    run_multithreading(n_tasks, task_type=\"io\")\n\n    print(\"\\n多進程 (Multiprocessing):\")\n    run_multiprocessing(n_tasks, task_type=\"io\")\n\n    print(\"\\nAsyncio:\")\n    asyncio.run(run_asyncio(n_tasks))\n\n    if sys.version_info >= (3, 13) and hasattr(sys, \"disable_gil\"):\n        print(\"\\n禁用 GIL 的多線程:\")\n        run_nogil_multithreading(n_tasks, task_type=\"io\")\n    else:\n        print(\"\\n禁用 GIL 的多線程: 需要 Python 3.13 並啟用 --disable-gil\")\n","python",[85,2334,2335,2340,2345,2350,2355,2360,2365,2370,2374,2379,2384,2389,2394,2399,2404,2409,2414,2419,2424,2428,2433,2438,2443,2448,2453,2457,2462,2467,2472,2476,2481,2486,2491,2496,2500,2505,2510,2515,2520,2525,2530,2535,2540,2545,2550,2555,2560,2564,2569,2573,2577,2581,2585,2589,2594,2598,2603,2608,2612,2617,2623,2629,2635,2641,2647,2652,2657,2663,2668,2673,2678,2683,2688,2694,2699,2705,2711,2717,2722,2728,2733,2739,2745,2751,2757,2763,2769,2775,2781,2786,2792,2798,2804,2809,2815,2821,2827,2832,2838,2844,2849,2855,2861,2867,2873,2879,2884,2890,2895,2901,2906,2911,2917,2922,2928,2934,2939,2944,2949,2955,2960],{"__ignoreMap":57},[385,2336,2337],{"class":387,"line":388},[385,2338,2339],{},"import time\n",[385,2341,2342],{"class":387,"line":232},[385,2343,2344],{},"import threading\n",[385,2346,2347],{"class":387,"line":237},[385,2348,2349],{},"import multiprocessing\n",[385,2351,2352],{"class":387,"line":404},[385,2353,2354],{},"import asyncio\n",[385,2356,2357],{"class":387,"line":410},[385,2358,2359],{},"import sys\n",[385,2361,2362],{"class":387,"line":416},[385,2363,2364],{},"from concurrent.futures import ThreadPoolExecutor\n",[385,2366,2367],{"class":387,"line":422},[385,2368,2369],{},"from functools import wraps\n",[385,2371,2372],{"class":387,"line":428},[385,2373,461],{"emptyLinePlaceholder":253},[385,2375,2376],{"class":387,"line":434},[385,2377,2378],{},"# 計時裝飾器，用於測量執行時間\n",[385,2380,2381],{"class":387,"line":440},[385,2382,2383],{},"def timing_decorator(func):\n",[385,2385,2386],{"class":387,"line":446},[385,2387,2388],{},"    @wraps(func)\n",[385,2390,2391],{"class":387,"line":452},[385,2392,2393],{},"    def wrapper(*args, **kwargs):\n",[385,2395,2396],{"class":387,"line":458},[385,2397,2398],{},"        start = time.time()\n",[385,2400,2401],{"class":387,"line":464},[385,2402,2403],{},"        result = func(*args, **kwargs)\n",[385,2405,2406],{"class":387,"line":470},[385,2407,2408],{},"        end = time.time()\n",[385,2410,2411],{"class":387,"line":476},[385,2412,2413],{},"        print(f\"{func.__name__} 執行時間: {end - start:.4f} 秒\")\n",[385,2415,2416],{"class":387,"line":482},[385,2417,2418],{},"        return result\n",[385,2420,2421],{"class":387,"line":488},[385,2422,2423],{},"    return wrapper\n",[385,2425,2426],{"class":387,"line":493},[385,2427,461],{"emptyLinePlaceholder":253},[385,2429,2430],{"class":387,"line":498},[385,2431,2432],{},"# CPU 密集型任務：計算 Fibonacci 數列\n",[385,2434,2435],{"class":387,"line":504},[385,2436,2437],{},"def fib(n):\n",[385,2439,2440],{"class":387,"line":510},[385,2441,2442],{},"    if n \u003C= 1:\n",[385,2444,2445],{"class":387,"line":516},[385,2446,2447],{},"        return n\n",[385,2449,2450],{"class":387,"line":522},[385,2451,2452],{},"    return fib(n - 1) + fib(n - 2)\n",[385,2454,2455],{"class":387,"line":528},[385,2456,461],{"emptyLinePlaceholder":253},[385,2458,2459],{"class":387,"line":534},[385,2460,2461],{},"# I/O 綁定任務：模擬網路請求（全局函數）\n",[385,2463,2464],{"class":387,"line":539},[385,2465,2466],{},"def sync_io_task():\n",[385,2468,2469],{"class":387,"line":545},[385,2470,2471],{},"    time.sleep(1)  # 模擬 1 秒的 I/O 延遲\n",[385,2473,2474],{"class":387,"line":551},[385,2475,461],{"emptyLinePlaceholder":253},[385,2477,2478],{"class":387,"line":557},[385,2479,2480],{},"# 異步 I/O 任務\n",[385,2482,2483],{"class":387,"line":562},[385,2484,2485],{},"async def io_bound_task():\n",[385,2487,2488],{"class":387,"line":567},[385,2489,2490],{},"    await asyncio.sleep(1)  # 模擬 1 秒的 I/O 延遲\n",[385,2492,2493],{"class":387,"line":573},[385,2494,2495],{},"    return \"I/O 任務完成\"\n",[385,2497,2498],{"class":387,"line":579},[385,2499,461],{"emptyLinePlaceholder":253},[385,2501,2502],{"class":387,"line":585},[385,2503,2504],{},"# 多線程實現\n",[385,2506,2507],{"class":387,"line":591},[385,2508,2509],{},"@timing_decorator\n",[385,2511,2512],{"class":387,"line":596},[385,2513,2514],{},"def run_multithreading(n_tasks, task_type=\"cpu\"):\n",[385,2516,2517],{"class":387,"line":602},[385,2518,2519],{},"    threads = []\n",[385,2521,2522],{"class":387,"line":608},[385,2523,2524],{},"    if task_type == \"cpu\":\n",[385,2526,2527],{"class":387,"line":613},[385,2528,2529],{},"        for _ in range(n_tasks):\n",[385,2531,2532],{"class":387,"line":619},[385,2533,2534],{},"            t = threading.Thread(target=fib, args=(35,))\n",[385,2536,2537],{"class":387,"line":625},[385,2538,2539],{},"            threads.append(t)\n",[385,2541,2542],{"class":387,"line":631},[385,2543,2544],{},"            t.start()\n",[385,2546,2547],{"class":387,"line":636},[385,2548,2549],{},"        for t in threads:\n",[385,2551,2552],{"class":387,"line":642},[385,2553,2554],{},"            t.join()\n",[385,2556,2557],{"class":387,"line":648},[385,2558,2559],{},"    else:  # I/O 任務\n",[385,2561,2562],{"class":387,"line":1274},[385,2563,2529],{},[385,2565,2566],{"class":387,"line":1280},[385,2567,2568],{},"            t = threading.Thread(target=sync_io_task)\n",[385,2570,2571],{"class":387,"line":1286},[385,2572,2539],{},[385,2574,2575],{"class":387,"line":1291},[385,2576,2544],{},[385,2578,2579],{"class":387,"line":1297},[385,2580,2549],{},[385,2582,2583],{"class":387,"line":1303},[385,2584,2554],{},[385,2586,2587],{"class":387,"line":1308},[385,2588,461],{"emptyLinePlaceholder":253},[385,2590,2591],{"class":387,"line":1314},[385,2592,2593],{},"# 多進程實現\n",[385,2595,2596],{"class":387,"line":1320},[385,2597,2509],{},[385,2599,2600],{"class":387,"line":1325},[385,2601,2602],{},"def run_multiprocessing(n_tasks, task_type=\"cpu\"):\n",[385,2604,2605],{"class":387,"line":1331},[385,2606,2607],{},"    processes = []\n",[385,2609,2610],{"class":387,"line":1337},[385,2611,2524],{},[385,2613,2615],{"class":387,"line":2614},59,[385,2616,2529],{},[385,2618,2620],{"class":387,"line":2619},60,[385,2621,2622],{},"            p = multiprocessing.Process(target=fib, args=(35,))\n",[385,2624,2626],{"class":387,"line":2625},61,[385,2627,2628],{},"            processes.append(p)\n",[385,2630,2632],{"class":387,"line":2631},62,[385,2633,2634],{},"            p.start()\n",[385,2636,2638],{"class":387,"line":2637},63,[385,2639,2640],{},"        for p in processes:\n",[385,2642,2644],{"class":387,"line":2643},64,[385,2645,2646],{},"            p.join()\n",[385,2648,2650],{"class":387,"line":2649},65,[385,2651,2559],{},[385,2653,2655],{"class":387,"line":2654},66,[385,2656,2529],{},[385,2658,2660],{"class":387,"line":2659},67,[385,2661,2662],{},"            p = multiprocessing.Process(target=sync_io_task)\n",[385,2664,2666],{"class":387,"line":2665},68,[385,2667,2628],{},[385,2669,2671],{"class":387,"line":2670},69,[385,2672,2634],{},[385,2674,2676],{"class":387,"line":2675},70,[385,2677,2640],{},[385,2679,2681],{"class":387,"line":2680},71,[385,2682,2646],{},[385,2684,2686],{"class":387,"line":2685},72,[385,2687,461],{"emptyLinePlaceholder":253},[385,2689,2691],{"class":387,"line":2690},73,[385,2692,2693],{},"# Asyncio 實現\n",[385,2695,2697],{"class":387,"line":2696},74,[385,2698,2509],{},[385,2700,2702],{"class":387,"line":2701},75,[385,2703,2704],{},"async def run_asyncio(n_tasks):\n",[385,2706,2708],{"class":387,"line":2707},76,[385,2709,2710],{},"    tasks = [io_bound_task() for _ in range(n_tasks)]\n",[385,2712,2714],{"class":387,"line":2713},77,[385,2715,2716],{},"    await asyncio.gather(*tasks)\n",[385,2718,2720],{"class":387,"line":2719},78,[385,2721,461],{"emptyLinePlaceholder":253},[385,2723,2725],{"class":387,"line":2724},79,[385,2726,2727],{},"# 禁用 GIL 的多線程實現（需 Python 3.13 --disable-gil）\n",[385,2729,2731],{"class":387,"line":2730},80,[385,2732,2509],{},[385,2734,2736],{"class":387,"line":2735},81,[385,2737,2738],{},"def run_nogil_multithreading(n_tasks, task_type=\"cpu\"):\n",[385,2740,2742],{"class":387,"line":2741},82,[385,2743,2744],{},"    with ThreadPoolExecutor(max_workers=n_tasks) as executor:\n",[385,2746,2748],{"class":387,"line":2747},83,[385,2749,2750],{},"        if task_type == \"cpu\":\n",[385,2752,2754],{"class":387,"line":2753},84,[385,2755,2756],{},"            futures = [executor.submit(fib, 35) for _ in range(n_tasks)]\n",[385,2758,2760],{"class":387,"line":2759},85,[385,2761,2762],{},"        else:  # I/O 任務\n",[385,2764,2766],{"class":387,"line":2765},86,[385,2767,2768],{},"            futures = [executor.submit(sync_io_task) for _ in range(n_tasks)]\n",[385,2770,2772],{"class":387,"line":2771},87,[385,2773,2774],{},"        for future in futures:\n",[385,2776,2778],{"class":387,"line":2777},88,[385,2779,2780],{},"            future.result()\n",[385,2782,2784],{"class":387,"line":2783},89,[385,2785,461],{"emptyLinePlaceholder":253},[385,2787,2789],{"class":387,"line":2788},90,[385,2790,2791],{},"# 主程式\n",[385,2793,2795],{"class":387,"line":2794},91,[385,2796,2797],{},"if __name__ == \"__main__\":\n",[385,2799,2801],{"class":387,"line":2800},92,[385,2802,2803],{},"    n_tasks = 4  # 任務數量\n",[385,2805,2807],{"class":387,"line":2806},93,[385,2808,461],{"emptyLinePlaceholder":253},[385,2810,2812],{"class":387,"line":2811},94,[385,2813,2814],{},"    print(\"=== CPU 密集型任務 (計算 Fibonacci 數列) ===\")\n",[385,2816,2818],{"class":387,"line":2817},95,[385,2819,2820],{},"    print(\"多線程 (Multithreading):\")\n",[385,2822,2824],{"class":387,"line":2823},96,[385,2825,2826],{},"    run_multithreading(n_tasks, task_type=\"cpu\")\n",[385,2828,2830],{"class":387,"line":2829},97,[385,2831,461],{"emptyLinePlaceholder":253},[385,2833,2835],{"class":387,"line":2834},98,[385,2836,2837],{},"    print(\"\\n多進程 (Multiprocessing):\")\n",[385,2839,2841],{"class":387,"line":2840},99,[385,2842,2843],{},"    run_multiprocessing(n_tasks, task_type=\"cpu\")\n",[385,2845,2847],{"class":387,"line":2846},100,[385,2848,461],{"emptyLinePlaceholder":253},[385,2850,2852],{"class":387,"line":2851},101,[385,2853,2854],{},"    if sys.version_info >= (3, 13) and hasattr(sys, \"disable_gil\"):\n",[385,2856,2858],{"class":387,"line":2857},102,[385,2859,2860],{},"        print(\"\\n禁用 GIL 的多線程:\")\n",[385,2862,2864],{"class":387,"line":2863},103,[385,2865,2866],{},"        run_nogil_multithreading(n_tasks, task_type=\"cpu\")\n",[385,2868,2870],{"class":387,"line":2869},104,[385,2871,2872],{},"    else:\n",[385,2874,2876],{"class":387,"line":2875},105,[385,2877,2878],{},"        print(\"\\n禁用 GIL 的多線程: 需要 Python 3.13 並啟用 --disable-gil\")\n",[385,2880,2882],{"class":387,"line":2881},106,[385,2883,461],{"emptyLinePlaceholder":253},[385,2885,2887],{"class":387,"line":2886},107,[385,2888,2889],{},"    print(\"\\n=== I/O 綁定任務 (模擬網路請求) ===\")\n",[385,2891,2893],{"class":387,"line":2892},108,[385,2894,2820],{},[385,2896,2898],{"class":387,"line":2897},109,[385,2899,2900],{},"    run_multithreading(n_tasks, task_type=\"io\")\n",[385,2902,2904],{"class":387,"line":2903},110,[385,2905,461],{"emptyLinePlaceholder":253},[385,2907,2909],{"class":387,"line":2908},111,[385,2910,2837],{},[385,2912,2914],{"class":387,"line":2913},112,[385,2915,2916],{},"    run_multiprocessing(n_tasks, task_type=\"io\")\n",[385,2918,2920],{"class":387,"line":2919},113,[385,2921,461],{"emptyLinePlaceholder":253},[385,2923,2925],{"class":387,"line":2924},114,[385,2926,2927],{},"    print(\"\\nAsyncio:\")\n",[385,2929,2931],{"class":387,"line":2930},115,[385,2932,2933],{},"    asyncio.run(run_asyncio(n_tasks))\n",[385,2935,2937],{"class":387,"line":2936},116,[385,2938,461],{"emptyLinePlaceholder":253},[385,2940,2942],{"class":387,"line":2941},117,[385,2943,2854],{},[385,2945,2947],{"class":387,"line":2946},118,[385,2948,2860],{},[385,2950,2952],{"class":387,"line":2951},119,[385,2953,2954],{},"        run_nogil_multithreading(n_tasks, task_type=\"io\")\n",[385,2956,2958],{"class":387,"line":2957},120,[385,2959,2872],{},[385,2961,2963],{"class":387,"line":2962},121,[385,2964,2878],{},[22,2966,2967],{"id":2967},"執行結果分析",[78,2969,2971],{"className":812,"code":2970,"language":814,"meta":57,"style":57},"$ uv run --no-project --python 3.13.5+freethreaded test.py\n\n=== CPU 密集型任務 (計算 Fibonacci 數列) ===\n多線程 (Multithreading):\nrun_multithreading 執行時間: 0.8912 秒\n\n多進程 (Multiprocessing):\nrun_multiprocessing 執行時間: 1.0846 秒\n\n禁用 GIL 的多線程:\nGIL 狀態: 禁用\nrun_nogil_multithreading 執行時間: 0.8713 秒\n\n=== I/O 綁定任務 (模擬網路請求) ===\n多線程 (Multithreading):\nrun_multithreading 執行時間: 1.0072 秒\n\n多進程 (Multiprocessing):\nrun_multiprocessing 執行時間: 1.1046 秒\n\nAsyncio:\nrun_asyncio 執行時間: 0.0000 秒\n\n禁用 GIL 的多線程:\nGIL 狀態: 禁用\nrun_nogil_multithreading 執行時間: 1.0093 秒\n",[85,2972,2973,2995,2999,3023,3031,3045,3049,3057,3069,3073,3084,3095,3107,3111,3124,3130,3141,3145,3151,3162,3166,3171,3183,3187,3195,3203],{"__ignoreMap":57},[385,2974,2975,2978,2981,2983,2986,2989,2992],{"class":387,"line":388},[385,2976,2977],{"class":827},"$",[385,2979,2980],{"class":831}," uv",[385,2982,1459],{"class":831},[385,2984,2985],{"class":851}," --no-project",[385,2987,2988],{"class":851}," --python",[385,2990,2991],{"class":831}," 3.13.5+freethreaded",[385,2993,2994],{"class":831}," test.py\n",[385,2996,2997],{"class":387,"line":232},[385,2998,461],{"emptyLinePlaceholder":253},[385,3000,3001,3004,3007,3010,3014,3017,3020],{"class":387,"line":237},[385,3002,3003],{"class":831},"===",[385,3005,3006],{"class":831}," CPU",[385,3008,3009],{"class":831}," 密集型任務",[385,3011,3013],{"class":3012},"sVt8B"," (計算 ",[385,3015,3016],{"class":831},"Fibonacci",[385,3018,3019],{"class":831}," 數列",[385,3021,3022],{"class":3012},") ===\n",[385,3024,3025,3028],{"class":387,"line":404},[385,3026,3027],{"class":827},"多線程",[385,3029,3030],{"class":3012}," (Multithreading):\n",[385,3032,3033,3036,3039,3042],{"class":387,"line":410},[385,3034,3035],{"class":827},"run_multithreading",[385,3037,3038],{"class":831}," 執行時間:",[385,3040,3041],{"class":851}," 0.8912",[385,3043,3044],{"class":831}," 秒\n",[385,3046,3047],{"class":387,"line":416},[385,3048,461],{"emptyLinePlaceholder":253},[385,3050,3051,3054],{"class":387,"line":422},[385,3052,3053],{"class":827},"多進程",[385,3055,3056],{"class":3012}," (Multiprocessing):\n",[385,3058,3059,3062,3064,3067],{"class":387,"line":428},[385,3060,3061],{"class":827},"run_multiprocessing",[385,3063,3038],{"class":831},[385,3065,3066],{"class":851}," 1.0846",[385,3068,3044],{"class":831},[385,3070,3071],{"class":387,"line":434},[385,3072,461],{"emptyLinePlaceholder":253},[385,3074,3075,3078,3081],{"class":387,"line":440},[385,3076,3077],{"class":827},"禁用",[385,3079,3080],{"class":831}," GIL",[385,3082,3083],{"class":831}," 的多線程:\n",[385,3085,3086,3089,3092],{"class":387,"line":446},[385,3087,3088],{"class":827},"GIL",[385,3090,3091],{"class":831}," 狀態:",[385,3093,3094],{"class":831}," 禁用\n",[385,3096,3097,3100,3102,3105],{"class":387,"line":452},[385,3098,3099],{"class":827},"run_nogil_multithreading",[385,3101,3038],{"class":831},[385,3103,3104],{"class":851}," 0.8713",[385,3106,3044],{"class":831},[385,3108,3109],{"class":387,"line":458},[385,3110,461],{"emptyLinePlaceholder":253},[385,3112,3113,3115,3118,3121],{"class":387,"line":464},[385,3114,3003],{"class":831},[385,3116,3117],{"class":831}," I/O",[385,3119,3120],{"class":831}," 綁定任務",[385,3122,3123],{"class":3012}," (模擬網路請求) ===\n",[385,3125,3126,3128],{"class":387,"line":470},[385,3127,3027],{"class":827},[385,3129,3030],{"class":3012},[385,3131,3132,3134,3136,3139],{"class":387,"line":476},[385,3133,3035],{"class":827},[385,3135,3038],{"class":831},[385,3137,3138],{"class":851}," 1.0072",[385,3140,3044],{"class":831},[385,3142,3143],{"class":387,"line":482},[385,3144,461],{"emptyLinePlaceholder":253},[385,3146,3147,3149],{"class":387,"line":488},[385,3148,3053],{"class":827},[385,3150,3056],{"class":3012},[385,3152,3153,3155,3157,3160],{"class":387,"line":493},[385,3154,3061],{"class":827},[385,3156,3038],{"class":831},[385,3158,3159],{"class":851}," 1.1046",[385,3161,3044],{"class":831},[385,3163,3164],{"class":387,"line":498},[385,3165,461],{"emptyLinePlaceholder":253},[385,3167,3168],{"class":387,"line":504},[385,3169,3170],{"class":827},"Asyncio:\n",[385,3172,3173,3176,3178,3181],{"class":387,"line":510},[385,3174,3175],{"class":827},"run_asyncio",[385,3177,3038],{"class":831},[385,3179,3180],{"class":851}," 0.0000",[385,3182,3044],{"class":831},[385,3184,3185],{"class":387,"line":516},[385,3186,461],{"emptyLinePlaceholder":253},[385,3188,3189,3191,3193],{"class":387,"line":522},[385,3190,3077],{"class":827},[385,3192,3080],{"class":831},[385,3194,3083],{"class":831},[385,3196,3197,3199,3201],{"class":387,"line":528},[385,3198,3088],{"class":827},[385,3200,3091],{"class":831},[385,3202,3094],{"class":831},[385,3204,3205,3207,3209,3212],{"class":387,"line":534},[385,3206,3099],{"class":827},[385,3208,3038],{"class":831},[385,3210,3211],{"class":851}," 1.0093",[385,3213,3044],{"class":831},[46,3215,3217],{"id":3216},"cpu-密集型任務計算-fibonacci-數列","CPU 密集型任務（計算 Fibonacci 數列）",[15,3219,3220,3221],{},"執行時間排序：",[296,3222,3223],{},"GIL Disabled (0.8713 秒) \u003C 多線程 (0.8912 秒) \u003C 多進程 (1.0846 秒)",[109,3225,3226,3231,3236],{},[112,3227,3228,3230],{},[296,3229,3027],{},"：受 GIL 限制，執行速度與單 CPU 執行差不多",[112,3232,3233,3235],{},[296,3234,3053],{},"：運用多個 CPU 核心加速",[112,3237,3238,3241],{},[296,3239,3240],{},"GIL Disabled","：運用多個 CPU 核心加速，且避免了多進程管理的額外開銷",[46,3243,3245],{"id":3244},"io-綁定任務模擬網路請求","I/O 綁定任務（模擬網路請求）",[15,3247,3220,3248],{},[296,3249,3250],{},"Asyncio (0.0000 秒) \u003C\u003C 多線程 (1.0072 秒) ≈ GIL Disabled (1.0093 秒) ≈ 多進程 (1.1046 秒)",[109,3252,3253,3259],{},[112,3254,3255,3258],{},[296,3256,3257],{},"多線程、多進程、GIL Disabled","：三者效能差不多",[112,3260,3261,3264,3265,3268],{},[296,3262,3263],{},"Asyncio","：效能最好，因為測試任務使用 ",[85,3266,3267],{},"await asyncio.sleep","，基本上不會有運行時間開銷",[22,3270,3271],{"id":3271},"實際應用場景",[15,3273,3274],{},[296,3275,3276],{},"首先需要考慮業務場景，做這個優化加速是否有需要？如果加速沒有很重要的話就是一個 Nice to have 的優化。",[46,3278,3280],{"id":3279},"不建議使用-disabled-gil","不建議使用 Disabled GIL",[15,3282,3283],{},"目前 Disabled GIL 還在測試階段，許多套件都還沒完全支援，也需要自己處理變數管理等問題，目前不考慮使用。",[46,3285,3287],{"id":3286},"適合使用-multiprocessing-的場景","適合使用 Multiprocessing 的場景",[15,3289,3290],{},"非 AI 的大量運算需求，可以使用 Multiprocessing 來加速：",[109,3292,3293,3299],{},[112,3294,3295,3298],{},[296,3296,3297],{},"售電平台的最佳參數求取","：目前策略使用網格搜索，可以切分網格分配給 CPU 執行",[112,3300,3301,3304],{},[296,3302,3303],{},"EV 管理平台","：需要定時計算電站使用率",[46,3306,3308],{"id":3307},"適合使用-asyncio-的場景","適合使用 Asyncio 的場景",[15,3310,3311],{},"網路的大量需求，可以使用 Asyncio 來加速：",[109,3313,3314,3320,3338,3348],{},[112,3315,3316,3319],{},[296,3317,3318],{},"MQTT / OCPP","：這類 pub/sub 協議",[112,3321,3322,2130,3325],{},[296,3323,3324],{},"爬蟲或大量 Third-party API 請求",[109,3326,3327],{},[112,3328,3329,3330,3333,3334,3337],{},"使用 ",[85,3331,3332],{},"aiohttp"," 替換 ",[85,3335,3336],{},"requests"," 模組",[112,3339,3340,2130,3343],{},[296,3341,3342],{},"Database 異步操作",[109,3344,3345],{},[112,3346,3347],{},"pymongo 遷移到 async client (≥ v4.13)",[112,3349,3350,2130,3353],{},[296,3351,3352],{},"Web Framework",[109,3354,3355],{},[112,3356,3357],{},"Flask 遷移到 FastAPI",[22,3359,1610],{"id":1610},[109,3361,3362,3369,3376],{},[112,3363,3364],{},[29,3365,3368],{"href":3366,"rel":3367},"https://www.youtube.com/watch?v=brYsDi-JajI",[33],"【python】asyncio的理解與入門，搞不明白協程？看這個視頻就夠了",[112,3370,3371],{},[29,3372,3375],{"href":3373,"rel":3374},"https://www.youtube.com/watch?v=K0BjgYZbgfE&t=94s",[33],"【python】await機制詳解。再來個硬核內容，把並行和依賴背後的原理全給你講明白",[112,3377,3378],{},[29,3379,3382],{"href":3380,"rel":3381},"https://pymongo.readthedocs.io/en/stable/async-tutorial.html",[33],"Async Tutorial - PyMongo 4.14.1 documentation",[1635,3384,3385],{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}",{"title":57,"searchDepth":232,"depth":232,"links":3387},[3388,3389,3395,3396,3397,3401,3406],{"id":269,"depth":232,"text":270},{"id":2024,"depth":232,"text":2024,"children":3390},[3391,3392,3393,3394],{"id":2027,"depth":237,"text":2028},{"id":2078,"depth":237,"text":2079},{"id":2117,"depth":237,"text":2118},{"id":2154,"depth":237,"text":2155},{"id":2176,"depth":232,"text":2176},{"id":2327,"depth":232,"text":2327},{"id":2967,"depth":232,"text":2967,"children":3398},[3399,3400],{"id":3216,"depth":237,"text":3217},{"id":3244,"depth":237,"text":3245},{"id":3271,"depth":232,"text":3271,"children":3402},[3403,3404,3405],{"id":3279,"depth":237,"text":3280},{"id":3286,"depth":237,"text":3287},{"id":3307,"depth":237,"text":3308},{"id":1610,"depth":232,"text":1610},"介紹 Python 並發處理方法，包括多線程、多進程、asyncio 以及其實務應用。",{},"/blog/concurrency",{"title":2011,"description":3407},"blog/concurrency",[1663,3413,3414,3263],"Concurrency","Threading","1DEoTJ_oFj-_n1U7PDKbn7wPsTmsBUK-5vAp6nZ_s0E",1774237782440]