1:"$Sreact.fragment" 2:I[56271,["2545","static/chunks/c16f53c3-391a1f5a73546b11.js","2619","static/chunks/2619-eccd988d1e161df3.js","3353","static/chunks/3353-35c19838f8cf7b3b.js","7177","static/chunks/app/layout-cb4429a7e3e88a9f.js"],"ThemeProviders"] 3:I[41402,["2545","static/chunks/c16f53c3-391a1f5a73546b11.js","2619","static/chunks/2619-eccd988d1e161df3.js","3353","static/chunks/3353-35c19838f8cf7b3b.js","7177","static/chunks/app/layout-cb4429a7e3e88a9f.js"],""] 4:I[91367,["2545","static/chunks/c16f53c3-391a1f5a73546b11.js","2619","static/chunks/2619-eccd988d1e161df3.js","3353","static/chunks/3353-35c19838f8cf7b3b.js","7177","static/chunks/app/layout-cb4429a7e3e88a9f.js"],"KBarSearchProvider"] 5:I[52619,["2619","static/chunks/2619-eccd988d1e161df3.js","1356","static/chunks/1356-354ecf51aaaf5203.js","6909","static/chunks/app/blog/%5B...slug%5D/page-d416e20a09ba8ecb.js"],""] 6:I[51350,["2545","static/chunks/c16f53c3-391a1f5a73546b11.js","2619","static/chunks/2619-eccd988d1e161df3.js","3353","static/chunks/3353-35c19838f8cf7b3b.js","7177","static/chunks/app/layout-cb4429a7e3e88a9f.js"],"KBarButton"] 10:I[57150,[],""] :HL["/_next/static/css/29ff447a95e95257.css","style"] :HL["/_next/static/css/77af622156c5e021.css","style"] :HL["/_next/static/css/ddf3d904beac0ed7.css","style"] 0:{"P":null,"b":"FUTryh0Sm2lrVLTvF5oMH","p":"","c":["","blog","port-feature-from-reference"],"i":false,"f":[[["",{"children":["blog",{"children":[["slug","port-feature-from-reference","c"],{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",true],["",["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/29ff447a95e95257.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/_next/static/css/77af622156c5e021.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"lang":"zh-cn","className":"scroll-smooth","suppressHydrationWarning":true,"children":[["$","link",null,{"rel":"apple-touch-icon","sizes":"76x76","href":"/static/favicons/apple-touch-icon.png"}],["$","link",null,{"rel":"icon","type":"image/png","sizes":"32x32","href":"/static/favicons/favicon-32x32.png"}],["$","link",null,{"rel":"icon","type":"image/png","sizes":"16x16","href":"/static/favicons/favicon-16x16.png"}],["$","link",null,{"rel":"manifest","href":"/static/favicons/site.webmanifest"}],["$","link",null,{"rel":"mask-icon","href":"/static/favicons/safari-pinned-tab.svg","color":"#5bbad5"}],["$","meta",null,{"name":"msapplication-TileColor","content":"#4f46e5"}],["$","meta",null,{"name":"theme-color","media":"(prefers-color-scheme: light)","content":"#ffffff"}],["$","meta",null,{"name":"theme-color","media":"(prefers-color-scheme: dark)","content":"#0f172a"}],["$","link",null,{"rel":"alternate","type":"application/rss+xml","href":"/feed.xml"}],["$","body",null,{"className":"bg-white pl-[calc(100vw-100%)] text-black antialiased dark:bg-gray-950 dark:text-white","children":["$","$L2",null,{"children":[["$undefined","$undefined","$undefined",["$","$L3",null,{"async":true,"defer":true,"src":"https://analytics.umami.is/script.js"}],"$undefined","$undefined"],["$","section",null,{"className":"mx-auto max-w-3xl px-4 sm:px-6 xl:max-w-5xl xl:px-0","children":[["$","$L4",null,{"kbarConfig":{"searchDocumentsPath":"/search.json"},"children":[["$","header",null,{"className":"flex items-center w-full bg-white dark:bg-gray-950 justify-between py-10","children":[["$","$L5",null,{"className":"break-words","href":"/","aria-label":"TechLog","children":["$","div",null,{"className":"flex items-center justify-between","children":[["$","div",null,{"className":"mr-3","children":["$","svg",null,{"xmlns":"http://www.w3.org/2000/svg","xmlnsXlink":"http://www.w3.org/1999/xlink","width":53.87,"height":43.61,"viewBox":"344.564 330.278 111.737 91.218","children":[["$","defs",null,{"children":[["$","linearGradient",null,{"id":"logo_svg__b","x1":420.97,"x2":420.97,"y1":331.28,"y2":418.5,"gradientUnits":"userSpaceOnUse","children":[["$","stop",null,{"offset":"0%","style":{"stopColor":"#06b6d4","stopOpacity":1}}],["$","stop",null,{"offset":"100%","style":{"stopColor":"#67e8f9","stopOpacity":1}}]]}],["$","linearGradient",null,{"id":"logo_svg__d","x1":377.89,"x2":377.89,"y1":331.28,"y2":418.5,"gradientUnits":"userSpaceOnUse","children":[["$","stop",null,{"offset":"0%","style":{"stopColor":"#06b6d4","stopOpacity":1}}],["$","stop",null,{"offset":"100%","style":{"stopColor":"#67e8f9","stopOpacity":1}}]]}],["$","path",null,{"id":"logo_svg__a","d":"M453.3 331.28v28.57l-64.66 58.65v-30.08z"}],["$","path",null,{"id":"logo_svg__c","d":"M410.23 331.28v28.57l-64.67 58.65v-30.08z"}]]}],["$","use",null,{"xlinkHref":"#logo_svg__a","fill":"url(#logo_svg__b)"}],["$","use",null,{"xlinkHref":"#logo_svg__c","fill":"url(#logo_svg__d)"}]]}]}],["$","div",null,{"className":"hidden h-6 text-2xl font-semibold sm:block","children":"TechLog"}]]}]}],["$","div",null,{"className":"flex items-center space-x-4 leading-5 sm:-mr-6 sm:space-x-6","children":[["$","div",null,{"className":"no-scrollbar hidden max-w-40 items-center gap-x-4 overflow-x-auto sm:flex md:max-w-72 lg:max-w-96","children":[["$","$L5","博客",{"className":"hover:text-primary-500 dark:hover:text-primary-400 m-1 font-medium text-gray-900 dark:text-gray-100","href":"/blog.html","children":"博客"}],["$","$L5","标签",{"className":"hover:text-primary-500 dark:hover:text-primary-400 m-1 font-medium text-gray-900 dark:text-gray-100","href":"/tags.html","children":"标签"}],["$","$L5","项目",{"className":"hover:text-primary-500 dark:hover:text-primary-400 m-1 font-medium text-gray-900 dark:text-gray-100","href":"/projects.html","children":"项目"}],["$","$L5","关于",{"className":"hover:text-primary-500 dark:hover:text-primary-400 m-1 font-medium text-gray-900 dark:text-gray-100","href":"/about.html","children":"关于"}]]}],["$","$L6",null,{"aria-label":"Search","children":["$","svg",null,{"xmlns":"http://www.w3.org/2000/svg","fill":"none","viewBox":"0 0 24 24","strokeWidth":1.5,"stroke":"currentColor","className":"hover:text-primary-500 dark:hover:text-primary-400 h-6 w-6 text-gray-900 dark:text-gray-100","children":"$L7"}]}],"$L8","$L9"]}]]}],"$La"]}],"$Lb"]}]]}]}]]}]]}],{"children":["blog","$Lc",{"children":[["slug","port-feature-from-reference","c"],"$Ld",{"children":["__PAGE__","$Le",{},null,false]},null,false]},null,false]},null,false],"$Lf",false]],"m":"$undefined","G":["$10",[]],"s":false,"S":true} 11:I[49217,["2545","static/chunks/c16f53c3-391a1f5a73546b11.js","2619","static/chunks/2619-eccd988d1e161df3.js","3353","static/chunks/3353-35c19838f8cf7b3b.js","7177","static/chunks/app/layout-cb4429a7e3e88a9f.js"],"default"] 12:I[394,["2545","static/chunks/c16f53c3-391a1f5a73546b11.js","2619","static/chunks/2619-eccd988d1e161df3.js","3353","static/chunks/3353-35c19838f8cf7b3b.js","7177","static/chunks/app/layout-cb4429a7e3e88a9f.js"],"default"] 13:I[9766,[],""] 14:I[98924,[],""] 16:I[24431,[],"OutletBoundary"] 18:I[15278,[],"AsyncMetadataOutlet"] 1a:I[24431,[],"ViewportBoundary"] 1c:I[24431,[],"MetadataBoundary"] 1d:"$Sreact.suspense" 7:["$","path",null,{"strokeLinecap":"round","strokeLinejoin":"round","d":"M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z"}] 8:["$","$L11",null,{}] 9:["$","$L12",null,{}] a:["$","main",null,{"className":"mb-auto","children":["$","$L13",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L14",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","div",null,{"className":"flex flex-col items-start justify-start md:mt-24 md:flex-row md:items-center md:justify-center md:space-x-6","children":[["$","div",null,{"className":"space-x-2 pt-6 pb-8 md:space-y-5","children":["$","h1",null,{"className":"text-6xl leading-9 font-extrabold tracking-tight text-gray-900 md:border-r-2 md:px-6 md:text-8xl md:leading-14 dark:text-gray-100","children":"404"}]}],["$","div",null,{"className":"max-w-md","children":[["$","p",null,{"className":"mb-4 text-xl leading-normal font-bold md:text-2xl","children":"Sorry we couldn't find this page."}],["$","p",null,{"className":"mb-8","children":"But dont worry, you can find plenty of other things on our homepage."}],["$","$L5",null,{"className":"focus:shadow-outline-blue inline rounded-lg border border-transparent bg-blue-600 px-4 py-2 text-sm leading-5 font-medium text-white shadow-xs transition-colors duration-150 hover:bg-blue-700 focus:outline-hidden dark:hover:bg-blue-500","href":"/","children":"Back to homepage"}]]}]]}],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}] b:["$","footer",null,{"children":["$","div",null,{"className":"mt-16 flex flex-col items-center","children":[["$","div",null,{"className":"mb-3 flex space-x-4","children":[["$","a",null,{"className":"text-sm text-gray-500 transition hover:text-gray-600","target":"_blank","rel":"noopener noreferrer","href":"mailto:birenwenmou@qq.com","children":[["$","span",null,{"className":"sr-only","children":"mail"}],["$","svg",null,{"xmlns":"http://www.w3.org/2000/svg","viewBox":"0 0 20 20","className":"hover:text-primary-500 dark:hover:text-primary-400 fill-current text-gray-700 dark:text-gray-200 h-6 w-6","children":[["$","title",null,{"children":"Mail"}],["$","path",null,{"d":"M2.003 5.884L10 9.882l7.997-3.998A2 2 0 0016 4H4a2 2 0 00-1.997 1.884z"}],["$","path",null,{"d":"M18 8.118l-8 4-8-4V14a2 2 0 002 2h12a2 2 0 002-2V8.118z"}]]}]]}],["$","a",null,{"className":"text-sm text-gray-500 transition hover:text-gray-600","target":"_blank","rel":"noopener noreferrer","href":"https://github.com/joker1point","children":[["$","span",null,{"className":"sr-only","children":"github"}],["$","svg",null,{"xmlns":"http://www.w3.org/2000/svg","viewBox":"0 0 24 24","className":"hover:text-primary-500 dark:hover:text-primary-400 fill-current text-gray-700 dark:text-gray-200 h-6 w-6","children":[["$","title",null,{"children":"GitHub"}],["$","path",null,{"d":"M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"}]]}]]}],null,null,null,null,null,null,null,null,null]}],["$","div",null,{"className":"mb-2 flex space-x-2 text-sm text-gray-500 dark:text-gray-400","children":[["$","div",null,{"children":"眠月"}],["$","div",null,{"children":" • "}],["$","div",null,{"children":"© 2026"}],["$","div",null,{"children":" • "}],["$","$L5",null,{"className":"break-words","href":"/","children":"TechLog"}]]}],["$","div",null,{"className":"mb-8 text-sm text-gray-500 dark:text-gray-400","children":["$","a",null,{"className":"break-words","target":"_blank","rel":"noopener noreferrer","href":"https://github.com/joker1point","children":"GitHub"}]}]]}]}] c:["$","$1","c",{"children":[null,["$","$L13",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L14",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] d:["$","$1","c",{"children":[null,["$","$L13",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L14",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] e:["$","$1","c",{"children":["$L15",[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/ddf3d904beac0ed7.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","$L16",null,{"children":["$L17",["$","$L18",null,{"promise":"$@19"}]]}]]}] f:["$","$1","h",{"children":[null,[["$","$L1a",null,{"children":"$L1b"}],null],["$","$L1c",null,{"children":["$","div",null,{"hidden":true,"children":["$","$1d",null,{"fallback":null,"children":"$L1e"}]}]}]]}] 1f:I[62088,["2619","static/chunks/2619-eccd988d1e161df3.js","1356","static/chunks/1356-354ecf51aaaf5203.js","6909","static/chunks/app/blog/%5B...slug%5D/page-d416e20a09ba8ecb.js"],"default"] 20:I[81356,["2619","static/chunks/2619-eccd988d1e161df3.js","1356","static/chunks/1356-354ecf51aaaf5203.js","6909","static/chunks/app/blog/%5B...slug%5D/page-d416e20a09ba8ecb.js"],"Image"] 15:[["$","script",null,{"type":"application/ld+json","dangerouslySetInnerHTML":{"__html":"{\"@context\":\"https://schema.org\",\"@type\":\"BlogPosting\",\"headline\":\"从参考项目移植功能的标准流程\",\"datePublished\":\"2026-06-20T00:00:00.000Z\",\"dateModified\":\"2026-06-20T00:00:00.000Z\",\"description\":\"将参考项目(如 NextChat、web-tools)的功能移植到当前项目的标准流程:从分析参考项目、确认集成范围、数据模型迁移、后端实现、前端实现、端到端验证到提 PR,覆盖 Python/FastAPI + Streamlit 技术栈的完整实践。\",\"image\":\"/static/images/twitter-card.png\",\"url\":\"https://techlog-18328.app.workbuddy.host/blog/port-feature-from-reference\",\"author\":[{\"@type\":\"Person\",\"name\":\"眠月\"}]}"}}],["$","section",null,{"className":"mx-auto max-w-3xl px-4 sm:px-6 xl:max-w-5xl xl:px-0","children":[["$","$L1f",null,{}],["$","article",null,{"children":["$","div",null,{"className":"xl:divide-y xl:divide-gray-200 xl:dark:divide-gray-700","children":[["$","header",null,{"className":"pt-6 xl:pb-6","children":["$","div",null,{"className":"space-y-1 text-center","children":[["$","dl",null,{"className":"space-y-10","children":[["$","div",null,{"children":[["$","dt",null,{"className":"sr-only","children":"Published on"}],["$","dd",null,{"className":"text-base leading-6 font-medium text-gray-500 dark:text-gray-400","children":["$","time",null,{"dateTime":"2026-06-20T00:00:00.000Z","children":"2026年6月20日星期六"}]}]]}],["$","div",null,{"children":[["$","dt",null,{"className":"sr-only","children":"Word count"}],["$","dd",null,{"className":"text-sm text-gray-400 dark:text-gray-500","children":["1,692"," 字"," · 阅读约 7 分钟"]}]]}]]}],["$","div",null,{"children":["$","h1",null,{"className":"text-3xl leading-9 font-extrabold tracking-tight text-gray-900 sm:text-4xl sm:leading-10 md:text-5xl md:leading-14 dark:text-gray-100","children":"从参考项目移植功能的标准流程"}]}]]}]}],["$","div",null,{"className":"grid-rows-[auto_1fr] divide-y divide-gray-200 pb-8 xl:grid xl:grid-cols-4 xl:gap-x-6 xl:divide-y-0 dark:divide-gray-700","children":[["$","dl",null,{"className":"pt-6 pb-10 xl:border-b xl:border-gray-200 xl:pt-11 xl:dark:border-gray-700","children":[["$","dt",null,{"className":"sr-only","children":"Authors"}],["$","dd",null,{"children":["$","ul",null,{"className":"flex flex-wrap justify-center gap-4 sm:space-x-12 xl:block xl:space-y-8 xl:space-x-0","children":[["$","li","眠月",{"className":"flex items-center space-x-2","children":[["$","$L20",null,{"src":"/static/images/avatar.png","width":38,"height":38,"alt":"avatar","className":"h-10 w-10 rounded-full"}],["$","dl",null,{"className":"text-sm leading-5 font-medium whitespace-nowrap","children":[["$","dt",null,{"className":"sr-only","children":"Name"}],["$","dd",null,{"className":"text-gray-900 dark:text-gray-100","children":"眠月"}],["$","dt",null,{"className":"sr-only","children":"Twitter"}],["$","dd",null,{"children":""}]]}]]}]]}]}]]}],["$","div",null,{"className":"divide-y divide-gray-200 xl:col-span-3 xl:row-span-2 xl:pb-0 dark:divide-gray-700","children":[["$","div",null,{"className":"prose dark:prose-invert max-w-none pt-10 pb-8","children":[["$","h2",null,{"className":"content-header","id":"从参考项目移植功能的标准流程","children":[["$","a",null,{"className":"break-words","href":"#从参考项目移植功能的标准流程","aria-hidden":"true","tabIndex":"-1","children":["$","span",null,{"className":"content-header-link","children":["$","svg",null,{"className":"h-5 linkicon w-5","fill":"currentColor","viewBox":"0 0 20 20","xmlns":"http://www.w3.org/2000/svg","children":[["$","path",null,{"d":"M12.232 4.232a2.5 2.5 0 0 1 3.536 3.536l-1.225 1.224a.75.75 0 0 0 1.061 1.06l1.224-1.224a4 4 0 0 0-5.656-5.656l-3 3a4 4 0 0 0 .225 5.865.75.75 0 0 0 .977-1.138 2.5 2.5 0 0 1-.142-3.667l3-3Z"}],["$","path",null,{"d":"M11.603 7.963a.75.75 0 0 0-.977 1.138 2.5 2.5 0 0 1 .142 3.667l-3 3a2.5 2.5 0 0 1-3.536-3.536l1.225-1.224a.75.75 0 0 0-1.061-1.06l-1.224 1.224a4 4 0 1 0 5.656 5.656l3-3a4 4 0 0 0-.225-5.865Z"}]]}]}]}],"从参考项目移植功能的标准流程"]}],["$","p",null,{"children":"在独立开发过程中,我们经常会遇到这样的场景:发现某个开源项目有个很棒的功能,想移植到自己的项目里。但真正动手时,往往会陷入\"看懂了代码,却不知道从哪里开始\"的困境。"}],["$","p",null,{"children":["经过多次实践,我总结出一套",["$","strong",null,{"children":"从参考项目移植功能的标准流程"}],",适用于 Python/FastAPI + Streamlit 技术栈,也可以推广到其他技术栈。"]}],"$L21","$L22","$L23","$L24","$L25","$L26","$L27","$L28","$L29","$L2a","$L2b","$L2c","$L2d","$L2e","$L2f","$L30","$L31","$L32","$L33","$L34","$L35","$L36","$L37","$L38","$L39","$L3a","$L3b","$L3c","$L3d","$L3e","$L3f","$L40","$L41","$L42","$L43","$L44","$L45","$L46","$L47","$L48","$L49","$L4a","$L4b","$L4c","$L4d","$L4e","$L4f","$L50","$L51","$L52","$L53","$L54","$L55","$L56","$L57","$L58","$L59","$L5a","$L5b","$L5c","$L5d","$L5e","$L5f","$L60","$L61","$L62","$L63","$L64","$L65","$L66","$L67","$L68","$L69","$L6a","$L6b","$L6c","$L6d","$L6e","$L6f","$L70","$L71","$L72","$L73","$L74","$L75","$L76","$L77"]}],"$L78","$L79"]}],"$L7a"]}]]}]}]]}]] 7b:I[16991,["2619","static/chunks/2619-eccd988d1e161df3.js","1356","static/chunks/1356-354ecf51aaaf5203.js","6909","static/chunks/app/blog/%5B...slug%5D/page-d416e20a09ba8ecb.js"],"default"] 7c:I[50528,["2619","static/chunks/2619-eccd988d1e161df3.js","1356","static/chunks/1356-354ecf51aaaf5203.js","6909","static/chunks/app/blog/%5B...slug%5D/page-d416e20a09ba8ecb.js"],"default"] 21:["$","hr",null,{}] 22:["$","h3",null,{"className":"content-header","id":"为什么需要标准流程","children":[["$","a",null,{"className":"break-words","href":"#为什么需要标准流程","aria-hidden":"true","tabIndex":"-1","children":["$","span",null,{"className":"content-header-link","children":["$","svg",null,{"className":"h-5 linkicon w-5","fill":"currentColor","viewBox":"0 0 20 20","xmlns":"http://www.w3.org/2000/svg","children":[["$","path",null,{"d":"M12.232 4.232a2.5 2.5 0 0 1 3.536 3.536l-1.225 1.224a.75.75 0 0 0 1.061 1.06l1.224-1.224a4 4 0 0 0-5.656-5.656l-3 3a4 4 0 0 0 .225 5.865.75.75 0 0 0 .977-1.138 2.5 2.5 0 0 1-.142-3.667l3-3Z"}],["$","path",null,{"d":"M11.603 7.963a.75.75 0 0 0-.977 1.138 2.5 2.5 0 0 1 .142 3.667l-3 3a2.5 2.5 0 0 1-3.536-3.536l1.225-1.224a.75.75 0 0 0-1.061-1.06l-1.224 1.224a4 4 0 1 0 5.656 5.656l3-3a4 4 0 0 0-.225-5.865Z"}]]}]}]}],"为什么需要标准流程?"]}] 23:["$","p",null,{"children":"移植功能不是简单的\"复制粘贴\"。它涉及:"}] 24:["$","ol",null,{"children":[["$","li",null,{"children":[["$","strong",null,{"children":"技术栈差异"}]," — 参考项目可能用 React,而你的项目用 Streamlit"]}],["$","li",null,{"children":[["$","strong",null,{"children":"架构适配"}]," — 参考项目的架构未必适合你的项目"]}],["$","li",null,{"children":[["$","strong",null,{"children":"数据模型迁移"}]," — 涉及数据库的部分需要 careful 处理"]}],["$","li",null,{"children":[["$","strong",null,{"children":"安全考量"}]," — 避免引入敏感信息或安全漏洞"]}]]}] 25:["$","p",null,{"children":"没有标准流程,很容易做多了、做偏了,或者踩一堆不必要的坑。"}] 26:["$","hr",null,{}] 27:["$","h2",null,{"className":"content-header","id":"标准流程7-步法","children":[["$","a",null,{"className":"break-words","href":"#标准流程7-步法","aria-hidden":"true","tabIndex":"-1","children":["$","span",null,{"className":"content-header-link","children":["$","svg",null,{"className":"h-5 linkicon w-5","fill":"currentColor","viewBox":"0 0 20 20","xmlns":"http://www.w3.org/2000/svg","children":[["$","path",null,{"d":"M12.232 4.232a2.5 2.5 0 0 1 3.536 3.536l-1.225 1.224a.75.75 0 0 0 1.061 1.06l1.224-1.224a4 4 0 0 0-5.656-5.656l-3 3a4 4 0 0 0 .225 5.865.75.75 0 0 0 .977-1.138 2.5 2.5 0 0 1-.142-3.667l3-3Z"}],["$","path",null,{"d":"M11.603 7.963a.75.75 0 0 0-.977 1.138 2.5 2.5 0 0 1 .142 3.667l-3 3a2.5 2.5 0 0 1-3.536-3.536l1.225-1.224a.75.75 0 0 0-1.061-1.06l-1.224 1.224a4 4 0 1 0 5.656 5.656l3-3a4 4 0 0 0-.225-5.865Z"}]]}]}]}],"标准流程:7 步法"]}] 28:["$","$L7b",null,{"className":"language-js","children":["$","code",null,{"className":"code-highlight language-js","children":[["$","span",null,{"className":"code-line","children":[["$","span",null,{"className":"token number","children":"1."}]," 分析参考项目\n"]}],["$","span",null,{"className":"code-line","children":" ↓\n"}],["$","span",null,{"className":"code-line","children":[["$","span",null,{"className":"token number","children":"2."}]," 确认集成范围(关键!)\n"]}],["$","span",null,{"className":"code-line","children":" ↓\n"}],["$","span",null,{"className":"code-line","children":[["$","span",null,{"className":"token number","children":"3."}]," 数据模型 ",["$","span",null,{"className":"token operator","children":"+"}]," 迁移设计\n"]}],["$","span",null,{"className":"code-line","children":" ↓\n"}],["$","span",null,{"className":"code-line","children":[["$","span",null,{"className":"token number","children":"4."}]," 后端实现(分层)\n"]}],["$","span",null,{"className":"code-line","children":" ↓\n"}],["$","span",null,{"className":"code-line","children":[["$","span",null,{"className":"token number","children":"5."}]," 前端实现\n"]}],["$","span",null,{"className":"code-line","children":" ↓\n"}],["$","span",null,{"className":"code-line","children":[["$","span",null,{"className":"token number","children":"6."}]," 端到端验证\n"]}],["$","span",null,{"className":"code-line","children":" ↓\n"}],["$","span",null,{"className":"code-line","children":[["$","span",null,{"className":"token number","children":"7."}]," 提 ",["$","span",null,{"className":"token constant","children":"PR"}],"\n"]}]]}]}] 29:["$","hr",null,{}] 2a:["$","h2",null,{"className":"content-header","id":"步骤-1分析参考项目","children":[["$","a",null,{"className":"break-words","href":"#步骤-1分析参考项目","aria-hidden":"true","tabIndex":"-1","children":["$","span",null,{"className":"content-header-link","children":["$","svg",null,{"className":"h-5 linkicon w-5","fill":"currentColor","viewBox":"0 0 20 20","xmlns":"http://www.w3.org/2000/svg","children":[["$","path",null,{"d":"M12.232 4.232a2.5 2.5 0 0 1 3.536 3.536l-1.225 1.224a.75.75 0 0 0 1.061 1.06l1.224-1.224a4 4 0 0 0-5.656-5.656l-3 3a4 4 0 0 0 .225 5.865.75.75 0 0 0 .977-1.138 2.5 2.5 0 0 1-.142-3.667l3-3Z"}],["$","path",null,{"d":"M11.603 7.963a.75.75 0 0 0-.977 1.138 2.5 2.5 0 0 1 .142 3.667l-3 3a2.5 2.5 0 0 1-3.536-3.536l1.225-1.224a.75.75 0 0 0-1.061-1.06l-1.224 1.224a4 4 0 1 0 5.656 5.656l3-3a4 4 0 0 0-.225-5.865Z"}]]}]}]}],"步骤 1:分析参考项目"]}] 2b:["$","p",null,{"children":[["$","strong",null,{"children":"目标"}],":理解参考项目的功能和技术栈。"]}] 2c:["$","p",null,{"children":[["$","strong",null,{"children":"具体做法"}],":"]}] 2d:["$","ol",null,{"children":[["$","li",null,{"children":["用 ",["$","code",null,{"children":"WebFetch"}]," 抓取参考项目的 README / 关键源码文件"]}],["$","li",null,{"children":"识别可移植的核心能力(如:会话管理、API 测试面板、设置页)"}],["$","li",null,{"children":"记录参考项目的技术栈(React/Vite/Next.js 等)与当前项目的差异"}]]}] 2e:["$","p",null,{"children":[["$","strong",null,{"children":"输出"}],":一份简单的功能清单,列出\"要移植什么\"。"]}] 2f:["$","hr",null,{}] 30:["$","h2",null,{"className":"content-header","id":"步骤-2确认集成范围关键","children":[["$","a",null,{"className":"break-words","href":"#步骤-2确认集成范围关键","aria-hidden":"true","tabIndex":"-1","children":["$","span",null,{"className":"content-header-link","children":["$","svg",null,{"className":"h-5 linkicon w-5","fill":"currentColor","viewBox":"0 0 20 20","xmlns":"http://www.w3.org/2000/svg","children":[["$","path",null,{"d":"M12.232 4.232a2.5 2.5 0 0 1 3.536 3.536l-1.225 1.224a.75.75 0 0 0 1.061 1.06l1.224-1.224a4 4 0 0 0-5.656-5.656l-3 3a4 4 0 0 0 .225 5.865.75.75 0 0 0 .977-1.138 2.5 2.5 0 0 1-.142-3.667l3-3Z"}],["$","path",null,{"d":"M11.603 7.963a.75.75 0 0 0-.977 1.138 2.5 2.5 0 0 1 .142 3.667l-3 3a2.5 2.5 0 0 1-3.536-3.536l1.225-1.224a.75.75 0 0 0-1.061-1.06l-1.224 1.224a4 4 0 1 0 5.656 5.656l3-3a4 4 0 0 0-.225-5.865Z"}]]}]}]}],"步骤 2:确认集成范围(关键!)"]}] 31:["$","blockquote",null,{"children":["$","p",null,{"children":["$","strong",null,{"children":"不要跳过这一步直接写代码,否则容易做多了或做偏了。"}]}]}] 32:["$","p",null,{"children":"用提问的方式与自己(或团队成员)对齐:"}] 33:["$","div",null,{"className":"w-full overflow-x-auto","children":["$","table",null,{"children":[["$","thead",null,{"children":["$","tr",null,{"children":[["$","th",null,{"children":"问题"}],["$","th",null,{"children":"选项"}]]}]}],["$","tbody",null,{"children":[["$","tr",null,{"children":[["$","td",null,{"children":["$","strong",null,{"children":"集成方式"}]}],["$","td",null,{"children":"后端端口 + 前端页面 / 完整移植原版 UI / 仅后端能力"}]]}],["$","tr",null,{"children":[["$","td",null,{"children":["$","strong",null,{"children":"核心特性"}]}],["$","td",null,{"children":"多选需要的能力(如 models 列表、流式延迟、探针)"}]]}],["$","tr",null,{"children":[["$","td",null,{"children":["$","strong",null,{"children":"配置继承"}]}],["$","td",null,{"children":"是否从现有设置页继承 provider/API key"}]]}]]}]]}]}] 34:["$","p",null,{"children":[["$","strong",null,{"children":"为什么这步关键?"}]," 因为参考项目的功能可能很丰富,但你不一定都需要。明确范围可以避免做无用功。"]}] 35:["$","hr",null,{}] 36:["$","h2",null,{"className":"content-header","id":"步骤-3数据模型--迁移设计","children":[["$","a",null,{"className":"break-words","href":"#步骤-3数据模型--迁移设计","aria-hidden":"true","tabIndex":"-1","children":["$","span",null,{"className":"content-header-link","children":["$","svg",null,{"className":"h-5 linkicon w-5","fill":"currentColor","viewBox":"0 0 20 20","xmlns":"http://www.w3.org/2000/svg","children":[["$","path",null,{"d":"M12.232 4.232a2.5 2.5 0 0 1 3.536 3.536l-1.225 1.224a.75.75 0 0 0 1.061 1.06l1.224-1.224a4 4 0 0 0-5.656-5.656l-3 3a4 4 0 0 0 .225 5.865.75.75 0 0 0 .977-1.138 2.5 2.5 0 0 1-.142-3.667l3-3Z"}],["$","path",null,{"d":"M11.603 7.963a.75.75 0 0 0-.977 1.138 2.5 2.5 0 0 1 .142 3.667l-3 3a2.5 2.5 0 0 1-3.536-3.536l1.225-1.224a.75.75 0 0 0-1.061-1.06l-1.224 1.224a4 4 0 1 0 5.656 5.656l3-3a4 4 0 0 0-.225-5.865Z"}]]}]}]}],"步骤 3:数据模型 + 迁移设计"]}] 37:["$","p",null,{"children":"如果功能涉及持久化(如会话管理),这步尤其重要。"}] 38:["$","h3",null,{"className":"content-header","id":"31-新增模型","children":[["$","a",null,{"className":"break-words","href":"#31-新增模型","aria-hidden":"true","tabIndex":"-1","children":["$","span",null,{"className":"content-header-link","children":["$","svg",null,{"className":"h-5 linkicon w-5","fill":"currentColor","viewBox":"0 0 20 20","xmlns":"http://www.w3.org/2000/svg","children":[["$","path",null,{"d":"M12.232 4.232a2.5 2.5 0 0 1 3.536 3.536l-1.225 1.224a.75.75 0 0 0 1.061 1.06l1.224-1.224a4 4 0 0 0-5.656-5.656l-3 3a4 4 0 0 0 .225 5.865.75.75 0 0 0 .977-1.138 2.5 2.5 0 0 1-.142-3.667l3-3Z"}],["$","path",null,{"d":"M11.603 7.963a.75.75 0 0 0-.977 1.138 2.5 2.5 0 0 1 .142 3.667l-3 3a2.5 2.5 0 0 1-3.536-3.536l1.225-1.224a.75.75 0 0 0-1.061-1.06l-1.224 1.224a4 4 0 1 0 5.656 5.656l3-3a4 4 0 0 0-.225-5.865Z"}]]}]}]}],"3.1 新增模型"]}] 39:["$","p",null,{"children":["在 ",["$","code",null,{"children":"models.py"}]," 加表 + 外键(",["$","strong",null,{"children":["优先 ",["$","code",null,{"children":"ON DELETE CASCADE"}]]}],"):"]}] 3a:["$","$L7b",null,{"className":"language-python","children":["$","code",null,{"className":"language-python code-highlight","children":[["$","span",null,{"className":"code-line","children":[["$","span",null,{"className":"token keyword","children":"class"}]," ",["$","span",null,{"className":"token class-name","children":"Conversation"}],["$","span",null,{"className":"token punctuation","children":"("}],"Base",["$","span",null,{"className":"token punctuation","children":")"}],["$","span",null,{"className":"token punctuation","children":":"}],"\n"]}],["$","span",null,{"className":"code-line","children":[" __tablename__ ",["$","span",null,{"className":"token operator","children":"="}]," ",["$","span",null,{"className":"token string","children":"\"conversations\""}],"\n"]}],["$","span",null,{"className":"code-line","children":[" ",["$","span",null,{"className":"token builtin","children":"id"}]," ",["$","span",null,{"className":"token operator","children":"="}]," Column",["$","span",null,{"className":"token punctuation","children":"("}],"Integer",["$","span",null,{"className":"token punctuation","children":","}]," primary_key",["$","span",null,{"className":"token operator","children":"="}],["$","span",null,{"className":"token boolean","children":"True"}],["$","span",null,{"className":"token punctuation","children":")"}],"\n"]}],["$","span",null,{"className":"code-line","children":[" session_id ",["$","span",null,{"className":"token operator","children":"="}]," Column",["$","span",null,{"className":"token punctuation","children":"("}],"String",["$","span",null,{"className":"token punctuation","children":","}]," nullable",["$","span",null,{"className":"token operator","children":"="}],["$","span",null,{"className":"token boolean","children":"False"}],["$","span",null,{"className":"token punctuation","children":")"}],"\n"]}],["$","span",null,{"className":"code-line","children":[" character_id ",["$","span",null,{"className":"token operator","children":"="}]," Column",["$","span",null,{"className":"token punctuation","children":"("}],"Integer",["$","span",null,{"className":"token punctuation","children":","}]," ForeignKey",["$","span",null,{"className":"token punctuation","children":"("}],["$","span",null,{"className":"token string","children":"\"characters.id\""}],["$","span",null,{"className":"token punctuation","children":","}]," ondelete",["$","span",null,{"className":"token operator","children":"="}],["$","span",null,{"className":"token string","children":"\"CASCADE\""}],["$","span",null,{"className":"token punctuation","children":")"}],["$","span",null,{"className":"token punctuation","children":")"}],"\n"]}],["$","span",null,{"className":"code-line","children":[" updated_at ",["$","span",null,{"className":"token operator","children":"="}]," Column",["$","span",null,{"className":"token punctuation","children":"("}],"DateTime",["$","span",null,{"className":"token punctuation","children":","}]," default",["$","span",null,{"className":"token operator","children":"="}],"datetime",["$","span",null,{"className":"token punctuation","children":"."}],"utcnow",["$","span",null,{"className":"token punctuation","children":")"}],"\n"]}]]}]}] 3b:["$","h3",null,{"className":"content-header","id":"32-迁移脚本","children":[["$","a",null,{"className":"break-words","href":"#32-迁移脚本","aria-hidden":"true","tabIndex":"-1","children":["$","span",null,{"className":"content-header-link","children":["$","svg",null,{"className":"h-5 linkicon w-5","fill":"currentColor","viewBox":"0 0 20 20","xmlns":"http://www.w3.org/2000/svg","children":[["$","path",null,{"d":"M12.232 4.232a2.5 2.5 0 0 1 3.536 3.536l-1.225 1.224a.75.75 0 0 0 1.061 1.06l1.224-1.224a4 4 0 0 0-5.656-5.656l-3 3a4 4 0 0 0 .225 5.865.75.75 0 0 0 .977-1.138 2.5 2.5 0 0 1-.142-3.667l3-3Z"}],["$","path",null,{"d":"M11.603 7.963a.75.75 0 0 0-.977 1.138 2.5 2.5 0 0 1 .142 3.667l-3 3a2.5 2.5 0 0 1-3.536-3.536l1.225-1.224a.75.75 0 0 0-1.061-1.06l-1.224 1.224a4 4 0 1 0 5.656 5.656l3-3a4 4 0 0 0-.225-5.865Z"}]]}]}]}],"3.2 迁移脚本"]}] 3c:["$","p",null,{"children":["写",["$","strong",null,{"children":"幂等迁移"}],"(",["$","code",null,{"children":"db_migration.py"}],"),处理存量数据回填:"]}] 3d:["$","$L7b",null,{"className":"language-python","children":["$","code",null,{"className":"language-python code-highlight","children":[["$","span",null,{"className":"code-line","children":[["$","span",null,{"className":"token comment","children":"# 幂等:检查列是否存在再 ALTER TABLE"}],"\n"]}],["$","span",null,{"className":"code-line","children":["cols ",["$","span",null,{"className":"token operator","children":"="}]," _sqlite_columns",["$","span",null,{"className":"token punctuation","children":"("}],"engine",["$","span",null,{"className":"token punctuation","children":","}]," ",["$","span",null,{"className":"token string","children":"\"conversations\""}],["$","span",null,{"className":"token punctuation","children":")"}],"\n"]}],["$","span",null,{"className":"code-line","children":[["$","span",null,{"className":"token keyword","children":"if"}]," ",["$","span",null,{"className":"token string","children":"\"session_id\""}]," ",["$","span",null,{"className":"token keyword","children":"not"}]," ",["$","span",null,{"className":"token keyword","children":"in"}]," cols",["$","span",null,{"className":"token punctuation","children":":"}],"\n"]}],["$","span",null,{"className":"code-line","children":[" conn",["$","span",null,{"className":"token punctuation","children":"."}],"execute",["$","span",null,{"className":"token punctuation","children":"("}],"text",["$","span",null,{"className":"token punctuation","children":"("}],["$","span",null,{"className":"token string","children":"\"ALTER TABLE conversations ADD COLUMN session_id ...\""}],["$","span",null,{"className":"token punctuation","children":")"}],["$","span",null,{"className":"token punctuation","children":")"}],"\n"]}],["$","span",null,{"className":"code-line","children":"\n"}],["$","span",null,{"className":"code-line","children":[["$","span",null,{"className":"token comment","children":"# 回填:为孤儿数据创建\"默认记录\"并 UPDATE 关联"}],"\n"]}]]}]}] 3e:["$","p",null,{"children":[["$","strong",null,{"children":"关键点"}],":"]}] 3f:["$","ul",null,{"children":[["$","li",null,{"children":"幂等:迁移脚本可重复运行不报错"}],["$","li",null,{"children":"回填:为孤儿数据创建\"默认记录\"并 UPDATE 关联"}]]}] 40:["$","h3",null,{"className":"content-header","id":"33-复合索引","children":[["$","a",null,{"className":"break-words","href":"#33-复合索引","aria-hidden":"true","tabIndex":"-1","children":["$","span",null,{"className":"content-header-link","children":["$","svg",null,{"className":"h-5 linkicon w-5","fill":"currentColor","viewBox":"0 0 20 20","xmlns":"http://www.w3.org/2000/svg","children":[["$","path",null,{"d":"M12.232 4.232a2.5 2.5 0 0 1 3.536 3.536l-1.225 1.224a.75.75 0 0 0 1.061 1.06l1.224-1.224a4 4 0 0 0-5.656-5.656l-3 3a4 4 0 0 0 .225 5.865.75.75 0 0 0 .977-1.138 2.5 2.5 0 0 1-.142-3.667l3-3Z"}],["$","path",null,{"d":"M11.603 7.963a.75.75 0 0 0-.977 1.138 2.5 2.5 0 0 1 .142 3.667l-3 3a2.5 2.5 0 0 1-3.536-3.536l1.225-1.224a.75.75 0 0 0-1.061-1.06l-1.224 1.224a4 4 0 1 0 5.656 5.656l3-3a4 4 0 0 0-.225-5.865Z"}]]}]}]}],"3.3 复合索引"]}] 41:["$","p",null,{"children":["为高频查询模式建索引(如 ",["$","code",null,{"children":"character_id + updated_at desc"}],")。"]}] 42:["$","hr",null,{}] 43:["$","h2",null,{"className":"content-header","id":"步骤-4后端实现分层","children":[["$","a",null,{"className":"break-words","href":"#步骤-4后端实现分层","aria-hidden":"true","tabIndex":"-1","children":["$","span",null,{"className":"content-header-link","children":["$","svg",null,{"className":"h-5 linkicon w-5","fill":"currentColor","viewBox":"0 0 20 20","xmlns":"http://www.w3.org/2000/svg","children":[["$","path",null,{"d":"M12.232 4.232a2.5 2.5 0 0 1 3.536 3.536l-1.225 1.224a.75.75 0 0 0 1.061 1.06l1.224-1.224a4 4 0 0 0-5.656-5.656l-3 3a4 4 0 0 0 .225 5.865.75.75 0 0 0 .977-1.138 2.5 2.5 0 0 1-.142-3.667l3-3Z"}],["$","path",null,{"d":"M11.603 7.963a.75.75 0 0 0-.977 1.138 2.5 2.5 0 0 1 .142 3.667l-3 3a2.5 2.5 0 0 1-3.536-3.536l1.225-1.224a.75.75 0 0 0-1.061-1.06l-1.224 1.224a4 4 0 1 0 5.656 5.656l3-3a4 4 0 0 0-.225-5.865Z"}]]}]}]}],"步骤 4:后端实现(分层)"]}] 44:["$","p",null,{"children":["按以下顺序写,",["$","strong",null,{"children":["每层写完跑一次 ",["$","code",null,{"children":"ast.parse"}]," 语法检查"]}],":"]}] 45:["$","h3",null,{"className":"content-header","id":"41-service-层backendservicesxxxpy","children":[["$","a",null,{"className":"break-words","href":"#41-service-层backendservicesxxxpy","aria-hidden":"true","tabIndex":"-1","children":["$","span",null,{"className":"content-header-link","children":["$","svg",null,{"className":"h-5 linkicon w-5","fill":"currentColor","viewBox":"0 0 20 20","xmlns":"http://www.w3.org/2000/svg","children":[["$","path",null,{"d":"M12.232 4.232a2.5 2.5 0 0 1 3.536 3.536l-1.225 1.224a.75.75 0 0 0 1.061 1.06l1.224-1.224a4 4 0 0 0-5.656-5.656l-3 3a4 4 0 0 0 .225 5.865.75.75 0 0 0 .977-1.138 2.5 2.5 0 0 1-.142-3.667l3-3Z"}],["$","path",null,{"d":"M11.603 7.963a.75.75 0 0 0-.977 1.138 2.5 2.5 0 0 1 .142 3.667l-3 3a2.5 2.5 0 0 1-3.536-3.536l1.225-1.224a.75.75 0 0 0-1.061-1.06l-1.224 1.224a4 4 0 1 0 5.656 5.656l3-3a4 4 0 0 0-.225-5.865Z"}]]}]}]}],"4.1 Service 层(",["$","code",null,{"children":"backend/services/xxx.py"}],")"]}] 46:["$","p",null,{"children":["核心业务逻辑,",["$","strong",null,{"children":"纯函数式"}],",不依赖框架:"]}] 47:["$","$L7b",null,{"className":"language-python","children":["$","code",null,{"className":"language-python code-highlight","children":[["$","span",null,{"className":"code-line","children":[["$","span",null,{"className":"token keyword","children":"def"}]," ",["$","span",null,{"className":"token function","children":"get_conversations"}],["$","span",null,{"className":"token punctuation","children":"("}],"db",["$","span",null,{"className":"token punctuation","children":","}]," character_id",["$","span",null,{"className":"token operator","children":"="}],["$","span",null,{"className":"token boolean","children":"None"}],["$","span",null,{"className":"token punctuation","children":")"}],["$","span",null,{"className":"token punctuation","children":":"}],"\n"]}],["$","span",null,{"className":"code-line","children":[" query ",["$","span",null,{"className":"token operator","children":"="}]," db",["$","span",null,{"className":"token punctuation","children":"."}],"query",["$","span",null,{"className":"token punctuation","children":"("}],"Conversation",["$","span",null,{"className":"token punctuation","children":")"}],"\n"]}],["$","span",null,{"className":"code-line","children":[" ",["$","span",null,{"className":"token keyword","children":"if"}]," character_id",["$","span",null,{"className":"token punctuation","children":":"}],"\n"]}],["$","span",null,{"className":"code-line","children":[" query ",["$","span",null,{"className":"token operator","children":"="}]," query",["$","span",null,{"className":"token punctuation","children":"."}],["$","span",null,{"className":"token builtin","children":"filter"}],["$","span",null,{"className":"token punctuation","children":"("}],"Conversation",["$","span",null,{"className":"token punctuation","children":"."}],"character_id ",["$","span",null,{"className":"token operator","children":"=="}]," character_id",["$","span",null,{"className":"token punctuation","children":")"}],"\n"]}],["$","span",null,{"className":"code-line","children":[" ",["$","span",null,{"className":"token keyword","children":"return"}]," query",["$","span",null,{"className":"token punctuation","children":"."}],"order_by",["$","span",null,{"className":"token punctuation","children":"("}],"Conversation",["$","span",null,{"className":"token punctuation","children":"."}],"updated_at",["$","span",null,{"className":"token punctuation","children":"."}],"desc",["$","span",null,{"className":"token punctuation","children":"("}],["$","span",null,{"className":"token punctuation","children":")"}],["$","span",null,{"className":"token punctuation","children":")"}],["$","span",null,{"className":"token punctuation","children":"."}],["$","span",null,{"className":"token builtin","children":"all"}],["$","span",null,{"className":"token punctuation","children":"("}],["$","span",null,{"className":"token punctuation","children":")"}],"\n"]}]]}]}] 48:["$","h3",null,{"className":"content-header","id":"42-schema-层schemaspy","children":[["$","a",null,{"className":"break-words","href":"#42-schema-层schemaspy","aria-hidden":"true","tabIndex":"-1","children":["$","span",null,{"className":"content-header-link","children":["$","svg",null,{"className":"h-5 linkicon w-5","fill":"currentColor","viewBox":"0 0 20 20","xmlns":"http://www.w3.org/2000/svg","children":[["$","path",null,{"d":"M12.232 4.232a2.5 2.5 0 0 1 3.536 3.536l-1.225 1.224a.75.75 0 0 0 1.061 1.06l1.224-1.224a4 4 0 0 0-5.656-5.656l-3 3a4 4 0 0 0 .225 5.865.75.75 0 0 0 .977-1.138 2.5 2.5 0 0 1-.142-3.667l3-3Z"}],["$","path",null,{"d":"M11.603 7.963a.75.75 0 0 0-.977 1.138 2.5 2.5 0 0 1 .142 3.667l-3 3a2.5 2.5 0 0 1-3.536-3.536l1.225-1.224a.75.75 0 0 0-1.061-1.06l-1.224 1.224a4 4 0 1 0 5.656 5.656l3-3a4 4 0 0 0-.225-5.865Z"}]]}]}]}],"4.2 Schema 层(",["$","code",null,{"children":"schemas.py"}],")"]}] 49:["$","p",null,{"children":"Pydantic 请求/响应模型:"}] 4a:["$","$L7b",null,{"className":"language-python","children":["$","code",null,{"className":"language-python code-highlight","children":[["$","span",null,{"className":"code-line","children":[["$","span",null,{"className":"token keyword","children":"class"}]," ",["$","span",null,{"className":"token class-name","children":"ConversationCreate"}],["$","span",null,{"className":"token punctuation","children":"("}],"BaseModel",["$","span",null,{"className":"token punctuation","children":")"}],["$","span",null,{"className":"token punctuation","children":":"}],"\n"]}],["$","span",null,{"className":"code-line","children":[" session_id",["$","span",null,{"className":"token punctuation","children":":"}]," ",["$","span",null,{"className":"token builtin","children":"str"}],"\n"]}],["$","span",null,{"className":"code-line","children":[" character_id",["$","span",null,{"className":"token punctuation","children":":"}]," ",["$","span",null,{"className":"token builtin","children":"int"}],"\n"]}],["$","span",null,{"className":"code-line","children":"\n"}],["$","span",null,{"className":"code-line","children":[" model_config ",["$","span",null,{"className":"token operator","children":"="}]," SettingsConfigDict",["$","span",null,{"className":"token punctuation","children":"("}],"extra",["$","span",null,{"className":"token operator","children":"="}],["$","span",null,{"className":"token string","children":"\"ignore\""}],["$","span",null,{"className":"token punctuation","children":")"}]," ",["$","span",null,{"className":"token comment","children":"# 避免 .env 多余字段报错"}],"\n"]}]]}]}] 4b:["$","blockquote",null,{"children":["$","p",null,{"children":[["$","strong",null,{"children":"踩坑"}],":Pydantic ",["$","code",null,{"children":"BaseSettings"}]," 加 ",["$","code",null,{"children":"extra=\"ignore\""}]," 避免 .env 多余字段报错。"]}]}] 4c:["$","h3",null,{"className":"content-header","id":"43-endpoint-层mainpy","children":[["$","a",null,{"className":"break-words","href":"#43-endpoint-层mainpy","aria-hidden":"true","tabIndex":"-1","children":["$","span",null,{"className":"content-header-link","children":["$","svg",null,{"className":"h-5 linkicon w-5","fill":"currentColor","viewBox":"0 0 20 20","xmlns":"http://www.w3.org/2000/svg","children":[["$","path",null,{"d":"M12.232 4.232a2.5 2.5 0 0 1 3.536 3.536l-1.225 1.224a.75.75 0 0 0 1.061 1.06l1.224-1.224a4 4 0 0 0-5.656-5.656l-3 3a4 4 0 0 0 .225 5.865.75.75 0 0 0 .977-1.138 2.5 2.5 0 0 1-.142-3.667l3-3Z"}],["$","path",null,{"d":"M11.603 7.963a.75.75 0 0 0-.977 1.138 2.5 2.5 0 0 1 .142 3.667l-3 3a2.5 2.5 0 0 1-3.536-3.536l1.225-1.224a.75.75 0 0 0-1.061-1.06l-1.224 1.224a4 4 0 1 0 5.656 5.656l3-3a4 4 0 0 0-.225-5.865Z"}]]}]}]}],"4.3 Endpoint 层(",["$","code",null,{"children":"main.py"}],")"]}] 4d:["$","p",null,{"children":"薄路由,调 service + 异常映射:"}] 4e:["$","$L7b",null,{"className":"language-python","children":["$","code",null,{"className":"language-python code-highlight","children":[["$","span",null,{"className":"code-line","children":[["$","span",null,{"className":"token punctuation annotation decorator","children":["@app",["$","span",null,{"className":"token punctuation","children":"."}],"post"]}],["$","span",null,{"className":"token punctuation","children":"("}],["$","span",null,{"className":"token string","children":"\"/api/conversations\""}],["$","span",null,{"className":"token punctuation","children":","}]," response_model",["$","span",null,{"className":"token operator","children":"="}],"ConversationResponse",["$","span",null,{"className":"token punctuation","children":")"}],"\n"]}],["$","span",null,{"className":"code-line","children":[["$","span",null,{"className":"token keyword","children":"def"}]," ",["$","span",null,{"className":"token function","children":"create_conversation"}],["$","span",null,{"className":"token punctuation","children":"("}],"req",["$","span",null,{"className":"token punctuation","children":":"}]," ConversationCreate",["$","span",null,{"className":"token punctuation","children":","}]," db",["$","span",null,{"className":"token punctuation","children":":"}]," Session ",["$","span",null,{"className":"token operator","children":"="}]," Depends",["$","span",null,{"className":"token punctuation","children":"("}],"get_db",["$","span",null,{"className":"token punctuation","children":")"}],["$","span",null,{"className":"token punctuation","children":")"}],["$","span",null,{"className":"token punctuation","children":":"}],"\n"]}],["$","span",null,{"className":"code-line","children":[" ",["$","span",null,{"className":"token keyword","children":"try"}],["$","span",null,{"className":"token punctuation","children":":"}],"\n"]}],["$","span",null,{"className":"code-line","children":[" ",["$","span",null,{"className":"token keyword","children":"return"}]," service",["$","span",null,{"className":"token punctuation","children":"."}],"create_conversation",["$","span",null,{"className":"token punctuation","children":"("}],"db",["$","span",null,{"className":"token punctuation","children":","}]," req",["$","span",null,{"className":"token punctuation","children":")"}],"\n"]}],["$","span",null,{"className":"code-line","children":[" ",["$","span",null,{"className":"token keyword","children":"except"}]," Exception ",["$","span",null,{"className":"token keyword","children":"as"}]," e",["$","span",null,{"className":"token punctuation","children":":"}],"\n"]}],["$","span",null,{"className":"code-line","children":[" ",["$","span",null,{"className":"token keyword","children":"raise"}]," HTTPException",["$","span",null,{"className":"token punctuation","children":"("}],"status_code",["$","span",null,{"className":"token operator","children":"="}],["$","span",null,{"className":"token number","children":"400"}],["$","span",null,{"className":"token punctuation","children":","}]," detail",["$","span",null,{"className":"token operator","children":"="}],["$","span",null,{"className":"token builtin","children":"str"}],["$","span",null,{"className":"token punctuation","children":"("}],"e",["$","span",null,{"className":"token punctuation","children":")"}],["$","span",null,{"className":"token punctuation","children":")"}],"\n"]}]]}]}] 4f:["$","h3",null,{"className":"content-header","id":"44-启动钩子","children":[["$","a",null,{"className":"break-words","href":"#44-启动钩子","aria-hidden":"true","tabIndex":"-1","children":["$","span",null,{"className":"content-header-link","children":["$","svg",null,{"className":"h-5 linkicon w-5","fill":"currentColor","viewBox":"0 0 20 20","xmlns":"http://www.w3.org/2000/svg","children":[["$","path",null,{"d":"M12.232 4.232a2.5 2.5 0 0 1 3.536 3.536l-1.225 1.224a.75.75 0 0 0 1.061 1.06l1.224-1.224a4 4 0 0 0-5.656-5.656l-3 3a4 4 0 0 0 .225 5.865.75.75 0 0 0 .977-1.138 2.5 2.5 0 0 1-.142-3.667l3-3Z"}],["$","path",null,{"d":"M11.603 7.963a.75.75 0 0 0-.977 1.138 2.5 2.5 0 0 1 .142 3.667l-3 3a2.5 2.5 0 0 1-3.536-3.536l1.225-1.224a.75.75 0 0 0-1.061-1.06l-1.224 1.224a4 4 0 1 0 5.656 5.656l3-3a4 4 0 0 0-.225-5.865Z"}]]}]}]}],"4.4 启动钩子"]}] 50:["$","p",null,{"children":["在 ",["$","code",null,{"children":"startup_event"}]," 中调 ",["$","code",null,{"children":"run_all_migrations(engine)"}],"。"]}] 51:["$","hr",null,{}] 52:["$","h2",null,{"className":"content-header","id":"步骤-5前端实现","children":[["$","a",null,{"className":"break-words","href":"#步骤-5前端实现","aria-hidden":"true","tabIndex":"-1","children":["$","span",null,{"className":"content-header-link","children":["$","svg",null,{"className":"h-5 linkicon w-5","fill":"currentColor","viewBox":"0 0 20 20","xmlns":"http://www.w3.org/2000/svg","children":[["$","path",null,{"d":"M12.232 4.232a2.5 2.5 0 0 1 3.536 3.536l-1.225 1.224a.75.75 0 0 0 1.061 1.06l1.224-1.224a4 4 0 0 0-5.656-5.656l-3 3a4 4 0 0 0 .225 5.865.75.75 0 0 0 .977-1.138 2.5 2.5 0 0 1-.142-3.667l3-3Z"}],["$","path",null,{"d":"M11.603 7.963a.75.75 0 0 0-.977 1.138 2.5 2.5 0 0 1 .142 3.667l-3 3a2.5 2.5 0 0 1-3.536-3.536l1.225-1.224a.75.75 0 0 0-1.061-1.06l-1.224 1.224a4 4 0 1 0 5.656 5.656l3-3a4 4 0 0 0-.225-5.865Z"}]]}]}]}],"步骤 5:前端实现"]}] 53:["$","h3",null,{"className":"content-header","id":"51-api-客户端api_clientpy","children":[["$","a",null,{"className":"break-words","href":"#51-api-客户端api_clientpy","aria-hidden":"true","tabIndex":"-1","children":["$","span",null,{"className":"content-header-link","children":["$","svg",null,{"className":"h-5 linkicon w-5","fill":"currentColor","viewBox":"0 0 20 20","xmlns":"http://www.w3.org/2000/svg","children":[["$","path",null,{"d":"M12.232 4.232a2.5 2.5 0 0 1 3.536 3.536l-1.225 1.224a.75.75 0 0 0 1.061 1.06l1.224-1.224a4 4 0 0 0-5.656-5.656l-3 3a4 4 0 0 0 .225 5.865.75.75 0 0 0 .977-1.138 2.5 2.5 0 0 1-.142-3.667l3-3Z"}],["$","path",null,{"d":"M11.603 7.963a.75.75 0 0 0-.977 1.138 2.5 2.5 0 0 1 .142 3.667l-3 3a2.5 2.5 0 0 1-3.536-3.536l1.225-1.224a.75.75 0 0 0-1.061-1.06l-1.224 1.224a4 4 0 1 0 5.656 5.656l3-3a4 4 0 0 0-.225-5.865Z"}]]}]}]}],"5.1 API 客户端(",["$","code",null,{"children":"api_client.py"}],")"]}] 54:["$","p",null,{"children":["每个后端端点对应一个函数,统一 ",["$","code",null,{"children":"_handle_response"}]," + 超时:"]}] 55:["$","$L7b",null,{"className":"language-python","children":["$","code",null,{"className":"language-python code-highlight","children":[["$","span",null,{"className":"code-line","children":[["$","span",null,{"className":"token keyword","children":"def"}]," ",["$","span",null,{"className":"token function","children":"create_conversation"}],["$","span",null,{"className":"token punctuation","children":"("}],"session_id",["$","span",null,{"className":"token punctuation","children":","}]," character_id",["$","span",null,{"className":"token punctuation","children":")"}],["$","span",null,{"className":"token punctuation","children":":"}],"\n"]}],["$","span",null,{"className":"code-line","children":[" ",["$","span",null,{"className":"token keyword","children":"return"}]," _handle_response",["$","span",null,{"className":"token punctuation","children":"("}],"requests",["$","span",null,{"className":"token punctuation","children":"."}],"post",["$","span",null,{"className":"token punctuation","children":"("}],"\n"]}],["$","span",null,{"className":"code-line","children":[" ",["$","span",null,{"className":"token string-interpolation","children":[["$","span",null,{"className":"token string","children":"f\""}],["$","span",null,{"className":"token interpolation","children":[["$","span",null,{"className":"token punctuation","children":"{"}],"API_BASE",["$","span",null,{"className":"token punctuation","children":"}"}]]}],["$","span",null,{"className":"token string","children":"/conversations\""}]]}],["$","span",null,{"className":"token punctuation","children":","}],"\n"]}],["$","span",null,{"className":"code-line","children":[" json",["$","span",null,{"className":"token operator","children":"="}],["$","span",null,{"className":"token punctuation","children":"{"}],["$","span",null,{"className":"token string","children":"\"session_id\""}],["$","span",null,{"className":"token punctuation","children":":"}]," session_id",["$","span",null,{"className":"token punctuation","children":","}]," ",["$","span",null,{"className":"token string","children":"\"character_id\""}],["$","span",null,{"className":"token punctuation","children":":"}]," character_id",["$","span",null,{"className":"token punctuation","children":"}"}],["$","span",null,{"className":"token punctuation","children":","}],"\n"]}],["$","span",null,{"className":"code-line","children":[" timeout",["$","span",null,{"className":"token operator","children":"="}],["$","span",null,{"className":"token number","children":"5"}],"\n"]}],["$","span",null,{"className":"code-line","children":[" ",["$","span",null,{"className":"token punctuation","children":")"}],["$","span",null,{"className":"token punctuation","children":")"}],"\n"]}]]}]}] 56:["$","h3",null,{"className":"content-header","id":"52-页面实现apppy","children":[["$","a",null,{"className":"break-words","href":"#52-页面实现apppy","aria-hidden":"true","tabIndex":"-1","children":["$","span",null,{"className":"content-header-link","children":["$","svg",null,{"className":"h-5 linkicon w-5","fill":"currentColor","viewBox":"0 0 20 20","xmlns":"http://www.w3.org/2000/svg","children":[["$","path",null,{"d":"M12.232 4.232a2.5 2.5 0 0 1 3.536 3.536l-1.225 1.224a.75.75 0 0 0 1.061 1.06l1.224-1.224a4 4 0 0 0-5.656-5.656l-3 3a4 4 0 0 0 .225 5.865.75.75 0 0 0 .977-1.138 2.5 2.5 0 0 1-.142-3.667l3-3Z"}],["$","path",null,{"d":"M11.603 7.963a.75.75 0 0 0-.977 1.138 2.5 2.5 0 0 1 .142 3.667l-3 3a2.5 2.5 0 0 1-3.536-3.536l1.225-1.224a.75.75 0 0 0-1.061-1.06l-1.224 1.224a4 4 0 1 0 5.656 5.656l3-3a4 4 0 0 0-.225-5.865Z"}]]}]}]}],"5.2 页面实现(",["$","code",null,{"children":"app.py"}],")"]}] 57:["$","p",null,{"children":["新增 ",["$","code",null,{"children":"render_page_xxx()"}],",注册到侧边栏导航。"]}] 58:["$","h3",null,{"className":"content-header","id":"53-ui-交互","children":[["$","a",null,{"className":"break-words","href":"#53-ui-交互","aria-hidden":"true","tabIndex":"-1","children":["$","span",null,{"className":"content-header-link","children":["$","svg",null,{"className":"h-5 linkicon w-5","fill":"currentColor","viewBox":"0 0 20 20","xmlns":"http://www.w3.org/2000/svg","children":[["$","path",null,{"d":"M12.232 4.232a2.5 2.5 0 0 1 3.536 3.536l-1.225 1.224a.75.75 0 0 0 1.061 1.06l1.224-1.224a4 4 0 0 0-5.656-5.656l-3 3a4 4 0 0 0 .225 5.865.75.75 0 0 0 .977-1.138 2.5 2.5 0 0 1-.142-3.667l3-3Z"}],["$","path",null,{"d":"M11.603 7.963a.75.75 0 0 0-.977 1.138 2.5 2.5 0 0 1 .142 3.667l-3 3a2.5 2.5 0 0 1-3.536-3.536l1.225-1.224a.75.75 0 0 0-1.061-1.06l-1.224 1.224a4 4 0 1 0 5.656 5.656l3-3a4 4 0 0 0-.225-5.865Z"}]]}]}]}],"5.3 UI 交互"]}] 59:["$","ul",null,{"children":[["$","li",null,{"children":["列表用 ",["$","code",null,{"children":"st.dataframe"}],",操作用 ",["$","code",null,{"children":"st.popover"}],"(二次确认删除)"]}],["$","li",null,{"children":["跨组件状态用 ",["$","code",null,{"children":"st.session_state"}],",切换实体时重置缓存"]}],["$","li",null,{"children":["双栏布局用 ",["$","code",null,{"children":"st.columns([1, 2.5])"}]]}]]}] 5a:["$","hr",null,{}] 5b:["$","h2",null,{"className":"content-header","id":"步骤-6端到端验证","children":[["$","a",null,{"className":"break-words","href":"#步骤-6端到端验证","aria-hidden":"true","tabIndex":"-1","children":["$","span",null,{"className":"content-header-link","children":["$","svg",null,{"className":"h-5 linkicon w-5","fill":"currentColor","viewBox":"0 0 20 20","xmlns":"http://www.w3.org/2000/svg","children":[["$","path",null,{"d":"M12.232 4.232a2.5 2.5 0 0 1 3.536 3.536l-1.225 1.224a.75.75 0 0 0 1.061 1.06l1.224-1.224a4 4 0 0 0-5.656-5.656l-3 3a4 4 0 0 0 .225 5.865.75.75 0 0 0 .977-1.138 2.5 2.5 0 0 1-.142-3.667l3-3Z"}],["$","path",null,{"d":"M11.603 7.963a.75.75 0 0 0-.977 1.138 2.5 2.5 0 0 1 .142 3.667l-3 3a2.5 2.5 0 0 1-3.536-3.536l1.225-1.224a.75.75 0 0 0-1.061-1.06l-1.224 1.224a4 4 0 1 0 5.656 5.656l3-3a4 4 0 0 0-.225-5.865Z"}]]}]}]}],"步骤 6:端到端验证"]}] 5c:["$","p",null,{"children":[["$","strong",null,{"children":"目标"}],":确保功能真正可用。"]}] 5d:["$","p",null,{"children":[["$","strong",null,{"children":"验证清单"}],":"]}] 5e:["$","ol",null,{"children":[["$","li",null,{"children":"重启后端,观察迁移日志(确认回填数量)"}],["$","li",null,{"children":["用 PowerShell ",["$","code",null,{"children":"Invoke-WebRequest"}]," 逐个调 API 端点"]}],["$","li",null,{"children":["写临时验证脚本调 ",["$","code",null,{"children":"api_client"}]," 函数(用完删除)"]}],["$","li",null,{"children":"重启前端,确认页面无报错"}]]}] 5f:["$","p",null,{"children":[["$","strong",null,{"children":"验证脚本模板"}],":"]}] 60:["$","$L7b",null,{"className":"language-js","children":["$","code",null,{"className":"code-highlight language-js","children":["$","span",null,{"className":"code-line","children":"创建 → 操作 → 搜索 → 删除 → 确认消失\n"}]}]}] 61:["$","hr",null,{}] 62:["$","h2",null,{"className":"content-header","id":"步骤-7提-pr","children":[["$","a",null,{"className":"break-words","href":"#步骤-7提-pr","aria-hidden":"true","tabIndex":"-1","children":["$","span",null,{"className":"content-header-link","children":["$","svg",null,{"className":"h-5 linkicon w-5","fill":"currentColor","viewBox":"0 0 20 20","xmlns":"http://www.w3.org/2000/svg","children":[["$","path",null,{"d":"M12.232 4.232a2.5 2.5 0 0 1 3.536 3.536l-1.225 1.224a.75.75 0 0 0 1.061 1.06l1.224-1.224a4 4 0 0 0-5.656-5.656l-3 3a4 4 0 0 0 .225 5.865.75.75 0 0 0 .977-1.138 2.5 2.5 0 0 1-.142-3.667l3-3Z"}],["$","path",null,{"d":"M11.603 7.963a.75.75 0 0 0-.977 1.138 2.5 2.5 0 0 1 .142 3.667l-3 3a2.5 2.5 0 0 1-3.536-3.536l1.225-1.224a.75.75 0 0 0-1.061-1.06l-1.224 1.224a4 4 0 1 0 5.656 5.656l3-3a4 4 0 0 0-.225-5.865Z"}]]}]}]}],"步骤 7:提 PR"]}] 63:["$","h3",null,{"className":"content-header","id":"71-提交前安全检查","children":[["$","a",null,{"className":"break-words","href":"#71-提交前安全检查","aria-hidden":"true","tabIndex":"-1","children":["$","span",null,{"className":"content-header-link","children":["$","svg",null,{"className":"h-5 linkicon w-5","fill":"currentColor","viewBox":"0 0 20 20","xmlns":"http://www.w3.org/2000/svg","children":[["$","path",null,{"d":"M12.232 4.232a2.5 2.5 0 0 1 3.536 3.536l-1.225 1.224a.75.75 0 0 0 1.061 1.06l1.224-1.224a4 4 0 0 0-5.656-5.656l-3 3a4 4 0 0 0 .225 5.865.75.75 0 0 0 .977-1.138 2.5 2.5 0 0 1-.142-3.667l3-3Z"}],["$","path",null,{"d":"M11.603 7.963a.75.75 0 0 0-.977 1.138 2.5 2.5 0 0 1 .142 3.667l-3 3a2.5 2.5 0 0 1-3.536-3.536l1.225-1.224a.75.75 0 0 0-1.061-1.06l-1.224 1.224a4 4 0 1 0 5.656 5.656l3-3a4 4 0 0 0-.225-5.865Z"}]]}]}]}],"7.1 提交前安全检查"]}] 64:["$","ul",null,{"children":[["$","li",null,{"children":[["$","strong",null,{"children":["重建 ",["$","code",null,{"children":".gitignore"}]]}],":排除 ",["$","code",null,{"children":".env"}],"、",["$","code",null,{"children":"data/"}],"、",["$","code",null,{"children":"usercontext/"}],"、",["$","code",null,{"children":"refproj/"}],"、",["$","code",null,{"children":"__pycache__/"}],"、",["$","code",null,{"children":"*.log"}]]}],["$","li",null,{"children":[["$","strong",null,{"children":"只 stage 主题相关文件"}],":明确列出文件名,不用 ",["$","code",null,{"children":"git add ."}]]}],["$","li",null,{"children":[["$","strong",null,{"children":"不纳入无关删除"}],":工作区可能有旧文件删除,不混入本次 PR"]}]]}] 65:["$","h3",null,{"className":"content-header","id":"72-分支与提交","children":[["$","a",null,{"className":"break-words","href":"#72-分支与提交","aria-hidden":"true","tabIndex":"-1","children":["$","span",null,{"className":"content-header-link","children":["$","svg",null,{"className":"h-5 linkicon w-5","fill":"currentColor","viewBox":"0 0 20 20","xmlns":"http://www.w3.org/2000/svg","children":[["$","path",null,{"d":"M12.232 4.232a2.5 2.5 0 0 1 3.536 3.536l-1.225 1.224a.75.75 0 0 0 1.061 1.06l1.224-1.224a4 4 0 0 0-5.656-5.656l-3 3a4 4 0 0 0 .225 5.865.75.75 0 0 0 .977-1.138 2.5 2.5 0 0 1-.142-3.667l3-3Z"}],["$","path",null,{"d":"M11.603 7.963a.75.75 0 0 0-.977 1.138 2.5 2.5 0 0 1 .142 3.667l-3 3a2.5 2.5 0 0 1-3.536-3.536l1.225-1.224a.75.75 0 0 0-1.061-1.06l-1.224 1.224a4 4 0 1 0 5.656 5.656l3-3a4 4 0 0 0-.225-5.865Z"}]]}]}]}],"7.2 分支与提交"]}] 66:["$","$L7b",null,{"className":"language-powershell","children":["$","code",null,{"className":"code-highlight language-powershell","children":[["$","span",null,{"className":"code-line","children":[["$","span",null,{"className":"token comment","children":"# 用 git switch(Git 2.23+,比 checkout 更安全)"}],"\n"]}],["$","span",null,{"className":"code-line","children":["git ",["$","span",null,{"className":"token keyword","children":"switch"}]," ",["$","span",null,{"className":"token operator","children":"-"}],"c feature/xxx\n"]}],["$","span",null,{"className":"code-line","children":"\n"}],["$","span",null,{"className":"code-line","children":[["$","span",null,{"className":"token comment","children":"# 明确列出文件"}],"\n"]}],["$","span",null,{"className":"code-line","children":["git add backend/services/xxx",["$","span",null,{"className":"token punctuation","children":"."}],"py backend/main",["$","span",null,{"className":"token punctuation","children":"."}],"py frontend/app",["$","span",null,{"className":"token punctuation","children":"."}],"py\n"]}],["$","span",null,{"className":"code-line","children":"\n"}],["$","span",null,{"className":"code-line","children":[["$","span",null,{"className":"token comment","children":"# PowerShell 不支持 bash heredoc,用 -F 文件方式提交"}],"\n"]}],["$","span",null,{"className":"code-line","children":["git commit ",["$","span",null,{"className":"token operator","children":"-"}],"F commit_msg",["$","span",null,{"className":"token punctuation","children":"."}],"txt\n"]}]]}]}] 67:["$","blockquote",null,{"children":["$","p",null,{"children":[["$","strong",null,{"children":"踩坑"}],":PowerShell 终端不兼容 ",["$","code",null,{"children":"$$(cat <<'EOF' ... EOF)"}],",commit 会卡住。解决:把消息写到临时文件,用 ",["$","code",null,{"children":"git commit -F"}],"。"]}]}] 68:["$","h3",null,{"className":"content-header","id":"73-推送与创建-pr","children":[["$","a",null,{"className":"break-words","href":"#73-推送与创建-pr","aria-hidden":"true","tabIndex":"-1","children":["$","span",null,{"className":"content-header-link","children":["$","svg",null,{"className":"h-5 linkicon w-5","fill":"currentColor","viewBox":"0 0 20 20","xmlns":"http://www.w3.org/2000/svg","children":[["$","path",null,{"d":"M12.232 4.232a2.5 2.5 0 0 1 3.536 3.536l-1.225 1.224a.75.75 0 0 0 1.061 1.06l1.224-1.224a4 4 0 0 0-5.656-5.656l-3 3a4 4 0 0 0 .225 5.865.75.75 0 0 0 .977-1.138 2.5 2.5 0 0 1-.142-3.667l3-3Z"}],["$","path",null,{"d":"M11.603 7.963a.75.75 0 0 0-.977 1.138 2.5 2.5 0 0 1 .142 3.667l-3 3a2.5 2.5 0 0 1-3.536-3.536l1.225-1.224a.75.75 0 0 0-1.061-1.06l-1.224 1.224a4 4 0 1 0 5.656 5.656l3-3a4 4 0 0 0-.225-5.865Z"}]]}]}]}],"7.3 推送与创建 PR"]}] 69:["$","$L7b",null,{"className":"language-powershell","children":["$","code",null,{"className":"code-highlight language-powershell","children":[["$","span",null,{"className":"code-line","children":[["$","span",null,{"className":"token comment","children":"# push 到 fork remote"}],"\n"]}],["$","span",null,{"className":"code-line","children":["git push ",["$","span",null,{"className":"token operator","children":"-"}],"u fork feature/xxx\n"]}],["$","span",null,{"className":"code-line","children":"\n"}],["$","span",null,{"className":"code-line","children":[["$","span",null,{"className":"token comment","children":"# 用 gh CLI 创建跨 fork PR(GitHub MCP token 可能失效)"}],"\n"]}],["$","span",null,{"className":"code-line","children":[["$","span",null,{"className":"token variable","children":"$$env"}],":GH_TOKEN=",["$","span",null,{"className":"token string","children":"\"\""}],"\n"]}],["$","span",null,{"className":"code-line","children":["gh pr create ",["$","span",null,{"className":"token operator","children":"--"}],"repo <上游owner>",["$","span",null,{"className":"token operator","children":"/"}]," ",["$","span",null,{"className":"token operator","children":"--"}],"head :feature/xxx ",["$","span",null,{"className":"token operator","children":"--"}],"base main ",["$","span",null,{"className":"token operator","children":"--"}],"title ",["$","span",null,{"className":"token string","children":"\"...\""}]," ",["$","span",null,{"className":"token operator","children":"--"}],"body-file pr_body",["$","span",null,{"className":"token punctuation","children":"."}],"md\n"]}]]}]}] 6a:["$","hr",null,{}] 6b:["$","h2",null,{"className":"content-header","id":"常见踩坑速查","children":[["$","a",null,{"className":"break-words","href":"#常见踩坑速查","aria-hidden":"true","tabIndex":"-1","children":["$","span",null,{"className":"content-header-link","children":["$","svg",null,{"className":"h-5 linkicon w-5","fill":"currentColor","viewBox":"0 0 20 20","xmlns":"http://www.w3.org/2000/svg","children":[["$","path",null,{"d":"M12.232 4.232a2.5 2.5 0 0 1 3.536 3.536l-1.225 1.224a.75.75 0 0 0 1.061 1.06l1.224-1.224a4 4 0 0 0-5.656-5.656l-3 3a4 4 0 0 0 .225 5.865.75.75 0 0 0 .977-1.138 2.5 2.5 0 0 1-.142-3.667l3-3Z"}],["$","path",null,{"d":"M11.603 7.963a.75.75 0 0 0-.977 1.138 2.5 2.5 0 0 1 .142 3.667l-3 3a2.5 2.5 0 0 1-3.536-3.536l1.225-1.224a.75.75 0 0 0-1.061-1.06l-1.224 1.224a4 4 0 1 0 5.656 5.656l3-3a4 4 0 0 0-.225-5.865Z"}]]}]}]}],"常见踩坑速查"]}] 6c:["$","div",null,{"className":"w-full overflow-x-auto","children":["$","table",null,{"children":[["$","thead",null,{"children":["$","tr",null,{"children":[["$","th",null,{"children":"现象"}],["$","th",null,{"children":"原因"}],["$","th",null,{"children":"解决"}]]}]}],["$","tbody",null,{"children":[["$","tr",null,{"children":[["$","td",null,{"children":"Pydantic ValidationError extra inputs"}],["$","td",null,{"children":".env 有未声明字段"}],["$","td",null,{"children":["$","code",null,{"children":"model_config = SettingsConfigDict(extra=\"ignore\")"}]}]]}],["$","tr",null,{"children":[["$","td",null,{"children":["URL 拼出 ",["$","code",null,{"children":"/v1/v1/models"}]]}],["$","td",null,{"children":["base_url 已带 ",["$","code",null,{"children":"/v1"}]]}],["$","td",null,{"children":["写 ",["$","code",null,{"children":"_join_url()"}]," 去重"]}]]}],["$","tr",null,{"children":[["$","td",null,{"children":["commit 卡在 ",["$","code",null,{"children":">>"}]," 提示符"]}],["$","td",null,{"children":"PowerShell 不支持 heredoc"}],["$","td",null,{"children":["$","code",null,{"children":"git commit -F file.txt"}]}]]}],["$","tr",null,{"children":[["$","td",null,{"children":"GitHub MCP Bad credentials"}],["$","td",null,{"children":"MCP token 过期"}],["$","td",null,{"children":[["$","code",null,{"children":"gh"}]," CLI + ",["$","code",null,{"children":"GH_TOKEN"}]]}]]}],["$","tr",null,{"children":[["$","td",null,{"children":"cross-repo PR \"No commits\""}],["$","td",null,{"children":"push 到了非 fork 仓库"}],["$","td",null,{"children":"确认 fork 关系,push 到正确 remote"}]]}],["$","tr",null,{"children":[["$","td",null,{"children":"迁移重复执行报错"}],["$","td",null,{"children":"ALTER TABLE 非幂等"}],["$","td",null,{"children":["先 ",["$","code",null,{"children":"PRAGMA table_info"}]," 检查列是否存在"]}]]}],["$","tr",null,{"children":[["$","td",null,{"children":"git log 进 pager 卡住"}],["$","td",null,{"children":"PAGER 未设置"}],["$","td",null,{"children":["$","code",null,{"children":"git --no-pager log"}]}]]}],["$","tr",null,{"children":[["$","td",null,{"children":"敏感文件被 stage"}],["$","td",null,{"children":".gitignore 缺失"}],["$","td",null,{"children":"提交前重建 .gitignore,只 add 明确文件"}]]}]]}]]}]}] 6d:["$","hr",null,{}] 6e:["$","h2",null,{"className":"content-header","id":"移植决策原则","children":[["$","a",null,{"className":"break-words","href":"#移植决策原则","aria-hidden":"true","tabIndex":"-1","children":["$","span",null,{"className":"content-header-link","children":["$","svg",null,{"className":"h-5 linkicon w-5","fill":"currentColor","viewBox":"0 0 20 20","xmlns":"http://www.w3.org/2000/svg","children":[["$","path",null,{"d":"M12.232 4.232a2.5 2.5 0 0 1 3.536 3.536l-1.225 1.224a.75.75 0 0 0 1.061 1.06l1.224-1.224a4 4 0 0 0-5.656-5.656l-3 3a4 4 0 0 0 .225 5.865.75.75 0 0 0 .977-1.138 2.5 2.5 0 0 1-.142-3.667l3-3Z"}],["$","path",null,{"d":"M11.603 7.963a.75.75 0 0 0-.977 1.138 2.5 2.5 0 0 1 .142 3.667l-3 3a2.5 2.5 0 0 1-3.536-3.536l1.225-1.224a.75.75 0 0 0-1.061-1.06l-1.224 1.224a4 4 0 1 0 5.656 5.656l3-3a4 4 0 0 0-.225-5.865Z"}]]}]}]}],"移植决策原则"]}] 6f:["$","ol",null,{"children":[["$","li",null,{"children":["$","p",null,{"children":[["$","strong",null,{"children":"逻辑集中后端"}]," — 测试/业务逻辑放后端 service,前端只做展示。便于复用与测试。"]}]}],["$","li",null,{"children":["$","p",null,{"children":[["$","strong",null,{"children":"配置继承"}]," — 新功能优先复用现有设置(如 LLM provider 配置),避免重复输入。"]}]}],["$","li",null,{"children":["$","p",null,{"children":[["$","strong",null,{"children":"合理简化"}]," — 用当前栈的等价组件替代原版(如 Streamlit ",["$","code",null,{"children":"st.popover"}]," 替代 React inline edit)。"]}]}],["$","li",null,{"children":["$","p",null,{"children":[["$","strong",null,{"children":"安全脱敏"}]," — 探针/调试接口的请求头密钥字段替换为 ",["$","code",null,{"children":"***"}],"。"]}]}],["$","li",null,{"children":["$","p",null,{"children":[["$","strong",null,{"children":"幂等迁移"}]," — 迁移脚本可重复运行不报错,存量数据自动回填。"]}]}]]}] 70:["$","hr",null,{}] 71:["$","h2",null,{"className":"content-header","id":"结语","children":[["$","a",null,{"className":"break-words","href":"#结语","aria-hidden":"true","tabIndex":"-1","children":["$","span",null,{"className":"content-header-link","children":["$","svg",null,{"className":"h-5 linkicon w-5","fill":"currentColor","viewBox":"0 0 20 20","xmlns":"http://www.w3.org/2000/svg","children":[["$","path",null,{"d":"M12.232 4.232a2.5 2.5 0 0 1 3.536 3.536l-1.225 1.224a.75.75 0 0 0 1.061 1.06l1.224-1.224a4 4 0 0 0-5.656-5.656l-3 3a4 4 0 0 0 .225 5.865.75.75 0 0 0 .977-1.138 2.5 2.5 0 0 1-.142-3.667l3-3Z"}],["$","path",null,{"d":"M11.603 7.963a.75.75 0 0 0-.977 1.138 2.5 2.5 0 0 1 .142 3.667l-3 3a2.5 2.5 0 0 1-3.536-3.536l1.225-1.224a.75.75 0 0 0-1.061-1.06l-1.224 1.224a4 4 0 1 0 5.656 5.656l3-3a4 4 0 0 0-.225-5.865Z"}]]}]}]}],"结语"]}] 72:["$","p",null,{"children":["移植功能不是目的,",["$","strong",null,{"children":"理解并复用优秀的实现"}],"才是。这套流程的核心思想是:"]}] 73:["$","ul",null,{"children":[["$","li",null,{"children":[["$","strong",null,{"children":"先想清楚,再动手"}]," — 步骤 1-2 是关键"]}],["$","li",null,{"children":[["$","strong",null,{"children":"分层实现,逐层验证"}]," — 避免一次性写一大堆代码"]}],["$","li",null,{"children":[["$","strong",null,{"children":"记录踩坑"}]," — 下次遇到同样问题,直接查表解决"]}]]}] 74:["$","p",null,{"children":"希望这套流程能帮你更高效地移植功能,少踩坑,多产出。"}] 75:["$","hr",null,{}] 76:["$","p",null,{"children":["$","strong",null,{"children":"附录:参考资源"}]}] 77:["$","ul",null,{"children":[["$","li",null,{"children":["$","a",null,{"className":"break-words","target":"_blank","rel":"noopener noreferrer","href":"https://github.com/Yidadaa/NextChat","children":"GitHub: NextChat"}]}],["$","li",null,{"children":["$","a",null,{"className":"break-words","target":"_blank","rel":"noopener noreferrer","href":"https://fastapi.tiangolo.com/","children":"FastAPI 官方文档"}]}],["$","li",null,{"children":["$","a",null,{"className":"break-words","target":"_blank","rel":"noopener noreferrer","href":"https://docs.streamlit.io/","children":"Streamlit 官方文档"}]}],["$","li",null,{"children":["$","a",null,{"className":"break-words","target":"_blank","rel":"noopener noreferrer","href":"https://docs.pydantic.dev/","children":"Pydantic 文档"}]}]]}] 78:["$","div",null,{"className":"pt-6 pb-6 text-sm text-gray-700 dark:text-gray-300","children":[["$","a",null,{"className":"break-words","target":"_blank","rel":"nofollow","href":"https://mobile.twitter.com/search?q=https%3A%2F%2Ftechlog-18328.app.workbuddy.host%2Fblog%2Fport-feature-from-reference","children":"Discuss on Twitter"}]," • ",["$","a",null,{"className":"break-words","target":"_blank","rel":"noopener noreferrer","href":"https://github.com/joker1point/techlog/blob/main/data/blog/port-feature-from-reference.mdx","children":"View on GitHub"}]]}] 79:["$","div",null,{"className":"pt-6 pb-6 text-center text-gray-700 dark:text-gray-300","id":"comment","children":["$","$L7c",null,{"slug":"port-feature-from-reference"}]}] 7a:["$","footer",null,{"children":[["$","div",null,{"className":"divide-gray-200 text-sm leading-5 font-medium xl:col-start-1 xl:row-start-2 xl:divide-y dark:divide-gray-700","children":[["$","div",null,{"className":"py-4 xl:py-8","children":[["$","h2",null,{"className":"text-xs tracking-wide text-gray-500 uppercase dark:text-gray-400","children":"Tags"}],["$","div",null,{"className":"flex flex-wrap","children":[["$","$L5","engineering",{"className":"text-primary-500 hover:text-primary-600 dark:hover:text-primary-400 mr-3 text-sm font-medium uppercase","href":"/tags/engineering.html","children":"engineering"}],["$","$L5","open-source",{"className":"text-primary-500 hover:text-primary-600 dark:hover:text-primary-400 mr-3 text-sm font-medium uppercase","href":"/tags/open-source.html","children":"open-source"}],["$","$L5","python",{"className":"text-primary-500 hover:text-primary-600 dark:hover:text-primary-400 mr-3 text-sm font-medium uppercase","href":"/tags/python.html","children":"python"}],["$","$L5","fastapi",{"className":"text-primary-500 hover:text-primary-600 dark:hover:text-primary-400 mr-3 text-sm font-medium uppercase","href":"/tags/fastapi.html","children":"fastapi"}],["$","$L5","streamlit",{"className":"text-primary-500 hover:text-primary-600 dark:hover:text-primary-400 mr-3 text-sm font-medium uppercase","href":"/tags/streamlit.html","children":"streamlit"}],["$","$L5","workflow",{"className":"text-primary-500 hover:text-primary-600 dark:hover:text-primary-400 mr-3 text-sm font-medium uppercase","href":"/tags/workflow.html","children":"workflow"}],["$","$L5","opinion",{"className":"text-primary-500 hover:text-primary-600 dark:hover:text-primary-400 mr-3 text-sm font-medium uppercase","href":"/tags/opinion.html","children":"opinion"}]]}]]}],["$","div",null,{"className":"flex justify-between py-4 xl:block xl:space-y-8 xl:py-8","children":[["$","div",null,{"children":[["$","h2",null,{"className":"text-xs tracking-wide text-gray-500 uppercase dark:text-gray-400","children":"Previous Article"}],["$","div",null,{"className":"text-primary-500 hover:text-primary-600 dark:hover:text-primary-400","children":["$","$L5",null,{"className":"break-words","href":"/blog/from-developer-to-junior-architect.html","children":"从程序员到初级架构师的思考路径"}]}]]}],["$","div",null,{"children":[["$","h2",null,{"className":"text-xs tracking-wide text-gray-500 uppercase dark:text-gray-400","children":"Next Article"}],["$","div",null,{"className":"text-primary-500 hover:text-primary-600 dark:hover:text-primary-400","children":["$","$L5",null,{"className":"break-words","href":"/blog/workflow-vs-agent-7-dimensions.html","children":"Workflow vs Agent:用 7 维度框架定位你的项目"}]}]]}]]}]]}],["$","div",null,{"className":"pt-4 xl:pt-8","children":["$","$L5",null,{"className":"text-primary-500 hover:text-primary-600 dark:hover:text-primary-400","href":"/blog.html","aria-label":"Back to the blog","children":"← Back to the blog"}]}]]}] 1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] 17:null 19:{"metadata":[["$","title","0",{"children":"从参考项目移植功能的标准流程 | TechLog"}],["$","meta","1",{"name":"description","content":"将参考项目(如 NextChat、web-tools)的功能移植到当前项目的标准流程:从分析参考项目、确认集成范围、数据模型迁移、后端实现、前端实现、端到端验证到提 PR,覆盖 Python/FastAPI + Streamlit 技术栈的完整实践。"}],["$","meta","2",{"name":"robots","content":"index, follow"}],["$","meta","3",{"name":"googlebot","content":"index, follow, max-video-preview:-1, max-image-preview:large, max-snippet:-1"}],["$","link","4",{"rel":"canonical","href":"https://techlog-18328.app.workbuddy.host/blog/port-feature-from-reference"}],["$","link","5",{"rel":"alternate","type":"application/rss+xml","href":"https://techlog-18328.app.workbuddy.host/feed.xml"}],["$","meta","6",{"property":"og:title","content":"从参考项目移植功能的标准流程"}],["$","meta","7",{"property":"og:description","content":"将参考项目(如 NextChat、web-tools)的功能移植到当前项目的标准流程:从分析参考项目、确认集成范围、数据模型迁移、后端实现、前端实现、端到端验证到提 PR,覆盖 Python/FastAPI + Streamlit 技术栈的完整实践。"}],["$","meta","8",{"property":"og:url","content":"https://techlog-18328.app.workbuddy.host/blog/port-feature-from-reference"}],["$","meta","9",{"property":"og:site_name","content":"TechLog"}],["$","meta","10",{"property":"og:locale","content":"en_US"}],["$","meta","11",{"property":"og:image","content":"https://techlog-18328.app.workbuddy.host/static/images/twitter-card.png"}],["$","meta","12",{"property":"og:type","content":"article"}],["$","meta","13",{"property":"article:published_time","content":"2026-06-20T00:00:00.000Z"}],["$","meta","14",{"property":"article:modified_time","content":"2026-06-20T00:00:00.000Z"}],["$","meta","15",{"property":"article:author","content":"眠月"}],["$","meta","16",{"name":"twitter:card","content":"summary_large_image"}],["$","meta","17",{"name":"twitter:title","content":"从参考项目移植功能的标准流程"}],["$","meta","18",{"name":"twitter:description","content":"将参考项目(如 NextChat、web-tools)的功能移植到当前项目的标准流程:从分析参考项目、确认集成范围、数据模型迁移、后端实现、前端实现、端到端验证到提 PR,覆盖 Python/FastAPI + Streamlit 技术栈的完整实践。"}],["$","meta","19",{"name":"twitter:image","content":"https://techlog-18328.app.workbuddy.host/static/images/twitter-card.png"}]],"error":null,"digest":"$undefined"} 1e:"$19:metadata"