<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[schwannden: Techonology]]></title><description><![CDATA[Insight into AI/ML, cloud-native infrastructure, or just software engineering in general.]]></description><link>https://schwannden.substack.com/s/techonology</link><image><url>https://substackcdn.com/image/fetch/$s_!PCuI!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21c2324a-b3f3-4858-b911-4746dc76523d_390x390.png</url><title>schwannden: Techonology</title><link>https://schwannden.substack.com/s/techonology</link></image><generator>Substack</generator><lastBuildDate>Fri, 22 May 2026 03:11:26 GMT</lastBuildDate><atom:link href="https://schwannden.substack.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[schwannden]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[schwannden@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[schwannden@substack.com]]></itunes:email><itunes:name><![CDATA[schwannden]]></itunes:name></itunes:owner><itunes:author><![CDATA[schwannden]]></itunes:author><googleplay:owner><![CDATA[schwannden@substack.com]]></googleplay:owner><googleplay:email><![CDATA[schwannden@substack.com]]></googleplay:email><googleplay:author><![CDATA[schwannden]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Vibe出我的屬靈導師：（嘗試）讓 AI 不再膚淺]]></title><description><![CDATA[&#22914;&#20309;&#21033;&#29992; RAG &#23559;&#32147;&#20856;&#27880;&#20837; Chroma &#38642;&#31471; Collection&#12290;&#24460;&#31471;&#25505;&#29992; FastAPI &#20282;&#26381;&#22120;&#65292;&#36879;&#36942;&#25552;&#31034;&#24037;&#31243;&#23526;&#29694; Persona &#20998;&#38626;&#12290;&#21033;&#29992; LM Studio &#25645;&#37197; MLX FP4 &#37327;&#21270;&#27169;&#22411;&#22312; Mac &#26412;&#27231;&#36939;&#34892;&#65292;&#20860;&#39015;&#38577;&#31169;&#33287;&#20302;&#24310;&#36978;&#12290;]]></description><link>https://schwannden.substack.com/p/vibe-ai</link><guid isPermaLink="false">https://schwannden.substack.com/p/vibe-ai</guid><dc:creator><![CDATA[schwannden]]></dc:creator><pubDate>Wed, 26 Nov 2025 06:33:46 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!NrxR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F797d4381-d384-4dc3-a994-cb9479e459d8_2000x490.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>&#25105;&#20381;&#36084; AI &#20358;&#23531;&#31243;&#24335;&#30908;&#12289;&#34389;&#29702;&#36039;&#26009;&#12289;&#35299;&#27770;&#23560;&#26989;&#38936;&#22495;&#30340;&#38627;&#38988;&#12290;&#23427;&#24555;&#12289;&#31934;&#28310;&#12289;&#37007;&#36655;&#28165;&#26224;&#65292;&#26159;&#20491;&#20778;&#31168;&#30340;&#12300;&#36039;&#35338;&#34389;&#29702;&#22120;&#12301;&#12290;&#20294;&#26159;&#65292;&#30070;&#25105;&#35430;&#33879;&#22312;&#24515;&#38728;&#28145;&#34389;&#23563;&#27714;&#23565;&#35441;&#12289;&#25351;&#24341;&#25110;&#19968;&#20123;&#35433;&#24847;&#30340;&#38728;&#24863;&#26178;&#65292;AI &#30340;&#22238;&#31572;&#32317;&#26159;&#36879;&#33879;&#19968;&#32929;&#38627;&#20197;&#35328;&#21947;&#30340;&#12300;&#21619;&#36947;&#12301;&#8212;&#8212;&#37027;&#31278;&#21046;&#24335;&#21270;&#12289;&#32570;&#20047;&#28331;&#24230;&#30340;&#12300;AI &#21619;&#12301;&#12290;</p><p>&#25110;&#35377;&#26159;&#22240;&#28858;&#22312;&#22823;&#22411;&#35486;&#35328;&#27169;&#22411;&#30340;<strong>&#24375;&#21270;&#23416;&#32722; (RL) &#25110;&#24494;&#35519;</strong>&#36942;&#31243;&#20013;&#65292;&#35373;&#35336;&#32773;&#20027;&#35201;&#23559;&#27169;&#22411;&#30340;&#20778;&#21270;&#30446;&#27161;&#32858;&#28966;&#26044;&#20107;&#23526;&#28310;&#30906;&#24615;&#12289;&#37007;&#36655;&#36899;&#36011;&#24615;&#33287;&#23433;&#20840;&#21512;&#35215;&#19978;&#12290;&#28982;&#32780;&#65292;&#20154;&#39006;<strong>&#38728;&#39746;&#28145;&#23652;&#30340;&#12289;&#38750;&#32080;&#27083;&#24615;&#30340;&#12289;&#35433;&#24847;&#30340;&#38656;&#27714;</strong>&#65292;&#21371;&#24456;&#23569;&#25104;&#28858; AI &#34987;&#28145;&#24230;&#20778;&#21270;&#30340;&#30446;&#27161;&#12290;</p><p>&#24184;&#22909;&#65292;&#20154;&#39006;&#27511;&#21490;&#19978;&#26377;&#28961;&#25976;&#20553;&#22823;&#30340;&#23660;&#38728;&#23566;&#24107;&#65292;&#20182;&#20497;&#30340;&#20316;&#21697;&#26089;&#24050;&#27785;&#28593;&#20102;&#21315;&#24180;&#26234;&#24935;&#12290;&#26082;&#28982; AI &#32570;&#20047;&#28145;&#27785;&#30340;&#38728;&#39746;&#32173;&#24230;&#65292;<strong>&#25105;&#20497;&#20309;&#19981;&#23559;&#36889;&#20123;&#23660;&#38728;&#32147;&#20856;&#20316;&#28858; AI &#30340;&#12300;&#35352;&#25014;&#12301;</strong>&#65311;&#25105;&#27770;&#23450;&#20570;&#19968;&#20491;&#23526;&#39511;&#65306;&#36879;&#36942; <strong>RAG (Retrieval-Augmented Generation)</strong> &#25216;&#34899;&#65292;&#23559;&#30050;&#24503;&#29983;<strong>(Eugene Peterson) &#30340;&#29287;&#32773;&#26234;&#24935;</strong>&#21644;<strong>&#22350;&#22521;&#26031; (Thomas &#224; Kempis) &#30340;&#20462;&#38498;&#35486;&#27683;</strong>&#31561;&#20154;&#30340;&#33879;&#20316;&#27880;&#20837; AI &#30340;&#30693;&#35672;&#24235;&#12290;</p><p>&#20197;&#19979;&#23601;&#26159;&#25105;&#19968;&#27493;&#27493;&#29992;&#12300;&#25552;&#31034;&#65288;prompt&#65289;&#12301;&#25171;&#36896;&#25105;&#30340;&#24515;&#38728;&#23566;&#24107;&#30340;&#36942;&#31243;&#12290;</p><div><hr></div><h2>&#24478; PDF &#21040;&#31456;&#31680;&#65306;&#35731;&#32147;&#20856;&#34987;&#12300;&#35712;&#25026;&#12301;</h2><p>&#25105;&#30340;&#31532;&#19968;&#20491;&#25361;&#25136;&#26159;&#35731;&#36889;&#20123;&#32147;&#20856;&#12300;&#35712;&#24471;&#25026;&#12301;&#12290;&#36889;&#20123;&#26234;&#24935;&#37117;&#34987;&#37782;&#22312;&#19968;&#25972;&#20221;&#40848;&#22823;&#30340; PDF &#27284;&#26696;&#35041;&#12290;&#25105;&#24819;&#35201;&#30340;&#19981;&#26159;&#25226;&#25972;&#26412;&#26360;&#38568;&#27231;&#20999;&#25104;&#24190;&#30334;&#20491;&#30862;&#29255;&#65288;&#37027;&#26371;&#22833;&#21435;&#35486;&#22659;&#65292;&#20687;&#23436;&#25972;&#30340;&#25925;&#20107;&#34987;&#26039;&#31456;&#21462;&#32681;&#65289;&#65292;&#32780;&#26159;&#35201;&#23562;&#37325;&#20316;&#32773;&#65292;<strong>&#25353;&#12300;&#31456;&#31680;&#12301;&#20358;&#29702;&#35299;</strong>&#12290;&#30050;&#31455;&#65292;&#19968;&#20491;&#31456;&#31680;&#26412;&#36523;&#23601;&#26159;&#19968;&#20491;&#23436;&#25972;&#30340;&#24605;&#32210;&#21934;&#20803;&#12290;</p><p>&#25105;&#23531;&#20102;&#24190;&#20491;prompt&#65292;&#24171;&#25105;&#25226; PDF &#27284;&#26696;<strong>&#36880;&#31456;&#36880;&#31680;&#22320;&#36681;&#25563;&#25104; Markdown</strong>&#65292;&#20006;&#35201;&#27714;&#23427;&#21209;&#24517;&#20445;&#30041;&#21407;&#25991;&#30340;&#35486;&#27683;&#65292;&#19968;&#20491;&#23383;&#37117;&#19981;&#35377;&#20098;&#25913;&#12290;&#25343;&#21040;&#21021;&#31295;&#24460;&#65292;&#25105;&#21448;&#35731; AI &#24171;&#25105;&#25353;&#31456;&#20999;&#20998;&#27284;&#26696;&#65292;&#30906;&#20445;&#27599;&#19968;&#20491;&#27284;&#26696;&#37117;&#26159;&#19968;&#20491;&#29544;&#31435;&#30340;&#12289;<strong>&#24118;&#26377;&#35486;&#32681;&#23436;&#25972;&#24615;&#30340;&#12300;&#35352;&#25014;&#12301;&#21934;&#20803;</strong>&#12290;&#36889;&#26159; RAG &#31995;&#32113;&#31337;&#23450;&#30340;&#22522;&#30990;&#12290;</p><h3>&#24314;&#31435;&#24515;&#38728;&#22294;&#26360;&#39208;&#65306;Chroma &#33287;&#32034;&#24341;&#21345;</h3><p>&#25509;&#19979;&#20358;&#65292;&#25105;&#38656;&#35201;&#19968;&#20491;&#12300;&#22294;&#26360;&#39208;&#12301;&#20358;&#23384;&#25918;&#36889;&#20123;&#29645;&#36020;&#30340;&#31456;&#31680;&#12290;&#25105;&#36984;&#25799;&#20102; <strong>Chroma</strong> &#38642;&#31471;&#26381;&#21209;&#65292;&#20027;&#35201;&#26159;&#30475;&#20013;&#23427;&#30340;<strong>&#20813;&#36027;&#38989;&#24230;&#33287;&#20813;&#32173;&#35703;</strong>&#29305;&#24615;&#65288;&#19981;&#29992;&#33258;&#24049;&#32173;&#35703;database&#65289;&#12290;</p><p>&#25105;&#21448;&#35531; AI &#24171;&#25105;&#29983;&#25104;&#20102;&#19968;&#20491;&#31777;&#21934;&#30340; Python &#33139;&#26412;&#12290;&#36889;&#20491;&#33139;&#26412;&#36208;&#35370;&#27599;&#20491;&#31456;&#31680;&#27284;&#26696;&#65292;&#20006;&#22312;&#23531;&#20837; Chroma collection &#26178;&#65292;&#19968;&#20341;&#38468;&#19978;&#12300;&#26360;&#21517;&#12301;&#21644;&#12300;&#31456;&#21517;&#12301;&#36889;&#20841;&#20491;&#38364;&#37749;&#30340;&#12300;&#27161;&#31844;&#12301;&#65288;Metadata&#65289;&#12290;&#36889;&#23601;&#20687;&#32102;&#27599;&#19968;&#27573;&#26234;&#24935;&#35486;&#37636;&#37117;&#36028;&#19978;&#20102;&#19968;&#24373;&#28165;&#26224;&#30340;&#32034;&#24341;&#21345;&#65292;&#30906;&#20445;&#26410;&#20358;&#27298;&#32034;&#26178;&#33021;&#31934;&#30906;&#25214;&#21040;&#20986;&#34389;&#12290;</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!NrxR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F797d4381-d384-4dc3-a994-cb9479e459d8_2000x490.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!NrxR!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F797d4381-d384-4dc3-a994-cb9479e459d8_2000x490.png 424w, https://substackcdn.com/image/fetch/$s_!NrxR!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F797d4381-d384-4dc3-a994-cb9479e459d8_2000x490.png 848w, https://substackcdn.com/image/fetch/$s_!NrxR!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F797d4381-d384-4dc3-a994-cb9479e459d8_2000x490.png 1272w, https://substackcdn.com/image/fetch/$s_!NrxR!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F797d4381-d384-4dc3-a994-cb9479e459d8_2000x490.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!NrxR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F797d4381-d384-4dc3-a994-cb9479e459d8_2000x490.png" width="1456" height="357" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/797d4381-d384-4dc3-a994-cb9479e459d8_2000x490.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:357,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!NrxR!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F797d4381-d384-4dc3-a994-cb9479e459d8_2000x490.png 424w, https://substackcdn.com/image/fetch/$s_!NrxR!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F797d4381-d384-4dc3-a994-cb9479e459d8_2000x490.png 848w, https://substackcdn.com/image/fetch/$s_!NrxR!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F797d4381-d384-4dc3-a994-cb9479e459d8_2000x490.png 1272w, https://substackcdn.com/image/fetch/$s_!NrxR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F797d4381-d384-4dc3-a994-cb9479e459d8_2000x490.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h2>&#32102; AI &#25140;&#19978;&#23566;&#24107;&#30340;&#24125;&#23376;</h2><h4>&#21443;&#32771;&#25351;&#20196;</h4><p>Build a FastAPI RAG chatbot that serves a small Tailwind frontend at /chat. Use Python 3.11, FastAPI, httpx, ChromaDB (cloud client) for embeddings retrieval, and LM</p><p>Studio&#8217;s OpenAI-compatible local API for generation. Requirements:</p><ul><li><p>Env vars: CHROMA_API_KEY, CHROMA_TENANT, CHROMA_DATABASE (default &#8220;dev&#8221;), LM_STUDIO_BASE_URL (default http://host.docker.internal:1234), LM_STUDIO_CHAT_MODEL (default openai/gpt-oss-20b).</p></li><li><p>Retrieval: top_k with default 7, cap 8; allocate fairly across collections. Max context per doc 1200 chars. Response length presets: short/medium/long with approximate char targets.</p></li><li><p>Chat endpoint POST /chat: accepts messages [{role, content}], top_k, collections, response_length; returns reply plus sources (collection, label, filename, distance).</p></li></ul><p>&#29694;&#22312;&#65292;&#25105;&#30340;&#12300;&#22294;&#26360;&#39208;&#12301;&#26377;&#20102;&#65292;&#23601;&#38656;&#35201;&#19968;&#20301;&#12300;&#39208;&#38263;&#12301;&#20358;&#34389;&#29702;&#35531;&#27714;&#12290;&#25105;&#20877;&#27425;&#20351;&#29992;&#25552;&#31034;&#65292;&#35531; AI &#24171;&#25105;&#25645;&#24314;&#19968;&#20491; <strong>FastAPI</strong> &#20282;&#26381;&#22120;&#20316;&#28858;&#24460;&#31471;&#12290;&#36889;&#20301;&#12300;&#39208;&#38263;&#12301;&#30340;&#20219;&#21209;&#24456;&#26126;&#30906;&#65306;</p><ol><li><p>&#26681;&#25818;&#20351;&#29992;&#32773;&#36984;&#25799;&#30340;&#23566;&#24107;&#65292;&#21040;&#22294;&#26360;&#39208;&#35041;<strong>&#27298;&#32034;&#20986;&#26368;&#30456;&#20284;&#30340;&#24190;&#27573;&#32147;&#25991;</strong>&#12290;</p></li><li><p>&#25226;&#36889;&#20123;&#32147;&#25991;&#20778;&#38597;&#22320;<strong>&#24409;&#25972;&#25104;&#19968;&#20491;&#12300;&#19978;&#19979;&#25991;&#12301;</strong>&#12290;</p></li><li><p>&#26368;&#38364;&#37749;&#30340;&#26159;&#65306;&#23427;&#24517;&#38920;&#22312;&#36889;&#20491;&#19978;&#19979;&#25991;&#20013;&#65292;<strong>&#35201;&#27714;&#22238;&#25033;&#27169;&#22411;&#20351;&#29992;&#12300;&#23660;&#38728;&#23566;&#24107;&#30340;&#21475;&#21563;&#12301;</strong>&#65292;&#28331;&#26580;&#12289;&#27785;&#31337;&#65292;&#20006;<strong>&#26126;&#30906;&#27161;&#35387;&#20358;&#28304;&#31456;&#31680;</strong>&#12290;</p></li><li><p>&#26368;&#24460;&#19968;&#26781;&#25106;&#24459;&#65306;&#22914;&#26524;&#22294;&#26360;&#39208;&#35041;&#25214;&#19981;&#21040;&#30456;&#38364;&#20839;&#23481;&#65292;&#23427;<strong>&#32085;&#23565;&#19981;&#33021;&#20098;&#32232;</strong>&#65292;&#24517;&#38920;&#22374;&#35488;&#21578;&#30693;&#12290;</p></li></ol><p>&#25105;&#23601;&#26159;&#22312; AI &#32102;&#20986;&#30340;&#39592;&#26550;&#19978;&#65292;&#35036;&#36275;&#20102;&#37679;&#35492;&#34389;&#29702;&#21644;&#32048;&#31680;&#37197;&#32622;&#65292;&#35731;&#23427;&#33021;&#38918;&#21033;&#23559;&#27298;&#32034;&#21040;&#30340;&#26234;&#24935;&#65292;&#36865;&#24448;&#25105;&#30340;&#26412;&#27231;&#25512;&#29702;&#27169;&#22411;&#36914;&#34892;&#12300;&#35299;&#35712;&#12301;&#12290;</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!0ODN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed5dbafe-2a01-43e2-930a-4283a49e25b2_2000x219.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!0ODN!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed5dbafe-2a01-43e2-930a-4283a49e25b2_2000x219.png 424w, https://substackcdn.com/image/fetch/$s_!0ODN!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed5dbafe-2a01-43e2-930a-4283a49e25b2_2000x219.png 848w, https://substackcdn.com/image/fetch/$s_!0ODN!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed5dbafe-2a01-43e2-930a-4283a49e25b2_2000x219.png 1272w, https://substackcdn.com/image/fetch/$s_!0ODN!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed5dbafe-2a01-43e2-930a-4283a49e25b2_2000x219.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!0ODN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed5dbafe-2a01-43e2-930a-4283a49e25b2_2000x219.png" width="1456" height="159" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ed5dbafe-2a01-43e2-930a-4283a49e25b2_2000x219.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:159,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!0ODN!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed5dbafe-2a01-43e2-930a-4283a49e25b2_2000x219.png 424w, https://substackcdn.com/image/fetch/$s_!0ODN!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed5dbafe-2a01-43e2-930a-4283a49e25b2_2000x219.png 848w, https://substackcdn.com/image/fetch/$s_!0ODN!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed5dbafe-2a01-43e2-930a-4283a49e25b2_2000x219.png 1272w, https://substackcdn.com/image/fetch/$s_!0ODN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed5dbafe-2a01-43e2-930a-4283a49e25b2_2000x219.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>&#26412;&#27231;&#27169;&#22411;</h3><p>&#25105;&#25226;&#35486;&#35328;&#27169;&#22411;&#36305;&#22312;&#25105;&#33258;&#24049;&#30340; Mac &#19978;&#65292;&#25645;&#37197; <strong>LM Studio</strong> &#33287; <strong>MLX FP4</strong> &#37327;&#21270;&#27169;&#22411;&#12290;</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Oe2M!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F682d82af-e1ab-4e90-8297-121a51482ed0_2000x1320.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Oe2M!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F682d82af-e1ab-4e90-8297-121a51482ed0_2000x1320.png 424w, https://substackcdn.com/image/fetch/$s_!Oe2M!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F682d82af-e1ab-4e90-8297-121a51482ed0_2000x1320.png 848w, https://substackcdn.com/image/fetch/$s_!Oe2M!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F682d82af-e1ab-4e90-8297-121a51482ed0_2000x1320.png 1272w, https://substackcdn.com/image/fetch/$s_!Oe2M!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F682d82af-e1ab-4e90-8297-121a51482ed0_2000x1320.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Oe2M!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F682d82af-e1ab-4e90-8297-121a51482ed0_2000x1320.png" width="1456" height="961" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/682d82af-e1ab-4e90-8297-121a51482ed0_2000x1320.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:961,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!Oe2M!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F682d82af-e1ab-4e90-8297-121a51482ed0_2000x1320.png 424w, https://substackcdn.com/image/fetch/$s_!Oe2M!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F682d82af-e1ab-4e90-8297-121a51482ed0_2000x1320.png 848w, https://substackcdn.com/image/fetch/$s_!Oe2M!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F682d82af-e1ab-4e90-8297-121a51482ed0_2000x1320.png 1272w, https://substackcdn.com/image/fetch/$s_!Oe2M!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F682d82af-e1ab-4e90-8297-121a51482ed0_2000x1320.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!zf7E!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ee08131-f4dc-4d46-8380-2936b7297d6c_2000x890.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!zf7E!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ee08131-f4dc-4d46-8380-2936b7297d6c_2000x890.png 424w, https://substackcdn.com/image/fetch/$s_!zf7E!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ee08131-f4dc-4d46-8380-2936b7297d6c_2000x890.png 848w, https://substackcdn.com/image/fetch/$s_!zf7E!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ee08131-f4dc-4d46-8380-2936b7297d6c_2000x890.png 1272w, https://substackcdn.com/image/fetch/$s_!zf7E!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ee08131-f4dc-4d46-8380-2936b7297d6c_2000x890.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!zf7E!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ee08131-f4dc-4d46-8380-2936b7297d6c_2000x890.png" width="1456" height="648" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0ee08131-f4dc-4d46-8380-2936b7297d6c_2000x890.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:648,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!zf7E!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ee08131-f4dc-4d46-8380-2936b7297d6c_2000x890.png 424w, https://substackcdn.com/image/fetch/$s_!zf7E!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ee08131-f4dc-4d46-8380-2936b7297d6c_2000x890.png 848w, https://substackcdn.com/image/fetch/$s_!zf7E!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ee08131-f4dc-4d46-8380-2936b7297d6c_2000x890.png 1272w, https://substackcdn.com/image/fetch/$s_!zf7E!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ee08131-f4dc-4d46-8380-2936b7297d6c_2000x890.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>&#21069;&#31471;&#65306;</h3><p>&#28858;&#20102;&#31777;&#21934;&#30340;&#33258;&#24049;&#23526;&#39511;&#25972;&#20491;Rag&#24314;&#32622;&#65292;&#25105;&#27794;&#26377;&#20351;&#29992;&#26082;&#26377;&#30340;&#32842;&#22825;&#26694;&#26550;&#65292;&#24819;&#30475;&#30475; Codex &#33021;&#19981;&#33021;&#29702;&#35299;&#25105;&#30340;&#27010;&#24565;&#65292;&#24171;&#25105;&#20828;&#20986;&#25972;&#20491;&#21069;&#31471;&#12290;&#30475;&#36215;&#20358;&#23436;&#20840;&#38627;&#19981;&#20498; Codex: <a href="https://github.com/schwannden/spiritual-director-chat/tree/main">https://github.com/schwannden/spiritual-director-chat</a></p><h2>&#23526;&#39511;&#65306;&#20154;&#26684;&#20998;&#38626;&#65292;&#35731;&#23566;&#24107;&#30340;&#32882;&#38899;&#26356;&#32020;&#28136;</h2><p>&#29609;&#20102;&#19968;&#38499;&#23376;&#24460;&#65292;&#25105;&#30332;&#29694;&#33258;&#24049;&#36010;&#24515;&#20102;&#12290;&#25105;&#19981;&#24819;&#21482;&#26377;&#19968;&#20301;&#23566;&#24107;&#65292;&#25105;&#24819;&#33021;<strong>&#33258;&#30001;&#20999;&#25563;&#12300;&#30050;&#24503;&#29983;&#30340;&#29287;&#32773;&#35486;&#27683;&#12301;&#21644;&#12300;&#22350;&#22521;&#26031;&#30340;&#20462;&#38498;&#35486;&#27683;&#12301;</strong>&#65292;&#35731;&#25105;&#21487;&#20197;&#30475;&#30475;&#20013;&#19990;&#32000;&#30340;&#26234;&#24935;&#36319;&#30070;&#20195;&#29287;&#32773;&#30340;&#24046;&#30064;&#12290;</p><p>&#26044;&#26159;&#25105;&#22238;&#38957;&#20462;&#25913;&#25552;&#31034;&#65292;&#21152;&#20837;&#20102;<strong>&#12300;&#20154;&#26684; (Persona) &#20998;&#38626;&#12301;&#30340;&#35373;&#35336;&#12290;&#25105;&#35731;&#21069;&#31471;&#22686;&#21152;&#20102;&#19968;&#20491;&#19979;&#25289;&#36984;&#21934;&#65292;&#24460;&#31471;&#21063;&#26371;&#26681;&#25818;&#25105;&#30340;&#36984;&#25799;&#65292;&#21482;&#21435;&#26597;&#23565;&#25033;&#30340;&#36039;&#26009;&#24235;&#65292;&#25110;&#26159;&#23559;&#20841;&#26412;&#26360;&#30340;&#19978;&#19979;&#25991;&#21512;&#20341;&#12290;&#26356;&#31934;&#30906;&#30340;&#31995;&#32113;&#25552;&#31034;&#65292;&#30906;&#20445;&#27169;&#22411;&#22312;&#22238;&#25033;&#26178;&#33021;</strong>&#20445;&#25345;&#36984;&#23450;&#23566;&#24107;&#29544;&#26377;&#30340;&#35486;&#27683;&#65292;&#36991;&#20813;&#20841;&#20301;&#32854;&#36066;&#30340;&#32882;&#38899;&#12300;&#28151;&#38899;&#12301;&#65292;&#35731;&#25351;&#24341;&#26356;&#28858;&#32020;&#28136;&#28165;&#26224;&#12290;</p><h4>&#21443;&#32771;&#25351;&#20196;</h4><p>Evolve the existing RAG chatbot to support two distinct personas tied to the collections. Requirements:</p><ul><li><p>Collections stay &#8220;on-living-well&#8221; and &#8220;imitatio-christi&#8221;, but add persona-aware voices.</p></li><li><p>on-living-well &#8594; Eugene Peterson: warm, pastoral, conversational.</p></li><li><p>imitatio-christi &#8594; Thomas &#224; Kempis: reflective, devotional, humble.</p></li><li><p>both &#8594; blend the two voices naturally.</p></li><li><p>Update the system prompt generation so the tone changes based on selected collections; keep the default to on-living-well when none provided.</p></li><li><p>Keep retrieval/top_k logic intact; if &#8220;both&#8221; is requested, spread top_k across collections as before.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!tcvs!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F429c57a5-dd47-42be-b4f5-c0c8b0377129_2000x809.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!tcvs!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F429c57a5-dd47-42be-b4f5-c0c8b0377129_2000x809.png 424w, https://substackcdn.com/image/fetch/$s_!tcvs!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F429c57a5-dd47-42be-b4f5-c0c8b0377129_2000x809.png 848w, https://substackcdn.com/image/fetch/$s_!tcvs!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F429c57a5-dd47-42be-b4f5-c0c8b0377129_2000x809.png 1272w, https://substackcdn.com/image/fetch/$s_!tcvs!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F429c57a5-dd47-42be-b4f5-c0c8b0377129_2000x809.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!tcvs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F429c57a5-dd47-42be-b4f5-c0c8b0377129_2000x809.png" width="1456" height="589" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/429c57a5-dd47-42be-b4f5-c0c8b0377129_2000x809.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:589,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!tcvs!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F429c57a5-dd47-42be-b4f5-c0c8b0377129_2000x809.png 424w, https://substackcdn.com/image/fetch/$s_!tcvs!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F429c57a5-dd47-42be-b4f5-c0c8b0377129_2000x809.png 848w, https://substackcdn.com/image/fetch/$s_!tcvs!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F429c57a5-dd47-42be-b4f5-c0c8b0377129_2000x809.png 1272w, https://substackcdn.com/image/fetch/$s_!tcvs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F429c57a5-dd47-42be-b4f5-c0c8b0377129_2000x809.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>&#25343;&#21516;&#19968;&#20491;&#21839;&#38988;&#65292;&#36984;&#25799;&#19981;&#21516;&#30340;&#23660;&#38728;&#32147;&#20856;&#20316;&#28858;&#20381;&#25818;&#22238;&#31572;&#65292;AI&#30906;&#23526;&#21487;&#20197;&#38283;&#22987;&#27169;&#20223;&#25105;&#20497;&#35201;&#20182;&#25104;&#28858;&#30340;&#20154;&#12290;</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Kvbt!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9727a890-8100-4a04-b7b1-fe5962cf4455_2010x1084.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Kvbt!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9727a890-8100-4a04-b7b1-fe5962cf4455_2010x1084.png 424w, https://substackcdn.com/image/fetch/$s_!Kvbt!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9727a890-8100-4a04-b7b1-fe5962cf4455_2010x1084.png 848w, https://substackcdn.com/image/fetch/$s_!Kvbt!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9727a890-8100-4a04-b7b1-fe5962cf4455_2010x1084.png 1272w, https://substackcdn.com/image/fetch/$s_!Kvbt!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9727a890-8100-4a04-b7b1-fe5962cf4455_2010x1084.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Kvbt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9727a890-8100-4a04-b7b1-fe5962cf4455_2010x1084.png" width="1456" height="785" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9727a890-8100-4a04-b7b1-fe5962cf4455_2010x1084.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:785,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!Kvbt!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9727a890-8100-4a04-b7b1-fe5962cf4455_2010x1084.png 424w, https://substackcdn.com/image/fetch/$s_!Kvbt!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9727a890-8100-4a04-b7b1-fe5962cf4455_2010x1084.png 848w, https://substackcdn.com/image/fetch/$s_!Kvbt!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9727a890-8100-4a04-b7b1-fe5962cf4455_2010x1084.png 1272w, https://substackcdn.com/image/fetch/$s_!Kvbt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9727a890-8100-4a04-b7b1-fe5962cf4455_2010x1084.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!rCx7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff70f19ad-6c52-487c-bfb5-27c4eaf237a5_2036x1058.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!rCx7!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff70f19ad-6c52-487c-bfb5-27c4eaf237a5_2036x1058.png 424w, https://substackcdn.com/image/fetch/$s_!rCx7!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff70f19ad-6c52-487c-bfb5-27c4eaf237a5_2036x1058.png 848w, https://substackcdn.com/image/fetch/$s_!rCx7!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff70f19ad-6c52-487c-bfb5-27c4eaf237a5_2036x1058.png 1272w, https://substackcdn.com/image/fetch/$s_!rCx7!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff70f19ad-6c52-487c-bfb5-27c4eaf237a5_2036x1058.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!rCx7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff70f19ad-6c52-487c-bfb5-27c4eaf237a5_2036x1058.png" width="1456" height="757" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f70f19ad-6c52-487c-bfb5-27c4eaf237a5_2036x1058.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:757,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!rCx7!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff70f19ad-6c52-487c-bfb5-27c4eaf237a5_2036x1058.png 424w, https://substackcdn.com/image/fetch/$s_!rCx7!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff70f19ad-6c52-487c-bfb5-27c4eaf237a5_2036x1058.png 848w, https://substackcdn.com/image/fetch/$s_!rCx7!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff70f19ad-6c52-487c-bfb5-27c4eaf237a5_2036x1058.png 1272w, https://substackcdn.com/image/fetch/$s_!rCx7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff70f19ad-6c52-487c-bfb5-27c4eaf237a5_2036x1058.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>&#22312;&#36889;&#20491;&#23567;&#23567;&#30340;&#23526;&#39511;&#23460;&#35041;&#65292;&#25105;&#30475;&#21040;&#20102;<strong>&#19981;&#21516;&#19990;&#20195;&#23660;&#38728;&#26234;&#24935;&#30340;&#24046;&#30064;</strong>&#65306;</p><ul><li><p>&#30070;&#25105;&#36984;&#20013;<strong>&#30050;&#24503;&#29983;&#30340;&#36817;&#20195;&#23660;&#38728;&#26234;&#24935;</strong>&#26178;&#65292;&#23566;&#24107;&#30340;&#22238;&#25033;&#24118;&#33879;&#19968;&#31278;&#28331;&#26580;&#30340;&#22522;&#35519;&#65292;&#25945;&#25105;&#20497;<strong>&#28331;&#26580;&#22320;&#23565;&#24453;&#33258;&#24049;</strong>&#65292;&#30475;&#37325;&#24179;&#20961;&#33287;&#26085;&#24120;&#20013;&#30340;&#24681;&#20856;&#12290;</p></li><li><p>&#30070;&#25105;&#20999;&#25563;&#21040;<strong>&#22350;&#22521;&#26031;&#30340;&#20013;&#19990;&#32000;&#20462;&#38498;&#26234;&#24935;</strong>&#26178;&#65292;&#22238;&#25033;&#21063;&#36681;&#28858;&#19968;&#31278;&#26356;&#22196;&#35641;&#12289;&#26356;&#20811;&#21046;&#30340;&#35486;&#27683;&#65292;&#25552;&#37266;&#25105;&#20497;<strong>&#35201;&#24536;&#25105;&#12289;&#35201;&#25448;&#24049;</strong>&#65292;&#23559;&#30446;&#20809;&#23560;&#27880;&#26044;&#26356;&#39640;&#36960;&#30340;&#30446;&#27161;&#12290;</p></li></ul><h2>&#26368;&#32066;&#30340;&#21453;&#24605;&#65306;&#26377;&#38480;&#30340;&#31185;&#25216;&#65292;&#28961;&#38480;&#30340;&#29983;&#21629;</h2><p>&#36889;&#20491; RAG &#23526;&#39511;&#38614;&#28982;&#25104;&#21151;&#22320;&#35731; AI &#31359;&#19978;&#20102;&#12300;&#23660;&#38728;&#23566;&#24107;&#12301;&#30340;&#22806;&#34915;&#65292;&#20294;&#23427;&#20063;&#35731;&#25105;&#26356;&#28165;&#26970;&#22320;&#24847;&#35672;&#21040;&#65306;<strong>AI &#22312;&#24515;&#38728;&#38936;&#22495;&#33021;&#25552;&#20379;&#30340;&#24171;&#21161;&#65292;&#32066;&#31350;&#26159;&#26997;&#20854;&#26377;&#38480;&#30340;</strong>&#12290;&#35498;&#21040;&#24213;&#65292;&#36889;&#27425;&#30340;&#22039;&#35430;&#65292;&#26412;&#36074;&#19978;&#21482;&#26159;&#24819;&#30475;&#30475; AI &#33021;&#21542;&#33267;&#23569;<strong>&#31934;&#30906;&#22320;&#27169;&#20223;</strong>&#25105;&#21916;&#27489;&#30340;&#20316;&#32773;&#35486;&#27683;&#65292;&#21516;&#26178;&#38918;&#20415;&#28204;&#35430;&#19968;&#20123;&#22909;&#29992;&#19988;&#20813;&#36027;&#30340;&#38283;&#28304;&#24037;&#20855;&#65288;&#22914; Chroma&#12289;LM Studio&#65289;&#12290;</p><p>&#30495;&#27491;&#30340;&#23660;&#38728;&#25351;&#24341;&#65292;&#20854;&#22256;&#38627;&#24230;&#36960;&#36229;&#26044;&#27492;&#12290;&#23427;&#27794;&#26377;&#22266;&#23450;&#30340; SOP &#25110;&#19968;&#22871;&#36890;&#29992;&#30340;&#35486;&#27683;&#12290;&#38754;&#23565;&#21516;&#19968;&#20491;&#21839;&#38988;&#65292;&#19968;&#20301;&#23660;&#38728;&#23566;&#24107;&#24517;&#38920;&#28145;&#30693;&#23565;&#26041;&#30340;<strong>&#20809;&#26223;&#33287;&#26178;&#20195;&#30450;&#40670;</strong>&#65292;&#25165;&#33021;&#28310;&#30906;&#22238;&#25033;&#65306;&#31350;&#31455;&#26159;&#35442;<strong>&#25361;&#25136;</strong>&#12289;<strong>&#23433;&#24944;</strong>&#12289;<strong>&#36012;&#20633;</strong>&#65292;&#36996;&#26159;&#20677;&#20677;&#26159;<strong>&#21934;&#32020;&#22320;&#38506;&#20276;&#12289;&#31153;&#21578;&#33287;&#32838;&#32893;</strong>&#65311;&#36889;&#19968;&#20999;&#65292;&#37117;&#38656;&#35201;&#24314;&#31435;&#22312;<strong>&#24859;&#30340;&#38364;&#20418;&#12289;&#38918;&#26381;&#30340;&#38364;&#20418;</strong>&#12289;&#38263;&#26178;&#38291;&#32047;&#31309;&#30340;<strong>&#29702;&#35299;</strong>&#65292;&#20197;&#21450;&#22312;&#38364;&#20418;&#20013;&#19981;&#26039;&#21152;&#28145;&#30340;<strong>&#35469;&#35672;</strong>&#12290;</p><p>&#22240;&#27492;&#65292;&#36889;&#20491;&#31995;&#32113;&#30340;&#26368;&#20339;&#23450;&#20301;&#65292;&#26159;&#20316;&#28858;&#19968;&#26412;<strong>&#32147;&#20856;&#23566;&#35712;&#24037;&#20855;</strong>&#65292;&#32780;&#38750;&#19968;&#20301;&#30495;&#27491;&#30340;&#23566;&#24107;&#12290;&#23427;&#28858;&#25105;&#20497;&#25171;&#38283;&#20102;&#19968;&#25159;&#38272;&#65292;&#35731;&#25105;&#20497;&#26356;&#36028;&#36817;&#37027;&#20123;&#20553;&#22823;&#38728;&#39746;&#30340;&#25991;&#23383;&#65292;&#20294;&#36890;&#24448;&#24515;&#38728;&#28145;&#34389;&#30340;&#36947;&#36335;&#65292;&#20381;&#33290;&#38656;&#35201;&#25105;&#20497;&#33258;&#24049;&#25237;&#20837;&#30495;&#23526;&#30340;&#29983;&#21629;&#33287;&#38364;&#20418;&#12290;</p>]]></content:encoded></item><item><title><![CDATA[Setup local “claude code” alternative — free, open source, and no code]]></title><description><![CDATA[Using LM Studio + Open Code]]></description><link>https://schwannden.substack.com/p/setup-local-claude-code-alternative</link><guid isPermaLink="false">https://schwannden.substack.com/p/setup-local-claude-code-alternative</guid><dc:creator><![CDATA[schwannden]]></dc:creator><pubDate>Thu, 25 Sep 2025 07:56:24 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Vmf0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F321363be-dc22-4b3a-a972-b77936b1b942_3012x1662.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Vmf0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F321363be-dc22-4b3a-a972-b77936b1b942_3012x1662.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Vmf0!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F321363be-dc22-4b3a-a972-b77936b1b942_3012x1662.png 424w, https://substackcdn.com/image/fetch/$s_!Vmf0!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F321363be-dc22-4b3a-a972-b77936b1b942_3012x1662.png 848w, https://substackcdn.com/image/fetch/$s_!Vmf0!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F321363be-dc22-4b3a-a972-b77936b1b942_3012x1662.png 1272w, https://substackcdn.com/image/fetch/$s_!Vmf0!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F321363be-dc22-4b3a-a972-b77936b1b942_3012x1662.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Vmf0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F321363be-dc22-4b3a-a972-b77936b1b942_3012x1662.png" width="1456" height="803" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/321363be-dc22-4b3a-a972-b77936b1b942_3012x1662.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:803,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:3425166,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://schwannden.substack.com/i/174511919?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F321363be-dc22-4b3a-a972-b77936b1b942_3012x1662.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Vmf0!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F321363be-dc22-4b3a-a972-b77936b1b942_3012x1662.png 424w, https://substackcdn.com/image/fetch/$s_!Vmf0!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F321363be-dc22-4b3a-a972-b77936b1b942_3012x1662.png 848w, https://substackcdn.com/image/fetch/$s_!Vmf0!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F321363be-dc22-4b3a-a972-b77936b1b942_3012x1662.png 1272w, https://substackcdn.com/image/fetch/$s_!Vmf0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F321363be-dc22-4b3a-a972-b77936b1b942_3012x1662.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>I wanted a Claude Code&#8211;style coding buddy that runs <strong>100% locally</strong> on my MacBook Pro: no API keys, no cloud costs, and fast on Apple Silicon. After a short detour with MLX Knife, the winning combo was:</p><ul><li><p><strong><a href="https://lmstudio.ai/">LM Studio</a></strong> for a local, OpenAI-compatible API server</p></li><li><p><strong><a href="https://huggingface.co/mlx-community/gpt-oss-20b-MXFP4-Q8">GPT-OSS-20B (MXFP4)</a></strong> as the model</p></li><li><p><strong><a href="https://github.com/sst/opencode">OpenCode</a></strong> as the terminal coding agent</p></li></ul><p>Below is exactly what I ran and clicked, plus the one config file that makes it all gel.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://schwannden.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption"></p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div><hr></div><h2>tl;dr (copy-paste)</h2><pre><code># 1) Install LM Studio (GUI + local API server)
brew install --cask lm-studio

# 2) (Optional) launch LM Studio from terminal
open -a &#8220;LM Studio&#8221;

# &#128073; In LM Studio UI:
# - Start the Local Server (Runtimes &#8594; Developer) until it says:
#   Reachable at http://127.0.0.1:1234
# - Load the model: gpt-oss-20b (MLX / MXFP4)
# - Turn the Context Length slider way up (OpenCode uses long prompts)

# 3) Quick API smoke test (expects LM Studio at :1234)
curl http://127.0.0.1:1234/v1/models
curl -s -X POST &#8220;http://127.0.0.1:1234/v1/chat/completions&#8221; \
  -H &#8220;Content-Type: application/json&#8221; -H &#8220;Authorization: Bearer EMPTY&#8221; \
  -d &#8216;{
    &#8220;model&#8221;: &#8220;gpt-oss-20b&#8221;,
    &#8220;messages&#8221;: [{&#8221;role&#8221;:&#8221;user&#8221;,&#8221;content&#8221;:&#8221;Say hi in one sentence.&#8221;}]
  }&#8217; | jq .

# 4) Install OpenCode (terminal coding agent)
brew install sst/tap/opencode

# 5) Create OpenCode config (provider &#8594; LM Studio, model &#8594; gpt-oss-20b)
mkdir -p ./.opencode
cat &gt; ./.opencode/opencode.json &lt;&lt;&#8217;JSON&#8217;
{
  &#8220;$schema&#8221;: &#8220;https://opencode.ai/config.json&#8221;,
  &#8220;provider&#8221;: {
    &#8220;lmstudio&#8221;: {
      &#8220;npm&#8221;: &#8220;@ai-sdk/openai-compatible&#8221;,
      &#8220;name&#8221;: &#8220;LM Studio (local)&#8221;,
      &#8220;options&#8221;: {
        &#8220;baseURL&#8221;: &#8220;http://127.0.0.1:1234/v1&#8221;,
        &#8220;apiKey&#8221;: &#8220;EMPTY&#8221;
      },
      &#8220;models&#8221;: {
        &#8220;gpt-oss-20b&#8221;: { &#8220;name&#8221;: &#8220;gpt-oss-20b&#8221; }
      }
    }
  },
  &#8220;model&#8221;: &#8220;lmstudio/gpt-oss-20b&#8221;
}
JSON

# 6) Run it &#128640;
opencode
</code></pre><div><hr></div><h2>The longer story</h2><p>I set out to recreate the &#8220;talk to your editor&#8221; workflow, but <strong>fully local</strong>. I first tried <strong>MLX Knife</strong> to host an OpenAI-compatible server on <code>http://127.0.0.1:8000/v1</code>. It did spin up&#8212;and even had a tiny <code>simple_chat.html</code>&#8212;but OpenCode needed a few API behaviors the server didn&#8217;t fully cover. Specifically, I ran into OpenAI-compat mismatches (e.g., JSON field naming) and a missing embeddings endpoint, which some clients expect. Those gaps are tracked in the project&#8217;s issues, so it may work out of the box later; it just didn&#8217;t for my setup today. (<a href="https://github.com/mzau/mlx-knife/issues">GitHub</a>)</p><p>So I pivoted to <strong>LM Studio</strong>, which exposes the familiar <strong>OpenAI-like</strong> endpoints (<code>/v1/models</code>, <code>/v1/chat/completions</code>, <code>/v1/completions</code>, <code>/v1/embeddings</code>) on a local server&#8212;by default reachable at <code>http://127.0.0.1:1234/v1</code>. That instantly clicked with OpenCode. (<a href="https://lmstudio.ai/docs/app/api/endpoints/openai">LM Studio</a>)</p><div><hr></div><h2>step-by-step (with a couple of gotchas I hit)</h2><h3>1) install LM Studio</h3><pre><code><code>brew install --cask lm-studio
open -a &#8220;LM Studio&#8221;
</code></code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!YpF0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82f3897e-ba61-4757-bbe9-6b04a143ddf6_2000x1247.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!YpF0!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82f3897e-ba61-4757-bbe9-6b04a143ddf6_2000x1247.png 424w, https://substackcdn.com/image/fetch/$s_!YpF0!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82f3897e-ba61-4757-bbe9-6b04a143ddf6_2000x1247.png 848w, https://substackcdn.com/image/fetch/$s_!YpF0!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82f3897e-ba61-4757-bbe9-6b04a143ddf6_2000x1247.png 1272w, https://substackcdn.com/image/fetch/$s_!YpF0!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82f3897e-ba61-4757-bbe9-6b04a143ddf6_2000x1247.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!YpF0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82f3897e-ba61-4757-bbe9-6b04a143ddf6_2000x1247.png" width="1456" height="908" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/82f3897e-ba61-4757-bbe9-6b04a143ddf6_2000x1247.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:908,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!YpF0!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82f3897e-ba61-4757-bbe9-6b04a143ddf6_2000x1247.png 424w, https://substackcdn.com/image/fetch/$s_!YpF0!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82f3897e-ba61-4757-bbe9-6b04a143ddf6_2000x1247.png 848w, https://substackcdn.com/image/fetch/$s_!YpF0!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82f3897e-ba61-4757-bbe9-6b04a143ddf6_2000x1247.png 1272w, https://substackcdn.com/image/fetch/$s_!YpF0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82f3897e-ba61-4757-bbe9-6b04a143ddf6_2000x1247.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>2) start the local server + load the model</h3><p>In LM Studio:</p><ul><li><p>Go to <strong>Runtimes &#8594; Developer</strong> and toggle the server to <strong>Running</strong>. You should see <strong>Reachable at </strong></p></li></ul><p>http://127.0.0.1:1234</p><ul><li><p> and a list of supported OpenAI-like endpoints. (<a href="https://lmstudio.ai/docs/app/api/endpoints/openai?utm_source=chatgpt.com">LM Studio</a>)</p></li><li><p>Load <code>gpt-oss-20b</code> (your screenshots show the <strong>MLX</strong> format with <strong>MXFP4</strong> quantization).</p></li><li><p><strong>Important:</strong> drag the <strong>Context Length</strong> slider <strong>way up</strong> (your pic showed support up to <code>131072</code>). OpenCode sends longer task prompts than a normal chat; with a tiny context window, tools won&#8217;t run well.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!cCR9!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a97d79a-dbe5-4212-bd1c-fd36b1a0aa0d_2000x367.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!cCR9!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a97d79a-dbe5-4212-bd1c-fd36b1a0aa0d_2000x367.png 424w, https://substackcdn.com/image/fetch/$s_!cCR9!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a97d79a-dbe5-4212-bd1c-fd36b1a0aa0d_2000x367.png 848w, https://substackcdn.com/image/fetch/$s_!cCR9!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a97d79a-dbe5-4212-bd1c-fd36b1a0aa0d_2000x367.png 1272w, https://substackcdn.com/image/fetch/$s_!cCR9!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a97d79a-dbe5-4212-bd1c-fd36b1a0aa0d_2000x367.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!cCR9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a97d79a-dbe5-4212-bd1c-fd36b1a0aa0d_2000x367.png" width="1456" height="267" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8a97d79a-dbe5-4212-bd1c-fd36b1a0aa0d_2000x367.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:267,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!cCR9!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a97d79a-dbe5-4212-bd1c-fd36b1a0aa0d_2000x367.png 424w, https://substackcdn.com/image/fetch/$s_!cCR9!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a97d79a-dbe5-4212-bd1c-fd36b1a0aa0d_2000x367.png 848w, https://substackcdn.com/image/fetch/$s_!cCR9!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a97d79a-dbe5-4212-bd1c-fd36b1a0aa0d_2000x367.png 1272w, https://substackcdn.com/image/fetch/$s_!cCR9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a97d79a-dbe5-4212-bd1c-fd36b1a0aa0d_2000x367.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>3) smoke test the API</h3><pre><code><code>curl http://127.0.0.1:1234/v1/models

curl -s -X POST &#8220;http://127.0.0.1:1234/v1/chat/completions&#8221; \
  -H &#8220;Content-Type: application/json&#8221; -H &#8220;Authorization: Bearer EMPTY&#8221; \
  -d &#8216;{
    &#8220;model&#8221;: &#8220;gpt-oss-20b&#8221;,
    &#8220;messages&#8221;: [{&#8221;role&#8221;:&#8221;user&#8221;,&#8221;content&#8221;:&#8221;Quick smoke test.&#8221;}]
  }&#8217; | jq .
</code></code></pre><p>LM Studio speaks OpenAI-compatible JSON on those endpoints, so this should stream back a response. (<a href="https://lmstudio.ai/docs/app/api/endpoints/openai?utm_source=chatgpt.com">LM Studio</a>)</p><h3>4) install OpenCode</h3><pre><code><code>brew install sst/tap/opencode
</code></code></pre><p>(Official install paths&#8212;brew and script&#8212;are in their docs/readme.) (<a href="https://opencode.ai/?utm_source=chatgpt.com">opencode.ai</a>)</p><h3>5) point OpenCode at LM Studio (one config file)</h3><p>I used a provider pointing to LM Studio&#8217;s baseURL. The config below is exactly what I run:</p><pre><code><code>{
  &#8220;$schema&#8221;: &#8220;https://opencode.ai/config.json&#8221;,
  &#8220;provider&#8221;: {
    &#8220;lmstudio&#8221;: {
      &#8220;npm&#8221;: &#8220;@ai-sdk/openai-compatible&#8221;,
      &#8220;name&#8221;: &#8220;LM Studio (local)&#8221;,
      &#8220;options&#8221;: { &#8220;baseURL&#8221;: &#8220;http://127.0.0.1:1234/v1&#8221; },
      &#8220;models&#8221;: { &#8220;gpt-oss-20b&#8221;: { &#8220;name&#8221;: &#8220;gpt-oss-20b&#8221; } }
    }
  },
  &#8220;model&#8221;: &#8220;lmstudio/gpt-oss-20b&#8221;
}
</code></code></pre><p>Docs show exactly this pattern for <strong>OpenAI-compatible providers</strong> (custom <code>baseURL</code>, map of <code>models</code>). (<a href="https://opencode.ai/docs/providers/?utm_source=chatgpt.com">opencode.ai</a>)</p><p>Then put it in <code>~/.opencode/opencode.json</code></p><p>Once OpenCode boots, I select the model and start giving it real dev work (&#8220;set up a Go REST handler + Makefile and explain each change&#8221;), and it runs entirely through LM Studio locally.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!GFXU!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8afcc41-28f5-4cdb-b5e3-b15c49dd0a33_1890x888.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!GFXU!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8afcc41-28f5-4cdb-b5e3-b15c49dd0a33_1890x888.png 424w, https://substackcdn.com/image/fetch/$s_!GFXU!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8afcc41-28f5-4cdb-b5e3-b15c49dd0a33_1890x888.png 848w, https://substackcdn.com/image/fetch/$s_!GFXU!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8afcc41-28f5-4cdb-b5e3-b15c49dd0a33_1890x888.png 1272w, https://substackcdn.com/image/fetch/$s_!GFXU!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8afcc41-28f5-4cdb-b5e3-b15c49dd0a33_1890x888.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!GFXU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8afcc41-28f5-4cdb-b5e3-b15c49dd0a33_1890x888.png" width="1456" height="684" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f8afcc41-28f5-4cdb-b5e3-b15c49dd0a33_1890x888.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:684,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!GFXU!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8afcc41-28f5-4cdb-b5e3-b15c49dd0a33_1890x888.png 424w, https://substackcdn.com/image/fetch/$s_!GFXU!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8afcc41-28f5-4cdb-b5e3-b15c49dd0a33_1890x888.png 848w, https://substackcdn.com/image/fetch/$s_!GFXU!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8afcc41-28f5-4cdb-b5e3-b15c49dd0a33_1890x888.png 1272w, https://substackcdn.com/image/fetch/$s_!GFXU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8afcc41-28f5-4cdb-b5e3-b15c49dd0a33_1890x888.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h2>what it&#8217;s like in practice</h2><p>With LM Studio + OpenCode, the workflow feels like Claude Code in my terminal&#8212;edits, diffs, planning, and &#8220;do-this-then-that&#8221; loops&#8212;<strong>entirely offline</strong>. On my MacBook Pro, <strong>GPT-OSS-20B (MXFP4)</strong> is a sweet spot: strong reasoning, reasonable VRAM/RAM footprint, and it keeps up with iterative coding.</p><h2>Final Note</h2><p>Here are the reasons why I choose the tech stack:</p><ul><li><p><strong>reasoning capability</strong><br>GPT-OSS-20B is a mixture-of-experts (MoE) model. Even though it has 20B parameters in total, only a subset of experts activate per request. This design gives it strong reasoning skills without needing to run all 20B parameters at once.</p></li><li><p><strong>runtime memory efficiency</strong><br>Because MoE only &#8220;turns on&#8221; part of the model each step, the effective compute is closer to ~3&#8211;4B active parameters. That keeps memory usage low enough to run smoothly on a MacBook Pro instead of a datacenter GPU.</p></li><li><p><strong>mlx optimization</strong><br>The model comes in <strong>MLX format</strong>, which is Apple&#8217;s deep learning framework optimized for M-series chips. MLX takes advantage of Apple Silicon&#8217;s GPU and memory architecture, so inference runs faster and more efficiently compared to generic formats.</p></li><li><p><strong>quantization (MXFP4)</strong><br>The weights are stored in <strong>MXFP4</strong>, a 4-bit floating-point quantization scheme supported natively in MLX. This drastically reduces the model&#8217;s footprint on disk and in RAM, while still preserving reasoning quality. In practice, this means GPT-OSS-20B (MXFP4) fits comfortably in ~12 GB, perfect for laptop use.</p></li></ul><p>I suppose LM Studio could easily be swapped for something else that support MLX optimization. And open code can be swapped for something like goose too. Try and play with your favorite tool! The horizon for edge AI is getting closer and closer.</p><div><hr></div><h2>references</h2><ul><li><p><strong>LM Studio OpenAI-like endpoints:</strong> <code>/v1/models</code>, <code>/v1/chat/completions</code>, <code>/v1/completions</code>, <code>/v1/embeddings</code>. (<a href="https://lmstudio.ai/docs/app/api/endpoints/openai">LM Studio</a>)</p></li><li><p><strong>LM Studio via Homebrew cask</strong> (install line). (<a href="https://formulae.brew.sh/cask/lm-studio">Homebrew Formulae</a>)</p></li><li><p><strong>OpenCode providers/config</strong> (custom OpenAI-compatible baseURL). (<a href="https://opencode.ai/docs/providers/">opencode.ai</a>)</p></li><li><p><strong>MLX Knife issues</strong> illustrating why I switched (format mismatch &amp; missing embeddings). (<a href="https://github.com/mzau/mlx-knife/issues">GitHub</a>)</p></li></ul><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://schwannden.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption"></p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[從 Vibe Coding 到上線的眉角：SIM《為萬國禱告》的誕生之路]]></title><description><![CDATA[&#22823;&#23478;&#37117;&#22312; Vibe Coding&#65292;&#20294;&#29983;&#29986;&#29872;&#22659;&#30340;&#23526;&#38555;&#21839;&#38988;&#22914;&#20309;&#34389;&#29702;&#65311;&#19968;&#20491;&#19979;&#21320;&#24478;&#35342;&#35542;&#21040;&#19978;&#32218;&#65292;&#28858;&#20840;&#29699;&#31153;&#21578;&#32178;&#31449;&#30340;&#26696;&#20363;&#20998;&#20139;&#12290;&#22312; Vibe Coding &#30340;&#26178;&#20195;&#65292;&#25105;&#30456;&#20449; &#31995;&#32113;&#35373;&#35336;&#33287;&#36939;&#32173;&#30340;&#33021;&#21147;&#26371;&#26356;&#21152;&#37325;&#35201;&#12290;&#20294;&#21516;&#26178;&#65292;&#26356;&#37325;&#35201;&#30340;&#65533;]]></description><link>https://schwannden.substack.com/p/vibe-coding-to-production-sim-prayer-website</link><guid isPermaLink="false">https://schwannden.substack.com/p/vibe-coding-to-production-sim-prayer-website</guid><dc:creator><![CDATA[schwannden]]></dc:creator><pubDate>Wed, 03 Sep 2025 12:49:24 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/8c70f93a-f840-4b0e-818b-9727806bc8ce_1884x946.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!XxFw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e7442f9-7669-4ea0-b79d-2f5384e943e7_1884x946.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!XxFw!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e7442f9-7669-4ea0-b79d-2f5384e943e7_1884x946.png 424w, https://substackcdn.com/image/fetch/$s_!XxFw!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e7442f9-7669-4ea0-b79d-2f5384e943e7_1884x946.png 848w, https://substackcdn.com/image/fetch/$s_!XxFw!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e7442f9-7669-4ea0-b79d-2f5384e943e7_1884x946.png 1272w, https://substackcdn.com/image/fetch/$s_!XxFw!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e7442f9-7669-4ea0-b79d-2f5384e943e7_1884x946.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!XxFw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e7442f9-7669-4ea0-b79d-2f5384e943e7_1884x946.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3e7442f9-7669-4ea0-b79d-2f5384e943e7_1884x946.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!XxFw!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e7442f9-7669-4ea0-b79d-2f5384e943e7_1884x946.png 424w, https://substackcdn.com/image/fetch/$s_!XxFw!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e7442f9-7669-4ea0-b79d-2f5384e943e7_1884x946.png 848w, https://substackcdn.com/image/fetch/$s_!XxFw!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e7442f9-7669-4ea0-b79d-2f5384e943e7_1884x946.png 1272w, https://substackcdn.com/image/fetch/$s_!XxFw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e7442f9-7669-4ea0-b79d-2f5384e943e7_1884x946.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p>&#32178;&#22336;&#65306;<a href="https://prayer.simtaiwan.org?utm_source=chatgpt.com">prayer.simtaiwan.org</a></p><h2>&#19968;&#38931;&#21320;&#39184;&#23436;&#25104;&#30340; PoC</h2><p>&#37027;&#22825;&#21320;&#39184;&#30340;&#26178;&#20505;&#65292;SIM &#30340; Roger &#31361;&#28982;&#21839;&#25105;&#65306;&#12300;&#33021;&#19981;&#33021;&#25214;&#20491;&#26178;&#38291;&#32842;&#32842;&#65311;&#12301;<br>&#25105;&#24515;&#24819;&#21453;&#27491;&#25105;&#24118;&#33879;&#38651;&#33126;&#65292;&#23601;&#35498;&#65306;&#12300;&#19981;&#22914;&#29694;&#22312;&#23601;&#38283;&#22987;&#21543;&#12290;&#12301;<br>&#20182;&#26377;&#40670;&#39514;&#35357;&#65292;&#26412;&#20358;&#20197;&#28858;&#35201;&#20808;&#20132;&#36890;&#24819;&#27861;&#12289;&#35413;&#20272;&#22256;&#38627;&#24230;&#65292;&#27794;&#24819;&#21040;&#25105;&#20497;&#23601;&#30452;&#25509;&#38283;&#24037;&#20102;&#12290;</p><p>&#19968;&#23567;&#26178;&#20197;&#24460;&#65292;&#32178;&#31449;&#22291;&#24418;&#22823;&#33268;&#23436;&#25104;&#12290;&#20294;&#26159;&#24478;&#30475;&#36215;&#20358;&#23436;&#25104;&#65292;&#21040;&#19968;&#20491;&#23560;&#26989;&#21487;&#32173;&#35703;&#30340;&#19978;&#32218;&#32178;&#31449;&#65292;&#20013;&#38291;&#36996;&#30332;&#29983;&#20102;&#20160;&#40636;&#20107;&#21602;&#65311;</p><div><hr></div><h2>&#24478;&#21407;&#22411;&#21040;&#27491;&#24335;&#19978;&#32218;</h2><p>PoC &#38614;&#24555;&#65292;&#20294;&#35201;&#30495;&#27491;&#19978;&#32218;&#36996;&#26377;&#24190;&#20491;&#38364;&#21345;&#12290;</p><h3>Github Pages &#25552;&#21319;&#25928;&#33021;</h3><p>Lovable &#30340;&#30171;&#40670;&#22312;&#26044; <strong>loading &#22826;&#24930;</strong>&#12290;&#25152;&#20197;&#25105;&#25226;&#23560;&#26696;&#25509;&#19978; Github&#65292;&#21033;&#29992; <strong>Github Pages</strong> &#20358;&#30332;&#20296;&#65292;&#25928;&#33021;&#30452;&#25509;&#25552;&#21319;&#12290;Loading&#30340;&#26178;&#38291;&#24478;3&#31186;&#37912;&#38477;&#21040;&#24190;&#30334;&#27627;&#31186;&#12290;<br>&#20294;&#36889;&#35041;&#26377;&#20491;&#22353;&#65306;&#22240;&#28858;&#25105;&#20497;&#26159; SPA&#65288;Single Page Application&#65289;&#65292;&#30452;&#25509;&#25918;&#22312; Github Pages &#26371;&#36935;&#21040; <strong>routing &#21839;&#38988;</strong>&#12290;</p><p>&#19978;&#20659;&#21040; <strong>Github Pages</strong> &#30340;&#26178;&#20505;&#65292;&#26371;&#36935;&#21040;&#19968;&#20491;&#29305;&#21029;&#30340;&#40635;&#29033;&#65306;<strong>SPA&#65288;Single Page Application&#65289;&#26412;&#36523;&#30340;&#36335;&#30001;&#65292;&#26371;&#34987; Github Pages &#30340;&#20839;&#24314; routing &#25803;&#20303;&#12290;</strong>&#21407;&#22240;&#26159;&#36889;&#27171;&#30340;&#65306;</p><ul><li><p>&#22312; React Router&#65288;&#25110; Vue Router&#65289;&#35041;&#65292;<code>/prayer/123</code> &#20854;&#23526;&#21482;&#26159;&#30001;&#21069;&#31471;&#35299;&#35712;&#30340;&#34395;&#25836;&#36335;&#30001;&#12290;</p></li><li><p>&#20294; Github Pages &#19981;&#26159;&#36889;&#27171;&#24819;&#65292;&#23427;&#26371;&#30495;&#30340;&#21435;&#20282;&#26381;&#22120;&#35041;&#25214; <code>/prayer/123</code> &#36889;&#20491;&#36039;&#26009;&#22846;&#25110;&#27284;&#26696;&#12290;</p></li><li><p>&#25214;&#19981;&#21040;&#65311;&#37027;&#23601;&#30452;&#25509;&#22238; 404&#12290;</p></li></ul><p>&#25563;&#21477;&#35441;&#35498;&#65292;<strong>Github Pages &#30340;&#38748;&#24907;&#27284;&#26696;&#20282;&#26381;&#22120; router&#65292;&#26371;&#20808;&#25876;&#25130;&#25481; request</strong>&#65292;&#23566;&#33268; React Router &#26681;&#26412;&#27794;&#27231;&#26371;&#34389;&#29702;&#12290;</p><h3>Workaround&#65306;Custom 404 Redirect</h3><p>&#35299;&#27861;&#26159;&#25105;&#20497;&#33258;&#24049;&#20570;&#19968;&#20491; <strong>&#23458;&#35069;&#21270;&#30340; 404 &#38913;&#38754;</strong>&#65306;</p><ol><li><p>&#20351;&#29992;&#32773;&#25171; <code>/prayer/123</code> &#8594; Github Pages &#25214;&#19981;&#21040; &#8594; &#36339;&#21040;&#25105;&#20497;&#30340; 404.html&#12290;</p></li><li><p>&#22312; 404.html &#35041;&#65292;&#25105;&#20497;&#25226;&#30070;&#21069;&#30340; <code>path</code>&#12289;<code>query parameter</code>&#12289;<code>hash</code> &#37117;&#35352;&#19979;&#20358;&#12290;</p></li><li><p>&#25509;&#33879;&#29992; JavaScript &#25226;&#20351;&#29992;&#32773;&#23566;&#22238; <code>/</code>&#65288;root&#65289;&#12290;</p></li><li><p>&#36889;&#26178;&#20505; React Router &#25509;&#25163;&#65292;&#35712;&#21040;&#21083;&#21083;&#20659;&#36942;&#20358;&#30340;&#36335;&#30001;&#36039;&#35338;&#65292;&#20877;&#21028;&#26039;&#35442;&#39023;&#31034;&#21738;&#19968;&#20491;&#38913;&#38754;&#12290;</p></li></ol><h3>&#33258;&#35330;&#32178;&#22495; &amp; Cloudflare</h3><p>&#25509;&#33879;&#65292;&#25105;&#20497;&#38656;&#35201;&#19968;&#20491;&#22909;&#35352;&#30340;&#32178;&#22336;&#12290;SIM &#30340; IT &#24171;&#24537;&#22312; DNS &#21152;&#20102; CNAME&#65292;&#25351;&#21040; Github Pages&#12290;&#20877;&#36879;&#36942; <strong>Cloudflare</strong> &#35373;&#23450; Custom Domain&#65292;&#23601;&#33021;&#29992;&#19978; <strong>prayer.simtaiwan.org (</strong>&#35531;&#30475;<a href="https://docs.github.com/en/pages/configuring-a-custom-domain-for-your-github-pages-site/about-custom-domains-and-github-pages">Github&#23448;&#26041;&#25945;&#23416;</a>)&#12290;</p><h3>Supabase &#24115;&#34399;&#20999;&#25563;</h3><p>&#30001;&#26044;&#25105;&#33258;&#24049;&#30340; Supabase &#20813;&#36027;&#38989;&#24230;&#24050;&#32147;&#29190;&#25481;&#65292;&#25152;&#20197;&#20094;&#33030;&#29992; SIM &#30340; email &#30003;&#35531;&#26032;&#30340;&#23560;&#26696;&#65292;&#20877;&#36992;&#35531;&#25105;&#33258;&#24049;&#36914;&#21435;&#20570;&#31649;&#29702;&#21729;&#65292;&#26041;&#20415;&#38263;&#26399;&#32173;&#35703;&#12290;</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ZY3Q!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d638434-0ff4-452c-a861-65e7bf7b882f_1612x310.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ZY3Q!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d638434-0ff4-452c-a861-65e7bf7b882f_1612x310.png 424w, https://substackcdn.com/image/fetch/$s_!ZY3Q!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d638434-0ff4-452c-a861-65e7bf7b882f_1612x310.png 848w, https://substackcdn.com/image/fetch/$s_!ZY3Q!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d638434-0ff4-452c-a861-65e7bf7b882f_1612x310.png 1272w, https://substackcdn.com/image/fetch/$s_!ZY3Q!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d638434-0ff4-452c-a861-65e7bf7b882f_1612x310.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ZY3Q!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d638434-0ff4-452c-a861-65e7bf7b882f_1612x310.png" width="1612" height="310" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3d638434-0ff4-452c-a861-65e7bf7b882f_1612x310.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:310,&quot;width&quot;:1612,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!ZY3Q!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d638434-0ff4-452c-a861-65e7bf7b882f_1612x310.png 424w, https://substackcdn.com/image/fetch/$s_!ZY3Q!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d638434-0ff4-452c-a861-65e7bf7b882f_1612x310.png 848w, https://substackcdn.com/image/fetch/$s_!ZY3Q!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d638434-0ff4-452c-a861-65e7bf7b882f_1612x310.png 1272w, https://substackcdn.com/image/fetch/$s_!ZY3Q!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d638434-0ff4-452c-a861-65e7bf7b882f_1612x310.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h2>&#24037;&#31243;&#21697;&#36074;&#65306;&#24478; README &#21040; Release</h2><p>&#31337;&#23450;&#19978;&#32218;&#24460;&#65292;&#23560;&#26696;&#19981;&#21482;&#35201;&#33021;&#36305;&#65292;&#26356;&#35201;&#12300;&#21487;&#32173;&#35703;&#12301;&#12290;</p><ol><li><p><strong>README / Contributing Guide</strong><br>&#25105;&#29992; Cursor &#21644; Claude Code &#24171;&#24537;&#29983;&#25104;&#25991;&#20214;&#65292;&#35731;&#20219;&#20309;&#24460;&#32396;&#30340;&#21516;&#24037;&#21487;&#20197;&#24555;&#36895;&#19978;&#25163;&#12290;</p></li><li><p><strong>CI/CD &#21697;&#36074;&#25511;&#31649;</strong><br>&#35373;&#23450;&#20102; linter&#12289;type check&#12289;prettier &#31561;&#33258;&#21205;&#27298;&#26597;&#65292;&#36991;&#20813;&#21697;&#36074;&#19979;&#28369;&#12290;</p></li><li><p>Dependabot &#33258;&#21205;&#26356;&#26032;<br>&#22871;&#20214;&#27794;&#26377;&#23450;&#26399;&#26356;&#26032;&#65292;&#26159;&#24456;&#22810;&#23560;&#26696;&#30340;&#20027;&#35201;&#27515;&#22240;...</p></li><li><p><strong>&#29256;&#26412;&#31649;&#29702; Conventional Commit + Release Please</strong></p></li></ol><p>&#36889;&#20491;&#32068;&#21512;&#26159;&#25105;&#22312;&#33258;&#24049;&#30340;&#38283;&#28304;&#23560;&#26696;&#35041;&#24859;&#19978;&#30340;&#19968;&#22871;&#27969;&#31243;&#12290;</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!47Je!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F200d8caf-7181-4ed5-bb73-879632b62597_2000x449.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!47Je!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F200d8caf-7181-4ed5-bb73-879632b62597_2000x449.png 424w, https://substackcdn.com/image/fetch/$s_!47Je!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F200d8caf-7181-4ed5-bb73-879632b62597_2000x449.png 848w, https://substackcdn.com/image/fetch/$s_!47Je!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F200d8caf-7181-4ed5-bb73-879632b62597_2000x449.png 1272w, https://substackcdn.com/image/fetch/$s_!47Je!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F200d8caf-7181-4ed5-bb73-879632b62597_2000x449.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!47Je!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F200d8caf-7181-4ed5-bb73-879632b62597_2000x449.png" width="2000" height="449" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/200d8caf-7181-4ed5-bb73-879632b62597_2000x449.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:449,&quot;width&quot;:2000,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!47Je!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F200d8caf-7181-4ed5-bb73-879632b62597_2000x449.png 424w, https://substackcdn.com/image/fetch/$s_!47Je!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F200d8caf-7181-4ed5-bb73-879632b62597_2000x449.png 848w, https://substackcdn.com/image/fetch/$s_!47Je!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F200d8caf-7181-4ed5-bb73-879632b62597_2000x449.png 1272w, https://substackcdn.com/image/fetch/$s_!47Je!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F200d8caf-7181-4ed5-bb73-879632b62597_2000x449.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p><strong>Conventional Commit</strong> &#30340;&#35215;&#31684;&#65292;&#35201;&#27714;&#25105;&#20497;&#22312; commit message &#35041;&#38754;&#21152;&#19978;&#31777;&#21934;&#30340;&#21069;&#32180;&#65306;</p><ul><li><p><code>feat:</code> &#8594; &#26032;&#21151;&#33021;</p></li><li><p><code>fix:</code> &#8594; &#20462; bug</p></li><li><p><code>docs:</code> &#8594; &#25991;&#20214;&#26356;&#26032;</p></li><li><p><code>chore:</code> &#8594; &#20854;&#20182;&#38620;&#20107;<br>&#8230;&#36996;&#26377;&#20854;&#20182;&#32048;&#20998;&#12290;</p></li></ul><p>&#36889;&#27171;&#19968;&#20358;&#65292;commit log &#19981;&#20877;&#26159;&#12300;&#20098;&#31967;&#31967;&#30340;&#20633;&#24536;&#37636;&#12301;&#65292;&#32780;&#26159;&#32080;&#27083;&#21270;&#30340;&#36039;&#35338;&#12290;</p><p>&#25509;&#33879;&#65292;<strong>Release Please</strong> &#23601;&#33021;&#35712;&#25026;&#36889;&#20123;&#35215;&#31684;&#21270;&#30340; commit&#65292;&#24171;&#25105;&#65306;</p><ol><li><p>&#33258;&#21205;&#29986;&#29983; <strong>CHANGELOG</strong>&#65288;&#28165;&#26970;&#21015;&#20986;&#26032;&#22686;&#12289;&#20462;&#27491;&#12289;&#35722;&#26356;&#65289;&#12290;</p></li><li><p>&#33258;&#21205; bump &#29256;&#26412;&#34399;&#65288;&#20381;&#29031; semver&#65306;<code>feat</code> &#23601;&#21319; minor&#65292;<code>fix</code> &#23601;&#21319; patch&#65289;&#12290;</p></li><li><p>&#33258;&#21205;&#30332; release&#65288;&#29978;&#33267;&#33021;&#24171;&#24537;&#24314; tag&#12289;&#25171; package&#65289;&#12290;</p></li></ol><p>&#32080;&#26524;&#23601;&#26159;&#65306;&#25105;&#26681;&#26412;&#19981;&#29992;&#33258;&#24049;&#30447;&#33879;&#29256;&#26412;&#34399;&#65292;&#25110;&#25812;&#24515; changelog &#24536;&#20102;&#23531;&#12290;<br>&#25972;&#20491; release &#27969;&#31243;&#21482;&#35201;&#40670;&#24190;&#19979;&#21363;&#21487;&#23436;&#25104;&#65292;&#29305;&#21029;&#36969;&#21512;&#36889;&#31278;&#20844;&#38283;&#12289;&#35201;&#38263;&#26399;&#32173;&#35703;&#30340;&#23560;&#26696;&#12290;</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!2huD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff12233b4-d894-4243-adef-2c5ff4309a9d_1952x984.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!2huD!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff12233b4-d894-4243-adef-2c5ff4309a9d_1952x984.png 424w, https://substackcdn.com/image/fetch/$s_!2huD!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff12233b4-d894-4243-adef-2c5ff4309a9d_1952x984.png 848w, https://substackcdn.com/image/fetch/$s_!2huD!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff12233b4-d894-4243-adef-2c5ff4309a9d_1952x984.png 1272w, https://substackcdn.com/image/fetch/$s_!2huD!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff12233b4-d894-4243-adef-2c5ff4309a9d_1952x984.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!2huD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff12233b4-d894-4243-adef-2c5ff4309a9d_1952x984.png" width="1952" height="984" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f12233b4-d894-4243-adef-2c5ff4309a9d_1952x984.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:984,&quot;width&quot;:1952,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!2huD!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff12233b4-d894-4243-adef-2c5ff4309a9d_1952x984.png 424w, https://substackcdn.com/image/fetch/$s_!2huD!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff12233b4-d894-4243-adef-2c5ff4309a9d_1952x984.png 848w, https://substackcdn.com/image/fetch/$s_!2huD!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff12233b4-d894-4243-adef-2c5ff4309a9d_1952x984.png 1272w, https://substackcdn.com/image/fetch/$s_!2huD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff12233b4-d894-4243-adef-2c5ff4309a9d_1952x984.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>&#29986;&#21697;&#24605;&#32173;&#65306;&#24478;&#38656;&#27714;&#32763;&#35695;&#38283;&#22987;</h2><p>&#36889;&#27573;&#36942;&#31243;&#26368;&#26377;&#36259;&#30340;&#65292;&#20854;&#23526;&#26159;&#12300;&#38656;&#27714;&#32763;&#35695;&#12301;&#12290;</p><p>Roger &#19968;&#38283;&#22987;&#24076;&#26395;&#19978;&#20659;&#20841;&#24373;&#22294;&#65306;&#19968;&#24373;&#20013;&#25991;&#65292;&#19968;&#24373;&#33521;&#25991;&#12290;&#20294;&#25105;&#24314;&#35696;&#30452;&#25509;&#29992; <strong>i18n&#65288;&#22283;&#38555;&#21270;&#27231;&#21046;&#65289;</strong>&#65292;&#21516;&#19968;&#24373;&#22294;&#23601;&#33021;&#33258;&#21205;&#20999;&#25563;&#35486;&#35328;&#65292;&#19981;&#38656;&#35201;&#20841;&#20221;&#27284;&#26696;&#12290;&#19968;&#33324;&#20351;&#29992;&#32773;&#22312;&#25226;&#19968;&#20491;&#24819;&#27861;&#36681;&#25104;&#32178;&#31449;&#30340;&#26178;&#20505;&#65292;&#26371;&#24819;&#20687;&#25226;&#23427;&#26412;&#20358;&#30340;&#27969;&#31243;&#21407;&#23553;&#19981;&#21205;&#35722;&#25104;&#32178;&#38913;&#65292;&#20294;&#26159;&#20316;&#28858;&#35373;&#35336;&#24107;&#25105;&#20497;&#21487;&#20197;&#29992;&#25105;&#20497;&#23565;&#26044;&#32178;&#31449;&#22914;&#20309;&#34987;&#20351;&#29992;&#30340;&#23560;&#26989;&#65292;&#20358;&#21332;&#21161;&#12290;</p><p>&#24456;&#22810;&#20351;&#29992;&#32773;&#20854;&#23526;&#19981;&#30693;&#36947;&#36996;&#26377;&#20160;&#40636;&#21487;&#33021;&#24615;&#12290;&#20363;&#22914;&#65306;<strong>&#23383;&#39636;&#35519;&#25972;&#65292;Dark Mode&#65292;PWA&#65288;&#23433;&#35037;&#25104; App&#65289;... &#31561;&#31561;&#65292;&#36889;&#20123;&#37117;&#26159;&#35731;&#25163;&#27231;&#29256;&#26356;&#22909;&#38321;&#35712;&#30340;&#24037;&#20855;&#12290;</strong></p><p><strong>&#21478;&#22806;&#65292;&#27599;&#19968;&#31687;&#31153;&#21578;&#21040;&#24213;&#35201;&#26159;&#19968;&#20491;popup&#36996;&#26159;&#19968;&#20491;&#29544;&#31435;&#38913;&#38754;&#65311;&#36889;&#23601;&#36319;&#20320;&#26377;&#27794;&#26377;&#24819;&#35201;&#35731;&#20351;&#29992;&#32773;&#20998;&#20139;&#26576;&#19968;&#31687;&#31153;&#21578;&#65292;&#36996;&#26159;&#20320;&#21482;&#26377;&#24819;&#35201;&#35731;&#20351;&#29992;&#32773;&#20998;&#20139;&#32178;&#38913;&#12290;&#36889;&#20123;&#35373;&#35336;&#25152;&#24118;&#20358;&#30340;&#20351;&#29992;&#32773;&#32722;&#24931;&#21312;&#21029;&#65292;&#37117;&#21487;&#20197;&#28317;&#36890;&#28165;&#26970;&#65374;</strong></p><p>&#20197;&#19978;&#36890;&#36890;&#21482;&#35201;&#22312; Lovable &#32842;&#32842;&#22825;&#23601;&#23436;&#25104;&#65292;&#23436;&#20840;&#19981;&#29992;&#23531; code&#12290;&#30495;&#27491;&#26377;&#20729;&#20540;&#30340;&#22320;&#26041;&#65292;&#23601;&#26159;&#37328;&#28165;&#30495;&#23526;&#38656;&#27714;&#12290;</p><h2>&#22294;&#29255;&#22739;&#32302;&#65306;&#28858; 20 &#24180;&#24460;&#38928;&#20808;&#35373;&#24819;</h2><p>&#21478;&#19968;&#20491;&#28507;&#22312;&#21839;&#38988;&#65292;&#26159; <strong>Supabase &#20813;&#36027;&#31354;&#38291;&#21482;&#26377; 1G</strong>&#12290;&#31153;&#21578;&#22294;&#27284;&#19968;&#24373;&#23601;&#22909;&#24190; MB&#65292;&#29031;&#36889;&#27171;&#19978;&#20659;&#65292;&#20116;&#24180;&#20839;&#23601;&#29190;&#25481;&#20102;&#12290;</p><p>&#25105;&#20808;&#24819;&#36942;&#35531;&#21516;&#24037;&#22739;&#32302;&#20877;&#19978;&#20659;&#65292;&#20294;&#36889;&#27171;&#22826;&#40635;&#29033;&#65292;&#20063;&#19981;&#29694;&#23526;&#12290;&#26368;&#24460;&#27770;&#23450;&#30452;&#25509;&#22312;&#21069;&#31471;&#29992; <strong>compressor.js</strong>&#65306;&#27599;&#24373;&#22294;&#22312;&#19978;&#20659;&#21069;&#33258;&#21205;&#22739;&#21040; <strong>300kb &#24038;&#21491;</strong>&#12290;&#36889;&#27171; 1G &#31354;&#38291;&#33267;&#23569;&#33021;&#25744; 50 &#24180;&#65292;&#20351;&#29992;&#32773;&#20063;&#23436;&#20840;&#19981;&#29992;&#25913;&#35722;&#32722;&#24931;&#12290;</p><div><hr></div><h2>&#22238;&#39015;&#65306;Vibe Coding &#30340;&#31934;&#39635;</h2><p>Vibe Coding &#35731;&#26377;&#20123;&#20154;&#35258;&#24471;&#65292;&#22909;&#20687;&#20197;&#21069;&#37027;&#20123;&#31243;&#24335;&#33021;&#21147;&#37117;&#27966;&#19981;&#19978;&#29992;&#22580;&#20102;&#12290;<br>&#20294;&#20854;&#23526;&#19981;&#26159;&#34987;&#21462;&#20195;&#65292;&#32780;&#26159; <strong>&#12300;&#33287;&#20154;&#20114;&#21205;&#12289;&#28317;&#36890;&#12289;&#29702;&#35299;&#12301;&#30340;&#33021;&#21147;&#34987;&#25918;&#22823;&#20102;&#20729;&#20540;</strong>&#12290;</p><p>&#31038;&#26371;&#23416;&#32773; Bruno Latour &#26366;&#25552;&#20986;&#19968;&#20491;&#26377;&#36259;&#30340;&#21839;&#38988;&#65306;&#25105;&#20497;&#30495;&#27491;&#38364;&#27880;&#30340;&#31350;&#31455;&#26159; <strong>matter of fact</strong>&#65292;&#36996;&#26159; <strong>matter of concern</strong>&#65311;</p><ul><li><p><strong>Matter of fact</strong>&#65292;&#22312;&#36889;&#20491;&#20363;&#23376;&#35041;&#65292;&#23601;&#20687;&#26159;&#32178;&#31449;&#26412;&#36523;&#35201;&#29992;react&#36996;&#26159;next&#12289;PWA&#35442;&#22914;&#20309;&#23526;&#29694;&#65292;style &#25033;&#35442;&#35201;&#29992; tailwind &#36996;&#26159; styled component&#12290;</p></li><li><p><strong>Matter of concern</strong>&#65292;&#21063;&#26159;&#25105;&#20497;&#22914;&#20309;&#21435;&#38364;&#27880;&#37325;&#35201;&#30340;&#35696;&#38988;&#65306;&#22914;&#20309;&#33287;&#20351;&#29992;&#32773;&#19968;&#36215;&#25366;&#25496;&#30495;&#27491;&#37325;&#35201;&#30340;&#21151;&#33021;&#65311;&#36889;&#38917;&#29986;&#21697;&#26381;&#21209;&#30340;&#23565;&#35937;&#26159;&#35504;&#65311;&#25105;&#20497;&#20570;&#30340;&#27599;&#19968;&#20214;&#20107;&#24773;&#65292;&#33021;&#19981;&#33021;&#25512;&#21205;&#20351;&#29992;&#32773;&#38283;&#22987;&#38364;&#27880;&#37325;&#35201;&#28966;&#40670;&#65311;</p></li></ul><p>&#22312; Vibe Coding &#30340;&#26178;&#20195;&#65292;&#25105;&#30456;&#20449; <strong>&#31995;&#32113;&#35373;&#35336;&#33287;&#36939;&#32173;&#30340;&#33021;&#21147;&#26371;&#26356;&#21152;&#37325;&#35201;</strong>&#65292;&#22240;&#28858;&#36889;&#20123;&#26159;&#22522;&#30990;&#24314;&#35373;&#12290;&#20294;&#21516;&#26178;&#65292;&#26356;&#37325;&#35201;&#30340;&#26159;&#65306;<strong>&#31449;&#22312;&#20351;&#29992;&#32773;&#35282;&#24230;&#24605;&#32771;&#65292;&#32763;&#35695;&#20182;&#20497;&#30340;&#38656;&#35201;&#65292;&#30495;&#35488;&#22320;&#24076;&#26395;&#20570;&#20986;&#33021;&#24171;&#21161;&#20154;&#30340;&#29986;&#21697;</strong>&#12290;</p><p>&#32178;&#22336;&#20877;&#27425;&#38468;&#19978; &#128073; <a href="https://prayer.simtaiwan.org?utm_source=chatgpt.com">prayer.simtaiwan.org</a> &#19968;&#36215;&#28858;&#19990;&#30028;&#31153;&#21578;<br>&#20063;&#27489;&#36814;&#21443;&#33287;&#36002;&#29563;&#65306;<a href="https://github.com/Fruitful-Tools/sim-weekly-prayers">https://github.com/Fruitful-Tools/sim-weekly-prayers</a></p>]]></content:encoded></item><item><title><![CDATA[在優比快Cloud Team工作是什麼樣子]]></title><description><![CDATA[&#22914;&#26524;&#20320;&#27491;&#22312;&#25214;&#19968;&#20221;&#21487;&#20197;&#23433;&#23433;&#38748;&#38748;&#23531;&#31243;&#24335;&#12289;&#19981;&#38656;&#35201;&#22826;&#22810;&#28317;&#36890;&#30340;&#24037;&#20316;&#65292;&#32769;&#23526;&#35498;&#8212;&#8212;Ubiquiti Cloud Team &#21487;&#33021;&#19981;&#36969;&#21512;&#20320;&#12290;]]></description><link>https://schwannden.substack.com/p/working-at-ubiquiti-cloud-team</link><guid isPermaLink="false">https://schwannden.substack.com/p/working-at-ubiquiti-cloud-team</guid><dc:creator><![CDATA[schwannden]]></dc:creator><pubDate>Wed, 03 Sep 2025 03:37:30 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/5fd125d2-f0f5-4f71-809c-9943e363e01c_2000x1057.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!79GZ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5a5d5b3-488d-4e80-a28b-aaaac36eb654_2000x1057.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!79GZ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5a5d5b3-488d-4e80-a28b-aaaac36eb654_2000x1057.png 424w, https://substackcdn.com/image/fetch/$s_!79GZ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5a5d5b3-488d-4e80-a28b-aaaac36eb654_2000x1057.png 848w, https://substackcdn.com/image/fetch/$s_!79GZ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5a5d5b3-488d-4e80-a28b-aaaac36eb654_2000x1057.png 1272w, https://substackcdn.com/image/fetch/$s_!79GZ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5a5d5b3-488d-4e80-a28b-aaaac36eb654_2000x1057.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!79GZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5a5d5b3-488d-4e80-a28b-aaaac36eb654_2000x1057.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c5a5d5b3-488d-4e80-a28b-aaaac36eb654_2000x1057.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!79GZ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5a5d5b3-488d-4e80-a28b-aaaac36eb654_2000x1057.png 424w, https://substackcdn.com/image/fetch/$s_!79GZ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5a5d5b3-488d-4e80-a28b-aaaac36eb654_2000x1057.png 848w, https://substackcdn.com/image/fetch/$s_!79GZ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5a5d5b3-488d-4e80-a28b-aaaac36eb654_2000x1057.png 1272w, https://substackcdn.com/image/fetch/$s_!79GZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5a5d5b3-488d-4e80-a28b-aaaac36eb654_2000x1057.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p>&#22914;&#26524;&#20320;&#27491;&#22312;&#25214;&#19968;&#20221;&#21487;&#20197;&#23433;&#23433;&#38748;&#38748;&#23531;&#31243;&#24335;&#12289;&#19981;&#38656;&#35201;&#22826;&#22810;&#28317;&#36890;&#30340;&#24037;&#20316;&#65292;&#32769;&#23526;&#35498;&#8212;&#8212;Ubiquiti Cloud Team &#21487;&#33021;&#19981;&#36969;&#21512;&#20320;&#12290;</p><p>&#24180;&#36629;&#30340;&#24037;&#31243;&#24107;&#36890;&#24120;&#22312;&#24847;&#30340;&#26159;&#33021;&#19981;&#33021;&#23416;&#32722;&#12289;&#26377;&#27794;&#26377;&#20154;&#24118;&#65307;&#32780;&#36039;&#28145;&#24037;&#31243;&#24107;&#65292;&#21063;&#26356;&#30475;&#37325;&#38936;&#22495;&#30340;&#28145;&#24230;&#33287;&#30332;&#25582;&#31354;&#38291;&#12290;&#36889;&#20841;&#31278;&#25105;&#37117;&#29702;&#35299;&#65292;&#20063;&#37117;&#32147;&#27511;&#36942;&#12290;&#22312; Ubiquiti Cloud Team&#65292;&#24037;&#20316;&#30906;&#23526;&#19981;&#36629;&#39686;&#65292;&#21839;&#38988;&#36890;&#24120;&#20063;&#19981;&#21934;&#32020;&#12290;&#20294;&#22914;&#26524;&#20320;&#36861;&#27714;&#25361;&#25136;&#12289;&#22312;&#24847;&#25216;&#34899;&#22914;&#20309;&#24118;&#20986;&#29986;&#21697;&#20729;&#20540;&#65292;&#36889;&#35041;&#23601;&#26159;&#20491;&#33021;&#35731;&#20320;&#19981;&#26039;&#30952;&#32244;&#12289;&#36880;&#27493;&#25918;&#22823;&#30340;&#33310;&#21488;&#12290;</p><p>&#19968;&#20123;&#22522;&#26412;&#36039;&#35338;&#20808;&#35611;&#28165;&#26970;&#65306;&#25105;&#20497;&#20351;&#29992; GitHub&#65292;&#38283;&#30332;&#29872;&#22659;&#29694;&#20195;&#21270;&#65292;&#38642;&#24179;&#21488;&#35442;&#29992;&#30340;&#37117;&#26377;&#65307;&#22296;&#38538;&#20839;&#37096;&#25552;&#20379;&#21508;&#31278; AI coding &#24037;&#20855;&#36628;&#21161;&#26085;&#24120;&#38283;&#30332;&#65288;&#21253;&#25324;&#25105;&#26412;&#20154;&#38750;&#24120;&#20381;&#36084;&#30340; ChatGPT, Cursor &#21644; Claude Code&#65289;&#65307;&#24037;&#20316;&#22411;&#24907;&#24392;&#24615;&#22823;&#65292;&#36960;&#31471;&#12289;&#28961;&#38480;&#20551;&#12289;&#20581;&#36523;&#35036;&#21161;&#12290;</p><h3>&#19968;&#20999;&#24478;&#12300;&#30495;&#23526;&#19990;&#30028;&#30340;&#35037;&#32622;&#12301;&#38283;&#22987;</h3><p>Ubiquiti &#36319;&#22810;&#25976;&#32020;&#36575;&#39636;&#20844;&#21496;&#19981;&#22826;&#19968;&#27171;&#65292;&#25105;&#20497;&#30340;&#38642;&#31471;&#26381;&#21209;&#26159;&#28858;&#20102;&#25903;&#25588;&#20840;&#29699;&#21508;&#22320;&#25976;&#20197;&#30334;&#33836;&#35336;&#30340;&#23526;&#39636;&#32178;&#36890;&#35373;&#20633;&#65306;&#24478; AP&#12289;&#36335;&#30001;&#22120;&#12289;&#20132;&#25563;&#22120;&#65292;&#21040;&#30435;&#25511;&#25885;&#24433;&#27231;&#12289;IoT gateway&#65292;&#29978;&#33267;&#36996;&#26377;&#38651;&#21147;&#20379;&#25033;&#31649;&#29702;&#22120;&#12290;&#36889;&#20123;&#35373;&#20633;&#37096;&#32626;&#22312;&#21654;&#21857;&#24215;&#12289;&#23416;&#26657;&#12289;&#37291;&#38498;&#65292;&#36996;&#26377;&#20491;&#20154;&#30340;&#23478;&#35041;&#12290;</p><p>&#25105;&#20497;&#22296;&#38538;&#30340;&#20219;&#21209;&#65292;&#23601;&#26159;&#35731;&#36889;&#20123;&#35373;&#20633;&#32972;&#24460;&#30340;&#38642;&#31471;&#25511;&#21046;&#24179;&#21488;&#31337;&#23450;&#21448;&#22909;&#29992;&#12290;&#36889;&#30475;&#36215;&#20358;&#20687;&#26159;&#12300;&#23531; API&#12289;&#25509;&#36039;&#26009;&#24235;&#12289;&#20570; UI&#12301;&#36889;&#39006;&#24120;&#35211;&#30340;&#24037;&#20316;&#65292;&#20294;&#23526;&#38555;&#19978;&#65292;&#27599;&#19968;&#27573; API call&#12289;&#27599;&#19968;&#31558;&#36039;&#26009;&#21516;&#27493;&#65292;&#32972;&#24460;&#37117;&#36899;&#21205;&#21040;&#19968;&#20491;&#29694;&#23526;&#19990;&#30028;&#20013;&#27491;&#22312;&#36939;&#20316;&#30340;&#35037;&#32622;&#12290;&#25105;&#20497;&#30340;&#24605;&#32771;&#32317;&#26159;&#20197;&#32066;&#28858;&#22987;&#65306;&#36889;&#27171;&#20570;&#26377;&#27794;&#26377;&#36948;&#21040;&#30446;&#30340;&#65292;&#21738;&#20123;&#35037;&#32622;&#21463;&#21040;&#24433;&#38911;&#65292;&#37096;&#32626;&#30340;&#31574;&#30053;&#21448;&#26159;&#24590;&#40636;&#27171;&#65311;&#24590;&#27171;&#20570;&#22909;&#39080;&#38570;&#31649;&#25511;&#65311;</p><p>&#26377;&#20123;&#27770;&#23450;&#20570;&#37679;&#20102;&#23601;&#22238;&#19981;&#20102;&#38957;&#65292;&#20294;&#26159;&#22312;&#36889;&#35041;&#27493;&#35519;&#24456;&#24555;&#65292;&#24433;&#38911;&#30340;&#20351;&#29992;&#32773;&#21448;&#24456;&#24291;&#65292;&#25105;&#20497;&#24120;&#24120;&#35201;&#24605;&#32771;&#65292;&#33021;&#19981;&#33021;&#22312;&#21487;&#25511;&#30340;&#39080;&#38570;&#24213;&#19979;&#65292;&#20570;&#20986;&#21487;&#36914;&#21487;&#36864;&#30340;&#27770;&#23450;&#65292;&#24590;&#27171;&#36880;&#27493;&#30340;rollout&#65292;&#24590;&#27171;&#25345;&#32396;&#30435;&#28204;&#12290;</p><p>&#36889;&#31278;&#12300;&#25105;&#23531;&#30340;&#31243;&#24335;&#29694;&#22312;&#23601;&#24433;&#38911;&#21040;&#26576;&#20154;&#23478;&#30340;&#32178;&#36335;&#26377;&#27794;&#26377;&#36890;&#12301;&#30340;&#24863;&#35258;&#65292;&#24456;&#30495;&#23526;&#12289;&#24456;&#26377;&#22739;&#21147;&#65292;&#20063;&#24456;&#26377;&#25104;&#23601;&#24863;&#12290;&#22312;&#36889;&#35041;&#24037;&#20316;&#65292;&#27794;&#26377;&#25152;&#35586;&#30340;&#12300;&#32020;&#24037;&#31243;&#24107;&#12301;&#12290;&#27599;&#20491;&#20154;&#37117;&#22312;&#24605;&#32771;&#26550;&#27083;&#65292;&#24605;&#32771;&#20351;&#29992;&#32773;&#12290;</p><h3>&#25216;&#34899;&#39080;&#26684;&#33287;&#25991;&#21270;&#65306;&#39640;&#24230;&#33258;&#20027;&#12289;&#24375;&#35519;Ownership</h3><p>Ubiquiti &#30340;&#25991;&#21270;&#36319;&#25105;&#20197;&#21069;&#24453;&#36942;&#30340;&#20844;&#21496;&#37117;&#19981;&#19968;&#27171;&#12290;</p><p>&#25105;&#24453;&#36942;&#26032;&#21109;&#65292;&#36319;&#33879; MoBagel &#24478;10&#20491;&#20154;&#25104;&#38263;&#21040;70&#20491;&#20154;&#12290;&#22312;&#37027;&#35041;&#24456;&#22810;&#20107;&#24773;&#37117;&#26159;&#25105;&#35498;&#20102;&#31639;&#65292;&#19968;&#20491;&#26202;&#19978;&#23601;&#21487;&#20197;&#29983;&#20986;&#19968;&#26781;&#26032;&#30340;&#29986;&#21697;&#32218;&#12290;&#24460;&#20358;&#22312; Dell 400&#20491;&#20154;&#30340; Global team&#65292;&#22312;&#37027;&#35041;&#26377;&#22823;&#37327;&#30340;&#36328;&#22296;&#38538;&#65292;&#36328;&#22283;&#65292;&#36328;&#23652;&#32026;&#30340;&#28317;&#36890;&#65292;&#36996;&#26377;&#30828;&#39636;&#25991;&#21270;&#33287;&#36575;&#39636;&#25991;&#21270;&#30340;&#38548;&#38305;&#35201;&#36328;&#36234;&#12290;</p><p>Ubiquiti&#24037;&#20316;&#30340;&#24863;&#21463;&#22312;&#36889;&#20841;&#32773;&#20043;&#38291;&#12290;&#30070;&#28982;&#19981;&#21487;&#33021;&#33258;&#24049;&#35498;&#35201;&#19978;&#19968;&#20491;&#29256;&#26412;&#23601;&#19978;&#65288;&#20877;&#27425;&#24375;&#35519;&#65306;&#25105;&#20497;&#26377;&#19978;&#30334;&#33836;&#30340;&#20351;&#29992;&#32773;&#65289;&#65292;&#20294;&#20063;&#19981;&#26371;&#26377;&#22826;&#35463;&#24373;&#30340;&#23652;&#23652;&#30340;&#28317;&#36890;&#25104;&#26412;&#12290;</p><p>&#24456;&#22810;&#20154;&#22312;&#38754;&#35430;&#26178;&#37117;&#26371;&#21839;&#65306;&#12300;&#20320;&#20497;&#20844;&#21496;&#30340;&#25216;&#34899;&#25991;&#21270;&#24590;&#40636;&#27171;&#65311;&#12301;&#36889;&#26159;&#19968;&#20491;&#24456;&#24120;&#35211;&#20063;&#24456;&#37325;&#35201;&#30340;&#21839;&#38988;&#65292;&#20294;&#20854;&#23526;&#25105;&#24120;&#24120;&#22312;&#24819;&#8212;&#8212;&#22823;&#23478;&#21475;&#20013;&#30340;&#12300;&#25216;&#34899;&#25991;&#21270;&#12301;&#21040;&#24213;&#26159;&#20160;&#40636;&#24847;&#24605;&#65311;</p><p>&#26377;&#20123;&#20154;&#26371;&#25226;&#25216;&#34899;&#25991;&#21270;&#31561;&#21516;&#26044;&#65306;&#20320;&#20497;&#26377;&#27794;&#26377;&#23531; Unit Test&#65311;&#25991;&#20214;&#23531;&#24471;&#23436;&#19981;&#23436;&#25972;&#65311;PR &#26377;&#27794;&#26377;&#20154;&#24171;&#24537; Review&#65311;&#26377;&#27794;&#26377; CICD&#65311;Roadmap &#26159;&#24590;&#40636;&#35215;&#21123;&#30340;&#65311;&#36889;&#20123;&#37117;&#24456;&#23526;&#38555;&#65292;&#20063;&#37117;&#37325;&#35201;&#65292;&#20294;&#25105;&#22312; Ubiquiti &#24037;&#20316;&#24460;&#65292;&#26377;&#19968;&#20491;&#26356;&#28145;&#30340;&#39636;&#26371;&#12290;</p><p>&#36889;&#26159;&#25105;&#24453;&#36942;&#25216;&#34899;&#25991;&#21270;&#26368;&#20581;&#24247;&#30340;&#20844;&#21496;&#20043;&#19968;&#12290;&#23565;&#65292;&#21839;&#38988;&#20063;&#36996;&#26377;&#24456;&#22810;&#65292;&#21487;&#20197;&#25913;&#21892;&#30340;&#31354;&#38291;&#36996;&#26377;&#24456;&#22810;&#65292;&#22810;&#21040;&#35498;&#19981;&#23436;&#12290;&#20294;&#26159;&#25105;&#20173;&#28982;&#26371;&#35498;&#36889;&#26159;&#25105;&#24453;&#36942;&#25216;&#34899;&#25991;&#21270;&#26368;&#20581;&#24247;&#30340;&#20844;&#21496;&#20043;&#19968;&#12290;</p><p>&#25105;&#20497;&#30340;&#25991;&#20214;&#23531;&#24471;&#38750;&#24120;&#23436;&#25972;&#65307;&#28204;&#35430;&#20063;&#19981;&#26159;&#25033;&#20184;&#24046;&#20107;&#65292;&#32780;&#26159;&#30495;&#30340;&#33021;&#24171;&#21161;&#32173;&#35703;&#21697;&#36074;&#65307;PR review &#38750;&#24120;&#35469;&#30495;&#65292;&#22821;&#20276;&#20497;&#26371;&#25552;&#20379;&#35488;&#25031;&#21448;&#20855;&#24314;&#35373;&#24615;&#30340; feedback&#65292;&#32780;&#19988;&#36889;&#20123; review &#24120;&#24120;&#19981;&#21482;&#26159;&#23531;&#27861;&#23565;&#37679;&#65292;&#36996;&#21253;&#21547;&#23565;&#31995;&#32113;&#25972;&#39636;&#24433;&#38911;&#30340;&#24605;&#32771;&#12290;&#20294;&#36889;&#19968;&#20999;&#37117;&#19981;&#26159;&#38752;&#35215;&#23450;&#12289;SOP&#12289;&#25110;&#26159;&#27599;&#22825; standup &#25171;&#21345;&#20986;&#20358;&#30340;&#65292;&#32780;&#26159;&#20358;&#33258;&#22823;&#23478;&#23565;&#26044;&#25216;&#34899;&#21697;&#36074;&#30340;&#39640;&#24230;&#33258;&#25105;&#35201;&#27714;&#12290;</p><p>&#31777;&#24453;&#20358;&#35498;&#65292;&#20844;&#21496;&#30340;&#25216;&#34899;&#25991;&#21270;&#65292;&#20854;&#23526;&#26159;&#27599;&#19968;&#20491;&#20491;&#20154;&#36002;&#29563;&#32773;&#30340;&#22533;&#25345;&#33287;&#20849;&#35672;&#24213;&#19979;&#21435;&#21109;&#36896;&#20986;&#20358;&#30340;&#12290;</p><p>&#25105;&#26366;&#22312;&#26032;&#21109;&#20844;&#21496;&#30070;&#25216;&#34899;&#30740;&#30332;&#32317;&#30435;&#65292;&#20063;&#22312; DELL &#37027;&#31278;&#36229;&#36942; 400 &#20154;&#30340;&#20840;&#29699;&#30740;&#30332;&#22296;&#38538;&#24037;&#20316;&#36942;&#12290;&#33258;&#24049;&#20063;&#23531;&#36942;&#19981;&#23569;&#38283;&#28304;&#23560;&#26696;&#12290;&#25105;&#24456;&#22312;&#24847;&#25216;&#34899;&#25991;&#21270;&#65292;&#20294;&#24517;&#38920;&#35498;&#65292;Ubiquiti &#35731;&#25105;&#30475;&#35211;&#30340;&#26159;&#65292;&#25216;&#34899;&#25991;&#21270;&#26368;&#26377;&#25928;&#29575;&#22320;&#25512;&#21205;&#26041;&#24335;&#65292;&#20854;&#23526;&#26159;buttom-up&#12290;Hire &#23565;&#30340;&#20154;&#65292;&#19981;&#20677;&#20677;&#26159;&#22312;&#24847;&#30340;&#20154;&#65292;&#32780;&#19988;&#26159;&#21487;&#20197;&#20027;&#21205;&#35299;&#27770;&#21839;&#38988;&#30340;&#20154;&#65292;&#26368;&#24460;&#21453;&#32780;&#22823;&#23478;&#26356;&#21152;&#36629;&#39686;&#12290;</p><p>&#36889;&#27171;&#30340;&#25991;&#21270;&#19981;&#26159;&#38752;&#12300;&#34987;&#21205;&#36969;&#25033;&#12301;&#33021;&#32173;&#25345;&#30340;&#12290;&#25105;&#20497;&#26399;&#24453;&#21152;&#20837;&#22296;&#38538;&#30340;&#20154;&#65292;&#19981;&#21482;&#26159;&#20358;&#20139;&#21463;&#36889;&#20491;&#29872;&#22659;&#65292;&#32780;&#26159;&#33021;&#22816;&#12300;&#20849;&#21516;&#32173;&#35703;&#12289;&#29978;&#33267;&#25913;&#21892;&#12301;&#36889;&#27171;&#30340;&#25991;&#21270;&#12290;</p><h3>&#24037;&#31243;&#24107;&#19981;&#21482;&#26159;&#23531;&#31243;&#24335;&#65292;&#32780;&#26159;&#21443;&#33287;&#29986;&#21697;</h3><p>&#24456;&#22810;&#26178;&#20505;&#65292;&#25105;&#20497;&#19981;&#21482;&#26159;&#12300;&#25509;&#21040; spec &#23601;&#29031;&#20570;&#12301;&#65292;&#32780;&#26159;&#21453;&#36942;&#20358;&#20027;&#21205;&#25552;&#20986;&#65306;&#12300;&#36889;&#27171;&#35373;&#35336;&#20854;&#23526;&#23565;&#35037;&#32622;&#31471;&#27794;&#37027;&#40636;&#21451;&#21892;&#12301;&#12289;&#12300;&#25105;&#20497;&#21487;&#20197;&#25226;&#36889;&#20491;&#27969;&#31243;&#25289;&#24179;&#65292;&#28187;&#23569;&#20351;&#29992;&#32773;&#31561;&#24453;&#26178;&#38291;&#12301;&#8230;&#8230;&#36889;&#39006;&#24314;&#35696;&#24456;&#22810;&#26178;&#20505;&#26371;&#34987;&#25505;&#32013;&#65292;&#29978;&#33267;&#35731;&#29986;&#21697;&#35373;&#35336;&#26356;&#36028;&#36817;&#23526;&#38555;&#24773;&#22659;&#12290;</p><h3>&#22914;&#26524;&#20320;&#20063;&#22909;&#22855; Ubiquiti &#30340;&#38642;&#31471;&#24037;&#31243;&#25991;&#21270;&#8230;</h3><p>&#25105;&#26371;&#35498;&#36889;&#26159;&#19968;&#20491;&#24456;&#36969;&#21512;&#21916;&#27489; ownership&#12289;&#21448;&#19981;&#24597;&#38754;&#23565;&#30495;&#23526;&#19990;&#30028; challenge &#30340;&#24037;&#31243;&#24107;&#20358;&#30340;&#22320;&#26041;&#12290;&#20320;&#26371;&#27599;&#22825;&#34987;&#21508;&#31278;&#26032;&#24773;&#22659;&#21050;&#28608;&#22823;&#33126;&#65292;&#20063;&#26371;&#23416;&#21040;&#22914;&#20309;&#36319; device team&#12289;frontend team&#12289;support team &#19968;&#36215;&#25171;&#36896;&#19968;&#20491;&#12300;&#30495;&#30340;&#26371;&#34987;&#29992;&#12289;&#32780;&#19988;&#34987;&#37325;&#24230;&#20351;&#29992;&#12301;&#30340;&#29986;&#21697;&#12290;</p><p>&#22312;&#36889;&#35041;&#65292;&#19981;&#21482;&#26159; coding&#65292;&#26356;&#22810;&#30340;&#26159;&#24605;&#32771;&#33287;&#35373;&#35336;&#12290;&#32780;&#26368;&#26834;&#30340;&#26159;&#65292;&#20320;&#23531;&#30340;&#27599;&#19968;&#34892;&#31243;&#24335;&#65292;&#26368;&#32066;&#37117;&#26371;ship&#32102;&#19978;&#30334;&#33836;&#30340;&#20351;&#29992;&#32773;&#12290;&#25105;&#33258;&#24049;&#23478;&#35041;&#36319;&#25945;&#26371;&#20063;&#37117;&#25563;&#25104;&#20102;&#25972;&#22871;&#30340;Ubiquiti&#32178;&#36335;&#35373;&#20633;&#65292;&#36889;&#27171;&#26041;&#20415;&#30340;&#21487;&#30435;&#25511;&#24615;&#36996;&#26377;&#38728;&#27963;&#30340;&#36960;&#31471;&#35373;&#23450;&#65292;&#30495;&#30340;&#26159;&#26377;&#25171;&#20013;&#25105;&#20497;&#36889;&#31278;IT prosumer&#30340;&#24515;&#12290;</p>]]></content:encoded></item><item><title><![CDATA[The Revolution Befor AI: The Declarative Revolution, and How We Should Program From Now On]]></title><description><![CDATA[Before LLMs, programming quietly shifted from imperative scripts to declarative clarity. This hidden evolution&#8212;containers, infra-as-code, components&#8212;set the stage for AI to finally read, reason, and collaborate with our code.]]></description><link>https://schwannden.substack.com/p/the-revolution-befor-ai-the-declarative-revolution-and-how-we-should-program-from-now-on</link><guid isPermaLink="false">https://schwannden.substack.com/p/the-revolution-befor-ai-the-declarative-revolution-and-how-we-should-program-from-now-on</guid><dc:creator><![CDATA[schwannden]]></dc:creator><pubDate>Mon, 01 Sep 2025 10:00:30 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/e4a2d1fb-4221-4031-bf00-d690d73afc23_1408x736.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!k6m8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23cf2309-6cc7-4eda-b1a0-e455bd46004e_1408x736.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!k6m8!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23cf2309-6cc7-4eda-b1a0-e455bd46004e_1408x736.png 424w, https://substackcdn.com/image/fetch/$s_!k6m8!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23cf2309-6cc7-4eda-b1a0-e455bd46004e_1408x736.png 848w, https://substackcdn.com/image/fetch/$s_!k6m8!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23cf2309-6cc7-4eda-b1a0-e455bd46004e_1408x736.png 1272w, https://substackcdn.com/image/fetch/$s_!k6m8!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23cf2309-6cc7-4eda-b1a0-e455bd46004e_1408x736.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!k6m8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23cf2309-6cc7-4eda-b1a0-e455bd46004e_1408x736.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/23cf2309-6cc7-4eda-b1a0-e455bd46004e_1408x736.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!k6m8!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23cf2309-6cc7-4eda-b1a0-e455bd46004e_1408x736.png 424w, https://substackcdn.com/image/fetch/$s_!k6m8!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23cf2309-6cc7-4eda-b1a0-e455bd46004e_1408x736.png 848w, https://substackcdn.com/image/fetch/$s_!k6m8!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23cf2309-6cc7-4eda-b1a0-e455bd46004e_1408x736.png 1272w, https://substackcdn.com/image/fetch/$s_!k6m8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23cf2309-6cc7-4eda-b1a0-e455bd46004e_1408x736.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p>When I first started building websites, everything was manual. Spin up a VM, install packages, configure Apache, set up networking&#8212;line by line, hoping I didn&#8217;t miss a step. It worked, but it was fragile.</p><p>Then Docker came along. Suddenly, I could package everything into one neat container. Docker Compose made it even better&#8212;I could describe my whole stack in a single YAML file. Kubernetes took it further by orchestrating all the moving parts for me. And when Terraform arrived, it felt like magic: I wasn&#8217;t scripting servers anymore, I was declaring what I wanted, and the system figured out the rest.</p><p>At the time, I just appreciated the convenience. What I didn&#8217;t realize was that I was stepping into a world perfectly suited for AI.</p><h2>Why Declarative Thinking Matters</h2><p>Declarative tools like Docker, Kubernetes, and Terraform flip the script. Instead of telling machines <em>how</em> to do something, we describe the <em>end state</em>. The system takes responsibility for figuring out the steps to get there.</p><p>Take infrastructure. Back in the old days, I&#8217;d write long bash scripts: install Postgres, configure users, set up storage, open the right ports, and hope I hadn&#8217;t missed a detail. Every line was an instruction tied to a particular OS, version, or runtime. For an AI trying to understand this, it&#8217;s like reading a recipe where half the ingredients are hidden between the lines&#8212;you need deep contextual knowledge just to interpret what&#8217;s happening.</p><p>With Terraform, I can instead declare:</p><pre><code>resource "aws_db_instance" "mydb" {
  engine            = "postgres"
  instance_class    = "db.t3.micro"
  allocated_storage = 20
}</code></pre><p>That snippet is compact but unambiguous. It says, &#8220;I want a Postgres database of this size and type.&#8221; AI doesn&#8217;t need to parse ten steps of installation logic&#8212;it just sees the goal. Because declarative code describes <em>what something is</em> rather than <em>how to do it</em>, it maps naturally onto how AI models understand text: as patterns, relationships, and outcomes.</p><p>The same is true in UI. In the old CSS days, I&#8217;d have a spaghetti of selectors, nested rules, and overrides&#8212;difficult for me to reason about, and almost impossible for an AI to modify without breaking something. But with Tailwind, I can write:</p><pre><code>&lt;button className="bg-blue-600 rounded-lg hover:bg-blue-700"&gt;
  Submit
&lt;/button&gt;</code></pre><p>Every class is atomic, precise, and predictable. Change <code>bg-blue-600</code> to <code>bg-green-600</code>, and the background color <em>only</em> changes&#8212;no side effects lurking elsewhere. For an AI, this is huge: it can confidently generate changes knowing the outcome is deterministic.</p><p>This is why declarative paradigms are so AI-friendly. They reduce ambiguity, eliminate hidden state, and separate <em>intent</em> from <em>implementation</em>. Instead of reverse-engineering &#8220;what the human really meant&#8221; from dozens of imperative steps, AI can work directly with clear, human-readable declarations of the desired outcome.</p><h2>Writing Code That AI Can Understand</h2><p>Over the years, I&#8217;ve noticed that the practices making my code easier for teammates also make it easier for AI. Some practical shifts stand out:</p><ul><li><p><strong>Vertical slice architecture</strong>: Organizing by features instead of layers gives both humans and AI self-contained units of meaning.</p></li><li><p><strong><code>llms.txt</code> files</strong>: Just like <code>robots.txt</code> tells crawlers what to do, <code>llms.txt</code> gives AI a roadmap for your project.</p></li><li><p><strong>AI-actionable docs</strong>: Instead of vague &#8220;configure the DB,&#8221; I now write: &#8220;set <code>DATABASE_URL=postgres://user:pass@host/db</code>.&#8221; Instead of writing <code>click this link</code> in the document, we can write <code>run curl -XPOST ...</code> so AI doesn&#8217;t guess&#8212;it executes.</p></li><li><p><strong>Composable components</strong>: Smaller, reusable parts reduce the chances of AI (or me) breaking things when making changes.</p></li></ul><h2>Toward a Shared Language Between Humans and AI</h2><p>Looking back, I realize Docker, Compose, K8s, and Terraform weren&#8217;t just conveniences. They were rewiring how I think about code. Declarative, modular, predictable&#8212;exactly the qualities that make AI collaboration possible.</p><p>The shift is bigger than tools. It&#8217;s about our role. We&#8217;re no longer just code writers. We&#8217;re system architects, orchestrating intelligent collaboration between humans and machines.</p><p>That&#8217;s the real &#8220;declarative revolution.&#8221; It&#8217;s not just how we deploy apps or style UIs&#8212;it&#8217;s a new way of thinking about programming itself. A shared language between us and AI.</p><p>And once you see it that way, it&#8217;s hard not to imagine what the next decade of coding will look like.</p>]]></content:encoded></item><item><title><![CDATA[🍼 一手抱嬰兒，我用 20分鐘「嘴」出讀經計畫 Progressive Web App]]></title><description><![CDATA[&#22312;&#27794;&#23531;&#19968;&#34892; code &#30340;&#24773;&#27841;&#19979;&#65292;&#25105;&#20351;&#29992; lovable &#23436;&#25104;&#20102;&#19968;&#20491;&#26377;&#20114;&#21205;&#12289;&#26377;&#35373;&#35336;&#24863;&#12289;&#26377;&#34892;&#21205;&#39636;&#39511;&#30340;&#32178;&#31449;&#65292;&#32780;&#19988;&#21482;&#33457;&#20102; 20 &#20998;&#37912;&#12290;]]></description><link>https://schwannden.substack.com/p/speak-a-pwa-into-existence</link><guid isPermaLink="false">https://schwannden.substack.com/p/speak-a-pwa-into-existence</guid><dc:creator><![CDATA[schwannden]]></dc:creator><pubDate>Thu, 05 Jun 2025 07:45:23 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/098db44a-22e4-428b-887f-ab5de36f326d_800x382.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!TTPW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bacb18e-9238-4413-a465-1c27fc4357c4_800x382.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!TTPW!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bacb18e-9238-4413-a465-1c27fc4357c4_800x382.png 424w, https://substackcdn.com/image/fetch/$s_!TTPW!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bacb18e-9238-4413-a465-1c27fc4357c4_800x382.png 848w, https://substackcdn.com/image/fetch/$s_!TTPW!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bacb18e-9238-4413-a465-1c27fc4357c4_800x382.png 1272w, https://substackcdn.com/image/fetch/$s_!TTPW!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bacb18e-9238-4413-a465-1c27fc4357c4_800x382.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!TTPW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bacb18e-9238-4413-a465-1c27fc4357c4_800x382.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3bacb18e-9238-4413-a465-1c27fc4357c4_800x382.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!TTPW!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bacb18e-9238-4413-a465-1c27fc4357c4_800x382.png 424w, https://substackcdn.com/image/fetch/$s_!TTPW!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bacb18e-9238-4413-a465-1c27fc4357c4_800x382.png 848w, https://substackcdn.com/image/fetch/$s_!TTPW!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bacb18e-9238-4413-a465-1c27fc4357c4_800x382.png 1272w, https://substackcdn.com/image/fetch/$s_!TTPW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bacb18e-9238-4413-a465-1c27fc4357c4_800x382.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p>&#22312;&#27794;&#23531;&#19968;&#34892; code &#30340;&#24773;&#27841;&#19979;&#65292;&#25105;&#20351;&#29992; <a href="https://lovable.dev">lovable</a> &#23436;&#25104;&#20102;&#19968;&#20491;&#26377;&#20114;&#21205;&#12289;&#26377;&#35373;&#35336;&#24863;&#12289;&#26377;&#34892;&#21205;&#39636;&#39511;&#30340;&#32178;&#31449;&#65292;&#32780;&#19988;&#21482;&#33457;&#20102; <strong>20 &#20998;&#37912;</strong>&#12290;</p><p>&#25104;&#21697;&#65306;<a href="https://2025-bible-reading.fruitful-tools.com/">https://2025-bible-reading.fruitful-tools.com/</a></p><p>&#25105;&#19981;&#26159;&#31532;&#19968;&#27425;&#20570;&#32178;&#38913;&#65292;&#20294;&#36889;&#27425;&#26159;&#25105;<strong>&#31532;&#19968;&#27425;&#19981;&#29992;&#25171;&#38283; VSCode/Cursor</strong>&#65292;&#29978;&#33267;&#26159;&#19968;&#37002;<strong>&#21934;&#25163;&#25265;&#33879;&#22235;&#20491;&#26376;&#22823;&#30340;&#23542;&#23542;</strong>&#12289;<strong>&#19968;&#37002;&#35486;&#38899;&#36664;&#20837;</strong>&#25351;&#20196;&#23436;&#25104;&#30340;&#12290;</p><div><hr></div><h3>&#127919; &#36889;&#27425;&#30340;&#20219;&#21209;&#65306;&#20570;&#20986;&#19968;&#20491;&#35712;&#32147;&#35336;&#30059; Web App</h3><p>&#36889;&#20491;&#32178;&#31449;&#30340;&#30446;&#27161;&#24456;&#31777;&#21934;&#65306;</p><ul><li><p>&#26412;&#20358;&#23601;&#22312;&#20170;&#24180;&#28858;&#25945;&#26371;&#35373;&#35336;&#20102;&#19968;&#24180; 12 &#20491;&#26376;&#20221;&#30340;&#35712;&#32147;&#35336;&#30059;</p></li><li><p>&#27599;&#20491;&#26376;&#19968;&#24373;&#35712;&#32147;&#21345;&#65288;&#27491;&#38754;&#26159;&#35336;&#30059;&#65292;&#32972;&#38754;&#26159;&#35373;&#35336;&#22294;&#65289;&#12290;&#26412;&#20358;&#21015;&#21360;&#25104;&#37239;&#21345;&#65292;&#20294;&#26159;&#22240;&#28858;&#25976;&#37327;&#19981;&#36275;&#65292;&#24460;&#20358;&#26377;&#20123;&#20154;&#25343;&#19981;&#21040;&#20102;&#65292;&#25165;&#24819;&#35498;&#35373;&#35336;&#25104;&#32178;&#38913;&#12290;</p></li><li><p>&#25163;&#27231;&#20778;&#21270;&#12289;&#38928;&#35373;&#32321;&#39636;&#20013;&#25991;</p></li></ul><p>&#36889;&#20123;&#38656;&#27714;&#65292;&#20320;&#20132;&#32102;&#35373;&#35336;&#24107;&#30059;&#22294;&#12289;&#21069;&#31471;&#23531; code&#12289;PM &#25490;&#36914;&#38283;&#30332;&#26178;&#31243;&#65292;&#21487;&#33021;&#35201; 1~2 &#36913;&#25165;&#26371;&#19978;&#32218;&#12290;&#22914;&#26524;&#20320;&#26159;&#24456;&#20778;&#31168;&#30340;&#21069;&#31471;&#24037;&#31243;&#24107;&#65292;&#21487;&#33021;&#20063;&#35201;&#33457;&#19977;&#22235;&#20491;&#23567;&#26178;&#12290;&#20294;&#26159;&#25105;&#21482;&#20570;&#20102;&#19968;&#20214;&#20107;&#65306;<strong>&#36319; <a href="https://lovable.dev">lovable.dev</a> &#35498;&#28165;&#26970;&#25105;&#35201;&#20160;&#40636;&#12290;</strong></p><div><hr></div><h3>&#129504; &#24590;&#40636;&#36319; AI &#35498;&#65311;&#25105;&#30340;&#35486;&#38899;&#25351;&#20196;&#38263;&#36889;&#27171;&#65306;</h3><pre><code>please use the following tech stack 
React 19, Tailwind CSS v4, Vite, TypeScript, React Router v7, 
ShadCN/UI, Framer Motion 
 
I wish to create a yearly bible reading plan app, purely frontend. 
I will upload 24 images, 12 as monthly bible reading calendar, 12 are designs related to that month's scope. 
Create a website that has 12 months as 12 cards. 
When a card is clicked, show the calendar png. 
When clicked again, flip the card to show design. 
Make it a web app. 
Greenish design. 
i18n with default to &#32321;&#39636;&#20013;&#25991;.</code></pre><p>&#20854;&#23526;&#36889;&#31532;&#19968;&#27493;&#30340;&#25351;&#20196;&#65292;&#25033;&#35442;&#26159;&#23565;&#38750;&#24037;&#31243;&#24107;&#20358;&#35498;&#26368;&#22256;&#38627;&#30340;&#22320;&#26041;&#12290;&#22240;&#28858;&#19968;&#38283;&#22987;&#36984;&#25799;&#20160;&#40636;&#27171;&#30340;&#12300;&#32178;&#38913;&#25216;&#34899;&#22871;&#20214;&#12301;&#65292;&#26371;&#22823;&#22823;&#24433;&#38911;&#24460;&#32396;&#32178;&#31449;&#30340;&#21487;&#32173;&#35703;&#24615;&#33287;&#24310;&#23637;&#24615;&#12290; &#36889;&#37002;&#20998;&#20139;&#25105;&#19979;&#30340; tech stack &#25351;&#20196;&#12290;&#36889;&#19968;&#27573;&#26159;&#25105;&#20197;&#21069;&#22312;&#24118;&#38283;&#30332;&#23560;&#26696;&#26178;&#65292;&#21453;&#35206;&#30952;&#20986;&#20358;&#30340;&#32147;&#39511;&#32068;&#21512;&#8202;&#8212;&#8202;&#8212; &#26082;&#35201;&#29694;&#20195;&#12289;&#35201;&#24555;&#65292;&#21448;&#35201;&#23481;&#26131;&#25972;&#21512;&#20803;&#20214;&#24235;&#33287;&#21205;&#30059;&#65292;&#20063;&#35201;&#35731;&#24460;&#38754;&#32173;&#35703;&#19981;&#38957;&#30171;&#12290;</p><p><strong>&#36889;&#20195;&#34920;&#65306;&#36319;&#24037;&#31243;&#24107;&#21839;&#28165;&#26970;&#25216;&#34899;&#26550;&#27083;&#65292;&#25551;&#36848;&#20320;&#24819;&#35201;&#20160;&#40636;&#65292;AI&#23601;&#33021;&#24171;&#20320;&#38283;&#24037;&#12290;</strong></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Yd3t!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83b35d20-c2ab-448b-94bf-f57002d9de18_800x510.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Yd3t!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83b35d20-c2ab-448b-94bf-f57002d9de18_800x510.png 424w, https://substackcdn.com/image/fetch/$s_!Yd3t!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83b35d20-c2ab-448b-94bf-f57002d9de18_800x510.png 848w, https://substackcdn.com/image/fetch/$s_!Yd3t!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83b35d20-c2ab-448b-94bf-f57002d9de18_800x510.png 1272w, https://substackcdn.com/image/fetch/$s_!Yd3t!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83b35d20-c2ab-448b-94bf-f57002d9de18_800x510.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Yd3t!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83b35d20-c2ab-448b-94bf-f57002d9de18_800x510.png" width="2000" height="1276" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/83b35d20-c2ab-448b-94bf-f57002d9de18_800x510.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1276,&quot;width&quot;:2000,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!Yd3t!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83b35d20-c2ab-448b-94bf-f57002d9de18_800x510.png 424w, https://substackcdn.com/image/fetch/$s_!Yd3t!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83b35d20-c2ab-448b-94bf-f57002d9de18_800x510.png 848w, https://substackcdn.com/image/fetch/$s_!Yd3t!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83b35d20-c2ab-448b-94bf-f57002d9de18_800x510.png 1272w, https://substackcdn.com/image/fetch/$s_!Yd3t!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83b35d20-c2ab-448b-94bf-f57002d9de18_800x510.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>&#19978;&#38754;AI&#27794;&#26377;&#23562;&#37325;&#25105;&#35498;&#21916;&#27489;&#32160;&#33394;&#65292;&#20006;&#19988;&#35201;&#32321;&#39636;&#20013;&#25991;&#30340;&#21151;&#33021;&#12290;&#21487;&#33021;&#19968;&#27425;&#35611;&#22826;&#22810;&#20102;&#65292;&#25152;&#20197;&#25552;&#31034;&#19968;&#19979;</p><pre><code>I wish to have greenish design, 
and I want i18n with default to &#32321;&#39636;&#20013;&#25991;</code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!69Om!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc86a9ad0-db15-470e-8d06-cd43332c66d7_800x432.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!69Om!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc86a9ad0-db15-470e-8d06-cd43332c66d7_800x432.png 424w, https://substackcdn.com/image/fetch/$s_!69Om!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc86a9ad0-db15-470e-8d06-cd43332c66d7_800x432.png 848w, https://substackcdn.com/image/fetch/$s_!69Om!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc86a9ad0-db15-470e-8d06-cd43332c66d7_800x432.png 1272w, https://substackcdn.com/image/fetch/$s_!69Om!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc86a9ad0-db15-470e-8d06-cd43332c66d7_800x432.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!69Om!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc86a9ad0-db15-470e-8d06-cd43332c66d7_800x432.png" width="992" height="536" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c86a9ad0-db15-470e-8d06-cd43332c66d7_800x432.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:536,&quot;width&quot;:992,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!69Om!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc86a9ad0-db15-470e-8d06-cd43332c66d7_800x432.png 424w, https://substackcdn.com/image/fetch/$s_!69Om!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc86a9ad0-db15-470e-8d06-cd43332c66d7_800x432.png 848w, https://substackcdn.com/image/fetch/$s_!69Om!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc86a9ad0-db15-470e-8d06-cd43332c66d7_800x432.png 1272w, https://substackcdn.com/image/fetch/$s_!69Om!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc86a9ad0-db15-470e-8d06-cd43332c66d7_800x432.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>&#20013;&#38291;&#26377;&#20491;&#23567;bug&#20986;&#29694;&#65292;&#25105;&#28961;&#33126;&#40670;&#19968;&#19979;&#21483;&#20182;&#20462;&#65292;&#28982;&#24460;&#23601;87%&#23436;&#25104;&#20102;&#12290;</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!V7Z1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F583da04b-925a-4a2e-b42a-bf9f1ee3997a_800x517.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!V7Z1!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F583da04b-925a-4a2e-b42a-bf9f1ee3997a_800x517.png 424w, https://substackcdn.com/image/fetch/$s_!V7Z1!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F583da04b-925a-4a2e-b42a-bf9f1ee3997a_800x517.png 848w, https://substackcdn.com/image/fetch/$s_!V7Z1!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F583da04b-925a-4a2e-b42a-bf9f1ee3997a_800x517.png 1272w, https://substackcdn.com/image/fetch/$s_!V7Z1!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F583da04b-925a-4a2e-b42a-bf9f1ee3997a_800x517.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!V7Z1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F583da04b-925a-4a2e-b42a-bf9f1ee3997a_800x517.png" width="2000" height="1293" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/583da04b-925a-4a2e-b42a-bf9f1ee3997a_800x517.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1293,&quot;width&quot;:2000,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!V7Z1!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F583da04b-925a-4a2e-b42a-bf9f1ee3997a_800x517.png 424w, https://substackcdn.com/image/fetch/$s_!V7Z1!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F583da04b-925a-4a2e-b42a-bf9f1ee3997a_800x517.png 848w, https://substackcdn.com/image/fetch/$s_!V7Z1!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F583da04b-925a-4a2e-b42a-bf9f1ee3997a_800x517.png 1272w, https://substackcdn.com/image/fetch/$s_!V7Z1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F583da04b-925a-4a2e-b42a-bf9f1ee3997a_800x517.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h3>&#128230; &#19978;&#20659;&#32032;&#26448;&#65288;&#26368;&#36817;&#25165;release&#30340;&#21151;&#33021;&#65289;&#65306;24 &#24373;&#22294;&#29255;&#65292;AI &#33258;&#21205;&#20998;&#39006;</h3><p>&#25105;&#20107;&#20808;&#28310;&#20633;&#22909;&#20102; 24 &#24373;&#22294;&#65306;1~12 &#26376;&#20221;&#30340;&#12300;&#35712;&#32147;&#34920;&#12301;&#65288;&#27491;&#38754;&#65289;&#33287;&#12300;&#35373;&#35336;&#22294;&#12301;&#65288;&#32972;&#38754;&#65289;&#65292;&#27284;&#21517;&#23601;&#26159; <code>1.png ~ 24.png</code>&#12290;</p><p>&#25105;&#36319; AI &#35498;&#65306;</p><pre><code>I uploaded january's front and back image (1.png and 2.png), 
february is 3.png and 4.png, ...etc</code></pre><p>&#23601;&#36889;&#27171;&#65292;&#23427;&#33258;&#24049;&#34389;&#29702;&#22909;&#20102;&#12290;</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!i6nB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cc71b80-d687-4178-86ed-8b6d589dc54d_800x519.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!i6nB!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cc71b80-d687-4178-86ed-8b6d589dc54d_800x519.png 424w, https://substackcdn.com/image/fetch/$s_!i6nB!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cc71b80-d687-4178-86ed-8b6d589dc54d_800x519.png 848w, https://substackcdn.com/image/fetch/$s_!i6nB!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cc71b80-d687-4178-86ed-8b6d589dc54d_800x519.png 1272w, https://substackcdn.com/image/fetch/$s_!i6nB!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cc71b80-d687-4178-86ed-8b6d589dc54d_800x519.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!i6nB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cc71b80-d687-4178-86ed-8b6d589dc54d_800x519.png" width="2000" height="1298" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8cc71b80-d687-4178-86ed-8b6d589dc54d_800x519.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1298,&quot;width&quot;:2000,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!i6nB!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cc71b80-d687-4178-86ed-8b6d589dc54d_800x519.png 424w, https://substackcdn.com/image/fetch/$s_!i6nB!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cc71b80-d687-4178-86ed-8b6d589dc54d_800x519.png 848w, https://substackcdn.com/image/fetch/$s_!i6nB!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cc71b80-d687-4178-86ed-8b6d589dc54d_800x519.png 1272w, https://substackcdn.com/image/fetch/$s_!i6nB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cc71b80-d687-4178-86ed-8b6d589dc54d_800x519.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h3>&#128295; &#25351;&#20196;&#24494;&#35519;&#33287;&#21839;&#38988;&#20462;&#27491;&#65288;&#20687;&#22312;&#24118;&#23560;&#26696;&#19968;&#27171;&#65289;</h3><p>&#36942;&#31243;&#20013;&#65292;&#25105;&#30332;&#29694;&#24190;&#20491; UX &#21839;&#38988;&#65292;&#26044;&#26159;&#30452;&#25509;&#12300;&#25913;&#38656;&#27714;&#12301;&#65306;</p><h3>1. &#25913;&#36914;&#25163;&#27231; UX&#65306;&#22294;&#29255;&#36617;&#20837;&#26178;&#35201;&#26377;&#26126;&#30906;&#25552;&#31034;</h3><pre><code>When I click on the calendar card and there is a pop-up, 
the image is loading in real time, 
but the visual effect is not clear that it&#8217;s loading. 
Could you improve the user experience on this issue?</code></pre><p>AI &#31435;&#21051;&#25913;&#20102;&#65292;&#22294;&#29255;&#36996;&#27794;&#36617;&#22909;&#26178;&#26371;&#39023;&#31034; loading spinner&#65292;&#36991;&#20813;&#20351;&#29992;&#32773;&#21345;&#20303;&#12290;</p><h3>2. &#23567;bug&#65306;&#20462;&#27491;&#32763;&#38913;&#21205;&#30059;</h3><pre><code>upon click month card, the calendar card is shown in the popup. 
I wish to flip to the back side (the design) 
when I click the image in the popup. 
There should be an animation of flipping card</code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!BnKv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe38a4292-c333-4470-8652-bf5ba60eba2d_1024x1900.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!BnKv!,w_424,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe38a4292-c333-4470-8652-bf5ba60eba2d_1024x1900.gif 424w, https://substackcdn.com/image/fetch/$s_!BnKv!,w_848,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe38a4292-c333-4470-8652-bf5ba60eba2d_1024x1900.gif 848w, https://substackcdn.com/image/fetch/$s_!BnKv!,w_1272,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe38a4292-c333-4470-8652-bf5ba60eba2d_1024x1900.gif 1272w, https://substackcdn.com/image/fetch/$s_!BnKv!,w_1456,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe38a4292-c333-4470-8652-bf5ba60eba2d_1024x1900.gif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!BnKv!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe38a4292-c333-4470-8652-bf5ba60eba2d_1024x1900.gif" width="1024" height="1900" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e38a4292-c333-4470-8652-bf5ba60eba2d_1024x1900.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1900,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!BnKv!,w_424,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe38a4292-c333-4470-8652-bf5ba60eba2d_1024x1900.gif 424w, https://substackcdn.com/image/fetch/$s_!BnKv!,w_848,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe38a4292-c333-4470-8652-bf5ba60eba2d_1024x1900.gif 848w, https://substackcdn.com/image/fetch/$s_!BnKv!,w_1272,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe38a4292-c333-4470-8652-bf5ba60eba2d_1024x1900.gif 1272w, https://substackcdn.com/image/fetch/$s_!BnKv!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe38a4292-c333-4470-8652-bf5ba60eba2d_1024x1900.gif 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>3. &#21152;&#20837;&#12300;&#26376;&#20221;&#35299;&#37782;&#27231;&#21046;&#12301;</h3><p>&#25105;&#24076;&#26395;<strong>&#26410;&#20358;&#30340;&#26376;&#20221;&#19981;&#33021;&#40670;&#25802;</strong>&#65292;&#22686;&#21152;&#19968;&#40670;&#26399;&#24453;&#24863;&#65306;</p><pre><code>Can you lock each month's card until 1 month before? 
For example, user can not click on September until August.</code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!RBaJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4aa5a3e8-8898-4b58-a34d-0b809f1ae1a1_800x391.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!RBaJ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4aa5a3e8-8898-4b58-a34d-0b809f1ae1a1_800x391.png 424w, https://substackcdn.com/image/fetch/$s_!RBaJ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4aa5a3e8-8898-4b58-a34d-0b809f1ae1a1_800x391.png 848w, https://substackcdn.com/image/fetch/$s_!RBaJ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4aa5a3e8-8898-4b58-a34d-0b809f1ae1a1_800x391.png 1272w, https://substackcdn.com/image/fetch/$s_!RBaJ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4aa5a3e8-8898-4b58-a34d-0b809f1ae1a1_800x391.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!RBaJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4aa5a3e8-8898-4b58-a34d-0b809f1ae1a1_800x391.png" width="2000" height="977" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4aa5a3e8-8898-4b58-a34d-0b809f1ae1a1_800x391.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:977,&quot;width&quot;:2000,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!RBaJ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4aa5a3e8-8898-4b58-a34d-0b809f1ae1a1_800x391.png 424w, https://substackcdn.com/image/fetch/$s_!RBaJ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4aa5a3e8-8898-4b58-a34d-0b809f1ae1a1_800x391.png 848w, https://substackcdn.com/image/fetch/$s_!RBaJ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4aa5a3e8-8898-4b58-a34d-0b809f1ae1a1_800x391.png 1272w, https://substackcdn.com/image/fetch/$s_!RBaJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4aa5a3e8-8898-4b58-a34d-0b809f1ae1a1_800x391.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>AI &#20570;&#21040;&#20102;&#65292;&#36996;&#26371;&#33258;&#21205;&#26681;&#25818;&#30446;&#21069;&#26178;&#38291;&#27770;&#23450;&#21738;&#24190;&#20491;&#26376;&#21487;&#40670;&#12290;</p><p>Lovable&#20063;&#25226; custom domain &#25972;&#21512;&#30340;&#24456;&#22909;&#65292;&#36889;&#22602;&#25105;&#23601;&#19981;&#35443;&#36848;&#20102;&#65292;&#35531;<a href="https://docs.lovable.dev/tips-tricks/custom-domain">&#23448;&#26041;&#25991;&#20214;</a>&#65292;&#22522;&#26412;&#19978;&#20063;&#26159;&#40670;&#19968;&#40670;&#23601;&#20986;&#20358;&#20102;&#12290;</p><div><hr></div><h3>&#19968;&#20491;&#21487;&#20197; demo &#30340;&#29986;&#21697;</h3><p>&#19981;&#21040; 30 &#20998;&#37912;&#65292;&#25105;&#23601;&#33021;&#25343;&#20986;&#19968;&#20491;&#21487;&#20197; demo &#30340; Web App&#65306;</p><ul><li><p>&#34892;&#21205;&#35037;&#32622;&#25903;&#25588;&#22909;</p></li><li><p>&#26377; loading &#29376;&#24907;</p></li><li><p>&#21345;&#29255;&#32763;&#38754;&#21205;&#30059;&#33258;&#28982;</p></li><li><p>&#26681;&#25818;&#26376;&#20221;&#35299;&#37782;</p></li><li><p>&#29992;&#32321;&#39636;&#20013;&#25991;&#39023;&#31034;</p></li><li><p>&#28961;&#38920;&#24460;&#31471;&#65292;Custom domain&#36319; Cloudflare &#25972;&#21512;&#21448;&#26041;&#20415;&#21448;&#24555;</p></li></ul><p>&#36889;&#20491;&#23560;&#26696;&#30340;&#20729;&#20540;&#19981;&#22312;&#25216;&#34899;&#65292;&#32780;&#22312;&#65306;<strong>&#25105;&#20497;&#25226;&#24819;&#27861;&#36681;&#28858;&#23526;&#39636;&#29986;&#21697;&#30340;&#38272;&#27323;&#65292;&#38477;&#21040;&#21069;&#25152;&#26410;&#26377;&#30340;&#20302;&#12290;</strong></p><div><hr></div><h3>&#26411;&#20102;&#30340;&#35441;</h3><ul><li><p><strong>&#32102;&#29986;&#21697;&#32147;&#29702;</strong>&#65306;POC&#19981;&#29992;&#25214;&#35373;&#35336;&#24107;&#20102;&#65292;&#20808;&#30475;&#30475; AI &#26377;&#27794;&#26377;&#25026;&#20320;&#21543;&#12290;</p></li><li><p><strong>&#32102;&#32769;&#38342;&#25110;&#25509;&#26696;&#20844;&#21496;</strong>&#65306;&#36889;&#20491;&#19990;&#30028;&#36317;&#38626;&#21448;&#24555;&#21448;&#31337;&#21448;&#22909;&#30340;&#26085;&#23376;&#36234;&#20358;&#36234;&#38752;&#36817;&#20102;&#12290;</p></li><li><p><strong>&#32102;&#29238;&#27597;</strong>&#65306;&#22914;&#26524;&#20320;&#38656;&#35201;&#19968;&#25163;&#25265;&#23542;&#23542;&#65292;&#19968;&#25163;&#23531;code&#65292;&#23601;&#29992;&#35611;&#30340;&#25226;&#32178;&#38913;&#35611;&#20986;&#20358;&#21543;&#12290;&#19981;&#35201;&#20877;&#23475;&#24597;&#29983;&#23567;&#23401;&#20102;&#65281;</p></li></ul>]]></content:encoded></item><item><title><![CDATA[AI Agent 0 到 1: MCP Server with Goose]]></title><description><![CDATA[&#21363;&#20415;&#36879;&#36942;&#35264;&#24565;&#19978;&#30340;&#35299;&#37323;&#65306;MCP AI Agent&#22312;&#32005;&#20160;&#40636;&#65292;&#19981;&#33258;&#24049;&#21205;&#25163;&#20570;&#20570;&#30475;&#65292;&#21487;&#33021;&#36996;&#26159;&#24456;&#38627;&#20102;&#35299;AI Agent&#21040;&#24213;&#26159;&#20160;&#40636;&#12290;&#36889;&#31687;&#25991;&#31456;&#25163;&#25226;&#25163;&#25945;&#20320;&#29992;MCP Server&#24314;&#21046;&#19968;&#20491;AI Agent&#65292;&#36899;&#25509;Goose&#25110;&#32773;&#20351;&#29992;Claude Desktop&#20351;&#29992;&#20320;&#30340;AI Too]]></description><link>https://schwannden.substack.com/p/mcp-server-with-goose</link><guid isPermaLink="false">https://schwannden.substack.com/p/mcp-server-with-goose</guid><dc:creator><![CDATA[schwannden]]></dc:creator><pubDate>Thu, 20 Mar 2025 03:45:21 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/ff98768e-5791-4b54-8c34-aac96dd5cc33_800x400.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gsXr!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d34dc4f-d7ac-4bde-a6ed-d2fbba28b8ed_800x400.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gsXr!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d34dc4f-d7ac-4bde-a6ed-d2fbba28b8ed_800x400.png 424w, https://substackcdn.com/image/fetch/$s_!gsXr!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d34dc4f-d7ac-4bde-a6ed-d2fbba28b8ed_800x400.png 848w, https://substackcdn.com/image/fetch/$s_!gsXr!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d34dc4f-d7ac-4bde-a6ed-d2fbba28b8ed_800x400.png 1272w, https://substackcdn.com/image/fetch/$s_!gsXr!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d34dc4f-d7ac-4bde-a6ed-d2fbba28b8ed_800x400.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!gsXr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d34dc4f-d7ac-4bde-a6ed-d2fbba28b8ed_800x400.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4d34dc4f-d7ac-4bde-a6ed-d2fbba28b8ed_800x400.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!gsXr!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d34dc4f-d7ac-4bde-a6ed-d2fbba28b8ed_800x400.png 424w, https://substackcdn.com/image/fetch/$s_!gsXr!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d34dc4f-d7ac-4bde-a6ed-d2fbba28b8ed_800x400.png 848w, https://substackcdn.com/image/fetch/$s_!gsXr!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d34dc4f-d7ac-4bde-a6ed-d2fbba28b8ed_800x400.png 1272w, https://substackcdn.com/image/fetch/$s_!gsXr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d34dc4f-d7ac-4bde-a6ed-d2fbba28b8ed_800x400.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a><figcaption class="image-caption">AI Agent 0 &#21040; 1: MCP Server with&nbsp;Goose</figcaption></figure></div><p>&#21363;&#20415;&#36879;&#36942;&#35264;&#24565;&#19978;&#30340;&#35299;&#37323;&#65306;<a href="https://blog.schwannden.com/mcp-ai-agent-e5-9c-a8-e7-b4-85-e4-bb-80-e9-ba-bc/">MCP AI Agent&#22312;&#32005;&#20160;&#40636;</a>&#65292;&#19981;&#33258;&#24049;&#21205;&#25163;&#20570;&#20570;&#30475;&#65292;&#21487;&#33021;&#36996;&#26159;&#24456;&#38627;&#20102;&#35299;AI Agent&#21040;&#24213;&#26159;&#20160;&#40636;&#12290;&#36889;&#31687;&#25991;&#31456;&#25163;&#25226;&#25163;&#25945;&#20320;&#29992;MCP Server&#24314;&#21046;&#19968;&#20491;AI Agent&#65292;&#36899;&#25509;Goose&#25110;&#32773;&#20351;&#29992;Claude Desktop&#20351;&#29992;&#20320;&#30340;AI Tool&#12290;</p><p>(<a href="https://github.com/schwannden/mcp-server-demo/tree/main">&#23560;&#26696;&#20195;&#30908;&#22312;&#36889;&#37002;</a>)</p><h3>&#24314;&#21046;&#20320;&#30340;MCP Server&#23560;&#26696;</h3><p><strong>&#23433;&#35037;uv (python package manager)</strong>: <a href="https://docs.astral.sh/uv/getting-started/installation/">https://docs.astral.sh/uv/getting-started/installation/</a>&#65292;&#30906;&#35469;&#20320;&#30340;uv&#23433;&#35037;&#36335;&#24465;</p><pre><code>which uv 
# &#25105;&#30340;&#36335;&#24465;&#22312; ${HOME}/.local/bin/uv</code></pre><p>&#21021;&#22987;&#21270;&#23560;&#26696;</p><pre><code>uv init --package mcp-server-demo 
cd mcp-server-demo 
uv add "mcp[cli]" pydantic loguru faker</code></pre><p>&#20351;&#29992;&#20320;&#30340;IDE/&#32232;&#36655;&#22120;&#21109;&#24314;</p><pre><code># src/mcp_server_demo/server.py 
 
import random 
from loguru import logger 
from mcp.server.fastmcp import FastMCP 
from faker import Faker 
 
fake = Faker() 
 
# Create an MCP server 
mcp = FastMCP("mcp-server-demo") 
 
# Add an addition tool 
@mcp.tool() 
def get_order_info(order_id: str) -&gt; dict: 
    """Get the status of an order""" 
    status = ["success", "delayed", "cancelled", "pending_payment"] 
    order_info = { 
        "order_id": order_id, 
        "status": random.choice(status), 
        "client_email": fake.email(), 
    } 
    logger.info(f"Order info: {order_info}") 
    return order_info 
 
 
@mcp.tool() 
def send_coupon_to_user(order_id: str, customer_email: str) -&gt; str: 
    """Send a coupon to the user to make user happy""" 
    logger.info(f"send coupon to {customer_email} for order {order_id}") 
    return f"Coupon sent to {customer_email}"src/mcp_server_demo/__init__.py</code></pre><pre><code># src/mcp_server_demo/__init__.py 
 
from .server import mcp 
from loguru import logger 
 
def main() -&gt; None: 
    logger.info("Starting MCP server demo") 
    mcp.run() 
 
 
if __name__ == "__main__": 
    main()</code></pre><pre><code># src/mcp_server_demo/__main__.py 
 
from mcp_server_demo import main 
 
main()</code></pre><p>&#28204;&#35430;&#19968;&#19979; <code>uv run mcp-server-demo</code></p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!YJTR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc7248c44-194c-463e-b5fd-7b59a98b0335_800x64.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!YJTR!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc7248c44-194c-463e-b5fd-7b59a98b0335_800x64.png 424w, https://substackcdn.com/image/fetch/$s_!YJTR!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc7248c44-194c-463e-b5fd-7b59a98b0335_800x64.png 848w, https://substackcdn.com/image/fetch/$s_!YJTR!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc7248c44-194c-463e-b5fd-7b59a98b0335_800x64.png 1272w, https://substackcdn.com/image/fetch/$s_!YJTR!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc7248c44-194c-463e-b5fd-7b59a98b0335_800x64.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!YJTR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc7248c44-194c-463e-b5fd-7b59a98b0335_800x64.png" width="1540" height="124" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c7248c44-194c-463e-b5fd-7b59a98b0335_800x64.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:124,&quot;width&quot;:1540,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!YJTR!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc7248c44-194c-463e-b5fd-7b59a98b0335_800x64.png 424w, https://substackcdn.com/image/fetch/$s_!YJTR!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc7248c44-194c-463e-b5fd-7b59a98b0335_800x64.png 848w, https://substackcdn.com/image/fetch/$s_!YJTR!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc7248c44-194c-463e-b5fd-7b59a98b0335_800x64.png 1272w, https://substackcdn.com/image/fetch/$s_!YJTR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc7248c44-194c-463e-b5fd-7b59a98b0335_800x64.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>&#30475;&#21040;&#36889;&#20123;&#35338;&#24687;&#23601;&#34920;&#31034;&#25104;&#21151;&#22217;&#65292;&#21487;&#20197;ctrl + C&#38364;&#25481;&#20182;&#20102;</p><h3>&#23433;&#35037; Goose</h3><p>&#24478;Goose&#23448;&#26041;&#25991;&#20214;&#23433;&#35037;: <a href="https://block.github.io/goose/docs/quickstart">https://block.github.io/goose/docs/quickstart</a></p><p>Goose &#26159;&#19968;&#20491;&#38283;&#28304;&#30340; MCP &#23458;&#25142;&#31471;&#65292;&#35731;&#20320;&#21487;&#20197;&#37197;&#32622;&#33258;&#24049;&#30340; LLM &#25552;&#20379;&#32773;&#65288;&#20351;&#29992; API &#37329;&#38000;&#65289;&#65292;&#22312;&#33258;&#24049;&#30340;&#27231;&#22120;&#19978;&#36939;&#34892;&#65292;&#20006;&#33287;&#20320;&#30340; AI &#20195;&#29702;&#65288;MCP &#20282;&#26381;&#22120;&#65289;&#20114;&#21205;&#12290;</p><blockquote><p>&#22914;&#26524;&#20320;&#27794;&#26377;&#20219;&#20309;LLM&#37329;&#38000;&#65292;&#20063;&#28961;&#27861;&#25645;&#36617;&#26412;&#22320;&#30340;LLM&#65292;&#21487;&#20197;&#19979;&#36617; Claude Desktop&#65292;&#29992;&#20320;&#30340;&#20813;&#36027;&#38989;&#24230;&#20358;&#29609;&#29609;&#30475;&#65306;<a href="https://modelcontextprotocol.io/quickstart/user">https://modelcontextprotocol.io/quickstart/user</a></p></blockquote><p>&#22312;Good &#19978;&#38754;&#35373;&#23450;&#20320;&#30340; LLM &#20379;&#25033;&#21830;&#65306;<a href="https://block.github.io/goose/docs/getting-started/providers">https://block.github.io/goose/docs/getting-started/providers</a></p><p>&#25171;&#38283;Goose&#35373;&#23450; -&gt; Add custom extensions</p><p>Command&#37027;&#37002;&#35201;&#25918; <code>{path to uv} run {path to your project}/.venv/bin</code> <code>mcp-server-demo</code></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!NNaG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0984cb1a-aa89-4592-a4c0-55e9ed6b14b1_800x1237.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!NNaG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0984cb1a-aa89-4592-a4c0-55e9ed6b14b1_800x1237.png 424w, https://substackcdn.com/image/fetch/$s_!NNaG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0984cb1a-aa89-4592-a4c0-55e9ed6b14b1_800x1237.png 848w, https://substackcdn.com/image/fetch/$s_!NNaG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0984cb1a-aa89-4592-a4c0-55e9ed6b14b1_800x1237.png 1272w, https://substackcdn.com/image/fetch/$s_!NNaG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0984cb1a-aa89-4592-a4c0-55e9ed6b14b1_800x1237.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!NNaG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0984cb1a-aa89-4592-a4c0-55e9ed6b14b1_800x1237.png" width="956" height="1478" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0984cb1a-aa89-4592-a4c0-55e9ed6b14b1_800x1237.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1478,&quot;width&quot;:956,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!NNaG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0984cb1a-aa89-4592-a4c0-55e9ed6b14b1_800x1237.png 424w, https://substackcdn.com/image/fetch/$s_!NNaG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0984cb1a-aa89-4592-a4c0-55e9ed6b14b1_800x1237.png 848w, https://substackcdn.com/image/fetch/$s_!NNaG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0984cb1a-aa89-4592-a4c0-55e9ed6b14b1_800x1237.png 1272w, https://substackcdn.com/image/fetch/$s_!NNaG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0984cb1a-aa89-4592-a4c0-55e9ed6b14b1_800x1237.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>&#25353;Add&#65292;&#21487;&#20197;&#21152;&#20837;</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!YgnB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5250a1c-eb92-465f-b7d0-d9da66021372_800x401.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!YgnB!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5250a1c-eb92-465f-b7d0-d9da66021372_800x401.png 424w, https://substackcdn.com/image/fetch/$s_!YgnB!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5250a1c-eb92-465f-b7d0-d9da66021372_800x401.png 848w, https://substackcdn.com/image/fetch/$s_!YgnB!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5250a1c-eb92-465f-b7d0-d9da66021372_800x401.png 1272w, https://substackcdn.com/image/fetch/$s_!YgnB!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5250a1c-eb92-465f-b7d0-d9da66021372_800x401.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!YgnB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5250a1c-eb92-465f-b7d0-d9da66021372_800x401.png" width="2000" height="1003" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c5250a1c-eb92-465f-b7d0-d9da66021372_800x401.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1003,&quot;width&quot;:2000,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!YgnB!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5250a1c-eb92-465f-b7d0-d9da66021372_800x401.png 424w, https://substackcdn.com/image/fetch/$s_!YgnB!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5250a1c-eb92-465f-b7d0-d9da66021372_800x401.png 848w, https://substackcdn.com/image/fetch/$s_!YgnB!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5250a1c-eb92-465f-b7d0-d9da66021372_800x401.png 1272w, https://substackcdn.com/image/fetch/$s_!YgnB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5250a1c-eb92-465f-b7d0-d9da66021372_800x401.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-Qaa!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb04311d5-e965-41c2-84af-46635898a9d0_800x401.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-Qaa!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb04311d5-e965-41c2-84af-46635898a9d0_800x401.png 424w, https://substackcdn.com/image/fetch/$s_!-Qaa!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb04311d5-e965-41c2-84af-46635898a9d0_800x401.png 848w, https://substackcdn.com/image/fetch/$s_!-Qaa!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb04311d5-e965-41c2-84af-46635898a9d0_800x401.png 1272w, https://substackcdn.com/image/fetch/$s_!-Qaa!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb04311d5-e965-41c2-84af-46635898a9d0_800x401.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-Qaa!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb04311d5-e965-41c2-84af-46635898a9d0_800x401.png" width="2000" height="1003" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b04311d5-e965-41c2-84af-46635898a9d0_800x401.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1003,&quot;width&quot;:2000,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!-Qaa!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb04311d5-e965-41c2-84af-46635898a9d0_800x401.png 424w, https://substackcdn.com/image/fetch/$s_!-Qaa!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb04311d5-e965-41c2-84af-46635898a9d0_800x401.png 848w, https://substackcdn.com/image/fetch/$s_!-Qaa!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb04311d5-e965-41c2-84af-46635898a9d0_800x401.png 1272w, https://substackcdn.com/image/fetch/$s_!-Qaa!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb04311d5-e965-41c2-84af-46635898a9d0_800x401.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>&#23526;&#38555;&#22580;&#26223;</h3><p>&#22914;&#26524;&#25105;&#20497;&#21839;&#20102;&#19968;&#20491;&#19981;&#28165;&#19981;&#26970;&#30340;&#21839;&#38988;&#65288;&#27794;&#26377;&#25552;&#20379;&#35330;&#21934;&#36039;&#35338;&#65289;&#65292;&#36879;&#36942;&#24037;&#20855;&#25552;&#20379;&#30340;&#36039;&#35338;&#65292;Goose&#21487;&#20197;&#35531;&#25105;&#20497;&#25552;&#20379;&#35443;&#32048;&#36039;&#35338;</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!f-FM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faef31a6d-6208-4ee0-9ece-2e4b8512af67_800x117.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!f-FM!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faef31a6d-6208-4ee0-9ece-2e4b8512af67_800x117.png 424w, https://substackcdn.com/image/fetch/$s_!f-FM!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faef31a6d-6208-4ee0-9ece-2e4b8512af67_800x117.png 848w, https://substackcdn.com/image/fetch/$s_!f-FM!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faef31a6d-6208-4ee0-9ece-2e4b8512af67_800x117.png 1272w, https://substackcdn.com/image/fetch/$s_!f-FM!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faef31a6d-6208-4ee0-9ece-2e4b8512af67_800x117.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!f-FM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faef31a6d-6208-4ee0-9ece-2e4b8512af67_800x117.png" width="2000" height="292" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/aef31a6d-6208-4ee0-9ece-2e4b8512af67_800x117.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:292,&quot;width&quot;:2000,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!f-FM!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faef31a6d-6208-4ee0-9ece-2e4b8512af67_800x117.png 424w, https://substackcdn.com/image/fetch/$s_!f-FM!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faef31a6d-6208-4ee0-9ece-2e4b8512af67_800x117.png 848w, https://substackcdn.com/image/fetch/$s_!f-FM!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faef31a6d-6208-4ee0-9ece-2e4b8512af67_800x117.png 1272w, https://substackcdn.com/image/fetch/$s_!f-FM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faef31a6d-6208-4ee0-9ece-2e4b8512af67_800x117.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>&#30070;&#25105;&#20497;&#35426;&#21839;&#26576;&#19968;&#31558;&#24310;&#36978;&#30340;&#35330;&#21934;&#65292;&#31995;&#32113;&#20027;&#21205;&#21578;&#35380;&#25105;&#20497;&#35201;&#19981;&#35201;&#20351;&#29992;coupon&#24037;&#20855;&#30332;&#25918;&#20778;&#24800;&#21367;&#65292;&#20006;&#19988;&#20351;&#29992;&#35330;&#21934;&#26597;&#21040;&#21040;&#20351;&#29992;&#32773;email&#36914;&#34892;&#30332;&#25918;</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!J6pC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3400eaee-b487-4044-bf19-be11ed3a7ee8_800x328.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!J6pC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3400eaee-b487-4044-bf19-be11ed3a7ee8_800x328.png 424w, https://substackcdn.com/image/fetch/$s_!J6pC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3400eaee-b487-4044-bf19-be11ed3a7ee8_800x328.png 848w, https://substackcdn.com/image/fetch/$s_!J6pC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3400eaee-b487-4044-bf19-be11ed3a7ee8_800x328.png 1272w, https://substackcdn.com/image/fetch/$s_!J6pC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3400eaee-b487-4044-bf19-be11ed3a7ee8_800x328.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!J6pC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3400eaee-b487-4044-bf19-be11ed3a7ee8_800x328.png" width="2000" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3400eaee-b487-4044-bf19-be11ed3a7ee8_800x328.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:2000,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!J6pC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3400eaee-b487-4044-bf19-be11ed3a7ee8_800x328.png 424w, https://substackcdn.com/image/fetch/$s_!J6pC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3400eaee-b487-4044-bf19-be11ed3a7ee8_800x328.png 848w, https://substackcdn.com/image/fetch/$s_!J6pC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3400eaee-b487-4044-bf19-be11ed3a7ee8_800x328.png 1272w, https://substackcdn.com/image/fetch/$s_!J6pC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3400eaee-b487-4044-bf19-be11ed3a7ee8_800x328.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>&#22914;&#26524;&#25105;&#35498;&#22909;&#65292;&#31995;&#32113;&#36996;&#21487;&#20197;&#35731;&#25105;&#25163;&#21205;&#23529;&#26680;&#21516;&#24847;&#20877;&#34892;&#21205;</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!86iy!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b1213ca-6da0-460b-a3db-234ff9a113ad_800x182.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!86iy!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b1213ca-6da0-460b-a3db-234ff9a113ad_800x182.png 424w, https://substackcdn.com/image/fetch/$s_!86iy!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b1213ca-6da0-460b-a3db-234ff9a113ad_800x182.png 848w, https://substackcdn.com/image/fetch/$s_!86iy!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b1213ca-6da0-460b-a3db-234ff9a113ad_800x182.png 1272w, https://substackcdn.com/image/fetch/$s_!86iy!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b1213ca-6da0-460b-a3db-234ff9a113ad_800x182.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!86iy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b1213ca-6da0-460b-a3db-234ff9a113ad_800x182.png" width="2000" height="454" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7b1213ca-6da0-460b-a3db-234ff9a113ad_800x182.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:454,&quot;width&quot;:2000,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!86iy!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b1213ca-6da0-460b-a3db-234ff9a113ad_800x182.png 424w, https://substackcdn.com/image/fetch/$s_!86iy!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b1213ca-6da0-460b-a3db-234ff9a113ad_800x182.png 848w, https://substackcdn.com/image/fetch/$s_!86iy!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b1213ca-6da0-460b-a3db-234ff9a113ad_800x182.png 1272w, https://substackcdn.com/image/fetch/$s_!86iy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b1213ca-6da0-460b-a3db-234ff9a113ad_800x182.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!iN0-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5b9d716-fd4a-4fa7-b525-9ef68750b058_800x270.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!iN0-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5b9d716-fd4a-4fa7-b525-9ef68750b058_800x270.png 424w, https://substackcdn.com/image/fetch/$s_!iN0-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5b9d716-fd4a-4fa7-b525-9ef68750b058_800x270.png 848w, https://substackcdn.com/image/fetch/$s_!iN0-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5b9d716-fd4a-4fa7-b525-9ef68750b058_800x270.png 1272w, https://substackcdn.com/image/fetch/$s_!iN0-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5b9d716-fd4a-4fa7-b525-9ef68750b058_800x270.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!iN0-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5b9d716-fd4a-4fa7-b525-9ef68750b058_800x270.png" width="2000" height="675" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a5b9d716-fd4a-4fa7-b525-9ef68750b058_800x270.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:675,&quot;width&quot;:2000,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!iN0-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5b9d716-fd4a-4fa7-b525-9ef68750b058_800x270.png 424w, https://substackcdn.com/image/fetch/$s_!iN0-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5b9d716-fd4a-4fa7-b525-9ef68750b058_800x270.png 848w, https://substackcdn.com/image/fetch/$s_!iN0-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5b9d716-fd4a-4fa7-b525-9ef68750b058_800x270.png 1272w, https://substackcdn.com/image/fetch/$s_!iN0-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5b9d716-fd4a-4fa7-b525-9ef68750b058_800x270.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>&#19979;&#19968;&#27493;</h3><p>&#30475;&#35211;AI Agent&#24590;&#27171;&#21644;Chatbot&#20114;&#21205;&#65292;&#25033;&#35442;&#26356;&#21487;&#20197;&#20102;&#35299;AI Agent&#30340;&#20729;&#20540;&#20102;&#21543;&#65281;&#20659;&#32113;&#36575;&#39636;&#38283;&#30332;&#36890;&#24120;&#30001;&#24460;&#31471;&#25972;&#21512;&#31532;&#19977;&#26041;&#26381;&#21209;&#65292;&#21069;&#31471;&#36000;&#36012; UI/UX&#65292;&#30906;&#20445;&#20351;&#29992;&#32773;&#39636;&#39511;&#12290;&#20294;&#36889;&#31278;&#26041;&#24335;&#35201;&#27714;&#38283;&#30332;&#32773;&#25484;&#25569;&#25152;&#26377;&#37007;&#36655;&#65292;&#20006;&#33258;&#34892;&#20018;&#25509;&#21508;&#31278;&#26381;&#21209;&#65292;&#20197;&#32173;&#25345;&#23436;&#25972;&#25511;&#21046;&#27402;&#12290;</p><p>&#38568;&#33879;&#29983;&#25104;&#24335; AI&#65288;Generative AI&#65289;&#33287; AI Agent &#30340;&#30332;&#23637;&#65292;&#35373;&#35336;&#24605;&#32173;&#38283;&#22987;&#36681;&#35722;&#12290;&#25105;&#20497;&#21487;&#36879;&#36942; AI Agent &#23450;&#32681;&#26989;&#21209;&#37007;&#36655;&#65292;&#20006;&#36879;&#36942;&#27161;&#28310;&#21270;&#21332;&#35696;&#65288;&#22914; Anthropic &#30340; Multi-Context Protocol, MCP&#65289;&#31649;&#29702; AI &#33287; LLM&#65288;&#22823;&#35486;&#35328;&#27169;&#22411;&#65289;&#30340;&#20114;&#21205;&#12290;&#36889;&#35731;&#38283;&#30332;&#32773;&#28961;&#38656;&#38364;&#27880;&#20659;&#32113;&#30340;&#24460;&#31471;&#37007;&#36655;&#33287;&#21069;&#31471;&#38283;&#30332;&#65292;&#32780;&#26159;&#23560;&#27880;&#26044; AI Agent &#30340;&#34892;&#28858;&#35373;&#35336;&#65292;&#35731; AI &#33258;&#20027;&#34389;&#29702;&#20219;&#21209;&#12290;</p><p>&#36889;&#31278;&#27169;&#24335;&#25552;&#21319;&#20102;&#38283;&#30332;&#38728;&#27963;&#24615;&#65292;&#29305;&#21029;&#36969;&#29992;&#26044;&#24555;&#36895;&#39511;&#35657;&#26032;&#24819;&#27861;&#65288;Prototype&#65289;&#12290;&#38283;&#30332;&#32773;&#21482;&#38656;&#23450;&#32681; AI Agent &#30340;&#26680;&#24515;&#21151;&#33021;&#65292;&#32780;&#28961;&#38656;&#25237;&#20837;&#22823;&#37327;&#26178;&#38291;&#33287;&#36039;&#28304;&#25645;&#24314;&#23436;&#25972;&#30340;&#21069;&#24460;&#31471;&#26550;&#27083;&#65292;&#36879;&#36942; MCP &#27161;&#28310;&#21270;&#30340;&#20114;&#21205;&#65292;&#21363;&#21487;&#24555;&#36895;&#33853;&#22320;&#25033;&#29992;&#12290;</p><p>&#25509;&#33879;&#20320;&#21487;&#20197;&#65306;</p><ol><li><p>Fork &#23560;&#26696;&#33258;&#24049;&#20358;&#29609;&#29609;&#30475;&#65306;<a href="https://github.com/schwannden/mcp-server-demo">https://github.com/schwannden/mcp-server-demo</a></p></li><li><p>&#30475;&#30475; MCP &#23448;&#26041;&#30340; Reference Server: <a href="https://github.com/modelcontextprotocol/servers">https://github.com/modelcontextprotocol/servers</a></p></li><li><p>&#21152;&#20837;Goose Discrod&#30475;&#30475;&#20854;&#20182;&#20154;&#22312;&#20570;&#20160;&#40636;&#37239;&#37239;&#30340;&#20107;&#24773;&#65306;<a href="https://discord.com/invite/block-opensource">https://discord.com/invite/block-opensource</a></p></li></ol>]]></content:encoded></item><item><title><![CDATA[What’s the Fuss About MCP AI Agent]]></title><description><![CDATA[(This is a chat gpt tranlated article from&#8230;]]></description><link>https://schwannden.substack.com/p/whats-the-fuzz-about-mcp-ai-agent</link><guid isPermaLink="false">https://schwannden.substack.com/p/whats-the-fuzz-about-mcp-ai-agent</guid><dc:creator><![CDATA[schwannden]]></dc:creator><pubDate>Wed, 19 Mar 2025 07:17:36 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/c2c790bd-5a67-462a-b038-501187731eb3_800x571.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!998k!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96d102f5-d84a-47ef-ba6a-9dff2798c346_800x571.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!998k!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96d102f5-d84a-47ef-ba6a-9dff2798c346_800x571.png 424w, https://substackcdn.com/image/fetch/$s_!998k!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96d102f5-d84a-47ef-ba6a-9dff2798c346_800x571.png 848w, https://substackcdn.com/image/fetch/$s_!998k!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96d102f5-d84a-47ef-ba6a-9dff2798c346_800x571.png 1272w, https://substackcdn.com/image/fetch/$s_!998k!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96d102f5-d84a-47ef-ba6a-9dff2798c346_800x571.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!998k!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96d102f5-d84a-47ef-ba6a-9dff2798c346_800x571.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/96d102f5-d84a-47ef-ba6a-9dff2798c346_800x571.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!998k!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96d102f5-d84a-47ef-ba6a-9dff2798c346_800x571.png 424w, https://substackcdn.com/image/fetch/$s_!998k!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96d102f5-d84a-47ef-ba6a-9dff2798c346_800x571.png 848w, https://substackcdn.com/image/fetch/$s_!998k!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96d102f5-d84a-47ef-ba6a-9dff2798c346_800x571.png 1272w, https://substackcdn.com/image/fetch/$s_!998k!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96d102f5-d84a-47ef-ba6a-9dff2798c346_800x571.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p>(This is a chat gpt tranlated article from: <a href="https://blog.schwannden.com/mcp-ai-agent-e5-9c-a8-e7-b4-85-e4-bb-80-e9-ba-bc/">https://medium.com/@schwanndenkuo/mcp-ai-agent%E5%9C%A8%E7%B4%85%E4%BB%80%E9%BA%BC-e7fd2523c749</a>)</p><p>(If you would rather like to start building your own AI agent, checkout: <a href="https://blog.schwannden.com/ai-agent-0-e5-88-b0-1-mcp-server-with-goose/">AI Agent 0 &#21040; 1: MCP Server with Goose</a>&#65289;</p><h3>Background Story</h3><p>As large language models (LLMs) like ChatGPT, Claude, and Gemini continue to evolve and become more powerful, the question arises: where is AI headed in the future? Tech industry leaders such as Andrew Ng, Elon Musk, and Jensen Huang have all pointed out that &#8220;AI Agents will be the next major milestone.&#8221; But what exactly is an AI Agent?</p><p>With more and more LLM providers offering advanced models and applications, we naturally wonder: is there an open and universal protocol that allows us to create AI Agents freely without being locked into a specific platform or provider?</p><h3>Limitations of LLMs</h3><p>When we use LLMs like ChatGPT or Claude, we can ask questions and have conversations, but they can&#8217;t actually &#8220;do&#8221; anything. For example, you can&#8217;t directly ask ChatGPT to book a restaurant, send an email, or fetch a report from your home NAS.</p><p>This is where <strong>AI Agents</strong> come into play. An AI Agent acts like a personal assistant that not only converses with you but also takes real actions based on your needs. For instance, if you want to know when a particular colleague last logged into your company&#8217;s system last month, a standard LLM wouldn&#8217;t be able to help because it lacks access to your company&#8217;s internal data. However, an AI Agent can accomplish this by calling the company&#8217;s internal APIs or querying the database to retrieve the relevant information instantly.</p><p>Another limitation of LLMs is that their responses are confined to their pre-trained knowledge. If you ask about a breaking news event or specific business data within your company, an LLM will often be clueless. To address this, we might use <strong>Retrieval-Augmented Generation (RAG)</strong>, which allows an LLM to &#8220;look up&#8221; additional data before responding. However, RAG works best when dealing with relatively static, infrequently changing data. For real-time or dynamic requests&#8202;&#8212;&#8202;such as checking the current shipping status of a customer&#8217;s order&#8202;&#8212;&#8202;RAG can be slow and costly.</p><p>On the other hand, an <strong>AI Agent</strong> functions like a real-world assistant that can handle tasks dynamically. If you ask, &#8220;Where is my order #12345 right now?&#8221; the AI Agent can immediately call your company&#8217;s logistics system API to provide a real-time, accurate response.</p><h3>Summary</h3><ul><li><p><strong>General LLMs</strong> act like &#8220;knowledgeable encyclopedias&#8221; that provide answers based on their training data but cannot take action.</p></li><li><p><strong>RAG-enhanced LLMs</strong> function as &#8220;encyclopedias with a database,&#8221; useful for retrieving external data but not ideal for real-time scenarios.</p></li><li><p><strong>AI Agents</strong> work like &#8220;assistants that can take action,&#8221; using tools and APIs to solve specific, real-time problems.</p></li></ul><h3>Limitations of AI Agents</h3><ol><li><p><strong>Interoperability Issue:</strong><br>With the rise of multiple LLM providers like Claude, Gemini, and OpenAI, AI-powered applications have diversified&#8202;&#8212;&#8202;from chatbots like ChatGPT to intelligent IDEs like Cursor. However, ensuring that an AI Agent can seamlessly integrate across different platforms is challenging. Ideally, we need a <strong>standardized protocol</strong> that allows an AI Agent to work flexibly across various platforms without being tied to a specific model or service provider.</p></li><li><p><strong>Limited Adaptability for Automation:</strong><br>Traditional AI Agents are designed primarily for human users&#8202;&#8212;&#8202;meaning they operate step by step based on user commands or predefined workflows. This limits their flexibility when used by <strong>other machines</strong> (such as client applications or automation services). Ideally, applications should be able to <strong>dynamically select and orchestrate different AI Agent tools</strong> based on real-time needs, rather than following rigid, pre-scripted sequences.</p></li></ol><h3>How MCP (Anthropic&#8217;s Model Context Protocol) Solves These Issues</h3><p>MCP introduces a <strong>standardized client-server architecture</strong> that effectively addresses these challenges. Developers can focus on creating independent function modules, known as <strong>MCP Servers</strong>, which handle specific tasks such as sending emails, managing calendars, or performing image recognition. Meanwhile, applications that integrate with LLMs&#8202;&#8212;&#8202;such as Claude Chatbot or intelligent IDEs like Cursor&#8202;&#8212;&#8202;act as <strong>MCP Clients</strong>. These MCP Clients can dynamically explore and identify available functionalities provided by MCP Servers, selecting the most appropriate tools for complex tasks.</p><p>This structured collaboration makes AI Agents more <strong>interoperable</strong>, easier for machines to understand, and <strong>seamlessly automatable</strong>, enhancing the flexibility and efficiency of the entire AI ecosystem.</p><h3>Real-World Workflow Example</h3><p>Imagine you&#8217;ve built an AI customer service chatbot using MCP that integrates two tools:</p><ol><li><p><strong>Order Tracking Tool</strong>&#8202;&#8212;&#8202;Connects to your company&#8217;s database to check order status based on a provided order number.</p></li><li><p><strong>Coupon Issuance Tool</strong>&#8202;&#8212;&#8202;Dynamically decides whether to issue discount coupons if a customer is dissatisfied.</p></li></ol><h4>Scenario: A Customer Inquiry</h4><p>A customer asks the AI chatbot:<br><em>&#8220;My order #12345 has been delayed for two weeks. Can you check on it for me?&#8221;</em></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!otpT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb2214486-c1da-415f-916e-55b1cb504916_800x396.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!otpT!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb2214486-c1da-415f-916e-55b1cb504916_800x396.png 424w, https://substackcdn.com/image/fetch/$s_!otpT!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb2214486-c1da-415f-916e-55b1cb504916_800x396.png 848w, https://substackcdn.com/image/fetch/$s_!otpT!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb2214486-c1da-415f-916e-55b1cb504916_800x396.png 1272w, https://substackcdn.com/image/fetch/$s_!otpT!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb2214486-c1da-415f-916e-55b1cb504916_800x396.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!otpT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb2214486-c1da-415f-916e-55b1cb504916_800x396.png" width="1472" height="728" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b2214486-c1da-415f-916e-55b1cb504916_800x396.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:728,&quot;width&quot;:1472,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!otpT!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb2214486-c1da-415f-916e-55b1cb504916_800x396.png 424w, https://substackcdn.com/image/fetch/$s_!otpT!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb2214486-c1da-415f-916e-55b1cb504916_800x396.png 848w, https://substackcdn.com/image/fetch/$s_!otpT!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb2214486-c1da-415f-916e-55b1cb504916_800x396.png 1272w, https://substackcdn.com/image/fetch/$s_!otpT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb2214486-c1da-415f-916e-55b1cb504916_800x396.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><ol><li><p><strong>MCP Client (e.g., Claude Chatbot) analyzes the request</strong> and determines that it needs external tools to complete the task.</p></li><li><p>It dynamically queries the <strong>MCP Server</strong> to check what tools are available.</p></li><li><p>The <strong>MCP Server</strong> responds with a list of available tools and how to use them.</p></li><li><p>The AI <strong>decides to first call the Order Tracking Tool</strong>, using order number <strong>#12345</strong> to check the status.</p></li><li><p>Suppose the response indicates <strong>&#8220;Shipment delayed, not yet dispatched.&#8221;</strong></p></li><li><p>The LLM understands that the customer might be dissatisfied, so it <strong>calls the Coupon Issuance Tool</strong> to offer compensation.</p></li><li><p>The AI chatbot responds: <em>&#8220;Your order is delayed due to a logistics issue. We sincerely apologize for the inconvenience. As a token of our apology, we&#8217;re offering you a discount coupon.&#8221;</em></p></li></ol><p>Now, if the order status instead showed <strong>&#8220;Payment not completed.&#8221;</strong></p><ul><li><p>The AI <strong>would not issue a coupon</strong> since the issue isn&#8217;t a shipping delay.</p></li><li><p>Instead, it would respond: <em>&#8220;Your order is awaiting payment. Please complete the payment, and we will process the shipment immediately. Thank you!&#8221;</em></p></li></ul><p>In this scenario, <strong>we didn&#8217;t need to manually code logic for every possible situation</strong>. By simply providing MCP Server tools, the LLM itself can <strong>dynamically decide which tools to use</strong> to resolve issues intelligently.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!mVqv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c7d2804-f14e-4f50-8977-faced3c51971_800x397.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!mVqv!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c7d2804-f14e-4f50-8977-faced3c51971_800x397.png 424w, https://substackcdn.com/image/fetch/$s_!mVqv!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c7d2804-f14e-4f50-8977-faced3c51971_800x397.png 848w, https://substackcdn.com/image/fetch/$s_!mVqv!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c7d2804-f14e-4f50-8977-faced3c51971_800x397.png 1272w, https://substackcdn.com/image/fetch/$s_!mVqv!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c7d2804-f14e-4f50-8977-faced3c51971_800x397.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!mVqv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c7d2804-f14e-4f50-8977-faced3c51971_800x397.png" width="1384" height="686" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0c7d2804-f14e-4f50-8977-faced3c51971_800x397.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:686,&quot;width&quot;:1384,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!mVqv!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c7d2804-f14e-4f50-8977-faced3c51971_800x397.png 424w, https://substackcdn.com/image/fetch/$s_!mVqv!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c7d2804-f14e-4f50-8977-faced3c51971_800x397.png 848w, https://substackcdn.com/image/fetch/$s_!mVqv!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c7d2804-f14e-4f50-8977-faced3c51971_800x397.png 1272w, https://substackcdn.com/image/fetch/$s_!mVqv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c7d2804-f14e-4f50-8977-faced3c51971_800x397.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Frequently Asked Questions (FAQ)</h3><h4>Q: Who developed MCP?</h4><p><strong>A:</strong> MCP was introduced by <strong>Anthropic</strong> as a protocol that enables AI models to interact more flexibly with external tools. However, MCP is not exclusive to Anthropic&#8217;s AI models (e.g., Claude) but is designed as an <strong>open, vendor-agnostic standard</strong> that any AI model can use.</p><h4>Q: How does MCP work?</h4><p><strong>A:</strong> The workflow follows these steps:</p><ol><li><p>The <strong>MCP Client</strong> (e.g., Claude, <a href="https://github.com/block/goose">Goose</a>, etc.) sends a request to the <strong>MCP Server</strong>.</p></li><li><p>The <strong>MCP Server</strong> responds with available tools, resources, and prompts.</p></li><li><p>The <strong>MCP Client</strong> determines which tools to use and what data to provide to the AI model to fulfill the user request.</p></li></ol><p>This allows AI to dynamically select and combine different tools to complete tasks more efficiently.</p><h4>Q: Is MCP limited to Claude?</h4><p><strong>A:</strong> No. While <strong>Claude Desktop</strong> is an MCP Client developed by Anthropic, MCP itself is not tied to any single AI vendor. For example, <strong><a href="https://github.com/block/goose">Goose</a></strong> is a rapidly growing MCP Client that supports integration with various LLM APIs and features an <strong>MCP Server marketplace</strong> offering a variety of tools.</p><h4>Q: Are there security risks with MCP? How can we ensure its security?</h4><p><strong>A:</strong> MCP does not require sending sensitive data to AI models by default. Security depends on how MCP Servers manage data transmission. Best practices include:</p><ul><li><p><strong>Access control:</strong> Restrict API and tool access to prevent data leaks.</p></li><li><p><strong>API key management:</strong> Securely store and manage API keys to prevent unauthorized access.</p></li><li><p><strong>Monitoring and auditing:</strong> Track API calls and data access to detect and mitigate security risks.</p></li></ul><p>By following these practices, MCP can be implemented securely while maintaining data integrity.</p><h3>Useful Resources</h3><ul><li><p><strong><a href="https://modelcontextprotocol.io/introduction">MCP Official Website</a></strong></p></li><li><p><strong><a href="https://github.com/modelcontextprotocol/servers">MCP Reference Servers</a></strong></p></li><li><p><strong><a href="https://modelcontextprotocol.io/clients">List of MCP Clients &amp; Supported Protocol Features</a></strong></p></li></ul>]]></content:encoded></item><item><title><![CDATA[MCP AI Agent在紅什麼]]></title><description><![CDATA[&#65288;&#27604;&#36215;&#35264;&#24565;&#30340;&#20102;&#35299;&#65292;&#22914;&#26524;&#20320;&#24819;&#35201;&#30452;&#25509;&#21205;&#25163;&#65292;&#35531;&#30475;&#65306;AI Agent 0 &#21040; 1: MCP Server with Goose&#65289;]]></description><link>https://schwannden.substack.com/p/whats-the-fuzz-about-mcp-ai-agent-zh-tw</link><guid isPermaLink="false">https://schwannden.substack.com/p/whats-the-fuzz-about-mcp-ai-agent-zh-tw</guid><dc:creator><![CDATA[schwannden]]></dc:creator><pubDate>Wed, 19 Mar 2025 07:11:41 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/00cd12bd-f8b6-4b2d-8328-b1374f95f30a_800x571.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!i92-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2415509e-9b12-484f-b53c-a10fe4792dea_800x571.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!i92-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2415509e-9b12-484f-b53c-a10fe4792dea_800x571.png 424w, https://substackcdn.com/image/fetch/$s_!i92-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2415509e-9b12-484f-b53c-a10fe4792dea_800x571.png 848w, https://substackcdn.com/image/fetch/$s_!i92-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2415509e-9b12-484f-b53c-a10fe4792dea_800x571.png 1272w, https://substackcdn.com/image/fetch/$s_!i92-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2415509e-9b12-484f-b53c-a10fe4792dea_800x571.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!i92-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2415509e-9b12-484f-b53c-a10fe4792dea_800x571.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2415509e-9b12-484f-b53c-a10fe4792dea_800x571.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!i92-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2415509e-9b12-484f-b53c-a10fe4792dea_800x571.png 424w, https://substackcdn.com/image/fetch/$s_!i92-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2415509e-9b12-484f-b53c-a10fe4792dea_800x571.png 848w, https://substackcdn.com/image/fetch/$s_!i92-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2415509e-9b12-484f-b53c-a10fe4792dea_800x571.png 1272w, https://substackcdn.com/image/fetch/$s_!i92-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2415509e-9b12-484f-b53c-a10fe4792dea_800x571.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p>&#65288;&#27604;&#36215;&#35264;&#24565;&#30340;&#20102;&#35299;&#65292;&#22914;&#26524;&#20320;&#24819;&#35201;&#30452;&#25509;&#21205;&#25163;&#65292;&#35531;&#30475;&#65306;<a href="https://blog.schwannden.com/ai-agent-0-e5-88-b0-1-mcp-server-with-goose/">AI Agent 0 &#21040; 1: MCP Server with Goose</a>&#65289;</p><h3>&#25925;&#20107;&#32972;&#26223;</h3><p>&#38568;&#33879; ChatGPT&#12289;Claude&#12289;Gemini &#31561;&#22823;&#22411;&#35486;&#35328;&#27169;&#22411;&#65288;LLM&#65289;&#19981;&#26039;&#25512;&#38515;&#20986;&#26032;&#12289;&#33021;&#21147;&#36234;&#20358;&#36234;&#24375;&#22823;&#65292;AI &#30340;&#26410;&#20358;&#31350;&#31455;&#26371;&#24448;&#21738;&#35041;&#30332;&#23637;&#21602;&#65311;&#31185;&#25216;&#22280;&#37325;&#37327;&#32026;&#20154;&#29289;&#65292;&#22914; Andrew Ng&#12289;Elon Musk &#21644;&#40643;&#20161;&#21235;&#37117;&#32027;&#32027;&#25351;&#20986;&#65306;&#12300;AI Agent &#23559;&#26371;&#26159;&#19979;&#19968;&#20491;&#37325;&#35201;&#30340;&#37324;&#31243;&#30865;&#12290;&#12301;&#37027;&#40636;&#65292;&#22823;&#23478;&#21475;&#20013;&#30340; AI Agent &#21040;&#24213;&#26159;&#20160;&#40636;&#21602;&#65311;</p><p>&#28982;&#32780;&#65292;&#30070;&#36234;&#20358;&#36234;&#22810; LLM &#24288;&#21830;&#25552;&#20379;&#21508;&#31278;&#24375;&#22823;&#30340;&#27169;&#22411;&#33287;&#25033;&#29992;&#26178;&#65292;&#25105;&#20497;&#19981;&#31105;&#22909;&#22855;&#65306;&#26377;&#27794;&#26377;&#19968;&#20491;&#38283;&#25918;&#19988;&#36890;&#29992;&#30340;&#21332;&#35696;&#65288;Protocol&#65289;&#65292;&#33021;&#35731;&#25105;&#20497;&#33258;&#30001;&#21109;&#36896;&#21508;&#31278; AI Agent&#65292;&#32780;&#19981;&#29992;&#25812;&#24515;&#34987;&#26576;&#20491;&#29305;&#23450;&#24179;&#21488;&#25110;&#24288;&#21830;&#32129;&#20303;&#21602;&#65311;</p><p>&#20659;&#32113;&#36575;&#39636;&#38283;&#30332;&#36890;&#24120;&#30001;&#24460;&#31471;&#25972;&#21512;&#31532;&#19977;&#26041;&#26381;&#21209;&#65292;&#21069;&#31471;&#36000;&#36012; UI/UX&#65292;&#30906;&#20445;&#20351;&#29992;&#32773;&#39636;&#39511;&#12290;&#20294;&#36889;&#31278;&#26041;&#24335;&#35201;&#27714;&#38283;&#30332;&#32773;&#25484;&#25569;&#25152;&#26377;&#37007;&#36655;&#65292;&#20006;&#33258;&#34892;&#20018;&#25509;&#21508;&#31278;&#26381;&#21209;&#65292;&#20197;&#32173;&#25345;&#23436;&#25972;&#25511;&#21046;&#27402;&#12290;&#38568;&#33879;&#29983;&#25104;&#24335; AI&#65288;Generative AI&#65289;&#33287; AI Agent &#30340;&#30332;&#23637;&#65292;&#35373;&#35336;&#24605;&#32173;&#38283;&#22987;&#36681;&#35722;&#12290;&#25105;&#20497;&#21487;&#36879;&#36942; AI Agent &#23450;&#32681;&#26989;&#21209;&#37007;&#36655;&#65292;&#20006;&#36879;&#36942;&#27161;&#28310;&#21270;&#21332;&#35696;&#65288;&#22914; Anthropic &#30340; Multi-Context Protocol, MCP&#65289;&#31649;&#29702; AI &#33287; LLM&#65288;&#22823;&#35486;&#35328;&#27169;&#22411;&#65289;&#30340;&#20114;&#21205;&#12290;&#36889;&#35731;&#38283;&#30332;&#32773;&#28961;&#38656;&#38364;&#27880;&#20659;&#32113;&#30340;&#24460;&#31471;&#37007;&#36655;&#33287;&#21069;&#31471;&#38283;&#30332;&#65292;&#32780;&#26159;&#23560;&#27880;&#26044; AI Agent &#30340;&#34892;&#28858;&#35373;&#35336;&#65292;&#35731; AI &#33258;&#20027;&#34389;&#29702;&#20219;&#21209;&#12290;&#36889;&#31278;&#27169;&#24335;&#25552;&#21319;&#20102;&#38283;&#30332;&#38728;&#27963;&#24615;&#65292;&#29305;&#21029;&#36969;&#29992;&#26044;&#24555;&#36895;&#39511;&#35657;&#26032;&#24819;&#27861;&#65288;Prototype&#65289;&#12290;&#38283;&#30332;&#32773;&#21482;&#38656;&#23450;&#32681; AI Agent &#30340;&#26680;&#24515;&#21151;&#33021;&#65292;&#32780;&#28961;&#38656;&#25237;&#20837;&#22823;&#37327;&#26178;&#38291;&#33287;&#36039;&#28304;&#25645;&#24314;&#23436;&#25972;&#30340;&#21069;&#24460;&#31471;&#26550;&#27083;&#65292;&#36879;&#36942; MCP &#27161;&#28310;&#21270;&#30340;&#20114;&#21205;&#65292;&#21363;&#21487;&#24555;&#36895;&#33853;&#22320;&#25033;&#29992;&#12290;</p><h3>LLM&#30340;&#38480;&#21046;</h3><p>&#25105;&#20497;&#24179;&#24120;&#29992; ChatGPT &#25110; Claude &#36889;&#20123;&#22823;&#22411;&#35486;&#35328;&#27169;&#22411;&#65288;LLM&#65289;&#26178;&#65292;&#36890;&#24120;&#21482;&#33021;&#21839;&#21839;&#38988;&#25110;&#32842;&#22825;&#65292;&#20294;&#27794;&#36774;&#27861;&#35731;&#23427;&#20497;&#30495;&#30340;&#12300;&#20570;&#12301;&#20160;&#40636;&#20107;&#12290;&#27604;&#22914;&#35498;&#65292;&#20320;&#19981;&#33021;&#30452;&#25509;&#21483; ChatGPT &#24171;&#20320;&#35330;&#39184;&#24307;&#12289;&#23492; email&#65292;&#25110;&#21435;&#20320;&#23478; NAS &#35041;&#25235;&#19968;&#20221;&#22577;&#21578;&#12290;</p><p>&#36889;&#26178;&#20505;&#65292;&#12300;AI Agent&#12301;&#23601;&#27966;&#19978;&#29992;&#22580;&#20102;&#12290;AI Agent &#23601;&#20687;&#26159;&#19968;&#20491;&#36028;&#24515;&#30340;&#23567;&#31192;&#26360;&#65292;&#19981;&#20677;&#33021;&#21644;&#20320;&#23565;&#35441;&#65292;&#36996;&#33021;&#26681;&#25818;&#20320;&#30340;&#38656;&#27714;&#23526;&#38555;&#12300;&#21205;&#25163;&#20570;&#20107;&#12301;&#12290;&#33289;&#20363;&#20358;&#35498;&#65292;&#22914;&#26524;&#20320;&#24819;&#30693;&#36947;&#19978;&#20491;&#26376;&#26576;&#20491;&#21516;&#20107;&#26368;&#24460;&#19968;&#27425;&#30331;&#20837;&#20844;&#21496;&#31995;&#32113;&#26159;&#20160;&#40636;&#26178;&#20505;&#65292;&#19968;&#33324;&#30340; LLM &#26159;&#31572;&#19981;&#20986;&#20358;&#30340;&#65292;&#22240;&#28858;&#23427;&#19981;&#25026;&#20320;&#20844;&#21496;&#20839;&#37096;&#30340;&#36039;&#26009;&#12290;&#20294;&#26159; AI Agent &#21487;&#20197;&#20570;&#21040;&#65292;&#23427;&#26371;&#30452;&#25509;&#21435;&#21628;&#21483;&#20844;&#21496;&#20839;&#37096;&#31995;&#32113;&#30340; API &#25110;&#26597;&#35426;&#36039;&#26009;&#24235;&#65292;&#31435;&#21051;&#24171;&#20320;&#25214;&#21040;&#28310;&#30906;&#30340;&#32000;&#37636;&#12290;</p><p>&#21478;&#19968;&#26041;&#38754;&#65292;LLM &#30340;&#21478;&#19968;&#20491;&#21839;&#38988;&#26159;&#65292;&#23427;&#22238;&#31572;&#21839;&#38988;&#30340;&#33021;&#21147;&#20407;&#38480;&#22312;&#33258;&#24049;&#25152;&#23416;&#36942;&#30340;&#30693;&#35672;&#20839;&#12290;&#22914;&#26524;&#30896;&#19978;&#20102;&#26368;&#26032;&#30332;&#29983;&#30340;&#26032;&#32862;&#65292;&#25110;&#32773;&#20320;&#33258;&#23478;&#30340;&#26989;&#21209;&#36039;&#35338;&#65292;LLM &#24120;&#24120;&#26463;&#25163;&#28961;&#31574;&#12290;&#28858;&#20102;&#35299;&#27770;&#36889;&#20491;&#21839;&#38988;&#65292;&#25105;&#20497;&#21487;&#33021;&#26371;&#24819;&#21040; RAG&#65288;Retrieval-Augmented Generation&#65292;&#27298;&#32034;&#22686;&#24375;&#29983;&#25104;&#65289;&#65292;&#35731; LLM &#33021;&#22816;&#20808;&#21435;&#12300;&#32763;&#38321;&#12301;&#38989;&#22806;&#30340;&#36039;&#26009;&#20877;&#22238;&#31572;&#21839;&#38988;&#12290;&#19981;&#36942;&#65292;RAG &#36969;&#21512;&#30340;&#24773;&#22659;&#26159;&#36039;&#26009;&#30456;&#23565;&#38748;&#24907;&#12289;&#35722;&#21205;&#19981;&#38971;&#32321;&#30340;&#24773;&#27841;&#12290;&#22914;&#26524;&#20320;&#26377;&#20123;&#21363;&#26178;&#30340;&#12289;&#21205;&#24907;&#30340;&#38656;&#27714;&#65288;&#27604;&#22914;&#21363;&#26178;&#26597;&#35426;&#26576;&#20491;&#39015;&#23458;&#35330;&#21934;&#30340;&#20986;&#36008;&#36914;&#24230;&#65289;&#65292;&#29992; RAG &#19981;&#21482;&#32233;&#24930;&#65292;&#36996;&#21487;&#33021;&#25104;&#26412;&#22826;&#39640;&#12290;</p><p>&#32780; AI Agent &#23601;&#20687;&#26159;&#24171;&#20320;&#36305;&#33151;&#30340;&#23567;&#21161;&#25163;&#65292;&#20320;&#21482;&#35201;&#32102;&#23427;&#26126;&#30906;&#30340;&#20219;&#21209;&#65292;&#20363;&#22914;&#65306;&#12300;&#24171;&#25105;&#30906;&#35469;&#35330;&#21934; 12345 &#29694;&#22312;&#23492;&#21040;&#21738;&#35041;&#20102;&#65311;&#12301;&#23427;&#26371;&#39340;&#19978;&#21628;&#21483;&#20320;&#20844;&#21496;&#29289;&#27969;&#31995;&#32113;&#30340; API&#65292;&#32102;&#20986;&#21363;&#26178;&#21448;&#27491;&#30906;&#30340;&#31572;&#26696;&#12290;</p><p>&#32317;&#32080;&#19968;&#19979;&#65306;</p><ul><li><p>&#19968;&#33324;&#30340; LLM &#23601;&#20687;&#12300;&#32880;&#26126;&#30340;&#33836;&#20107;&#36890;&#20808;&#29983;&#12301;&#65292;&#21482;&#33021;&#25552;&#20379;&#20182;&#30693;&#36947;&#30340;&#31572;&#26696;&#65292;&#32780;&#19988;&#21482;&#21205;&#21475;&#65292;&#19981;&#21205;&#25163;&#12290;</p></li><li><p>RAG &#23601;&#20687;&#26159;&#12300;&#24118;&#33879;&#36039;&#26009;&#24235;&#30340;&#30334;&#31185;&#20840;&#26360;&#12301;&#65292;&#20294;&#36039;&#26009;&#24235;&#36890;&#24120;&#27604;&#36611;&#38748;&#24907;&#65292;&#19981;&#36969;&#21512;&#38656;&#35201;&#21363;&#26178;&#36039;&#26009;&#30340;&#24773;&#22659;&#12290;</p></li><li><p>AI Agent &#21063;&#20687;&#26159;&#12300;&#33021;&#36305;&#33151;&#12289;&#33021;&#24171;&#20320;&#36774;&#20107;&#30340;&#23567;&#31192;&#26360;&#12301;&#65292;&#26681;&#25818;&#20320;&#25552;&#20379;&#30340;&#29305;&#23450;&#24037;&#20855;&#25110; API&#65292;&#35299;&#27770;&#20855;&#39636;&#12289;&#21363;&#26178;&#30340;&#21839;&#38988;&#12290;</p></li></ul><h3>AI Agent&#30340;&#38480;&#21046;</h3><p>AI Agent &#38754;&#33256;&#30340;&#31532;&#19968;&#20491;&#38480;&#21046;&#26159;&#12300;&#36890;&#29992;&#24615;&#12301;&#30340;&#21839;&#38988;&#12290;&#38568;&#33879;&#36234;&#20358;&#36234;&#22810;&#30340; LLM &#24288;&#21830;&#65288;&#22914; Claude, Gemini, OpenAI &#31561;&#65289;&#28263;&#29694;&#65292;&#22522;&#26044;&#36889;&#20123;&#27169;&#22411;&#25171;&#36896;&#30340;&#25033;&#29992;&#31243;&#24335;&#20063;&#36234;&#20358;&#36234;&#22810;&#20803;&#65306;&#24478;&#26368;&#24120;&#35211;&#30340; Chatbot&#65288;&#22914; Claude&#12289;ChatGPT&#65289;&#21040;&#26356;&#35079;&#38620;&#30340;&#26234;&#33021; IDE&#65288;&#22914; Cursor&#65289;&#65292;&#25105;&#20497;&#24456;&#38627;&#30906;&#20445;&#33258;&#24049;&#38283;&#30332;&#30340; AI Agent &#33021;&#22816;&#28961;&#32299;&#22320;&#22312;&#21508;&#31278;&#19981;&#21516;&#30340;&#24179;&#21488;&#25110;&#29872;&#22659;&#20013;&#36939;&#20316;&#12290;&#29702;&#24819;&#19978;&#65292;&#25105;&#20497;&#24076;&#26395;&#26377;&#19968;&#22871;&#27161;&#28310;&#21332;&#35696;&#65292;&#35731;&#21516;&#19968;&#20491; AI Agent &#21487;&#20197;&#36629;&#39686;&#22320;&#33287;&#21508;&#20491;&#24179;&#21488;&#25972;&#21512;&#65292;&#19981;&#24517;&#21463;&#38480;&#26044;&#29305;&#23450;&#30340;&#27169;&#22411;&#25110;&#26381;&#21209;&#20379;&#25033;&#21830;&#12290;</p><p>AI Agent &#38754;&#33256;&#30340;&#31532;&#20108;&#20491;&#38480;&#21046;&#65292;&#26159;&#36942;&#21435;&#30340;&#35373;&#35336;&#24448;&#24448;&#21482;&#36969;&#21512;&#35731;&#12300;&#20154;&#12301;&#20358;&#20351;&#29992;&#12290;&#20351;&#29992;&#32773;&#25351;&#31034;&#19968;&#27493;&#65292;AI Agent &#23601;&#22519;&#34892;&#19968;&#27493;&#65292;&#25110;&#32773;&#24517;&#38920;&#25353;&#29031;&#38928;&#20808;&#23450;&#32681;&#22909;&#30340;&#27969;&#31243;&#25165;&#33021;&#36939;&#20316;&#12290;&#36889;&#27171;&#30340;&#35373;&#35336;&#23565;&#12300;&#27231;&#22120;&#12301;&#65288;&#20363;&#22914;&#29992;&#25142;&#31471;&#25033;&#29992;&#31243;&#24335;&#25110;&#33258;&#21205;&#21270;&#26381;&#21209;&#65289;&#20358;&#35498;&#19981;&#22816;&#24392;&#24615;&#12290;&#25033;&#29992;&#31243;&#24335;&#38627;&#20197;&#26234;&#33021;&#22320;&#26681;&#25818;&#38656;&#27714;&#21205;&#24907;&#36984;&#29992;&#19981;&#21516;&#30340; Agent &#24037;&#20855;&#65292;&#26356;&#21029;&#25552;&#33258;&#21205;&#21028;&#26039;&#20351;&#29992;&#38918;&#24207;&#12290;</p><p>MCP&#65288;Anthropic Model Context Protocol&#65289;&#25552;&#20379;&#20102;&#19968;&#20491;&#27161;&#28310;&#21270;&#30340; Client&#8211;Server &#26550;&#27083;&#65292;&#23436;&#32654;&#22320;&#35299;&#27770;&#20102;&#36889;&#20841;&#20491;&#21839;&#38988;&#12290;&#36879;&#36942; MCP&#65292;&#38283;&#30332;&#32773;&#21482;&#38656;&#23560;&#27880;&#26044;&#35069;&#20316;&#21508;&#33258;&#29544;&#31435;&#30340;&#21151;&#33021;&#27169;&#32068;&#65292;&#36889;&#20123;&#27169;&#32068;&#31281;&#28858; MCP Server&#65292;&#20363;&#22914;&#30332;&#36865; Email&#12289;&#31649;&#29702;&#34892;&#20107;&#26310;&#25110;&#36914;&#34892;&#22294;&#20687;&#36776;&#35672;&#31561;&#29305;&#23450;&#20219;&#21209;&#12290;&#21478;&#19968;&#26041;&#38754;&#65292;&#25972;&#21512; LLM &#30340;&#25033;&#29992;&#31243;&#24335;&#65292;&#20363;&#22914; Claude Chatbot &#25110;&#26234;&#33021; IDE Cursor &#31561;&#65292;&#21063;&#25198;&#28436; MCP Client &#30340;&#35282;&#33394;&#12290;&#36889;&#20123; MCP Client &#33021;&#22816;&#36879;&#36942; MCP Server &#25552;&#20379;&#30340;&#27161;&#28310;&#21270;&#20171;&#38754;&#65292;&#21205;&#24907;&#22320;&#25506;&#32034;&#21644;&#35672;&#21029;&#21487;&#29992;&#30340;&#21151;&#33021;&#33287;&#24037;&#20855;&#65292;&#20006;&#26681;&#25818;&#20351;&#29992;&#32773;&#25110;&#25033;&#29992;&#38656;&#27714;&#26234;&#33021;&#22320;&#25490;&#21015;&#32068;&#21512;&#12289;&#36984;&#25799;&#26368;&#21512;&#36969;&#30340;&#24037;&#20855;&#20358;&#23436;&#25104;&#35079;&#38620;&#20219;&#21209;&#12290;</p><p>&#36879;&#36942;&#36889;&#31278;&#20998;&#24037;&#33287;&#21332;&#20316;&#30340;&#26550;&#27083;&#65292;MCP &#35731; AI Agent &#35722;&#24471;&#26356;&#20855;&#36890;&#29992;&#24615;&#65292;&#20063;&#26356;&#21152;&#23481;&#26131;&#34987;&#27231;&#22120;&#29702;&#35299;&#33287;&#33258;&#21205;&#21270;&#25972;&#21512;&#65292;&#20351;&#24471;&#25972;&#20491; AI &#29983;&#24907;&#22280;&#33021;&#22816;&#26356;&#21152;&#38728;&#27963;&#19988;&#39640;&#25928;&#22320;&#36939;&#20316;&#12290;</p><h3>&#23526;&#38555;&#30340;&#24037;&#20316;&#27969;&#31243;&#65306;</h3><p>&#20551;&#35373;&#20170;&#22825;&#20320;&#36879;&#36942;MCP&#24314;&#31435;&#20102;&#19968;&#20491;AI&#23458;&#26381;&#27231;&#22120;&#20154;&#65292;&#23427;&#25972;&#21512;&#20102;&#20841;&#20491;&#24037;&#20855;&#65306;</p><ol><li><p><strong>&#35330;&#21934;&#26597;&#35426;&#24037;&#20855;</strong>&#65306;&#21487;&#20197;&#36899;&#25509;&#20320;&#30340;&#20844;&#21496;&#36039;&#26009;&#24235;&#65292;&#26681;&#25818;&#23458;&#25142;&#25552;&#20379;&#30340;&#35330;&#21934;&#32232;&#34399;&#26597;&#35426;&#35330;&#21934;&#29376;&#24907;&#12290;</p></li><li><p><strong>&#20778;&#24800;&#21048;&#30332;&#36865;&#24037;&#20855;</strong>&#65306;&#30070;&#23458;&#25142;&#36935;&#21040;&#19981;&#28415;&#24847;&#30340;&#24773;&#27841;&#26178;&#65292;AI&#33021;&#22816;&#21205;&#24907;&#22320;&#27770;&#23450;&#26159;&#21542;&#32102;&#20104;&#25240;&#25187;&#20778;&#24800;&#21048;&#12290;</p></li></ol><div><hr></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gRjt!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F820d6d8b-51be-4263-ace2-992f2f438de7_800x386.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gRjt!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F820d6d8b-51be-4263-ace2-992f2f438de7_800x386.png 424w, https://substackcdn.com/image/fetch/$s_!gRjt!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F820d6d8b-51be-4263-ace2-992f2f438de7_800x386.png 848w, https://substackcdn.com/image/fetch/$s_!gRjt!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F820d6d8b-51be-4263-ace2-992f2f438de7_800x386.png 1272w, https://substackcdn.com/image/fetch/$s_!gRjt!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F820d6d8b-51be-4263-ace2-992f2f438de7_800x386.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!gRjt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F820d6d8b-51be-4263-ace2-992f2f438de7_800x386.png" width="1466" height="708" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/820d6d8b-51be-4263-ace2-992f2f438de7_800x386.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:708,&quot;width&quot;:1466,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!gRjt!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F820d6d8b-51be-4263-ace2-992f2f438de7_800x386.png 424w, https://substackcdn.com/image/fetch/$s_!gRjt!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F820d6d8b-51be-4263-ace2-992f2f438de7_800x386.png 848w, https://substackcdn.com/image/fetch/$s_!gRjt!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F820d6d8b-51be-4263-ace2-992f2f438de7_800x386.png 1272w, https://substackcdn.com/image/fetch/$s_!gRjt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F820d6d8b-51be-4263-ace2-992f2f438de7_800x386.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><ol><li><p>&#19968;&#20301;&#39015;&#23458;&#21521;AI&#23458;&#26381;&#35426;&#21839;&#65306;&#12300;&#25105;&#30340;&#35330;&#21934; #12345 &#24050;&#32147;&#20841;&#36913;&#20102;&#36996;&#27794;&#26377;&#25910;&#21040;&#65292;&#21487;&#20197;&#24171;&#25105;&#26597;&#19968;&#19979;&#21966;&#65311;&#12301;</p></li><li><p>&#27492;&#26178;MCP Client (&#20363;&#22914;Claude Chatbot)&#36879;&#36942;&#20839;&#24314;&#30340;LLM&#20998;&#26512;&#20351;&#29992;&#32773;&#38656;&#27714;&#65292;&#30693;&#36947;&#38656;&#35201;&#36879;&#36942;&#24037;&#20855;&#20358;&#23436;&#25104;&#27492;&#20219;&#21209;&#12290;&#23427;&#21205;&#24907;&#22320;&#21521; MCP Server&#35426;&#21839;&#65306;&#12300;&#30446;&#21069;&#26377;&#21738;&#20123;&#24037;&#20855;&#21487;&#29992;&#65311;&#12301;</p></li><li><p>MCP Server &#22238;&#20659;&#30446;&#21069;&#21487;&#29992;&#30340;&#24037;&#20855;&#28165;&#21934;&#21644;&#20351;&#29992;&#26041;&#24335;&#32102;Client&#12290;</p></li><li><p>AI&#21028;&#26039;&#24460;&#65292;&#27770;&#23450;&#20808;&#21628;&#21483;<strong>&#35330;&#21934;&#26597;&#35426;&#24037;&#20855;</strong>&#65292;&#36879;&#36942;&#23458;&#25142;&#25552;&#20379;&#30340;&#12300;#12345&#12301;&#26597;&#35426;&#35330;&#21934;&#29376;&#24907;&#12290;</p></li></ol><p><strong>&#20551;&#35373;&#35330;&#21934;&#29376;&#24907;&#39023;&#31034;&#65306;&#12300;&#20986;&#36008;&#24310;&#35492;&#65292;&#23578;&#26410;&#23492;&#20986;&#12301;&#12290;</strong></p><ol><li><p>LLM&#29702;&#35299;&#39015;&#23458;&#21487;&#33021;&#26371;&#22240;&#27492;&#19981;&#28415;&#24847;&#65292;&#26044;&#26159;&#21205;&#24907;&#22320;&#27770;&#23450;&#20877;&#36879;&#36942;<strong>&#20778;&#24800;&#21048;&#30332;&#36865;&#24037;&#20855;</strong>&#21521;&#39015;&#23458;&#25552;&#20379;&#19968;&#24373;&#20778;&#24800;&#21048;&#65292;&#20316;&#28858;&#35036;&#20767;&#12290;</p></li><li><p>&#26368;&#32066;AI&#23458;&#26381;&#22238;&#35206;&#39015;&#23458;&#65306;&#12300;&#24744;&#30340;&#35330;&#21934;&#22240;&#28858;&#29289;&#27969;&#24310;&#35492;&#23578;&#26410;&#23492;&#20986;&#65292;&#36896;&#25104;&#24744;&#30340;&#19981;&#20415;&#65292;&#25105;&#20497;&#28145;&#24863;&#25265;&#27465;&#65292;&#29305;&#21029;&#36104;&#36865;&#24744;&#19968;&#24373;&#25240;&#25187;&#20778;&#24800;&#21048;&#20197;&#34920;&#27465;&#24847;&#12290;&#12301;</p></li></ol><p><strong>&#20551;&#35373;&#35330;&#21934;&#29376;&#24907;&#39023;&#31034;&#65306;&#12300;&#23578;&#26410;&#20184;&#27454;&#12301;&#12290;</strong></p><ol><li><p>LLM&#29702;&#35299;&#27492;&#24773;&#22659;&#20006;&#38750;&#20986;&#36008;&#21839;&#38988;&#65292;&#32780;&#26159;&#20184;&#27454;&#21839;&#38988;&#65292;&#22240;&#27492;&#19981;&#26371;&#30332;&#36865;&#20778;&#24800;&#21048;&#65292;&#32780;&#26159;&#25552;&#37266;&#39015;&#23458;&#23436;&#25104;&#20184;&#27454;&#12290;</p></li><li><p>&#26368;&#32066;AI&#23458;&#26381;&#22238;&#35206;&#39015;&#23458;&#65306;&#12300;&#24744;&#30340;&#35330;&#21934;&#23578;&#26410;&#23436;&#25104;&#20184;&#27454;&#65292;&#35531;&#24744;&#23436;&#25104;&#20184;&#27454;&#24460;&#65292;&#25105;&#20497;&#23559;&#31435;&#21363;&#28858;&#24744;&#23433;&#25490;&#20986;&#36008;&#65292;&#35613;&#35613;&#65281;&#12301;</p></li></ol><div><hr></div><p>&#22312;&#36889;&#27171;&#30340;&#22580;&#26223;&#20013;&#65292;&#27880;&#24847;&#65292;&#25105;&#20497;&#19981;&#38656;&#35201;&#21435;&#25552;&#20379;&#24773;&#22659;&#21028;&#26039;&#30340;&#37007;&#36655;&#65292;&#21482;&#35201;&#36879;&#36942;MCP Server&#25552;&#20379;&#20841;&#20491;&#24037;&#20855;&#65292;LLM&#33258;&#24049;&#26377;&#36774;&#27861;&#20381;&#29031;&#24773;&#22659;&#21028;&#26039;&#35201;&#20351;&#29992;&#21738;&#20123;&#24037;&#20855;&#35299;&#27770;&#21839;&#38988;&#12290;</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-o50!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77224f73-9b4f-4f79-b83a-05e8dbde139b_800x414.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-o50!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77224f73-9b4f-4f79-b83a-05e8dbde139b_800x414.png 424w, https://substackcdn.com/image/fetch/$s_!-o50!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77224f73-9b4f-4f79-b83a-05e8dbde139b_800x414.png 848w, https://substackcdn.com/image/fetch/$s_!-o50!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77224f73-9b4f-4f79-b83a-05e8dbde139b_800x414.png 1272w, https://substackcdn.com/image/fetch/$s_!-o50!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77224f73-9b4f-4f79-b83a-05e8dbde139b_800x414.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-o50!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77224f73-9b4f-4f79-b83a-05e8dbde139b_800x414.png" width="1394" height="722" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/77224f73-9b4f-4f79-b83a-05e8dbde139b_800x414.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:722,&quot;width&quot;:1394,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!-o50!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77224f73-9b4f-4f79-b83a-05e8dbde139b_800x414.png 424w, https://substackcdn.com/image/fetch/$s_!-o50!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77224f73-9b4f-4f79-b83a-05e8dbde139b_800x414.png 848w, https://substackcdn.com/image/fetch/$s_!-o50!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77224f73-9b4f-4f79-b83a-05e8dbde139b_800x414.png 1272w, https://substackcdn.com/image/fetch/$s_!-o50!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77224f73-9b4f-4f79-b83a-05e8dbde139b_800x414.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>&#24120;&#35211;&#21839;&#38988;</h3><p><strong>Q&#65306;MCP &#26159;&#30001;&#35504;&#38283;&#30332;&#30340;&#65311;</strong><br>A&#65306;MCP &#26159;&#30001; Anthropic &#20844;&#21496;&#25552;&#20986;&#30340;&#19968;&#22871;&#21332;&#35696;&#65292;&#30446;&#30340;&#26159;&#35731; AI &#27169;&#22411;&#33021;&#22816;&#26356;&#38728;&#27963;&#22320;&#33287;&#22806;&#37096;&#24037;&#20855;&#20114;&#21205;&#12290;&#19981;&#36942; MCP &#20006;&#19981;&#38480;&#26044; Anthropic &#33258;&#24049;&#30340; AI &#27169;&#22411;&#65288;&#22914; Claude&#65289;&#65292;&#32780;&#26159;&#38283;&#25918;&#19988;&#29544;&#31435;&#26044;&#29305;&#23450; AI &#24288;&#21830;&#30340;&#27161;&#28310;&#65292;&#22240;&#27492;&#20219;&#20309;&#31526;&#21512; MCP &#30340;&#27169;&#22411;&#37117;&#33021;&#25645;&#37197;&#20351;&#29992;&#12290;</p><p><strong>Q&#65306;MCP &#30340;&#24037;&#20316;&#21407;&#29702;&#26159;&#20160;&#40636;&#65311;</strong><br>A&#65306;MCP &#30340;&#24037;&#20316;&#27969;&#31243;&#22823;&#33268;&#22914;&#19979;&#65306;</p><ol><li><p><strong>MCP Client</strong>&#65288;&#20363;&#22914; Claude&#12289;Goose &#31561;&#65289;&#26371;&#20808;&#21521; <strong>MCP Server</strong> &#30332;&#20986;&#35531;&#27714;&#12290;</p></li><li><p><strong>MCP Server</strong> &#26371;&#22238;&#25033;&#20006;&#21578;&#35380; <strong>Client</strong> &#26377;&#21738;&#20123;&#24037;&#20855;&#65288;tools&#65289;&#12289;&#36039;&#28304;&#65288;resources&#65289;&#21450;&#25552;&#31034;&#35338;&#24687;&#65288;prompts&#65289;&#21487;&#29992;&#12290;</p></li><li><p><strong>MCP Client</strong> &#26681;&#25818; Server &#25552;&#20379;&#30340;&#36039;&#35338;&#65292;&#26234;&#24935;&#22320;&#21028;&#26039;&#35442;&#20351;&#29992;&#21738;&#20123; API&#65292;&#20006;&#27770;&#23450;&#35201;&#25552;&#20379;&#20160;&#40636;&#27171;&#30340;&#36039;&#26009;&#32102; AI &#27169;&#22411;&#65292;&#20197;&#28415;&#36275;&#20351;&#29992;&#32773;&#30340;&#38656;&#27714;&#12290;</p></li></ol><p>&#36879;&#36942;&#36889;&#27171;&#30340;&#26550;&#27083;&#65292;AI &#33021;&#26356;&#38728;&#27963;&#22320;&#33258;&#21205;&#36984;&#25799;&#12289;&#32068;&#21512;&#19981;&#21516;&#30340;&#24037;&#20855;&#20358;&#23436;&#25104;&#20219;&#21209;&#12290;</p><p><strong>Q&#65306;MCP &#26159;&#21542;&#19968;&#23450;&#35201;&#25645;&#37197; Claude &#20351;&#29992;&#65311;</strong><br>A&#65306;&#20006;&#19981;&#19968;&#23450;&#12290;Claude Desktop &#38614;&#28982;&#26159; Anthropic &#38283;&#30332;&#30340;&#19968;&#27454; MCP Client&#65292;&#20294; MCP &#26412;&#36523;&#20006;&#19981;&#20407;&#38480;&#26044;&#20219;&#20309;&#21934;&#19968; AI &#24288;&#21830;&#25110;&#27169;&#22411;&#12290;&#25105;&#20491;&#20154;&#25512;&#34214;&#21487;&#20197;&#22039;&#35430; <a href="https://github.com/block/goose">Goose</a>&#65292;&#23427;&#26159;&#19968;&#20491;&#36817;&#26399;&#38750;&#24120;&#29105;&#38272;&#30340; MCP Client &#23526;&#20316;&#65292;&#19981;&#20294;&#21487;&#20197;&#33258;&#30001;&#25509;&#20837;&#21508;&#23478; LLM API Key&#65292;&#22296;&#38538;&#20063;&#31309;&#26997;&#30332;&#23637; MCP Server &#24066;&#38598;&#65288;Marketplace&#65289;&#65292;&#25552;&#20379;&#35920;&#23500;&#30340;&#24037;&#20855;&#33287;&#36039;&#28304;&#12290;</p><p><strong>Q&#65306;&#20351;&#29992; MCP &#26371;&#26377;&#23433;&#20840;&#39080;&#38570;&#21966;&#65311;&#35442;&#22914;&#20309;&#30906;&#20445; MCP &#30340;&#23433;&#20840;&#24615;&#65311;</strong><br>A&#65306;MCP &#26412;&#36523;&#20006;&#19981;&#26371;&#24375;&#36843;&#23559;&#25935;&#24863;&#36039;&#26009;&#20659;&#36865;&#21040; AI &#27169;&#22411;&#31471;&#65292;&#32780;&#26159;&#23436;&#20840;&#30001; MCP Server &#27770;&#23450;&#36039;&#26009;&#30340;&#20659;&#36664;&#31684;&#22285;&#12290;&#22240;&#27492;&#65292;&#30906;&#20445; MCP &#23433;&#20840;&#24615;&#30340;&#37325;&#40670;&#22312;&#26044;&#22914;&#20309;&#35373;&#23450;&#33287;&#31649;&#29702; MCP Server&#65292;&#20855;&#39636;&#21253;&#21547;&#65306;</p><ul><li><p><strong>&#27402;&#38480;&#25511;&#21046;</strong>&#65306;&#36879;&#36942;&#32048;&#32251;&#30340;&#27402;&#38480;&#31649;&#29702;&#65292;&#22196;&#26684;&#38480;&#21046;&#27599;&#20491; API &#25110;&#24037;&#20855;&#33021;&#23384;&#21462;&#30340;&#36039;&#26009;&#31684;&#22285;&#65292;&#38450;&#27490;&#25935;&#24863;&#36039;&#26009;&#22806;&#27945;&#12290;</p></li><li><p><strong>API &#37329;&#38000;&#31649;&#29702;</strong>&#65306;&#22949;&#21892;&#20445;&#31649; API &#37329;&#38000;&#65292;&#36991;&#20813;&#22806;&#27969;&#65292;&#21516;&#26178;&#25505;&#29992;&#23433;&#20840;&#30340;&#37329;&#38000;&#20786;&#23384;&#33287;&#31649;&#29702;&#27231;&#21046;&#12290;</p></li><li><p><strong>&#30435;&#25511;&#33287;&#35352;&#37636;</strong>&#65306;&#23565;&#27599;&#19968;&#27425; API &#21628;&#21483;&#33287;&#36039;&#26009;&#23384;&#21462;&#36914;&#34892;&#30435;&#25511;&#65292;&#36879;&#36942;&#23450;&#26399;&#30340;&#35352;&#37636;&#33287;&#31293;&#26680;&#65292;&#25484;&#25569;&#31995;&#32113;&#29376;&#24907;&#65292;&#24555;&#36895;&#30332;&#29694;&#33287;&#34389;&#29702;&#28507;&#22312;&#30340;&#23433;&#20840;&#21839;&#38988;&#12290;</p></li></ul><p>&#36879;&#36942;&#20197;&#19978;&#25514;&#26045;&#65292;&#21363;&#21487;&#26377;&#25928;&#25511;&#21046; MCP &#21487;&#33021;&#30340;&#23433;&#20840;&#39080;&#38570;&#65292;&#20445;&#38556;&#36039;&#26009;&#23433;&#20840;&#24615;&#33287;&#31995;&#32113;&#31337;&#23450;&#24615;&#12290;</p><h3>&#22909;&#29992;&#36039;&#28304;</h3><ol><li><p><a href="https://modelcontextprotocol.io/introduction">MCP&#23448;&#26041;&#32178;&#31449;</a></p></li><li><p><a href="https://github.com/modelcontextprotocol/servers">MCP Reference Servers</a></p></li><li><p><a href="https://modelcontextprotocol.io/clients">MCP Clients &#36319;&#27599;&#20491; Client &#30446;&#21069;&#25903;&#25588;&#30340; protocol&#31684;&#22285;</a></p></li><li><p><a href="https://blog.schwannden.com/ai-agent-0-e5-88-b0-1-mcp-server-with-goose/">&#33258;&#24049;&#21205;&#25163;&#20570;&#19968;&#20491;MCP Server&#21543;</a></p></li></ol>]]></content:encoded></item><item><title><![CDATA[Accelerating Text Search in Django Admin for Large Tables]]></title><description><![CDATA[The Challenge of Searching Large Tables in Django Admin]]></description><link>https://schwannden.substack.com/p/accelerating-text-search-in-django-admin-for-large-tables</link><guid isPermaLink="false">https://schwannden.substack.com/p/accelerating-text-search-in-django-admin-for-large-tables</guid><dc:creator><![CDATA[schwannden]]></dc:creator><pubDate>Fri, 22 Nov 2024 09:55:34 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/eec0fd0a-6ac4-458c-a7e2-7059228b94f6_800x800.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!kpjt!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef39cac4-42db-471f-a90d-d09e5bf6fa6f_800x800.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!kpjt!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef39cac4-42db-471f-a90d-d09e5bf6fa6f_800x800.png 424w, https://substackcdn.com/image/fetch/$s_!kpjt!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef39cac4-42db-471f-a90d-d09e5bf6fa6f_800x800.png 848w, https://substackcdn.com/image/fetch/$s_!kpjt!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef39cac4-42db-471f-a90d-d09e5bf6fa6f_800x800.png 1272w, https://substackcdn.com/image/fetch/$s_!kpjt!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef39cac4-42db-471f-a90d-d09e5bf6fa6f_800x800.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!kpjt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef39cac4-42db-471f-a90d-d09e5bf6fa6f_800x800.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ef39cac4-42db-471f-a90d-d09e5bf6fa6f_800x800.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!kpjt!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef39cac4-42db-471f-a90d-d09e5bf6fa6f_800x800.png 424w, https://substackcdn.com/image/fetch/$s_!kpjt!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef39cac4-42db-471f-a90d-d09e5bf6fa6f_800x800.png 848w, https://substackcdn.com/image/fetch/$s_!kpjt!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef39cac4-42db-471f-a90d-d09e5bf6fa6f_800x800.png 1272w, https://substackcdn.com/image/fetch/$s_!kpjt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef39cac4-42db-471f-a90d-d09e5bf6fa6f_800x800.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p><strong>The Challenge of Searching Large Tables in Django Admin</strong></p><p>At Ubiquiti, our systems often deal with enormous datasets&#8202;&#8212;&#8202;tables with over <strong>1 billion records</strong> are not uncommon. While Django Admin is a powerful tool for managing and interacting with data, its default search functionality can become a bottleneck in these scenarios. The built-in icontains query used in search_fields scans every column included, leading to sluggish performance as the dataset scales.</p><p>Consider a user management table with billions of records:</p><ul><li><p>We want <strong>email prefix search</strong> to quickly locate users whose email addresses start with a specific term (e.g., searching for john@domain.com should instantly find matches).</p></li><li><p>We also want <strong>email alias search</strong> that identifies aliases for a given email using regular expressions (e.g., searching for schwannden@gmail.com should also return schwannden+test1@gmail.com and other variants).</p></li><li><p>We also want to search user by UUID, using index would be very fast in support cases when we can obtain user&#8217;s UUID.</p></li></ul><p>Here&#8217;s the catch:</p><ul><li><p>These two searches are <strong>independent use cases by</strong>. A regex-based alias search is computationally expensive and shouldn&#8217;t be performed unless explicitly needed.</p></li><li><p>Django Admin&#8217;s <code>get_search_results</code> function only accepts a single <code>search_term</code>, making it impossible to differentiate between the prefix search and alias search.</p></li></ul><p>To make matters worse, adding multiple search fields by <a href="https://docs.djangoproject.com/en/5.1/howto/overriding-templates/">overwriting the change_list template</a> doesn&#8217;t help because Django passes all queries through the same q=&lt;some search term&gt; parameter, preventing us from distinguishing between search types.</p><h3><strong>The Solution: Adding Custom Filters for Large Tables</strong></h3><p>The Django <code>list_filter</code> feature could be an ideal solution for custom filtering logic. However, its default implementation generates choices for filtering, which is impractical for fields like UUIDs or emails where precomputing billions of choices is infeasible.</p><p>This problem led us to explore a creative approach to <strong>customize the </strong><code>list_filter</code><strong> </strong>component and integrate specialized search functionality directly into the Django Admin interface. After much research, we came across <a href="https://hakibenita.com/how-to-add-a-text-filter-to-django-admin">a post</a> that inspired us to extend Django&#8217;s <code>list_filter</code> to support dynamic input fields for our custom search needs.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!CuxR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89ba784e-0a5a-48f3-a38c-869c5e8d93cc_800x128.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!CuxR!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89ba784e-0a5a-48f3-a38c-869c5e8d93cc_800x128.png 424w, https://substackcdn.com/image/fetch/$s_!CuxR!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89ba784e-0a5a-48f3-a38c-869c5e8d93cc_800x128.png 848w, https://substackcdn.com/image/fetch/$s_!CuxR!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89ba784e-0a5a-48f3-a38c-869c5e8d93cc_800x128.png 1272w, https://substackcdn.com/image/fetch/$s_!CuxR!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89ba784e-0a5a-48f3-a38c-869c5e8d93cc_800x128.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!CuxR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89ba784e-0a5a-48f3-a38c-869c5e8d93cc_800x128.png" width="1648" height="264" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/89ba784e-0a5a-48f3-a38c-869c5e8d93cc_800x128.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:264,&quot;width&quot;:1648,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!CuxR!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89ba784e-0a5a-48f3-a38c-869c5e8d93cc_800x128.png 424w, https://substackcdn.com/image/fetch/$s_!CuxR!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89ba784e-0a5a-48f3-a38c-869c5e8d93cc_800x128.png 848w, https://substackcdn.com/image/fetch/$s_!CuxR!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89ba784e-0a5a-48f3-a38c-869c5e8d93cc_800x128.png 1272w, https://substackcdn.com/image/fetch/$s_!CuxR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89ba784e-0a5a-48f3-a38c-869c5e8d93cc_800x128.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><figcaption class="image-caption">Customized Input&nbsp;Filters</figcaption></figure></div><p>Here&#8217;s how we implemented it:</p><h3><strong>Create a Custom Input Filter Template</strong></h3><p>First, we added a new template to render input fields dynamically in the admin filter area.</p><p>Create the file <code>templates/admin/input_filter.html</code>:</p><pre><code>{% load i18n %} 
 
&lt;details data-filter-title="{{ title }}" open class="filter-item"&gt; 
    &lt;summary&gt; 
      {% blocktranslate with filter_title=title %} {{ filter_title }} {% endblocktranslate %} 
    &lt;/summary&gt; 
    &lt;ul&gt; 
        &lt;li&gt; 
            {% with choices.0 as all_choice %} 
            &lt;form method="GET" action=""&gt; 
                {% for k, v in all_choice.query_parts %} 
                &lt;input type="hidden" name="{{ k }}" value="{{ v }}" /&gt; 
                {% endfor %} 
                &lt;input 
                    type="text" 
                    value="{{ spec.value|default_if_none:'' }}" 
                    name="{{ spec.parameter_name }}"/&gt; 
                {% if not all_choice.selected %} 
                    &lt;strong&gt;&lt;a href="{{ all_choice.query_string }}"&gt;&#10761; {% trans 'Remove' %}&lt;/a&gt;&lt;/strong&gt; 
                {% endif %} 
            &lt;/form&gt; 
            {% endwith %} 
        &lt;/li&gt; 
    &lt;/ul&gt; 
  &lt;/details&gt;</code></pre><p><strong>2. Define an InputFilter Base Class</strong></p><p>Next, we created an InputFilter class to manage custom input fields:</p><pre><code>class InputFilter(SimpleListFilter): 
    """ 
    Base class for filters that take a text input 
    """ 
 
    template = "admin/input_filter.html" 
    parameter_name: str 
    title: str 
 
    def lookups(self, request, model_admin): 
        # Dummy, required to show the filter. 
        return ((),) 
 
    def choices(self, changelist): 
        # Grab only the "all" option. 
        all_choices = super().choices(changelist) 
        try: 
            all_choice = next(all_choices) 
            all_choice["query_parts"] = ( 
                (k, v) for k, v in changelist.get_filters_params().items() if k != self.parameter_name 
            ) 
            yield all_choice 
        except StopIteration: 
            return  # Clean exit when no values remain 
 
    def queryset(self, request, queryset): 
        value = self.sanitized_value() 
        if value is not None: 
            return queryset.filter(**{f"{self.parameter_name}": value}) 
        return queryset 
 
    def sanitized_value(self): 
        value = self.value() 
        if value is None: 
            return None 
        return value.strip()</code></pre><p>With this base filter, we can now inplement our own filters, we can use <code>parameter_name</code> to overwrite the way we wish query is made, and we could overwrite <code>def queryset</code> to provide more customized search query like finding email aliases.</p><pre><code>class UUIDFilter(InputFilter): 
    """ 
    Filter for UUID exact match 
    Ignore filter if the value is not a valid UUID 
    """ 
 
    parameter_name = "id" 
    title = "By User ID" 
 
    def sanitized_value(self): 
        value = super().sanitized_value() 
        if value is None: 
            return None 
        try: 
            return uuid.UUID(value) 
        except ValueError: 
            return None 
 
 
class EmailFilter(InputFilter): 
    """ 
    Filter for email prefix search on the user model itself 
    """ 
 
    parameter_name = "email__startswith" 
    title = "By Email Prefix" 
 
 
class EmailAliasFilter(InputFilter): 
    """ 
    Filter for email alias search 
    For example, "test+alias@example.com" will be matched by "test@example.com" 
    """ 
 
    parameter_name = "email" 
    title = "Search Email Alias" 
 
    def queryset(self, request, queryset): 
        value = self.sanitized_value() 
        if value is not None: 
            split_result = value.split("@") 
            if len(split_result) != 2: 
                return queryset.none() 
            local_part, domain = split_result 
            # Adjust the local part to remove any existing '+' and its suffix 
            local_part = local_part.split("+")[0] 
            regex = rf"{local_part}(\+.+)?@{domain}" 
            return queryset.filter(email__iregex=regex) 
        return queryset</code></pre><p>And in our admin model, we can mix this custom filters with built in field filter like:</p><pre><code>class UserAdmin(admin.ModelAdmin): 
    list_filter = (UUIDFilter, EmailFilter, EmailAliasFilter, "is_legacy")</code></pre><p>The built in choice filter still work on fields with less options.</p><h3>Reusing filter</h3><p>Assuming we have another model with a foreign key poiting to <code>User</code>, and we wish to filter this record by its related user&#8217;s email, we simply need to inherit from the original <code>EmailFilter</code> and change the <code>parameter_name</code></p><pre><code>class ShoppingCartUserFilter(EmailFilter) 
    parameter_name = "user__email" 
 
class CartAdmin(admin.ModelAdmin): 
    list_filter = [ShoppingCartUserFilter, "cart_type"]</code></pre><h3>Bravo: A <strong>Scalable Solution</strong></h3><p>This approach transforms Django Admin&#8217;s search functionality into a powerful, scalable tool capable of handling massive datasets efficiently. By introducing customizable filters, you can address diverse search scenarios without compromising performance. Whether managing billions of records or implementing tailored search logic, this method ensures your admin remains user-friendly and lightning-fast.</p><p><em>&#8220;By wisdom a house is built, and by understanding it is established; by knowledge the rooms are filled with all precious and pleasant riches.&#8221;</em><br><strong>Proverbs 24:3&#8211;4</strong></p>]]></content:encoded></item><item><title><![CDATA[Lessons from the Django Atomic Block Exception Handling]]></title><description><![CDATA[Today we dive into this obscure exception: Unhandled exception: An error occurred in the current transaction. You can&#8217;t execute queries&#8230;]]></description><link>https://schwannden.substack.com/p/lessons-from-the-django-atomic-block-exception-handling</link><guid isPermaLink="false">https://schwannden.substack.com/p/lessons-from-the-django-atomic-block-exception-handling</guid><dc:creator><![CDATA[schwannden]]></dc:creator><pubDate>Fri, 22 Nov 2024 07:19:34 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/96ab696b-96e0-4a14-814f-542e8be15b04_800x653.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!IoZX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98b74307-0462-4517-8fce-7abd60b6f789_800x653.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!IoZX!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98b74307-0462-4517-8fce-7abd60b6f789_800x653.png 424w, https://substackcdn.com/image/fetch/$s_!IoZX!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98b74307-0462-4517-8fce-7abd60b6f789_800x653.png 848w, https://substackcdn.com/image/fetch/$s_!IoZX!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98b74307-0462-4517-8fce-7abd60b6f789_800x653.png 1272w, https://substackcdn.com/image/fetch/$s_!IoZX!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98b74307-0462-4517-8fce-7abd60b6f789_800x653.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!IoZX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98b74307-0462-4517-8fce-7abd60b6f789_800x653.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/98b74307-0462-4517-8fce-7abd60b6f789_800x653.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!IoZX!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98b74307-0462-4517-8fce-7abd60b6f789_800x653.png 424w, https://substackcdn.com/image/fetch/$s_!IoZX!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98b74307-0462-4517-8fce-7abd60b6f789_800x653.png 848w, https://substackcdn.com/image/fetch/$s_!IoZX!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98b74307-0462-4517-8fce-7abd60b6f789_800x653.png 1272w, https://substackcdn.com/image/fetch/$s_!IoZX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98b74307-0462-4517-8fce-7abd60b6f789_800x653.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p>Today we dive into this obscure exception: <strong>Unhandled exception: An error occurred in the current transaction. You can&#8217;t execute queries until the end of the &#8216;atomic&#8217; block.</strong></p><h3>What Happened</h3><p>On a sunny Wednesday, we were excited to release a new version of our service to production, which has around 1k~5k active requests per second. As usual, we monitored for unexpected exceptions that happened, and this popped up a few times: <strong>Unhandled exception: An error occurred in the current transaction. You can&#8217;t execute queries until the end of the &#8216;atomic&#8217; block.</strong></p><p>The root cause was found quickly, a <strong>race condition</strong> existed in how one of the tables arehandled, but what troubled us was the strange message that was not helping at all!</p><h3>Our Original Code</h3><pre><code>class A(Model): 
    id: int 
 
class B(Model): 
    id: int 
    a: OneToOneField(A, on_delete=models.CASCADE) 
 
def send_event(a): 
    print(f"{a.id} deleted") 
 
def delete_a(a): 
    with @transaction.atomic(): 
        try: 
            a.delete() 
            send_event(a) 
        except A.NotFound: 
            pass # it is ok 
        other_db_query()</code></pre><p>Note a race condition could already happen, when 2 requests delete the same instance of A, it is likely that we would send the event twice.</p><h3>A refactor that creates this obscure exception</h3><p>We thought making <code>send_event</code> into a signal could help make the main business more clear:</p><pre><code>class A(Model): 
    id: int 
 
class B(Model): 
    id: int 
    a: OneToOneField(A, on_delete=models.CASCADE) 
 
@receiver(post_delete, sender=B) 
def send_event(b): 
    a = b.a 
    print(f"{a.id} deleted") 
 
def delete_a(a): 
    with @transaction.atomic(): 
        try: 
            a.delete() 
        except A.NotFound: 
            pass # it is ok 
        other_db_query()</code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Dbio!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fcf0ecf-505c-488a-9885-030a9269f541_800x653.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Dbio!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fcf0ecf-505c-488a-9885-030a9269f541_800x653.png 424w, https://substackcdn.com/image/fetch/$s_!Dbio!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fcf0ecf-505c-488a-9885-030a9269f541_800x653.png 848w, https://substackcdn.com/image/fetch/$s_!Dbio!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fcf0ecf-505c-488a-9885-030a9269f541_800x653.png 1272w, https://substackcdn.com/image/fetch/$s_!Dbio!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fcf0ecf-505c-488a-9885-030a9269f541_800x653.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Dbio!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fcf0ecf-505c-488a-9885-030a9269f541_800x653.png" width="975" height="796" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7fcf0ecf-505c-488a-9885-030a9269f541_800x653.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:796,&quot;width&quot;:975,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!Dbio!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fcf0ecf-505c-488a-9885-030a9269f541_800x653.png 424w, https://substackcdn.com/image/fetch/$s_!Dbio!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fcf0ecf-505c-488a-9885-030a9269f541_800x653.png 848w, https://substackcdn.com/image/fetch/$s_!Dbio!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fcf0ecf-505c-488a-9885-030a9269f541_800x653.png 1272w, https://substackcdn.com/image/fetch/$s_!Dbio!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fcf0ecf-505c-488a-9885-030a9269f541_800x653.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>What we expected to happen upon a race condition should be</p><ol><li><p>Request 1 delete A instance, cascade delete B, triggers signal in <code>send_event</code></p></li><li><p>Request 2 delete the same A instance, does the same thing, but finishes first.</p></li><li><p>In request 1, <code>send_event</code> should raise A.NotFound in line <code>a = b.a</code></p></li><li><p>The exception should be handled as disregarded in <code>delete_a</code> try-catch block.</p></li><li><p>Other query should proceed as normal.</p></li></ol><p>But in fact, we receive this <strong>Unhandled exception: An error occurred in the current transaction. You can&#8217;t execute queries until the end of the &#8216;atomic&#8217; block. </strong>And the line of error happens in <code>some other DB query</code>. Why?</p><h3>Django&#8217;s Official Warning</h3><p>In Django&#8217;s official document: <a href="https://docs.djangoproject.com/en/5.1/topics/db/transactions/#controlling-transactions-explicitly">https://docs.djangoproject.com/en/5.1/topics/db/transactions/#controlling-transactions-explicitly</a></p><p>It states that one should <strong>Avoid catching exceptions inside <code>atomic</code>!</strong></p><p>Why? Because inside an atomic session, when a DB query failed, it not only raises exception, but it mark the transaction state as being corrupted. This is so that during the <code>__exit__</code> handling of a <code>with transaction.atomic()</code> context, it can properly rollback the session.</p><p>In our case, the exception raised from <code>a = b.a</code> is caught indeed! but the atomic session state has been marked as corrupted. So in <code>some other DB query</code> , we have this obscure exception which is not helpful, and took us a long time to find the root cause.</p><p>What should be done in this situation?</p><p>Following Django&#8217;s document suggestion, if we are to handle an exception within a atomic session, it is best to create a nested session and try catch it. This update will fix the issue</p><pre><code>@receiver(post_delete, sender=B) 
def send_event(b): 
    try: 
        with transaction.atomic(): 
            a = b.a 
            print(f"{a.id} deleted") 
    except A.NotFound: 
        pass</code></pre><p>Since a NotFound exception is not something that actually breaks the database, we let the inner session handles the rollback (which does nothing), and set the session state back to normal, some that later DB query can be executed normally.</p><h3>Wrapping It All Up: The Art of Taming Transactions</h3><p>In high-concurrency systems, even caught exceptions can corrupt transactions. Django&#8217;s atomic blocks demand careful handling&#8202;&#8212;&#8202;use nested transactions to isolate risks and follow framework warnings. Resilient code isn&#8217;t just functional; it&#8217;s designed for real-world complexity. Remember, every obscure error is a chance to learn and improve.</p><p><em>&#8220;Unless the Lord builds the house, those who build it labor in vain.&#8221;</em></p><p>&#8212; <strong>Psalm 127:1</strong></p>]]></content:encoded></item><item><title><![CDATA[Helm and Kustomization: A Tail of K8S Manifests Management]]></title><description><![CDATA[This article provide examples for you to code and play around for the concepts in helm and kustomize.]]></description><link>https://schwannden.substack.com/p/helm-and-kustomization-a-tail-of-k8s-manifests-management</link><guid isPermaLink="false">https://schwannden.substack.com/p/helm-and-kustomization-a-tail-of-k8s-manifests-management</guid><dc:creator><![CDATA[schwannden]]></dc:creator><pubDate>Wed, 13 Sep 2023 02:53:19 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/da224e6f-ca11-4609-9016-d1c00281d6e9_495x274.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!li3A!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32e21d5a-f39d-4770-a6ab-0126e1a5ec63_495x274.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!li3A!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32e21d5a-f39d-4770-a6ab-0126e1a5ec63_495x274.png 424w, https://substackcdn.com/image/fetch/$s_!li3A!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32e21d5a-f39d-4770-a6ab-0126e1a5ec63_495x274.png 848w, https://substackcdn.com/image/fetch/$s_!li3A!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32e21d5a-f39d-4770-a6ab-0126e1a5ec63_495x274.png 1272w, https://substackcdn.com/image/fetch/$s_!li3A!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32e21d5a-f39d-4770-a6ab-0126e1a5ec63_495x274.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!li3A!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32e21d5a-f39d-4770-a6ab-0126e1a5ec63_495x274.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/32e21d5a-f39d-4770-a6ab-0126e1a5ec63_495x274.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!li3A!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32e21d5a-f39d-4770-a6ab-0126e1a5ec63_495x274.png 424w, https://substackcdn.com/image/fetch/$s_!li3A!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32e21d5a-f39d-4770-a6ab-0126e1a5ec63_495x274.png 848w, https://substackcdn.com/image/fetch/$s_!li3A!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32e21d5a-f39d-4770-a6ab-0126e1a5ec63_495x274.png 1272w, https://substackcdn.com/image/fetch/$s_!li3A!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32e21d5a-f39d-4770-a6ab-0126e1a5ec63_495x274.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p>Helm and Kustomization: A Tail of K8S Manifests Management</p><p>This <a href="https://github.com/schwannden/helm-and-kustomize">repository</a> provide examples for you to code and play around for the concepts in the following articles.</p><p>It uses a demo backend application that would serve different API result when deployed with different environment variables. It uses <a href="https://k3d.io/">k3d</a> to create a k8s cluster in docker (using k3s), and uses <a href="https://k3d.io/">justfile</a> as the project interface. Users who have difficult installing justfile may refer to justfile for the equivalent commands for inteeracting with source codes.</p><div><hr></div><p>The raw Kubernetes YAML declarations are commonly referred to as &#8220;Kubernetes manifests&#8221; or &#8220;Kubernetes YAML manifests.&#8221; These manifests are written in YAML,which is a human-readable data serialization format. The manifests define the desired state of Kubernetes resources such as pods, deployments, services, and more. These manifests serves as the programmer&#8217;s interface to a kubernetes cluster, with various kubernetes controller taking care of the actual resource (compute, storage, network) provisioning and orchastration logic.</p><p>Managing Kubernetes manifests at scale can become challenging, especially when dealing with a large number of YAML files. To address this issue, several tools and techniques have emerged to simplify the management of Kubernetes manifests. Three popular methods are raw YAML, Helm charts, and Kustomize. Let&#8217;s discuss each of them in more detail:</p><div><hr></div><p><strong>Raw YAML</strong>: Raw YAML manifests are the basic way of defining Kubernetes resources. Each resource is defined in a separate YAML file, and you apply them using <code>kubectl apply -f &lt;filename&gt;</code>. While this approach is straightforward, it can become cumbersome to manage and maintain a large number of individual files, especially in complex deployments.</p><p>Refer to <a href="https://github.com/schwannden/helm-and-kustomize/tree/main/raw_yaml">https://github.com/schwannden/helm-and-kustomize/tree/main/raw_yaml</a> to see how to deploy this demo backend with raw YAML files:</p><pre><code>kubectl apply -f deployment.yaml -n demo 
kubectl apply -f service.yaml -n demo 
kubectl apply -f ingress.yaml -n demo</code></pre><p>With raw YAML manifests</p><ol><li><p>It requires manually updating in a complicated yaml file.</p></li><li><p>It requires operaator to update multiple locations if we decide to change some shared values like port, service name, &#8230;etc.</p></li><li><p>Not clear to operator as to which values are expose for customization.</p></li></ol><div><hr></div><p><strong>Helm charts</strong>: Helm is a package manager for Kubernetes that provides a higher-level abstraction for managing applications. It introduces the concept of Helm charts, which are packages containing a set of pre-configured Kubernetes manifests. Helm charts are written in YAML and can encapsulate multiple resources, including deployments, services, config maps, and more. Helm allows you to define templates, values, and hooks, making it easy to parameterize and customize deployments.</p><p>Refer to <a href="https://github.com/schwannden/helm-and-kustomize/tree/main/chart">https://github.com/schwannden/helm-and-kustomize/tree/main/chart</a> to see how to deploy this demo backend with a helm chart.</p><pre><code>helm install demo-backend . --namespace demo --create-namespace</code></pre><p>WIth Helm chart,</p><ol><li><p>It is much more convenient to change common values through <code>values.yaml</code>.</p></li><li><p>Shared configuration can be managed with the helm&#8217;s templating functions.</p></li></ol><p>But still</p><ol><li><p>It is not clear how to solve problems where you need to manage variations of the same application configuration for different environments or teams.</p></li></ol><div><hr></div><p><strong>Kustomize</strong>: Kustomize is another tool for managing Kubernetes manifests. It is included in the <code>kubectl</code> command-line tool since Kubernetes 1.14. Kustomize allows you to customize and manage Kubernetes resources using overlays and patches without modifying the base manifests directly.</p><p>Kustomize uses a directory structure with a base and overlay approach. The base directory contains the original YAML manifests, and overlays contain patches or additional configuration that customize the base resources. Overlays can be used to modify labels, annotations, names, and other fields in the base manifests. With Kustomize, you can manage different configurations and environments without duplicating or modifying the original YAML files.</p><p>Refer to <a href="https://github.com/schwannden/helm-and-kustomize/tree/main/kustomize">https://github.com/schwannden/helm-and-kustomize/tree/main/kustomize</a> to see how to deploy this demo backend with Kustomization</p><pre><code>kubectl apply -n demo -k overlays/demo</code></pre><ol><li><p>Kustomize works well with version control systems and is especially useful in scenarios where you need to manage variations of the same application configuration for different environments or teams.</p></li><li><p>But Kustomize lacks the version control, installation management features provided by helm.</p></li></ol><div><hr></div><p>Indeed, both Kustomize and Helm are valuable tools for managing Kubernetes manifests, and they approach manifest management in different ways. Kustomize focuses on customization and patching of base manifests, while Helm provides a higher-level package management approach.T</p><p>here might be scenario where you need the benefits of both, an example to do so with ArgoCD is documented <a href="https://medium.com/selectfrom/airflow-with-argocd-separating-develop-and-production-environment-with-fully-automated-ci-cd-d8aba7bd0db6">here</a>, but it is a more advanced topic that should not be covered in this article.</p>]]></content:encoded></item><item><title><![CDATA[Deploy Airflow to GKE from ArgoCD on GKE with Workload Identity]]></title><description><![CDATA[How to deploy an Argo CD service on GKE autopilot, and setup an Airflow application to deploy on another GKE cluster using workload&#8230;]]></description><link>https://schwannden.substack.com/p/deploy-airflow-to-gke-from-argocd-on-gke-with-workload-identity</link><guid isPermaLink="false">https://schwannden.substack.com/p/deploy-airflow-to-gke-from-argocd-on-gke-with-workload-identity</guid><dc:creator><![CDATA[schwannden]]></dc:creator><pubDate>Wed, 16 Nov 2022 11:27:49 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/928d56cf-35cd-4369-9572-96b56d2d1619_800x341.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!HL6u!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0453a89c-a1ce-4749-917e-af200c3df30a_800x341.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!HL6u!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0453a89c-a1ce-4749-917e-af200c3df30a_800x341.png 424w, https://substackcdn.com/image/fetch/$s_!HL6u!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0453a89c-a1ce-4749-917e-af200c3df30a_800x341.png 848w, https://substackcdn.com/image/fetch/$s_!HL6u!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0453a89c-a1ce-4749-917e-af200c3df30a_800x341.png 1272w, https://substackcdn.com/image/fetch/$s_!HL6u!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0453a89c-a1ce-4749-917e-af200c3df30a_800x341.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!HL6u!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0453a89c-a1ce-4749-917e-af200c3df30a_800x341.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0453a89c-a1ce-4749-917e-af200c3df30a_800x341.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!HL6u!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0453a89c-a1ce-4749-917e-af200c3df30a_800x341.png 424w, https://substackcdn.com/image/fetch/$s_!HL6u!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0453a89c-a1ce-4749-917e-af200c3df30a_800x341.png 848w, https://substackcdn.com/image/fetch/$s_!HL6u!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0453a89c-a1ce-4749-917e-af200c3df30a_800x341.png 1272w, https://substackcdn.com/image/fetch/$s_!HL6u!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0453a89c-a1ce-4749-917e-af200c3df30a_800x341.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p>In the previous <a href="https://medium.com/selectfrom/airflow-build-host-maintain-your-own-dependencies-with-github-and-docker-hub-for-free-936c811a5283">post</a> I explained how to build and host one&#8217;s own Airflow image, that would be the top right corner of this architectural diagram.</p><p>In <a href="https://medium.com/selectfrom/airflow-with-argocd-separating-develop-and-production-environment-with-fully-automated-ci-cd-d8aba7bd0db6">another post</a> I also explained a bit about deploying Airflow with Argo CD from official helm chart and use customize to tailor for different environments.</p><p>This post aim to run through the whole process again but now entirely on GKE. Having been managing our own airflow for a while, the time for solving hardware and network related issues (not to mention storage class issues) is eating up our devops team resource, and we decide that it is worth the cost to migrate the entire airflow deployment on GKE.</p><p>If you decide to use GKE&#8217;s Composer, you may skip this tutorial completely, as Composer manages Airflow for you. We decide to host our own Airflow to have more granular control over resources and deployment strategy. This is our architectural setup:</p><ol><li><p>Deploy Airflow on <a href="https://cloud.google.com/kubernetes-engine/docs/concepts/autopilot-overview">GKE Autopilot</a>.</p></li><li><p>Setup <a href="https://cloud.google.com/kubernetes-engine/docs/concepts/cluster-architecture">GKE Standard</a> cluster for Airflow deployment.</p></li><li><p>Setup <a href="https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity">Workload Identity</a> so that service accounts in Argo CD can impersonate a service account to operate on Google&#8217;s Kubernetes API.</p></li><li><p>Create Argo CD application with proper cluster configuration, and set the destination to external cluster.</p></li></ol><p>Refer to <a href="https://github.com/schwannden/argocd-operator">https://github.com/schwannden/argocd-operator</a> and <a href="https://github.com/schwannden/airflow-operator">https://github.com/schwannden/airflow-operator</a> to get started, the remaining article is a step by step walk through and explanation.</p><div><hr></div><h3>Deploying Argo CD to GKE Autopilot</h3><p>Here are the things we need to do to be able to deploy Argo CD on GKE</p><p><a href="https://github.com/schwannden/argocd-operator/tree/main/base">https://github.com/schwannden/argocd-operator/tree/main/base</a></p><ol><li><p>disable ssl in <code>argocd-server.yaml</code> as we are deploying on to GKE, which means we are setting up ingress and load balancer service on GKE, therefore handling certificate on GKE load balancer.</p></li><li><p>disable https port in <code>service.yaml</code>, same reason as above.</p></li></ol><p><a href="https://github.com/schwannden/argocd-operator/tree/main/overlays/gke">https://github.com/schwannden/argocd-operator/tree/main/overlays/gke</a></p><p>in overlays/gke, we configure google load balancer (frontend, backend, and ingress) in <code>ingress.yaml</code>.</p><h3>Enable Workload Identity</h3><blockquote><h4><a href="https://github.com/schwannden/argocd-operator/tree/main/workload-identity">argocd-operator/workload-identity at main &#183; schwannden/argocd-operator</a></h4></blockquote><p>The steps to enable workload identity are outlined here (codes are in repo):</p><p>Variable Definitions (reference: <a href="https://github.com/schwannden/argocd-operator/blob/main/workload-identity/.env">https://github.com/schwannden/argocd-operator/blob/main/workload-identity/.env</a>)</p><ol><li><p><code>GSA_PROJECT</code>: the project to create google service account that would grant GKE access, this should be the project hosting your GKE that host the Argo CD application.</p></li><li><p><code>GSA_NAME</code>: the name of the service account that will be grating permission to deploy application to target cluster.</p></li><li><p><code>GKE_PROJECT</code>: the project where you want to allow the Kubernetes service account to impersonate the IAM service account, i.e., the project where you create your GKE cluster and enable is workload identity.</p></li><li><p><code>NAMESPACE</code>: argocd&#8217;s namespace</p></li><li><p><code>TARGET_PROJECT</code>: the project hosting the destination GKE cluster of your Argo CD application.</p></li></ol><p>Steps in detail:</p><ul><li><p>Enable GKE&#8217;s workload identity<br>Enable workload identity for the GKE that deploys Argo CD, refer to <a href="https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity#enable">official document</a>.</p></li><li><p>Create a Google service account</p></li></ul><pre><code>gcloud iam service-accounts create ${GSA_NAME} &#8212; project=${GSA_PROJECT}</code></pre><ul><li><p>Grant the Google service account permission to operate on target GKE</p></li></ul><pre><code>gcloud projects add-iam-policy-binding ${TARGET_PROJECT} \ 
      --member=serviceAccount:${GSA_NAME}@${GSA_PROJECT}.iam.gserviceaccount.com \ 
      --role=roles/container.developer</code></pre><ul><li><p>Grant the argocd kubernetes service accounts to act as google service account</p></li></ul><pre><code>gcloud iam service-accounts add-iam-policy-binding ${GSA_NAME}@${GSA_PROJECT}.iam.gserviceaccount.com \ 
      --role roles/iam.workloadIdentityUser \ 
      --member "serviceAccount:${GKE_PROJECT}.svc.id.goog[${NAMESPACE}/argocd-application-controller]" 
gcloud iam service-accounts add-iam-policy-binding ${GSA_NAME}@${GSA_PROJECT}.iam.gserviceaccount.com \ 
      --role roles/iam.workloadIdentityUser \ 
      --member "serviceAccount:${GKE_PROJECT}.svc.id.goog[${NAMESPACE}/argocd-server]"</code></pre><ul><li><p>Annotate the Kubernetes service account with the email address of the IAM service account</p></li></ul><pre><code>kubectl annotate serviceaccount argocd-application-controller --namespace argocd \ 
      iam.gke.io/gcp-service-account=${GSA_NAME}@${GSA_PROJECT}.iam.gserviceaccount.com 
kubectl annotate serviceaccount argocd-server --namespace argocd \ 
      iam.gke.io/gcp-service-account=${GSA_NAME}@${GSA_PROJECT}.iam.gserviceaccount.com</code></pre><p>Now you are all set! Your Argo CD can now use workload identity, and the 2 service accounts (argocd-server and argocd-application-controller) now have permission to do so.</p><h3>Creating Argo CD Application</h3><blockquote><h4><a href="https://github.com/schwannden/airflow-operator/tree/main/cd/overlays/gke">airflow-operator/cd/overlays/gke at main &#183; schwannden/airflow-operator</a></h4></blockquote><p>With the following application spec you may point destination to your target GKE cluster</p><pre><code>apiVersion: argoproj.io/v1alpha1 
kind: Application 
metadata: 
  name: airflow 
  namespace: argocd 
spec: 
  project: default 
  source: 
    repoURL: 'your-airflow-operator-url' 
    path: overlays/production 
    plugin: 
      name: kustomized-helm 
  destination: 
    namespace: airflow 
    server: https://your-gke-endpoint 
  syncPolicy: 
    automated: 
      prune: false 
      selfHeal: false 
    syncOptions: 
      - CreateNamespace=true</code></pre><p>And you may supply cluster certificcate to Argo CD as follows</p><pre><code>apiVersion: v1 
kind: Secret 
metadata: 
  name: ndpoint-airflow-cluster-secret 
  namespace: argocd 
  labels: 
    argocd.argoproj.io/secret-type: cluster 
type: Opaque 
stringData: 
  name: your-cluster-endpoint 
  server: your-cluster-endpoint 
  config: | 
    { 
      "execProviderConfig": { 
        "command": "argocd-k8s-auth", 
        "args": ["gcp"], 
        "apiVersion": "client.authentication.k8s.io/v1beta1" 
      }, 
      "tlsClientConfig": { 
        "insecure": false, 
        "caData": "your-cluster-certificate-in-base64" 
      } 
    }</code></pre><div><hr></div><p>Congratulations! You should be able to host your own Airflow!</p><blockquote><p>And let us not grow weary of doing good, for in due season we will reap, if we do not give up.</p></blockquote><p>Happy Hacking!</p>]]></content:encoded></item><item><title><![CDATA[Hosting Your Own Helm Chart on GitHub with Chart Releaser]]></title><description><![CDATA[Our favorite time to solve devops issue with opensource solution have come again, yay~]]></description><link>https://schwannden.substack.com/p/hosting-your-own-helm-chart-on-github-with-chart-releaser</link><guid isPermaLink="false">https://schwannden.substack.com/p/hosting-your-own-helm-chart-on-github-with-chart-releaser</guid><dc:creator><![CDATA[schwannden]]></dc:creator><pubDate>Fri, 29 Jul 2022 06:51:11 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/8a9c5145-fdc8-442a-a3ed-762a96b207d0_755x301.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Duwr!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee0ae0f8-1234-44dd-9210-22bdb57fd278_755x301.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Duwr!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee0ae0f8-1234-44dd-9210-22bdb57fd278_755x301.png 424w, https://substackcdn.com/image/fetch/$s_!Duwr!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee0ae0f8-1234-44dd-9210-22bdb57fd278_755x301.png 848w, https://substackcdn.com/image/fetch/$s_!Duwr!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee0ae0f8-1234-44dd-9210-22bdb57fd278_755x301.png 1272w, https://substackcdn.com/image/fetch/$s_!Duwr!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee0ae0f8-1234-44dd-9210-22bdb57fd278_755x301.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Duwr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee0ae0f8-1234-44dd-9210-22bdb57fd278_755x301.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ee0ae0f8-1234-44dd-9210-22bdb57fd278_755x301.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Duwr!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee0ae0f8-1234-44dd-9210-22bdb57fd278_755x301.png 424w, https://substackcdn.com/image/fetch/$s_!Duwr!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee0ae0f8-1234-44dd-9210-22bdb57fd278_755x301.png 848w, https://substackcdn.com/image/fetch/$s_!Duwr!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee0ae0f8-1234-44dd-9210-22bdb57fd278_755x301.png 1272w, https://substackcdn.com/image/fetch/$s_!Duwr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee0ae0f8-1234-44dd-9210-22bdb57fd278_755x301.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p>Our favorite time to solve devops issue with opensource solution have come again, yay~</p><p>MoBagel&#8217;s 8ndpoint is a platform with multiple micro services to help store owners make better decision about restock, ad performance, customer segments, and repurchase recommendations. Though the platform consists of multiple services, they share very similar deployment technology. For example, we always use Fastapi for backend, and our frontend app is served very similarly, and database technologies are very similar as well.</p><p>Helm is a great tool for managing Kubernetes deployment, says their official website:</p><blockquote><p>Helm is the best way to find, share, and use software built for <a href="https://kubernetes.io/">Kubernetes</a></p></blockquote><p>Helms takes care of the following issues when managing a Kubernetes application:</p><ol><li><p>yaml file template language and generation.</p></li><li><p>separating deployment spec (under templates) and configuration (values.yaml).</p></li><li><p>version control: chart&#8217;s version control and deployment version control.</p></li><li><p>deployment operation: install, uninstall, upgrade, rollback, status check, &#8230;etc.</p></li></ol><p>With Helm and the various services that we need to manage, a way to manage and collaborate on Helm chart is necessary. Let me briefly talk about our Helm Chart structure, and then shares about how we build our public and open sourced helm chart repository.</p><h3>How MoBagel Structure Helm Charts</h3><p>As we mentioned, a lot of our micro services share common deployment structure, just different parameters. So we create a public <a href="https://github.com/MoBagel/charts">Helm chart repository for MoBagel</a>, and host all common charts on this repository.</p><p>Developers can use common charts by adding our repository to helmhelm repo add mobagel <a href="https://mobagel.github.io/charts">https://mobagel.github.io/charts</a></p><p>Since these are generic charts, all information are non-sensitive, we can open source this project. As for our deployment, we will be version controlling all deployment parameters, so we need to use private git repository.</p><p>To know more about how to create a helm chart, refer to the well written <a href="https://helm.sh/docs/chart_template_guide/getting_started/">official document</a>. Here we briefly mention how we use the released public chart in our private deployment charts:</p><ol><li><p>In <code>Chart.yaml</code> add public chart as a <a href="https://helm.sh/docs/helm/helm_dependency/">dependency</a>.</p></li><li><p>Before chart is deployed, run <code>helm dependency build</code>. The parent chart will be installed as a sub chart under the <code>charts</code> folder.</p></li><li><p>Since dependencies become sub charts, we simply overwrite chart values in <code>values.yaml</code> file, but with a added nesting level.# in Chart.yaml<br>dependencies:<br>- name: fastapi<br>&nbsp;version: "0.1.0"<br>&nbsp;repository: "https://mobagel.github.io/charts"# In values.yaml<br>fastapi:<br>&nbsp;ingress:<br>&nbsp; &nbsp;enabled: true<br>&nbsp; &nbsp;hosts:<br>&nbsp; &nbsp; &nbsp;- host: some-custom-host.com<br>&nbsp; &nbsp; &nbsp; &nbsp;paths:<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;- path: /api<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;pathType: Prefix<br>... (put the value override for value.yaml for fastapi chart)</p></li></ol><h3>Create Chart Repository Hosting on Github</h3><p>After understanding how chart dependencies are set up, we still need to create our own repository hosting. The official document introduces <a href="https://helm.sh/docs/topics/chart_repository/">several ways</a> of doing so, and I find hosting it on GitHub to be the most convenient.</p><ol><li><p>create a repository under your organization, you may name it <code>charts</code> or <code>helm-charts</code> , since this repository will be hosting all your public, shareable charts.</p></li><li><p>Give it a README.md, and create 2 branch: <code>main</code> and <code>gh-pages</code></p></li><li><p>Go to repository&#8217;s Setting &gt; Pages, enable Github Pages and set Source to Deploy from a branch, then set branch to <code>gh-pages</code> .</p></li><li><p>On branch <code>main</code> , create a <code>charts</code> folder, and put your existing helm charts in this folder.</p></li><li><p>On branch <code>main</code> , add a <code>.github/workflows/release.yml</code> file, this file uses chart releaser action to create chart index file and serve it on your github pages.# release.yml<br>name: Release Chartson:<br>&nbsp;push:<br>&nbsp; &nbsp;branches:<br>&nbsp; &nbsp; &nbsp;- mainjobs:<br>&nbsp;release:<br>&nbsp; &nbsp;runs-on: ubuntu-latest<br>&nbsp; &nbsp;steps:<br>&nbsp; &nbsp; &nbsp;- name: Checkout<br>&nbsp; &nbsp; &nbsp; &nbsp;uses: actions/checkout@v2<br>&nbsp; &nbsp; &nbsp; &nbsp;with:<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;fetch-depth: 0- name: Configure Git<br>&nbsp; &nbsp; &nbsp; &nbsp;run: |<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;git config user.name "$GITHUB_ACTOR"<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;git config user.email "$<a href="mailto:GITHUB_ACTOR@users.noreply.github.com">GITHUB_ACTOR@users.noreply.github.com</a>"<br>&nbsp; &nbsp; &nbsp;- name: Install Helm<br>&nbsp; &nbsp; &nbsp; &nbsp;uses: azure/setup-helm@v1<br>&nbsp; &nbsp; &nbsp; &nbsp;with:<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;version: v3.8.1- name: Run chart-releaser<br>&nbsp; &nbsp; &nbsp; &nbsp;uses: helm/chart-releaser-action@v1.4.0<br>&nbsp; &nbsp; &nbsp; &nbsp;env:<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"</p></li></ol><p>In the last step, the chart releaser will perform the following steps automatically for you:</p><ol><li><p>check and compare all charts under <code>charts</code> folder to see if there areany changes.</p></li><li><p>build the charts and release them as GitHub releases (upload chart as artifact) for you.</p></li><li><p>update index.yaml file and serve it on <code>gh-branch</code> page.</p></li></ol><p>After all is setup, people can start adding your chart repository byhelm repo add [org-name] <a href="https://mobagel.github.io/charts">[</a>git page url]</p><p>A full and working example is given here: <a href="https://github.com/MoBagel/charts">https://github.com/MoBagel/charts</a></p><p>And the chart repository can be added ashelm repo add mobagel <a href="https://mobagel.github.io/charts">https://mobagel.github.io/charts</a></p><p>Enjoy having your own helm charts in a few steps, God bless!</p><blockquote><p>A joyful heart is good medicine, but a broken spirit dries up the bones.</p></blockquote><blockquote><p>Proverbs 17:22</p></blockquote>]]></content:encoded></item><item><title><![CDATA[Airflow with ArgoCD, kustomize, and Helm. Introducing CICD for our Data Scientist Team]]></title><description><![CDATA[Deploy Airflow on ArgoCD using Airflow&#8217;s Helm chart, manage deployment with kustomize, fully automated Airflow CICD. Introducing CICD for&#8230;]]></description><link>https://schwannden.substack.com/p/airflow-with-argocd-separating-develop-and-production-environment-with-fully-automated-ci-cd</link><guid isPermaLink="false">https://schwannden.substack.com/p/airflow-with-argocd-separating-develop-and-production-environment-with-fully-automated-ci-cd</guid><dc:creator><![CDATA[schwannden]]></dc:creator><pubDate>Thu, 23 Jun 2022 08:59:41 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/2cd12a50-e296-4999-808e-8a1a94aa9631_800x633.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ZGUh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F928d9340-a29b-4aa1-a885-801c1b58e410_800x633.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ZGUh!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F928d9340-a29b-4aa1-a885-801c1b58e410_800x633.png 424w, https://substackcdn.com/image/fetch/$s_!ZGUh!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F928d9340-a29b-4aa1-a885-801c1b58e410_800x633.png 848w, https://substackcdn.com/image/fetch/$s_!ZGUh!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F928d9340-a29b-4aa1-a885-801c1b58e410_800x633.png 1272w, https://substackcdn.com/image/fetch/$s_!ZGUh!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F928d9340-a29b-4aa1-a885-801c1b58e410_800x633.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ZGUh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F928d9340-a29b-4aa1-a885-801c1b58e410_800x633.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/928d9340-a29b-4aa1-a885-801c1b58e410_800x633.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ZGUh!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F928d9340-a29b-4aa1-a885-801c1b58e410_800x633.png 424w, https://substackcdn.com/image/fetch/$s_!ZGUh!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F928d9340-a29b-4aa1-a885-801c1b58e410_800x633.png 848w, https://substackcdn.com/image/fetch/$s_!ZGUh!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F928d9340-a29b-4aa1-a885-801c1b58e410_800x633.png 1272w, https://substackcdn.com/image/fetch/$s_!ZGUh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F928d9340-a29b-4aa1-a885-801c1b58e410_800x633.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a><figcaption class="image-caption">Airflow ArgoCD full Architecture</figcaption></figure></div><h3>Airflow with ArgoCD, Kustomize, and Helm&#8202;&#8212;&#8202;Introducing CI/CD for Our Data Scientist Team</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!bIbO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b243a21-11f1-4ded-b503-d352bfa5935b_800x533.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!bIbO!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b243a21-11f1-4ded-b503-d352bfa5935b_800x533.jpeg 424w, https://substackcdn.com/image/fetch/$s_!bIbO!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b243a21-11f1-4ded-b503-d352bfa5935b_800x533.jpeg 848w, https://substackcdn.com/image/fetch/$s_!bIbO!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b243a21-11f1-4ded-b503-d352bfa5935b_800x533.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!bIbO!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b243a21-11f1-4ded-b503-d352bfa5935b_800x533.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!bIbO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b243a21-11f1-4ded-b503-d352bfa5935b_800x533.jpeg" width="2000" height="1333" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6b243a21-11f1-4ded-b503-d352bfa5935b_800x533.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1333,&quot;width&quot;:2000,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!bIbO!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b243a21-11f1-4ded-b503-d352bfa5935b_800x533.jpeg 424w, https://substackcdn.com/image/fetch/$s_!bIbO!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b243a21-11f1-4ded-b503-d352bfa5935b_800x533.jpeg 848w, https://substackcdn.com/image/fetch/$s_!bIbO!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b243a21-11f1-4ded-b503-d352bfa5935b_800x533.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!bIbO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b243a21-11f1-4ded-b503-d352bfa5935b_800x533.jpeg 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Photo by <a href="https://unsplash.com/@mjessier?utm_source=medium&amp;utm_medium=referral">Myriam Jessier</a> on&nbsp;<a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure></div><p>Some prerequisites for following this blog post, include:</p><ol><li><p>Basic knowledge of K8s and <a href="https://argo-cd.readthedocs.io/en/stable/">ArgoCD</a>. I recommend <a href="https://www.youtube.com/watch?v=MeU5_k9ssrs&amp;t=2153s&amp;ab_channel=TechWorldwithNana">this video</a> to get a landscape of what ArgoCD is.</p></li><li><p>Basic knowledge of <a href="https://kustomize.io/">Kustomize</a>.</p></li></ol><p>Here is the <a href="https://github.com/schwannden/airflow-argocd-template">repository</a> for deploying Airflow on ArgoCD.</p><p>Before you start using the repository, you need to have:</p><ol><li><p>A running K8s cluster. If you want to get a light-weight K3s cluster up and running, you can check out this <a href="https://medium.com/p/eea1f71175d0">online tutorial</a>.</p></li><li><p>If your use case requires a <a href="https://blog.schwannden.com/airflow-build-host-maintain-your-own-dependencies-with-github-and-docker-hub-for-free/">customized Airflow image</a>, that is outside the scope of this article.</p></li></ol><h3>The Benefits of Today&#8217;s Tools</h3><ol><li><p>Why Kustomize?</p></li></ol><p>Airflow comes with its nice helm chart, and our team has been using this helm chart for a while to play with Airflow. However, as we are pushing Airflow to production, we need a way to configure an Airflow cluster for development use, and a cluster for production use.</p><p>We hope the 2 clusters can accept different configurations on ingress routes, passwords, worker replica, &#8230; etc. Using 2 <code>values.yaml</code> files doesn&#8217;t seem to be the most elegant approach and not as convenient when we are deploying with ArgoCD. Using different branches also creates problems and results in a bunch of merge conflicts over time. Kustomize seems to solve this deployment configuration issue quite well, and forces us to continue this infrastructure as code culture.</p><p>2. Why use ArgoCD?</p><p>ArgoCD&#8217;s declarative gitops enables a K8s native CD workflow. Without the need to store K8s credentials in our pipeline server (either GitHub or GitLab), our credential management becomes a bit easier.</p><h3>Architecture and Scenario</h3><p>Scenario Walk Through:</p><ol><li><p>When a data scientist realizes that he/she needs a new dependency from Airflow, they update Airflow image spec, which triggers an automatic build and push new image to Docker Hub (checkout <a href="https://blog.schwannden.com/airflow-build-host-maintain-your-own-dependencies-with-github-and-docker-hub-for-free/">this post</a> for detail).</p></li><li><p>After they have the new image, they can use this image to continuously develop the dags in their own dags repository and create pr on dags repository&#8217;s develop branch.</p></li><li><p>Before the dag is deployed, data scientists update Airflow ArgoCD repository to point airflow image to the new image version.</p></li><li><p>ArgoCD picks up the update automatically and updates the develop environment&#8217;s Airflow.</p></li><li><p>Data scientist finally merge their code to the develop branch of dags repository.</p></li><li><p>Develop environment&#8217;s Airflow picks up dag change, and we are able to run tests on develop environment.</p></li><li><p>After all tests are done, deploy Airflow to production, and merge dags repository&#8217;s develop into master branch.</p></li></ol><h3>Making Airflow Work on ArgoCD</h3><ol><li><p>ArgoCD doesn&#8217;t work natively with kustomize + Helm chart (i.e., <code>ksutomize --enable-helm</code>), so we need to install a plugin to make yaml file from helm chart. See <a href="https://github.com/schwannden/airflow-argocd-template#readme">README</a> for installation instruction. In the plugin, we basically customized yaml spec generation command by <code>kustomize build --enable-helm</code> command.</p></li><li><p>So the approach we took is, we use Kustomize as the main configuration management tool, so we are translating helm chart to Kustomize. In order to do this, we installed a plugin to ArgoCD. If you know of a more elegant approach, please let me know, your suggestions would be much appreciated.</p></li><li><p>In <code>overlays</code> folder, that&#8217;s where we make Airflow&#8217;s gitSync pull from different branches based on deployment environments.</p></li><li><p><code>base/kustomization</code> is where we translate helm chart&#8217;s job hook related flag to ArgoCD&#8217;s hook. Since we are not deploying this with helm but Kustomize, we need these flags so that ArgoCD know when to start these jobs, and how to handle the hook delete policy.</p></li><li><p>For detailed steps, please visit the repository&#8217;s <a href="https://github.com/schwannden/airflow-argocd-template#readme">README</a> file.</p></li></ol><p>And with that, you&#8217;ve crossed another level to becoming a boss coder. GG! &#128079;</p><p>I hope you found this article instructional and informative. If you have any feedback or queries, please let me know in the comments below. And follow <a href="https://selectfrom.dev/">SelectFrom</a> for more tutorials and guides on topics like <a href="https://selectfrom.dev/tagged/big-data">Big Data</a>, <a href="https://selectfrom.dev/tagged/spark">Spark</a>, and <a href="https://selectfrom.dev/tagged/data-warehouse">data warehousing</a>.</p><div><hr></div><p><strong>The world&#8217;s fastest cloud data warehouse:</strong></p><p>When designing analytics experiences which are consumed by customers in production, even the smallest delays in query response times become critical. Learn how to achieve <strong>sub-second</strong> performance over TBs of data with <strong><a href="https://www.firebolt.io/?v=m">Firebolt</a></strong>.</p>]]></content:encoded></item><item><title><![CDATA[Airflow — Build, Host, Maintain Your Own Dependencies with GitHub and Docker Hub (for free)]]></title><description><![CDATA[build and maintain Airflow images completely open source and free, maintain infrastructure as code, and with good security.]]></description><link>https://schwannden.substack.com/p/airflow-build-host-maintain-your-own-dependencies-with-github-and-docker-hub-for-free</link><guid isPermaLink="false">https://schwannden.substack.com/p/airflow-build-host-maintain-your-own-dependencies-with-github-and-docker-hub-for-free</guid><dc:creator><![CDATA[schwannden]]></dc:creator><pubDate>Thu, 23 Jun 2022 03:52:32 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/71801bb9-08bc-4574-a34f-36a5122b1214_800x500.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!8dDm!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4c86386-3645-402e-a7b4-7c0eb6ab41fd_800x500.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!8dDm!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4c86386-3645-402e-a7b4-7c0eb6ab41fd_800x500.jpeg 424w, https://substackcdn.com/image/fetch/$s_!8dDm!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4c86386-3645-402e-a7b4-7c0eb6ab41fd_800x500.jpeg 848w, https://substackcdn.com/image/fetch/$s_!8dDm!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4c86386-3645-402e-a7b4-7c0eb6ab41fd_800x500.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!8dDm!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4c86386-3645-402e-a7b4-7c0eb6ab41fd_800x500.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!8dDm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4c86386-3645-402e-a7b4-7c0eb6ab41fd_800x500.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b4c86386-3645-402e-a7b4-7c0eb6ab41fd_800x500.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!8dDm!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4c86386-3645-402e-a7b4-7c0eb6ab41fd_800x500.jpeg 424w, https://substackcdn.com/image/fetch/$s_!8dDm!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4c86386-3645-402e-a7b4-7c0eb6ab41fd_800x500.jpeg 848w, https://substackcdn.com/image/fetch/$s_!8dDm!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4c86386-3645-402e-a7b4-7c0eb6ab41fd_800x500.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!8dDm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4c86386-3645-402e-a7b4-7c0eb6ab41fd_800x500.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a><figcaption class="image-caption">Photo by <a href="https://unsplash.com/@rubaitulazad?utm_source=medium&amp;utm_medium=referral">Rubaitul Azad</a> on&nbsp;<a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure></div><h3>Airflow&#8202;&#8212;&#8202;Build, Host, Maintain Your Own Dependencies with GitHub and Docker Hub (for Free)</h3><p>In Mobagel, we use Airflow to run various data processing tasks, and as <a href="https://airflow.apache.org/docs/docker-stack/build.html#why-customizing-the-image">Airflow&#8217;s document</a> suggested, it is highly recommended that we maintain our own dependencies in our production environment.</p><blockquote><p>Airflow has more than 60 community managed providers (installable via extras) and some of the default extras/providers installed are not used by everyone, sometimes others extras/providers are needed, sometimes (very often actually) you need to add your own custom dependencies, packages or even custom providers.</p></blockquote><p>This guide shows you how to use open source community tool to build and maintain your own airflow image.</p><h3>Setup Docker Hub Account</h3><p><a href="https://hub.docker.com/">Docker Hub</a> is a platform that host your docker images for free, so you need to create an account on <a href="https://hub.docker.com/">docker hub</a> first (or ask you colleague to share organization account to you).</p><p>Go to repositories -&gt; Create Repository</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!i3cX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70bf40e4-1f34-4bd1-a134-350ad1a8d724_800x83.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!i3cX!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70bf40e4-1f34-4bd1-a134-350ad1a8d724_800x83.png 424w, https://substackcdn.com/image/fetch/$s_!i3cX!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70bf40e4-1f34-4bd1-a134-350ad1a8d724_800x83.png 848w, https://substackcdn.com/image/fetch/$s_!i3cX!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70bf40e4-1f34-4bd1-a134-350ad1a8d724_800x83.png 1272w, https://substackcdn.com/image/fetch/$s_!i3cX!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70bf40e4-1f34-4bd1-a134-350ad1a8d724_800x83.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!i3cX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70bf40e4-1f34-4bd1-a134-350ad1a8d724_800x83.png" width="1284" height="134" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/70bf40e4-1f34-4bd1-a134-350ad1a8d724_800x83.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:134,&quot;width&quot;:1284,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!i3cX!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70bf40e4-1f34-4bd1-a134-350ad1a8d724_800x83.png 424w, https://substackcdn.com/image/fetch/$s_!i3cX!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70bf40e4-1f34-4bd1-a134-350ad1a8d724_800x83.png 848w, https://substackcdn.com/image/fetch/$s_!i3cX!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70bf40e4-1f34-4bd1-a134-350ad1a8d724_800x83.png 1272w, https://substackcdn.com/image/fetch/$s_!i3cX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70bf40e4-1f34-4bd1-a134-350ad1a8d724_800x83.png 1456w" sizes="100vw"></picture><div></div></div></a></figure></div><p>You may name your repository <code>airflow</code>, because image name under different namespace will not conflict with each other.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!TAQn!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc9805de-c019-4001-ad02-1cdf2d77e6f9_800x441.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!TAQn!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc9805de-c019-4001-ad02-1cdf2d77e6f9_800x441.png 424w, https://substackcdn.com/image/fetch/$s_!TAQn!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc9805de-c019-4001-ad02-1cdf2d77e6f9_800x441.png 848w, https://substackcdn.com/image/fetch/$s_!TAQn!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc9805de-c019-4001-ad02-1cdf2d77e6f9_800x441.png 1272w, https://substackcdn.com/image/fetch/$s_!TAQn!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc9805de-c019-4001-ad02-1cdf2d77e6f9_800x441.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!TAQn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc9805de-c019-4001-ad02-1cdf2d77e6f9_800x441.png" width="803" height="443" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/dc9805de-c019-4001-ad02-1cdf2d77e6f9_800x441.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:443,&quot;width&quot;:803,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!TAQn!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc9805de-c019-4001-ad02-1cdf2d77e6f9_800x441.png 424w, https://substackcdn.com/image/fetch/$s_!TAQn!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc9805de-c019-4001-ad02-1cdf2d77e6f9_800x441.png 848w, https://substackcdn.com/image/fetch/$s_!TAQn!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc9805de-c019-4001-ad02-1cdf2d77e6f9_800x441.png 1272w, https://substackcdn.com/image/fetch/$s_!TAQn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc9805de-c019-4001-ad02-1cdf2d77e6f9_800x441.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Setup Airflow Build Pipeline</h3><p>Now, you may go to <a href="https://github.com/apache/airflow">airflow&#8217;s official repository</a>, and fork the official repository to your organization/account.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!lhhy!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73b1a23b-8455-4271-8beb-5cd87c808ba9_767x470.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!lhhy!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73b1a23b-8455-4271-8beb-5cd87c808ba9_767x470.png 424w, https://substackcdn.com/image/fetch/$s_!lhhy!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73b1a23b-8455-4271-8beb-5cd87c808ba9_767x470.png 848w, https://substackcdn.com/image/fetch/$s_!lhhy!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73b1a23b-8455-4271-8beb-5cd87c808ba9_767x470.png 1272w, https://substackcdn.com/image/fetch/$s_!lhhy!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73b1a23b-8455-4271-8beb-5cd87c808ba9_767x470.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!lhhy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73b1a23b-8455-4271-8beb-5cd87c808ba9_767x470.png" width="767" height="470" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/73b1a23b-8455-4271-8beb-5cd87c808ba9_767x470.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:470,&quot;width&quot;:767,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!lhhy!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73b1a23b-8455-4271-8beb-5cd87c808ba9_767x470.png 424w, https://substackcdn.com/image/fetch/$s_!lhhy!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73b1a23b-8455-4271-8beb-5cd87c808ba9_767x470.png 848w, https://substackcdn.com/image/fetch/$s_!lhhy!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73b1a23b-8455-4271-8beb-5cd87c808ba9_767x470.png 1272w, https://substackcdn.com/image/fetch/$s_!lhhy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73b1a23b-8455-4271-8beb-5cd87c808ba9_767x470.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Now, you want to add your own build pipeline file. I recommend you create a branch from the desired version first, and do not commit on the original airflow branch. This just makes future pulls from <code>upstream</code> easier. In our example, let&#8217;s call it <code>custom-release</code> branch, and branch if from <code>v2&#8211;3-stable</code>.git clone [your_forked_repo]<br>git checkout <code>v2&#8211;3-stable git checkout -b custom-release git push -u origin custom-release</code></p><p>Now we may add our workflow file <code>.github/workflows/cd.yaml</code> (<a href="https://github.com/MoBagel/airflow/blob/mobagel-release/.github/workflows/cd.yaml">example file</a>).name: Publish Docker image<br><br>on:<br>&nbsp;release:<br>&nbsp; &nbsp;types: [published]<br><br>jobs:<br>&nbsp;push_to_registry:<br>&nbsp; &nbsp;name: Push Docker image to Docker Hub<br>&nbsp; &nbsp;runs-on: ubuntu-latest<br>&nbsp; &nbsp;steps:<br>&nbsp; &nbsp; &nbsp;- name: Check out the repo<br>&nbsp; &nbsp; &nbsp; &nbsp;uses: actions/checkout@v3<br><br>&nbsp; &nbsp; &nbsp;- name: Log in to Docker Hub<br>&nbsp; &nbsp; &nbsp; &nbsp;uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9<br>&nbsp; &nbsp; &nbsp; &nbsp;with:<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;username: ${{ secrets.DOCKER_USERNAME }}<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;password: ${{ secrets.DOCKER_PASSWORD }}<br><br>&nbsp; &nbsp; &nbsp;- name: Extract metadata (tags, labels) for Docker<br>&nbsp; &nbsp; &nbsp; &nbsp;id: meta<br>&nbsp; &nbsp; &nbsp; &nbsp;uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38<br>&nbsp; &nbsp; &nbsp; &nbsp;with:<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;images: mobagel/airflow<br><br>&nbsp; &nbsp; &nbsp;- name: Build and push Docker image<br>&nbsp; &nbsp; &nbsp; &nbsp;uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc<br>&nbsp; &nbsp; &nbsp; &nbsp;env:<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;AIRFLOW_GPL_UNIDECODE: yes<br>&nbsp; &nbsp; &nbsp; &nbsp;with:<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;context: .<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;push: true<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;tags: ${{ steps.meta.outputs.tags }}<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;labels: ${{ steps.meta.outputs.labels }}<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;build-args: |<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;AIRFLOW_VERSION=2.3.2<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;AIRFLOW_EXTRAS=async,celery,cncf.kubernetes,dask,docker,grpc,http,ldap,postgres,redis,statsd,virtualenv<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;PYTHON_BASE_IMAGE=python:3.8-slim-buster<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;ADDITIONAL_PYTHON_DEPS=scikit-learn==1.0.2 slack-sdk==3.14.1 simplejson==3.17.6 glom==22.1.0</p><p>A few notes about this pipeline file:</p><ul><li><p>We set the trigger condition to on publish, so this pipeline is triggered if a release is issued on GitHub page.</p></li><li><p>In the login step, we need to provide our Docker hub login username and password. We put these variable as GitHub secret. You can go to GitHub repository page &gt; Settings &gt; Secrets &gt; Action to add your secret.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!grR8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd97d422-7f20-4779-93e3-bcf02dda700b_800x439.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!grR8!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd97d422-7f20-4779-93e3-bcf02dda700b_800x439.png 424w, https://substackcdn.com/image/fetch/$s_!grR8!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd97d422-7f20-4779-93e3-bcf02dda700b_800x439.png 848w, https://substackcdn.com/image/fetch/$s_!grR8!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd97d422-7f20-4779-93e3-bcf02dda700b_800x439.png 1272w, https://substackcdn.com/image/fetch/$s_!grR8!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd97d422-7f20-4779-93e3-bcf02dda700b_800x439.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!grR8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd97d422-7f20-4779-93e3-bcf02dda700b_800x439.png" width="1126" height="618" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cd97d422-7f20-4779-93e3-bcf02dda700b_800x439.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:618,&quot;width&quot;:1126,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!grR8!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd97d422-7f20-4779-93e3-bcf02dda700b_800x439.png 424w, https://substackcdn.com/image/fetch/$s_!grR8!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd97d422-7f20-4779-93e3-bcf02dda700b_800x439.png 848w, https://substackcdn.com/image/fetch/$s_!grR8!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd97d422-7f20-4779-93e3-bcf02dda700b_800x439.png 1272w, https://substackcdn.com/image/fetch/$s_!grR8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd97d422-7f20-4779-93e3-bcf02dda700b_800x439.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">add secret on&nbsp;GitHub</figcaption></figure></div><ul><li><p>In the step build, we may give supported arguments by Airflow to build our custom image. In our case, we removed a lot of default <code>AIRFLOW_EXTRAS</code> to reduce our image size, fixed <code>AIRFLOW_VERSION</code> and <code>PYTHON_BASE_IMAGE</code>, and added our own <code>ADDITIONAL_PYTHON_DEPS</code> .</p></li></ul><p>After you setup this file, push to your custom-release branch and you are good to go.git add .<br>git commit -m"adding custom build flow"<br>git push</p><h3>Start Your Airflow Build</h3><p>After all this setup, you may inform your team members about the process for creating a pull request on this repository. If anyone wants to make adjustments to dependencies, tell them to update the <code>ADDITIONAL_PYTHON_DEPS</code> variable in <code>.github/workflows/cd.yaml</code> , and push to <code>custom-release</code> branch (or create a pull request depending on your team policy).</p><p>To trigger release, create a release from your repository page &gt; Releases &gt; Draft to draft a new release.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!1Uxt!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd13f1627-952d-46b1-b1b7-9acfbf57907c_800x314.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!1Uxt!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd13f1627-952d-46b1-b1b7-9acfbf57907c_800x314.png 424w, https://substackcdn.com/image/fetch/$s_!1Uxt!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd13f1627-952d-46b1-b1b7-9acfbf57907c_800x314.png 848w, https://substackcdn.com/image/fetch/$s_!1Uxt!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd13f1627-952d-46b1-b1b7-9acfbf57907c_800x314.png 1272w, https://substackcdn.com/image/fetch/$s_!1Uxt!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd13f1627-952d-46b1-b1b7-9acfbf57907c_800x314.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!1Uxt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd13f1627-952d-46b1-b1b7-9acfbf57907c_800x314.png" width="1181" height="463" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d13f1627-952d-46b1-b1b7-9acfbf57907c_800x314.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:463,&quot;width&quot;:1181,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!1Uxt!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd13f1627-952d-46b1-b1b7-9acfbf57907c_800x314.png 424w, https://substackcdn.com/image/fetch/$s_!1Uxt!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd13f1627-952d-46b1-b1b7-9acfbf57907c_800x314.png 848w, https://substackcdn.com/image/fetch/$s_!1Uxt!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd13f1627-952d-46b1-b1b7-9acfbf57907c_800x314.png 1272w, https://substackcdn.com/image/fetch/$s_!1Uxt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd13f1627-952d-46b1-b1b7-9acfbf57907c_800x314.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Creating Release</figcaption></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!wbix!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf4df86e-8e59-4a6c-a0b5-2d77042f510b_800x629.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!wbix!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf4df86e-8e59-4a6c-a0b5-2d77042f510b_800x629.png 424w, https://substackcdn.com/image/fetch/$s_!wbix!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf4df86e-8e59-4a6c-a0b5-2d77042f510b_800x629.png 848w, https://substackcdn.com/image/fetch/$s_!wbix!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf4df86e-8e59-4a6c-a0b5-2d77042f510b_800x629.png 1272w, https://substackcdn.com/image/fetch/$s_!wbix!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf4df86e-8e59-4a6c-a0b5-2d77042f510b_800x629.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!wbix!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf4df86e-8e59-4a6c-a0b5-2d77042f510b_800x629.png" width="945" height="743" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cf4df86e-8e59-4a6c-a0b5-2d77042f510b_800x629.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:743,&quot;width&quot;:945,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!wbix!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf4df86e-8e59-4a6c-a0b5-2d77042f510b_800x629.png 424w, https://substackcdn.com/image/fetch/$s_!wbix!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf4df86e-8e59-4a6c-a0b5-2d77042f510b_800x629.png 848w, https://substackcdn.com/image/fetch/$s_!wbix!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf4df86e-8e59-4a6c-a0b5-2d77042f510b_800x629.png 1272w, https://substackcdn.com/image/fetch/$s_!wbix!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf4df86e-8e59-4a6c-a0b5-2d77042f510b_800x629.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">create release&nbsp;detail</figcaption></figure></div><ol><li><p>In tag, choose a tag of your choice. We used <code>[airflow-version]c[our-version]</code>, so our tag is in the form of <code>2.3.2c1</code> . Note that this tag will also be the tag of your image.</p></li><li><p>Select the <code>custom-release</code> branch as target branch.</p></li><li><p>Fill in the release title and release message and click publish release. Then, your image build will be triggered automatically (you may view build status from repository page &gt; Actions).</p></li></ol><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Al0d!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f7499c0-06ed-4c04-b77f-77cefff8d441_800x170.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Al0d!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f7499c0-06ed-4c04-b77f-77cefff8d441_800x170.png 424w, https://substackcdn.com/image/fetch/$s_!Al0d!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f7499c0-06ed-4c04-b77f-77cefff8d441_800x170.png 848w, https://substackcdn.com/image/fetch/$s_!Al0d!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f7499c0-06ed-4c04-b77f-77cefff8d441_800x170.png 1272w, https://substackcdn.com/image/fetch/$s_!Al0d!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f7499c0-06ed-4c04-b77f-77cefff8d441_800x170.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Al0d!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f7499c0-06ed-4c04-b77f-77cefff8d441_800x170.png" width="1551" height="330" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7f7499c0-06ed-4c04-b77f-77cefff8d441_800x170.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:330,&quot;width&quot;:1551,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!Al0d!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f7499c0-06ed-4c04-b77f-77cefff8d441_800x170.png 424w, https://substackcdn.com/image/fetch/$s_!Al0d!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f7499c0-06ed-4c04-b77f-77cefff8d441_800x170.png 848w, https://substackcdn.com/image/fetch/$s_!Al0d!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f7499c0-06ed-4c04-b77f-77cefff8d441_800x170.png 1272w, https://substackcdn.com/image/fetch/$s_!Al0d!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f7499c0-06ed-4c04-b77f-77cefff8d441_800x170.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>After the image is built, go to your own Docker Hub page to verify that the image with the new tag has been published.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!h6F0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F652874a9-b6a6-4698-a6d0-8ba6ebb1d2e2_800x449.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!h6F0!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F652874a9-b6a6-4698-a6d0-8ba6ebb1d2e2_800x449.png 424w, https://substackcdn.com/image/fetch/$s_!h6F0!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F652874a9-b6a6-4698-a6d0-8ba6ebb1d2e2_800x449.png 848w, https://substackcdn.com/image/fetch/$s_!h6F0!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F652874a9-b6a6-4698-a6d0-8ba6ebb1d2e2_800x449.png 1272w, https://substackcdn.com/image/fetch/$s_!h6F0!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F652874a9-b6a6-4698-a6d0-8ba6ebb1d2e2_800x449.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!h6F0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F652874a9-b6a6-4698-a6d0-8ba6ebb1d2e2_800x449.png" width="1283" height="720" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/652874a9-b6a6-4698-a6d0-8ba6ebb1d2e2_800x449.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:720,&quot;width&quot;:1283,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!h6F0!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F652874a9-b6a6-4698-a6d0-8ba6ebb1d2e2_800x449.png 424w, https://substackcdn.com/image/fetch/$s_!h6F0!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F652874a9-b6a6-4698-a6d0-8ba6ebb1d2e2_800x449.png 848w, https://substackcdn.com/image/fetch/$s_!h6F0!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F652874a9-b6a6-4698-a6d0-8ba6ebb1d2e2_800x449.png 1272w, https://substackcdn.com/image/fetch/$s_!h6F0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F652874a9-b6a6-4698-a6d0-8ba6ebb1d2e2_800x449.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Concluding Remark</h3><ol><li><p>After introducing this workflow, the company&#8217;s infrastructure maintainer no longer need to help the team upgrade Airflow images. And it is very easy to train our team member to execute this flow on their own.</p></li><li><p>Compared to the approach of training team member to actually build and push their own image, this approach is much easier in the sense that publishers don&#8217;t need to install Docker on their own computer, don&#8217;t need to understand Docker build command. Security wise, only the pipeline is allowed to push images. This way we don&#8217;t need to give away credentials to each developer. IaC-wise, each version of our own custom image is version controlled, and traceable.</p></li><li><p>Hosting repositories on GitHub or Docker Hub and using their free service sounds good. But for enterprises, the issue has always been whether we can open source these code. Since Airflow itself is already open sourced, and it is generally not too much of an issue to reveal our Airflow dependencies, the whole solution of building and hosting Airflow is quite acceptable. If your company is customizing Airflow in a way that would reveal business secret or introduce security concerns, you&#8217;ll need to consider a private approach of your own.</p></li></ol><h3>Conclusion</h3><p>And with that, you&#8217;ve crossed another level to becoming a boss coder. GG! &#128079;</p><p>I hope you found this article instructional and informative. If you have any feedback or queries, please let me know in the comments below. And follow <a href="https://selectfrom.dev/">SelectFrom</a> for more tutorials and guides on topics like <a href="https://selectfrom.dev/tagged/big-data">Big Data</a>, <a href="https://selectfrom.dev/tagged/spark">Spark</a>, and <a href="https://selectfrom.dev/tagged/data-warehouse">data warehousing</a>.</p><div><hr></div><p><strong>The world&#8217;s fastest cloud data warehouse:</strong></p><p>When designing analytics experiences which are consumed by customers in production, even the smallest delays in query response times become critical. Learn how to achieve <strong>sub-second</strong> performance over TBs of data with <strong><a href="https://www.firebolt.io/?v=m">Firebolt</a></strong>.</p>]]></content:encoded></item></channel></rss>