fix: 清洗标签并收敛入库标签噪音
This commit is contained in:
@@ -0,0 +1,615 @@
|
|||||||
|
{
|
||||||
|
"totalTags": 859,
|
||||||
|
"keepCount": 100,
|
||||||
|
"dropCount": 759,
|
||||||
|
"uncoveredProjects": [],
|
||||||
|
"forcedAdded": [
|
||||||
|
{
|
||||||
|
"id": "cmkqnj1pt0001l504vmrywqeg",
|
||||||
|
"name": "需要配置",
|
||||||
|
"projects": 163
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"keepTop100": [
|
||||||
|
{
|
||||||
|
"id": "cmk9fpg4h0000kt048l3a2nqu",
|
||||||
|
"name": "Python",
|
||||||
|
"nameEn": "Python",
|
||||||
|
"projects": 268
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqimx520005jx04vyegybp9",
|
||||||
|
"name": "AI开发工具",
|
||||||
|
"nameEn": "AI Development Tool",
|
||||||
|
"projects": 213
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqnj1pt0001l504vmrywqeg",
|
||||||
|
"name": "需要配置",
|
||||||
|
"nameEn": "Requires Configuration",
|
||||||
|
"projects": 163
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkjjb37v00064j12b84dsrwg",
|
||||||
|
"name": "TypeScript",
|
||||||
|
"nameEn": "TypeScript",
|
||||||
|
"projects": 118
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks0yz0h002xl1040w65w47d",
|
||||||
|
"name": "AI代理",
|
||||||
|
"nameEn": "AI Agents",
|
||||||
|
"projects": 52
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkql448t0002l504m7bkovze",
|
||||||
|
"name": "自动化",
|
||||||
|
"nameEn": "Automation",
|
||||||
|
"projects": 46
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqjvuo5000ejr04y9ksio48",
|
||||||
|
"name": "Docker",
|
||||||
|
"nameEn": "Docker",
|
||||||
|
"projects": 43
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkjjb37v00074j122wpemyas",
|
||||||
|
"name": "Node.js",
|
||||||
|
"nameEn": "Node.js",
|
||||||
|
"projects": 40
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkoutfvz0005jr04knetubim",
|
||||||
|
"name": "JavaScript",
|
||||||
|
"nameEn": "JavaScript",
|
||||||
|
"projects": 34
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqklwxm0018jr04zyzlrmlv",
|
||||||
|
"name": "React",
|
||||||
|
"nameEn": "React",
|
||||||
|
"projects": 34
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqjgpzq0002l104qknt9pho",
|
||||||
|
"name": "开源",
|
||||||
|
"nameEn": "Open Source",
|
||||||
|
"projects": 33
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqnvcbg0001jo04us31pcaj",
|
||||||
|
"name": "低学习成本",
|
||||||
|
"nameEn": "Low Learning Curve",
|
||||||
|
"projects": 31
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqk1dbi000ljr045f2rm18m",
|
||||||
|
"name": "PyTorch",
|
||||||
|
"nameEn": "PyTorch",
|
||||||
|
"projects": 30
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkrv2jpg0000l204swrx5xh4",
|
||||||
|
"name": "LangChain",
|
||||||
|
"nameEn": "LangChain",
|
||||||
|
"projects": 29
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqlcukz000dl5044c6z8542",
|
||||||
|
"name": "多智能体系统",
|
||||||
|
"nameEn": "Multi-Agent System",
|
||||||
|
"projects": 28
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqk1dc5000ojr04jq0exq1z",
|
||||||
|
"name": "大语言模型",
|
||||||
|
"nameEn": null,
|
||||||
|
"projects": 25
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqimwxl0000jx04l8q0rdzo",
|
||||||
|
"name": "LLM",
|
||||||
|
"nameEn": "LLM",
|
||||||
|
"projects": 22
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks0rbk1002jl104uc7yg2xv",
|
||||||
|
"name": "Go",
|
||||||
|
"nameEn": "Go",
|
||||||
|
"projects": 20
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqm1c6j0003jl04clkn9qyb",
|
||||||
|
"name": "企业级",
|
||||||
|
"nameEn": "Enterprise",
|
||||||
|
"projects": 20
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqnn8hj0006l504j2uedblz",
|
||||||
|
"name": "需要基础",
|
||||||
|
"nameEn": "Requires Basics",
|
||||||
|
"projects": 20
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks0yz1e002yl104nw4apfcq",
|
||||||
|
"name": "Library",
|
||||||
|
"nameEn": "Library",
|
||||||
|
"projects": 18
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkrx8euz0007ky04hrgq5ryg",
|
||||||
|
"name": "MCP",
|
||||||
|
"nameEn": "Model Context Protocol",
|
||||||
|
"projects": 18
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks16lb0003ml104acf4zi7e",
|
||||||
|
"name": "Markdown",
|
||||||
|
"nameEn": "Markdown",
|
||||||
|
"projects": 17
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqklwr20016jr04opezrtpo",
|
||||||
|
"name": "Next.js",
|
||||||
|
"nameEn": "Next.js",
|
||||||
|
"projects": 17
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks003y10015l104xruieqo9",
|
||||||
|
"name": "RAG",
|
||||||
|
"nameEn": "RAG",
|
||||||
|
"projects": 17
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqm1bzv0000jl04c3ks3w7w",
|
||||||
|
"name": "Java",
|
||||||
|
"nameEn": "Java",
|
||||||
|
"projects": 16
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks0c9ll0022l10436svssah",
|
||||||
|
"name": "MIT许可",
|
||||||
|
"nameEn": "MIT License",
|
||||||
|
"projects": 16
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqnn8gl0005l5042jdeuuur",
|
||||||
|
"name": "LangGraph",
|
||||||
|
"nameEn": "LangGraph",
|
||||||
|
"projects": 15
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkrx664z0000ky0481j124cq",
|
||||||
|
"name": "FastAPI",
|
||||||
|
"nameEn": "FastAPI",
|
||||||
|
"projects": 14
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqjvuir0008jr048591dzwp",
|
||||||
|
"name": "Rust",
|
||||||
|
"nameEn": "Rust",
|
||||||
|
"projects": 14
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkrzlq020001l104sosii49r",
|
||||||
|
"name": "复杂部署",
|
||||||
|
"nameEn": "Complex Deployment",
|
||||||
|
"projects": 14
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkjjb38l000c4j12oucpveph",
|
||||||
|
"name": "工作流自动化",
|
||||||
|
"nameEn": "Workflow Automation",
|
||||||
|
"projects": 14
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks3iu8m006wl104ndk8no10",
|
||||||
|
"name": "C#",
|
||||||
|
"nameEn": "C#",
|
||||||
|
"projects": 11
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqnn8mm0007l5045q6z4zb0",
|
||||||
|
"name": "OpenAI",
|
||||||
|
"nameEn": "OpenAI",
|
||||||
|
"projects": 11
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkrzvdfk000tl104jsq77zig",
|
||||||
|
"name": "OpenAI API",
|
||||||
|
"nameEn": "OpenAI API",
|
||||||
|
"projects": 11
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks3d1wy006ll104gltxc3et",
|
||||||
|
"name": "SDK",
|
||||||
|
"nameEn": "SDK",
|
||||||
|
"projects": 11
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks1aoxo003xl104gap1drce",
|
||||||
|
"name": "强化学习",
|
||||||
|
"nameEn": "Reinforcement Learning",
|
||||||
|
"projects": 11
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks1aoyn003yl104r4zspprz",
|
||||||
|
"name": "机器学习",
|
||||||
|
"nameEn": "Machine Learning",
|
||||||
|
"projects": 11
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmksczul60015jv04ft4n1cb2",
|
||||||
|
"name": "GitHub",
|
||||||
|
"nameEn": "GitHub",
|
||||||
|
"projects": 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks0m380002gl104o6f9cjtj",
|
||||||
|
"name": "PostgreSQL",
|
||||||
|
"nameEn": "PostgreSQL",
|
||||||
|
"projects": 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkrx8f0j000aky046uomv3rs",
|
||||||
|
"name": "多模态",
|
||||||
|
"nameEn": "Multimodal",
|
||||||
|
"projects": 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqjs7e50004jr042mrpi3ac",
|
||||||
|
"name": "桌面应用",
|
||||||
|
"nameEn": null,
|
||||||
|
"projects": 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqk4i0d000vjr04ai18b8h3",
|
||||||
|
"name": "知识管理",
|
||||||
|
"nameEn": null,
|
||||||
|
"projects": 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqk4i0g000wjr04t82lzvaq",
|
||||||
|
"name": "向量数据库",
|
||||||
|
"nameEn": null,
|
||||||
|
"projects": 9
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkrzlq0q0003l10453qhsniu",
|
||||||
|
"name": "Transformers",
|
||||||
|
"nameEn": "Transformers",
|
||||||
|
"projects": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks1ubzb004pl104mvs08pmu",
|
||||||
|
"name": "vLLM",
|
||||||
|
"nameEn": "vLLM",
|
||||||
|
"projects": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks0eqvq0028l10426ogrvkb",
|
||||||
|
"name": "云端服务",
|
||||||
|
"nameEn": "Cloud Service",
|
||||||
|
"projects": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks13z53003el104re4468xt",
|
||||||
|
"name": "代码生成",
|
||||||
|
"nameEn": "Code Generation",
|
||||||
|
"projects": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks05ci1001gl104lus7os2q",
|
||||||
|
"name": "企业级AI应用",
|
||||||
|
"nameEn": "Enterprise AI",
|
||||||
|
"projects": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks1s7p2004il104kpwprttq",
|
||||||
|
"name": "智能体框架",
|
||||||
|
"nameEn": "Agent Framework",
|
||||||
|
"projects": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqjs7dw0003jr04uy2bguck",
|
||||||
|
"name": "跨平台",
|
||||||
|
"nameEn": null,
|
||||||
|
"projects": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqklwxr0019jr048wp0hds8",
|
||||||
|
"name": "AI 助手",
|
||||||
|
"nameEn": "AI Assistant",
|
||||||
|
"projects": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks5ey0j001el204vjk69nwg",
|
||||||
|
"name": "AI安全",
|
||||||
|
"nameEn": "AI Security",
|
||||||
|
"projects": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqjs77s0000jr041cypbjkn",
|
||||||
|
"name": "Electron",
|
||||||
|
"nameEn": "Electron",
|
||||||
|
"projects": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkry59tc0001jy04kr21i343",
|
||||||
|
"name": "Playwright",
|
||||||
|
"nameEn": "Playwright",
|
||||||
|
"projects": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmk9fpg5f0003kt04fcgbvj7y",
|
||||||
|
"name": "多智能体",
|
||||||
|
"nameEn": "Multi-Agent",
|
||||||
|
"projects": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkrzxk4i0011l1045cubzwwr",
|
||||||
|
"name": "深度学习",
|
||||||
|
"nameEn": "Deep Learning",
|
||||||
|
"projects": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks13z46003dl1047ba0entr",
|
||||||
|
"name": "AI代理框架",
|
||||||
|
"nameEn": "AI Agent Framework",
|
||||||
|
"projects": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkry59u70003jy049eoxzkc7",
|
||||||
|
"name": "AI研究",
|
||||||
|
"nameEn": "AI Research",
|
||||||
|
"projects": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks2a6r1005il104ormxbqud",
|
||||||
|
"name": "Web应用",
|
||||||
|
"nameEn": "Web Application",
|
||||||
|
"projects": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkshrboj0000la04h6bmugvj",
|
||||||
|
"name": "学术研究",
|
||||||
|
"nameEn": "Academic Research",
|
||||||
|
"projects": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks112wx0039l104zltcl2hp",
|
||||||
|
"name": "工作流编排",
|
||||||
|
"nameEn": "Workflow Orchestration",
|
||||||
|
"projects": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkrzvdfe000sl104q0gge67f",
|
||||||
|
"name": "教程",
|
||||||
|
"nameEn": "Tutorial",
|
||||||
|
"projects": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks3w0g0000bl904u4lmk5d5",
|
||||||
|
"name": "浏览器自动化",
|
||||||
|
"nameEn": "Browser Automation",
|
||||||
|
"projects": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkt8m55q000nla04kgfld9x3",
|
||||||
|
"name": "知识图谱",
|
||||||
|
"nameEn": "Knowledge Graph",
|
||||||
|
"projects": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqklwxj0017jr04czubkbbi",
|
||||||
|
"name": "知识库",
|
||||||
|
"nameEn": "Knowledge Base",
|
||||||
|
"projects": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkshubus0004la04zjuh5nn5",
|
||||||
|
"name": "自主智能体",
|
||||||
|
"nameEn": "Autonomous Agents",
|
||||||
|
"projects": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqk4i0d000ujr045d2njmc7",
|
||||||
|
"name": "隐私保护",
|
||||||
|
"nameEn": null,
|
||||||
|
"projects": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks02o6m001al104gi1jztpn",
|
||||||
|
"name": "Apache-2.0",
|
||||||
|
"nameEn": "Apache-2.0",
|
||||||
|
"projects": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks4roac000el204paypouzl",
|
||||||
|
"name": "CLI",
|
||||||
|
"nameEn": "CLI",
|
||||||
|
"projects": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkt7yhut0000la040ij92vgk",
|
||||||
|
"name": "MCP协议",
|
||||||
|
"nameEn": "MCP Protocol",
|
||||||
|
"projects": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks5rr2e0020l204sn9ydvka",
|
||||||
|
"name": "Nest.js",
|
||||||
|
"nameEn": "Nest.js",
|
||||||
|
"projects": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkjjb37z00084j121651lnjl",
|
||||||
|
"name": "Vue.js",
|
||||||
|
"nameEn": "Vue.js",
|
||||||
|
"projects": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmksg1wc90061jv04114izr2d",
|
||||||
|
"name": "Web自动化",
|
||||||
|
"nameEn": "Web Automation",
|
||||||
|
"projects": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkuulu5d000jjc04u7hx67sz",
|
||||||
|
"name": "分布式系统",
|
||||||
|
"nameEn": "Distributed Systems",
|
||||||
|
"projects": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqimx3n0001jx043mvrmarx",
|
||||||
|
"name": "可视化",
|
||||||
|
"nameEn": "Visualization",
|
||||||
|
"projects": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmksf0ik1004ijv047hiusscw",
|
||||||
|
"name": "多模态AI",
|
||||||
|
"nameEn": "Multimodal AI",
|
||||||
|
"projects": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmktoexz8001wld046f13barg",
|
||||||
|
"name": "多语言支持",
|
||||||
|
"nameEn": "Multi-language Support",
|
||||||
|
"projects": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks2gqie005ol104v4d6nvck",
|
||||||
|
"name": "数据分析",
|
||||||
|
"nameEn": "Data Analytics",
|
||||||
|
"projects": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks0equs0026l104sl4td34d",
|
||||||
|
"name": "聊天机器人",
|
||||||
|
"nameEn": "Chatbot",
|
||||||
|
"projects": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkjjb38j000b4j125a3fg282",
|
||||||
|
"name": "自托管",
|
||||||
|
"nameEn": "Self-hosted",
|
||||||
|
"projects": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmktkj5to001hl704wtmqjft4",
|
||||||
|
"name": "自然语言处理",
|
||||||
|
"nameEn": "Natural Language Processing",
|
||||||
|
"projects": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkrzxk4a0010l104u7irj9je",
|
||||||
|
"name": "计算机视觉",
|
||||||
|
"nameEn": "Computer Vision",
|
||||||
|
"projects": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkto2g8o0019ld04b61pgj4y",
|
||||||
|
"name": "AI研究资源",
|
||||||
|
"nameEn": "AI Research Resource",
|
||||||
|
"projects": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmktk5hid000sl7049a2bo5al",
|
||||||
|
"name": "Benchmark",
|
||||||
|
"nameEn": "Benchmark",
|
||||||
|
"projects": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkv6j7uq000gk4043x1gfnp1",
|
||||||
|
"name": "Bun",
|
||||||
|
"nameEn": "Bun",
|
||||||
|
"projects": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkti8i470000kz04bpsajkzk",
|
||||||
|
"name": "ChromaDB",
|
||||||
|
"nameEn": "ChromaDB",
|
||||||
|
"projects": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks47ct90013l904x03rupwb",
|
||||||
|
"name": "Flask",
|
||||||
|
"nameEn": "Flask",
|
||||||
|
"projects": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkql9bho0007l50406oflxr0",
|
||||||
|
"name": "Gradio",
|
||||||
|
"nameEn": "Gradio",
|
||||||
|
"projects": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks112qf0034l104gkihwp10",
|
||||||
|
"name": "JavaScript/TypeScript",
|
||||||
|
"nameEn": "JavaScript/TypeScript",
|
||||||
|
"projects": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks1s7ut004jl104gvlybgfs",
|
||||||
|
"name": "LiteLLM",
|
||||||
|
"nameEn": "LiteLLM",
|
||||||
|
"projects": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks4pb54000bl204erpb9bbp",
|
||||||
|
"name": "LlamaIndex",
|
||||||
|
"nameEn": "LlamaIndex",
|
||||||
|
"projects": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmksfnrz1005qjv044pnraikr",
|
||||||
|
"name": "LLM应用",
|
||||||
|
"nameEn": "LLM Applications",
|
||||||
|
"projects": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqimx480003jx04bcqob7pb",
|
||||||
|
"name": "NLP",
|
||||||
|
"nameEn": "NLP",
|
||||||
|
"projects": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqm1c690001jl04ge5elv46",
|
||||||
|
"name": "Spring Boot",
|
||||||
|
"nameEn": "Spring Boot",
|
||||||
|
"projects": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks2a6l3005gl1040u4s0k4s",
|
||||||
|
"name": "Streamlit",
|
||||||
|
"nameEn": "Streamlit",
|
||||||
|
"projects": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkth0k4x0026ju04t0euxxwk",
|
||||||
|
"name": "VS Code扩展",
|
||||||
|
"nameEn": "VS Code Extension",
|
||||||
|
"projects": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkscxy490012jv04rf4k4h4h",
|
||||||
|
"name": "学术资源",
|
||||||
|
"nameEn": "Academic Resource",
|
||||||
|
"projects": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkscgc0z0007jv04nwmzbn8c",
|
||||||
|
"name": "工具调用",
|
||||||
|
"nameEn": "Tool Calling",
|
||||||
|
"projects": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkuxtw0m002ojm04pl9qazxs",
|
||||||
|
"name": "用户通知",
|
||||||
|
"nameEn": "User Notification",
|
||||||
|
"projects": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
[
|
||||||
|
"cmk9fpg4h0000kt048l3a2nqu",
|
||||||
|
"cmkjjb37v00064j12b84dsrwg",
|
||||||
|
"cmkqimx520005jx04vyegybp9",
|
||||||
|
"cmks16lb0003ml104acf4zi7e",
|
||||||
|
"cmks0yz0h002xl1040w65w47d",
|
||||||
|
"cmkqjgpzq0002l104qknt9pho",
|
||||||
|
"cmkql448t0002l504m7bkovze",
|
||||||
|
"cmkqm1bzv0000jl04c3ks3w7w",
|
||||||
|
"cmkoutfvz0005jr04knetubim",
|
||||||
|
"cmks0rbk1002jl104uc7yg2xv",
|
||||||
|
"cmks3iu8m006wl104ndk8no10",
|
||||||
|
"cmkqk1dc5000ojr04jq0exq1z",
|
||||||
|
"cmkqimwxl0000jx04l8q0rdzo",
|
||||||
|
"cmks0yz1e002yl104nw4apfcq",
|
||||||
|
"cmkqjvuo5000ejr04y9ksio48",
|
||||||
|
"cmkjjb37v00074j122wpemyas",
|
||||||
|
"cmkqnvcbg0001jo04us31pcaj",
|
||||||
|
"cmkrzlq020001l104sosii49r",
|
||||||
|
"cmks1aoyn003yl104r4zspprz",
|
||||||
|
"cmks5ey0j001el204vjk69nwg",
|
||||||
|
"cmkshrboj0000la04h6bmugvj",
|
||||||
|
"cmkscgc0z0007jv04nwmzbn8c",
|
||||||
|
"cmkscxy490012jv04rf4k4h4h",
|
||||||
|
"cmkuxtw0m002ojm04pl9qazxs",
|
||||||
|
"cmkqklwxm0018jr04zyzlrmlv",
|
||||||
|
"cmkqk1dbi000ljr045f2rm18m",
|
||||||
|
"cmkrv2jpg0000l204swrx5xh4",
|
||||||
|
"cmkqlcukz000dl5044c6z8542",
|
||||||
|
"cmkqm1c6j0003jl04clkn9qyb",
|
||||||
|
"cmkqnn8hj0006l504j2uedblz",
|
||||||
|
"cmkrx8euz0007ky04hrgq5ryg",
|
||||||
|
"cmkqklwr20016jr04opezrtpo",
|
||||||
|
"cmks003y10015l104xruieqo9",
|
||||||
|
"cmks0c9ll0022l10436svssah",
|
||||||
|
"cmkqnn8gl0005l5042jdeuuur",
|
||||||
|
"cmkrx664z0000ky0481j124cq",
|
||||||
|
"cmkqjvuir0008jr048591dzwp",
|
||||||
|
"cmkjjb38l000c4j12oucpveph",
|
||||||
|
"cmkqnn8mm0007l5045q6z4zb0",
|
||||||
|
"cmkrzvdfk000tl104jsq77zig",
|
||||||
|
"cmks3d1wy006ll104gltxc3et",
|
||||||
|
"cmks1aoxo003xl104gap1drce",
|
||||||
|
"cmksczul60015jv04ft4n1cb2",
|
||||||
|
"cmks0m380002gl104o6f9cjtj",
|
||||||
|
"cmkrx8f0j000aky046uomv3rs",
|
||||||
|
"cmkqjs7e50004jr042mrpi3ac",
|
||||||
|
"cmkqk4i0d000vjr04ai18b8h3",
|
||||||
|
"cmkqk4i0g000wjr04t82lzvaq",
|
||||||
|
"cmkrzlq0q0003l10453qhsniu",
|
||||||
|
"cmks1ubzb004pl104mvs08pmu",
|
||||||
|
"cmks0eqvq0028l10426ogrvkb",
|
||||||
|
"cmks13z53003el104re4468xt",
|
||||||
|
"cmks05ci1001gl104lus7os2q",
|
||||||
|
"cmks1s7p2004il104kpwprttq",
|
||||||
|
"cmkqjs7dw0003jr04uy2bguck",
|
||||||
|
"cmkqklwxr0019jr048wp0hds8",
|
||||||
|
"cmkqjs77s0000jr041cypbjkn",
|
||||||
|
"cmkry59tc0001jy04kr21i343",
|
||||||
|
"cmk9fpg5f0003kt04fcgbvj7y",
|
||||||
|
"cmkrzxk4i0011l1045cubzwwr",
|
||||||
|
"cmks13z46003dl1047ba0entr",
|
||||||
|
"cmkry59u70003jy049eoxzkc7",
|
||||||
|
"cmks2a6r1005il104ormxbqud",
|
||||||
|
"cmks112wx0039l104zltcl2hp",
|
||||||
|
"cmkrzvdfe000sl104q0gge67f",
|
||||||
|
"cmks3w0g0000bl904u4lmk5d5",
|
||||||
|
"cmkt8m55q000nla04kgfld9x3",
|
||||||
|
"cmkqklwxj0017jr04czubkbbi",
|
||||||
|
"cmkshubus0004la04zjuh5nn5",
|
||||||
|
"cmkqk4i0d000ujr045d2njmc7",
|
||||||
|
"cmks02o6m001al104gi1jztpn",
|
||||||
|
"cmks4roac000el204paypouzl",
|
||||||
|
"cmkt7yhut0000la040ij92vgk",
|
||||||
|
"cmks5rr2e0020l204sn9ydvka",
|
||||||
|
"cmkjjb37z00084j121651lnjl",
|
||||||
|
"cmksg1wc90061jv04114izr2d",
|
||||||
|
"cmkuulu5d000jjc04u7hx67sz",
|
||||||
|
"cmkqimx3n0001jx043mvrmarx",
|
||||||
|
"cmksf0ik1004ijv047hiusscw",
|
||||||
|
"cmktoexz8001wld046f13barg",
|
||||||
|
"cmks2gqie005ol104v4d6nvck",
|
||||||
|
"cmks0equs0026l104sl4td34d",
|
||||||
|
"cmkjjb38j000b4j125a3fg282",
|
||||||
|
"cmktkj5to001hl704wtmqjft4",
|
||||||
|
"cmkrzxk4a0010l104u7irj9je",
|
||||||
|
"cmkto2g8o0019ld04b61pgj4y",
|
||||||
|
"cmktk5hid000sl7049a2bo5al",
|
||||||
|
"cmkv6j7uq000gk4043x1gfnp1",
|
||||||
|
"cmkti8i470000kz04bpsajkzk",
|
||||||
|
"cmks47ct90013l904x03rupwb",
|
||||||
|
"cmkql9bho0007l50406oflxr0",
|
||||||
|
"cmks112qf0034l104gkihwp10",
|
||||||
|
"cmks1s7ut004jl104gvlybgfs",
|
||||||
|
"cmks4pb54000bl204erpb9bbp",
|
||||||
|
"cmksfnrz1005qjv044pnraikr",
|
||||||
|
"cmkqimx480003jx04bcqob7pb",
|
||||||
|
"cmkqm1c690001jl04ge5elv46",
|
||||||
|
"cmks2a6l3005gl1040u4s0k4s",
|
||||||
|
"cmkth0k4x0026ju04t0euxxwk",
|
||||||
|
"cmkqnj1pt0001l504vmrywqeg"
|
||||||
|
]
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
[
|
||||||
|
"cmk9fpg4h0000kt048l3a2nqu",
|
||||||
|
"cmkqimx520005jx04vyegybp9",
|
||||||
|
"cmkjjb37v00064j12b84dsrwg",
|
||||||
|
"cmks0yz0h002xl1040w65w47d",
|
||||||
|
"cmkql448t0002l504m7bkovze",
|
||||||
|
"cmkqjvuo5000ejr04y9ksio48",
|
||||||
|
"cmkjjb37v00074j122wpemyas",
|
||||||
|
"cmkoutfvz0005jr04knetubim",
|
||||||
|
"cmkqklwxm0018jr04zyzlrmlv",
|
||||||
|
"cmkqjgpzq0002l104qknt9pho",
|
||||||
|
"cmkqnvcbg0001jo04us31pcaj",
|
||||||
|
"cmkqk1dbi000ljr045f2rm18m",
|
||||||
|
"cmkrv2jpg0000l204swrx5xh4",
|
||||||
|
"cmkqlcukz000dl5044c6z8542",
|
||||||
|
"cmkqk1dc5000ojr04jq0exq1z",
|
||||||
|
"cmkqimwxl0000jx04l8q0rdzo",
|
||||||
|
"cmks0rbk1002jl104uc7yg2xv",
|
||||||
|
"cmkqm1c6j0003jl04clkn9qyb",
|
||||||
|
"cmkqnn8hj0006l504j2uedblz",
|
||||||
|
"cmks0yz1e002yl104nw4apfcq",
|
||||||
|
"cmkrx8euz0007ky04hrgq5ryg",
|
||||||
|
"cmks16lb0003ml104acf4zi7e",
|
||||||
|
"cmkqklwr20016jr04opezrtpo",
|
||||||
|
"cmks003y10015l104xruieqo9",
|
||||||
|
"cmkqm1bzv0000jl04c3ks3w7w",
|
||||||
|
"cmks0c9ll0022l10436svssah",
|
||||||
|
"cmkqnn8gl0005l5042jdeuuur",
|
||||||
|
"cmkrzlq020001l104sosii49r",
|
||||||
|
"cmkrx664z0000ky0481j124cq",
|
||||||
|
"cmkqjvuir0008jr048591dzwp",
|
||||||
|
"cmkjjb38l000c4j12oucpveph",
|
||||||
|
"cmks3iu8m006wl104ndk8no10",
|
||||||
|
"cmks1aoyn003yl104r4zspprz",
|
||||||
|
"cmkqnn8mm0007l5045q6z4zb0",
|
||||||
|
"cmkrzvdfk000tl104jsq77zig",
|
||||||
|
"cmks3d1wy006ll104gltxc3et",
|
||||||
|
"cmks1aoxo003xl104gap1drce",
|
||||||
|
"cmksczul60015jv04ft4n1cb2",
|
||||||
|
"cmks0m380002gl104o6f9cjtj",
|
||||||
|
"cmkrx8f0j000aky046uomv3rs",
|
||||||
|
"cmkqjs7e50004jr042mrpi3ac",
|
||||||
|
"cmkqk4i0d000vjr04ai18b8h3",
|
||||||
|
"cmkqk4i0g000wjr04t82lzvaq",
|
||||||
|
"cmkrzlq0q0003l10453qhsniu",
|
||||||
|
"cmks1ubzb004pl104mvs08pmu",
|
||||||
|
"cmks0eqvq0028l10426ogrvkb",
|
||||||
|
"cmks13z53003el104re4468xt",
|
||||||
|
"cmks05ci1001gl104lus7os2q",
|
||||||
|
"cmks1s7p2004il104kpwprttq",
|
||||||
|
"cmkqjs7dw0003jr04uy2bguck",
|
||||||
|
"cmks5ey0j001el204vjk69nwg",
|
||||||
|
"cmkqklwxr0019jr048wp0hds8",
|
||||||
|
"cmkqjs77s0000jr041cypbjkn",
|
||||||
|
"cmkry59tc0001jy04kr21i343",
|
||||||
|
"cmk9fpg5f0003kt04fcgbvj7y",
|
||||||
|
"cmkrzxk4i0011l1045cubzwwr",
|
||||||
|
"cmkshrboj0000la04h6bmugvj",
|
||||||
|
"cmks13z46003dl1047ba0entr",
|
||||||
|
"cmkry59u70003jy049eoxzkc7",
|
||||||
|
"cmks2a6r1005il104ormxbqud",
|
||||||
|
"cmks112wx0039l104zltcl2hp",
|
||||||
|
"cmkrzvdfe000sl104q0gge67f",
|
||||||
|
"cmks3w0g0000bl904u4lmk5d5",
|
||||||
|
"cmkt8m55q000nla04kgfld9x3",
|
||||||
|
"cmkqklwxj0017jr04czubkbbi",
|
||||||
|
"cmkshubus0004la04zjuh5nn5",
|
||||||
|
"cmkqk4i0d000ujr045d2njmc7",
|
||||||
|
"cmks02o6m001al104gi1jztpn",
|
||||||
|
"cmks4roac000el204paypouzl",
|
||||||
|
"cmkt7yhut0000la040ij92vgk",
|
||||||
|
"cmks5rr2e0020l204sn9ydvka",
|
||||||
|
"cmkjjb37z00084j121651lnjl",
|
||||||
|
"cmksg1wc90061jv04114izr2d",
|
||||||
|
"cmkuulu5d000jjc04u7hx67sz",
|
||||||
|
"cmkqimx3n0001jx043mvrmarx",
|
||||||
|
"cmksf0ik1004ijv047hiusscw",
|
||||||
|
"cmktoexz8001wld046f13barg",
|
||||||
|
"cmks2gqie005ol104v4d6nvck",
|
||||||
|
"cmks0equs0026l104sl4td34d",
|
||||||
|
"cmkjjb38j000b4j125a3fg282",
|
||||||
|
"cmktkj5to001hl704wtmqjft4",
|
||||||
|
"cmkrzxk4a0010l104u7irj9je",
|
||||||
|
"cmkscgc0z0007jv04nwmzbn8c",
|
||||||
|
"cmkscxy490012jv04rf4k4h4h",
|
||||||
|
"cmkto2g8o0019ld04b61pgj4y",
|
||||||
|
"cmktk5hid000sl7049a2bo5al",
|
||||||
|
"cmkv6j7uq000gk4043x1gfnp1",
|
||||||
|
"cmkti8i470000kz04bpsajkzk",
|
||||||
|
"cmks47ct90013l904x03rupwb",
|
||||||
|
"cmkql9bho0007l50406oflxr0",
|
||||||
|
"cmks112qf0034l104gkihwp10",
|
||||||
|
"cmks1s7ut004jl104gvlybgfs",
|
||||||
|
"cmks4pb54000bl204erpb9bbp",
|
||||||
|
"cmksfnrz1005qjv044pnraikr",
|
||||||
|
"cmkqimx480003jx04bcqob7pb",
|
||||||
|
"cmkqm1c690001jl04ge5elv46",
|
||||||
|
"cmks2a6l3005gl1040u4s0k4s",
|
||||||
|
"cmkth0k4x0026ju04t0euxxwk",
|
||||||
|
"cmkuuqo3g000njc04l72uwte3",
|
||||||
|
"cmkuxtw0m002ojm04pl9qazxs"
|
||||||
|
]
|
||||||
@@ -0,0 +1,784 @@
|
|||||||
|
{
|
||||||
|
"totalTags": 859,
|
||||||
|
"candidatesAfterGenericFilter": 831,
|
||||||
|
"selectedCount": 100,
|
||||||
|
"selectedProjectsCoverage": {
|
||||||
|
"coveredProjects": 453,
|
||||||
|
"totalProjects": 454,
|
||||||
|
"coverageRatio": 0.9978,
|
||||||
|
"uncoveredProjects": 1
|
||||||
|
},
|
||||||
|
"dropCount": 759,
|
||||||
|
"topSelected": [
|
||||||
|
{
|
||||||
|
"id": "cmk9fpg4h0000kt048l3a2nqu",
|
||||||
|
"name": "Python",
|
||||||
|
"nameEn": "Python",
|
||||||
|
"projects": 268
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqimx520005jx04vyegybp9",
|
||||||
|
"name": "AI开发工具",
|
||||||
|
"nameEn": "AI Development Tool",
|
||||||
|
"projects": 213
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkjjb37v00064j12b84dsrwg",
|
||||||
|
"name": "TypeScript",
|
||||||
|
"nameEn": "TypeScript",
|
||||||
|
"projects": 118
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks0yz0h002xl1040w65w47d",
|
||||||
|
"name": "AI代理",
|
||||||
|
"nameEn": "AI Agents",
|
||||||
|
"projects": 52
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkql448t0002l504m7bkovze",
|
||||||
|
"name": "自动化",
|
||||||
|
"nameEn": "Automation",
|
||||||
|
"projects": 46
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqjvuo5000ejr04y9ksio48",
|
||||||
|
"name": "Docker",
|
||||||
|
"nameEn": "Docker",
|
||||||
|
"projects": 43
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkjjb37v00074j122wpemyas",
|
||||||
|
"name": "Node.js",
|
||||||
|
"nameEn": "Node.js",
|
||||||
|
"projects": 40
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkoutfvz0005jr04knetubim",
|
||||||
|
"name": "JavaScript",
|
||||||
|
"nameEn": "JavaScript",
|
||||||
|
"projects": 34
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqklwxm0018jr04zyzlrmlv",
|
||||||
|
"name": "React",
|
||||||
|
"nameEn": "React",
|
||||||
|
"projects": 34
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqjgpzq0002l104qknt9pho",
|
||||||
|
"name": "开源",
|
||||||
|
"nameEn": "Open Source",
|
||||||
|
"projects": 33
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqnvcbg0001jo04us31pcaj",
|
||||||
|
"name": "低学习成本",
|
||||||
|
"nameEn": "Low Learning Curve",
|
||||||
|
"projects": 31
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqk1dbi000ljr045f2rm18m",
|
||||||
|
"name": "PyTorch",
|
||||||
|
"nameEn": "PyTorch",
|
||||||
|
"projects": 30
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkrv2jpg0000l204swrx5xh4",
|
||||||
|
"name": "LangChain",
|
||||||
|
"nameEn": "LangChain",
|
||||||
|
"projects": 29
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqlcukz000dl5044c6z8542",
|
||||||
|
"name": "多智能体系统",
|
||||||
|
"nameEn": "Multi-Agent System",
|
||||||
|
"projects": 28
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqk1dc5000ojr04jq0exq1z",
|
||||||
|
"name": "大语言模型",
|
||||||
|
"nameEn": null,
|
||||||
|
"projects": 25
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqimwxl0000jx04l8q0rdzo",
|
||||||
|
"name": "LLM",
|
||||||
|
"nameEn": "LLM",
|
||||||
|
"projects": 22
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks0rbk1002jl104uc7yg2xv",
|
||||||
|
"name": "Go",
|
||||||
|
"nameEn": "Go",
|
||||||
|
"projects": 20
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqm1c6j0003jl04clkn9qyb",
|
||||||
|
"name": "企业级",
|
||||||
|
"nameEn": "Enterprise",
|
||||||
|
"projects": 20
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqnn8hj0006l504j2uedblz",
|
||||||
|
"name": "需要基础",
|
||||||
|
"nameEn": "Requires Basics",
|
||||||
|
"projects": 20
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks0yz1e002yl104nw4apfcq",
|
||||||
|
"name": "Library",
|
||||||
|
"nameEn": "Library",
|
||||||
|
"projects": 18
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkrx8euz0007ky04hrgq5ryg",
|
||||||
|
"name": "MCP",
|
||||||
|
"nameEn": "Model Context Protocol",
|
||||||
|
"projects": 18
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks16lb0003ml104acf4zi7e",
|
||||||
|
"name": "Markdown",
|
||||||
|
"nameEn": "Markdown",
|
||||||
|
"projects": 17
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqklwr20016jr04opezrtpo",
|
||||||
|
"name": "Next.js",
|
||||||
|
"nameEn": "Next.js",
|
||||||
|
"projects": 17
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks003y10015l104xruieqo9",
|
||||||
|
"name": "RAG",
|
||||||
|
"nameEn": "RAG",
|
||||||
|
"projects": 17
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqm1bzv0000jl04c3ks3w7w",
|
||||||
|
"name": "Java",
|
||||||
|
"nameEn": "Java",
|
||||||
|
"projects": 16
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks0c9ll0022l10436svssah",
|
||||||
|
"name": "MIT许可",
|
||||||
|
"nameEn": "MIT License",
|
||||||
|
"projects": 16
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqnn8gl0005l5042jdeuuur",
|
||||||
|
"name": "LangGraph",
|
||||||
|
"nameEn": "LangGraph",
|
||||||
|
"projects": 15
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkrzlq020001l104sosii49r",
|
||||||
|
"name": "复杂部署",
|
||||||
|
"nameEn": "Complex Deployment",
|
||||||
|
"projects": 14
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkrx664z0000ky0481j124cq",
|
||||||
|
"name": "FastAPI",
|
||||||
|
"nameEn": "FastAPI",
|
||||||
|
"projects": 14
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqjvuir0008jr048591dzwp",
|
||||||
|
"name": "Rust",
|
||||||
|
"nameEn": "Rust",
|
||||||
|
"projects": 14
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkjjb38l000c4j12oucpveph",
|
||||||
|
"name": "工作流自动化",
|
||||||
|
"nameEn": "Workflow Automation",
|
||||||
|
"projects": 14
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks3iu8m006wl104ndk8no10",
|
||||||
|
"name": "C#",
|
||||||
|
"nameEn": "C#",
|
||||||
|
"projects": 11
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks1aoyn003yl104r4zspprz",
|
||||||
|
"name": "机器学习",
|
||||||
|
"nameEn": "Machine Learning",
|
||||||
|
"projects": 11
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqnn8mm0007l5045q6z4zb0",
|
||||||
|
"name": "OpenAI",
|
||||||
|
"nameEn": "OpenAI",
|
||||||
|
"projects": 11
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkrzvdfk000tl104jsq77zig",
|
||||||
|
"name": "OpenAI API",
|
||||||
|
"nameEn": "OpenAI API",
|
||||||
|
"projects": 11
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks3d1wy006ll104gltxc3et",
|
||||||
|
"name": "SDK",
|
||||||
|
"nameEn": "SDK",
|
||||||
|
"projects": 11
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks1aoxo003xl104gap1drce",
|
||||||
|
"name": "强化学习",
|
||||||
|
"nameEn": "Reinforcement Learning",
|
||||||
|
"projects": 11
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmksczul60015jv04ft4n1cb2",
|
||||||
|
"name": "GitHub",
|
||||||
|
"nameEn": "GitHub",
|
||||||
|
"projects": 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks0m380002gl104o6f9cjtj",
|
||||||
|
"name": "PostgreSQL",
|
||||||
|
"nameEn": "PostgreSQL",
|
||||||
|
"projects": 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkrx8f0j000aky046uomv3rs",
|
||||||
|
"name": "多模态",
|
||||||
|
"nameEn": "Multimodal",
|
||||||
|
"projects": 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqjs7e50004jr042mrpi3ac",
|
||||||
|
"name": "桌面应用",
|
||||||
|
"nameEn": null,
|
||||||
|
"projects": 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqk4i0d000vjr04ai18b8h3",
|
||||||
|
"name": "知识管理",
|
||||||
|
"nameEn": null,
|
||||||
|
"projects": 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqk4i0g000wjr04t82lzvaq",
|
||||||
|
"name": "向量数据库",
|
||||||
|
"nameEn": null,
|
||||||
|
"projects": 9
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkrzlq0q0003l10453qhsniu",
|
||||||
|
"name": "Transformers",
|
||||||
|
"nameEn": "Transformers",
|
||||||
|
"projects": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks1ubzb004pl104mvs08pmu",
|
||||||
|
"name": "vLLM",
|
||||||
|
"nameEn": "vLLM",
|
||||||
|
"projects": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks0eqvq0028l10426ogrvkb",
|
||||||
|
"name": "云端服务",
|
||||||
|
"nameEn": "Cloud Service",
|
||||||
|
"projects": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks13z53003el104re4468xt",
|
||||||
|
"name": "代码生成",
|
||||||
|
"nameEn": "Code Generation",
|
||||||
|
"projects": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks05ci1001gl104lus7os2q",
|
||||||
|
"name": "企业级AI应用",
|
||||||
|
"nameEn": "Enterprise AI",
|
||||||
|
"projects": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks1s7p2004il104kpwprttq",
|
||||||
|
"name": "智能体框架",
|
||||||
|
"nameEn": "Agent Framework",
|
||||||
|
"projects": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqjs7dw0003jr04uy2bguck",
|
||||||
|
"name": "跨平台",
|
||||||
|
"nameEn": null,
|
||||||
|
"projects": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks5ey0j001el204vjk69nwg",
|
||||||
|
"name": "AI安全",
|
||||||
|
"nameEn": "AI Security",
|
||||||
|
"projects": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqklwxr0019jr048wp0hds8",
|
||||||
|
"name": "AI 助手",
|
||||||
|
"nameEn": "AI Assistant",
|
||||||
|
"projects": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqjs77s0000jr041cypbjkn",
|
||||||
|
"name": "Electron",
|
||||||
|
"nameEn": "Electron",
|
||||||
|
"projects": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkry59tc0001jy04kr21i343",
|
||||||
|
"name": "Playwright",
|
||||||
|
"nameEn": "Playwright",
|
||||||
|
"projects": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmk9fpg5f0003kt04fcgbvj7y",
|
||||||
|
"name": "多智能体",
|
||||||
|
"nameEn": "Multi-Agent",
|
||||||
|
"projects": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkrzxk4i0011l1045cubzwwr",
|
||||||
|
"name": "深度学习",
|
||||||
|
"nameEn": "Deep Learning",
|
||||||
|
"projects": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkshrboj0000la04h6bmugvj",
|
||||||
|
"name": "学术研究",
|
||||||
|
"nameEn": "Academic Research",
|
||||||
|
"projects": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks13z46003dl1047ba0entr",
|
||||||
|
"name": "AI代理框架",
|
||||||
|
"nameEn": "AI Agent Framework",
|
||||||
|
"projects": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkry59u70003jy049eoxzkc7",
|
||||||
|
"name": "AI研究",
|
||||||
|
"nameEn": "AI Research",
|
||||||
|
"projects": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks2a6r1005il104ormxbqud",
|
||||||
|
"name": "Web应用",
|
||||||
|
"nameEn": "Web Application",
|
||||||
|
"projects": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks112wx0039l104zltcl2hp",
|
||||||
|
"name": "工作流编排",
|
||||||
|
"nameEn": "Workflow Orchestration",
|
||||||
|
"projects": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkrzvdfe000sl104q0gge67f",
|
||||||
|
"name": "教程",
|
||||||
|
"nameEn": "Tutorial",
|
||||||
|
"projects": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks3w0g0000bl904u4lmk5d5",
|
||||||
|
"name": "浏览器自动化",
|
||||||
|
"nameEn": "Browser Automation",
|
||||||
|
"projects": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkt8m55q000nla04kgfld9x3",
|
||||||
|
"name": "知识图谱",
|
||||||
|
"nameEn": "Knowledge Graph",
|
||||||
|
"projects": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqklwxj0017jr04czubkbbi",
|
||||||
|
"name": "知识库",
|
||||||
|
"nameEn": "Knowledge Base",
|
||||||
|
"projects": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkshubus0004la04zjuh5nn5",
|
||||||
|
"name": "自主智能体",
|
||||||
|
"nameEn": "Autonomous Agents",
|
||||||
|
"projects": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqk4i0d000ujr045d2njmc7",
|
||||||
|
"name": "隐私保护",
|
||||||
|
"nameEn": null,
|
||||||
|
"projects": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks02o6m001al104gi1jztpn",
|
||||||
|
"name": "Apache-2.0",
|
||||||
|
"nameEn": "Apache-2.0",
|
||||||
|
"projects": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks4roac000el204paypouzl",
|
||||||
|
"name": "CLI",
|
||||||
|
"nameEn": "CLI",
|
||||||
|
"projects": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkt7yhut0000la040ij92vgk",
|
||||||
|
"name": "MCP协议",
|
||||||
|
"nameEn": "MCP Protocol",
|
||||||
|
"projects": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks5rr2e0020l204sn9ydvka",
|
||||||
|
"name": "Nest.js",
|
||||||
|
"nameEn": "Nest.js",
|
||||||
|
"projects": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkjjb37z00084j121651lnjl",
|
||||||
|
"name": "Vue.js",
|
||||||
|
"nameEn": "Vue.js",
|
||||||
|
"projects": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmksg1wc90061jv04114izr2d",
|
||||||
|
"name": "Web自动化",
|
||||||
|
"nameEn": "Web Automation",
|
||||||
|
"projects": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkuulu5d000jjc04u7hx67sz",
|
||||||
|
"name": "分布式系统",
|
||||||
|
"nameEn": "Distributed Systems",
|
||||||
|
"projects": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqimx3n0001jx043mvrmarx",
|
||||||
|
"name": "可视化",
|
||||||
|
"nameEn": "Visualization",
|
||||||
|
"projects": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmksf0ik1004ijv047hiusscw",
|
||||||
|
"name": "多模态AI",
|
||||||
|
"nameEn": "Multimodal AI",
|
||||||
|
"projects": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmktoexz8001wld046f13barg",
|
||||||
|
"name": "多语言支持",
|
||||||
|
"nameEn": "Multi-language Support",
|
||||||
|
"projects": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks2gqie005ol104v4d6nvck",
|
||||||
|
"name": "数据分析",
|
||||||
|
"nameEn": "Data Analytics",
|
||||||
|
"projects": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks0equs0026l104sl4td34d",
|
||||||
|
"name": "聊天机器人",
|
||||||
|
"nameEn": "Chatbot",
|
||||||
|
"projects": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkjjb38j000b4j125a3fg282",
|
||||||
|
"name": "自托管",
|
||||||
|
"nameEn": "Self-hosted",
|
||||||
|
"projects": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmktkj5to001hl704wtmqjft4",
|
||||||
|
"name": "自然语言处理",
|
||||||
|
"nameEn": "Natural Language Processing",
|
||||||
|
"projects": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkrzxk4a0010l104u7irj9je",
|
||||||
|
"name": "计算机视觉",
|
||||||
|
"nameEn": "Computer Vision",
|
||||||
|
"projects": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkscgc0z0007jv04nwmzbn8c",
|
||||||
|
"name": "工具调用",
|
||||||
|
"nameEn": "Tool Calling",
|
||||||
|
"projects": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkscxy490012jv04rf4k4h4h",
|
||||||
|
"name": "学术资源",
|
||||||
|
"nameEn": "Academic Resource",
|
||||||
|
"projects": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkto2g8o0019ld04b61pgj4y",
|
||||||
|
"name": "AI研究资源",
|
||||||
|
"nameEn": "AI Research Resource",
|
||||||
|
"projects": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmktk5hid000sl7049a2bo5al",
|
||||||
|
"name": "Benchmark",
|
||||||
|
"nameEn": "Benchmark",
|
||||||
|
"projects": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkv6j7uq000gk4043x1gfnp1",
|
||||||
|
"name": "Bun",
|
||||||
|
"nameEn": "Bun",
|
||||||
|
"projects": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkti8i470000kz04bpsajkzk",
|
||||||
|
"name": "ChromaDB",
|
||||||
|
"nameEn": "ChromaDB",
|
||||||
|
"projects": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks47ct90013l904x03rupwb",
|
||||||
|
"name": "Flask",
|
||||||
|
"nameEn": "Flask",
|
||||||
|
"projects": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkql9bho0007l50406oflxr0",
|
||||||
|
"name": "Gradio",
|
||||||
|
"nameEn": "Gradio",
|
||||||
|
"projects": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks112qf0034l104gkihwp10",
|
||||||
|
"name": "JavaScript/TypeScript",
|
||||||
|
"nameEn": "JavaScript/TypeScript",
|
||||||
|
"projects": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks1s7ut004jl104gvlybgfs",
|
||||||
|
"name": "LiteLLM",
|
||||||
|
"nameEn": "LiteLLM",
|
||||||
|
"projects": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks4pb54000bl204erpb9bbp",
|
||||||
|
"name": "LlamaIndex",
|
||||||
|
"nameEn": "LlamaIndex",
|
||||||
|
"projects": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmksfnrz1005qjv044pnraikr",
|
||||||
|
"name": "LLM应用",
|
||||||
|
"nameEn": "LLM Applications",
|
||||||
|
"projects": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqimx480003jx04bcqob7pb",
|
||||||
|
"name": "NLP",
|
||||||
|
"nameEn": "NLP",
|
||||||
|
"projects": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqm1c690001jl04ge5elv46",
|
||||||
|
"name": "Spring Boot",
|
||||||
|
"nameEn": "Spring Boot",
|
||||||
|
"projects": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks2a6l3005gl1040u4s0k4s",
|
||||||
|
"name": "Streamlit",
|
||||||
|
"nameEn": "Streamlit",
|
||||||
|
"projects": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkth0k4x0026ju04t0euxxwk",
|
||||||
|
"name": "VS Code扩展",
|
||||||
|
"nameEn": "VS Code Extension",
|
||||||
|
"projects": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkuuqo3g000njc04l72uwte3",
|
||||||
|
"name": "文件系统操作",
|
||||||
|
"nameEn": "File System Operations",
|
||||||
|
"projects": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkuxtw0m002ojm04pl9qazxs",
|
||||||
|
"name": "用户通知",
|
||||||
|
"nameEn": "User Notification",
|
||||||
|
"projects": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"genericRemovedTop": [
|
||||||
|
{
|
||||||
|
"id": "cmkoutfta0001jr04qlwpr3xh",
|
||||||
|
"name": "活跃维护",
|
||||||
|
"nameEn": "Active Maintenance",
|
||||||
|
"projects": 317
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqk1dbs000njr0418v38jh4",
|
||||||
|
"name": "可商用",
|
||||||
|
"nameEn": null,
|
||||||
|
"projects": 171
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqnj1pt0001l504vmrywqeg",
|
||||||
|
"name": "需要配置",
|
||||||
|
"nameEn": "Requires Configuration",
|
||||||
|
"projects": 163
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqk1div000qjr04ckie91k5",
|
||||||
|
"name": "AI",
|
||||||
|
"nameEn": "AI",
|
||||||
|
"projects": 115
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkoutfub0004jr049qn5fxc0",
|
||||||
|
"name": "易上手",
|
||||||
|
"nameEn": "Easy to Start",
|
||||||
|
"projects": 107
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkoutftj0002jr04xqiuu0cb",
|
||||||
|
"name": "框架",
|
||||||
|
"nameEn": "Framework",
|
||||||
|
"projects": 90
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqk1d5o000kjr049wqf7i5o",
|
||||||
|
"name": "开发工具",
|
||||||
|
"nameEn": null,
|
||||||
|
"projects": 88
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqnvc540000jo04qrexxuhd",
|
||||||
|
"name": "开箱即用",
|
||||||
|
"nameEn": "Ready to Use",
|
||||||
|
"projects": 72
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqkapuh0010jr043s59my3y",
|
||||||
|
"name": "文档完善",
|
||||||
|
"nameEn": "Well Documented",
|
||||||
|
"projects": 69
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqk4i0g000xjr04sn06e7wt",
|
||||||
|
"name": "本地部署",
|
||||||
|
"nameEn": null,
|
||||||
|
"projects": 38
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkoutfu10003jr04dw11uzvu",
|
||||||
|
"name": "有示例",
|
||||||
|
"nameEn": "Has Examples",
|
||||||
|
"projects": 36
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkql442c0000l504qbomadup",
|
||||||
|
"name": "需要Docker",
|
||||||
|
"nameEn": "Requires Docker",
|
||||||
|
"projects": 36
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkryxmob0003l80414uo5zqz",
|
||||||
|
"name": "CLI工具",
|
||||||
|
"nameEn": "CLI Tool",
|
||||||
|
"projects": 33
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks1fjqc0042l104cqi0dh9v",
|
||||||
|
"name": "工具",
|
||||||
|
"nameEn": "Tool",
|
||||||
|
"projects": 33
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqkaq140011jr04n5aqr763",
|
||||||
|
"name": "平台",
|
||||||
|
"nameEn": "Platform",
|
||||||
|
"projects": 29
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkrzlq0e0002l104jfnarvv8",
|
||||||
|
"name": "智能体",
|
||||||
|
"nameEn": "Agent",
|
||||||
|
"projects": 16
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmk9fpg560001kt04ab5369hg",
|
||||||
|
"name": "开发框架",
|
||||||
|
"nameEn": "Development Framework",
|
||||||
|
"projects": 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmk9fpg580002kt04sslxarl5",
|
||||||
|
"name": "人工智能",
|
||||||
|
"nameEn": "Artificial Intelligence",
|
||||||
|
"projects": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkrv2jvc0001l204703u5836",
|
||||||
|
"name": "开发者工具",
|
||||||
|
"nameEn": "Developer Tool",
|
||||||
|
"projects": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmksdlj9o0029jv04x9tdyc11",
|
||||||
|
"name": "科研工具",
|
||||||
|
"nameEn": "Research Tool",
|
||||||
|
"projects": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks4tb3k000jl204z6oxfuyh",
|
||||||
|
"name": "终端工具",
|
||||||
|
"nameEn": "Terminal Tools",
|
||||||
|
"projects": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqjgpzv0003l104asyyc6dg",
|
||||||
|
"name": "低代码平台",
|
||||||
|
"nameEn": "Low-Code Platform",
|
||||||
|
"projects": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkrxh1kc000hky04rpxvrj1w",
|
||||||
|
"name": "数据库工具",
|
||||||
|
"nameEn": "Database Tools",
|
||||||
|
"projects": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks05cng001il1049zztjibt",
|
||||||
|
"name": "Agent平台",
|
||||||
|
"nameEn": "Agent Platform",
|
||||||
|
"projects": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkjjb38300094j12lum5vwjj",
|
||||||
|
"name": "集成平台",
|
||||||
|
"nameEn": "Integration Platform",
|
||||||
|
"projects": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkt8ex6g000ala0488t486bj",
|
||||||
|
"name": "平台/框架",
|
||||||
|
"nameEn": "Platform/Framework",
|
||||||
|
"projects": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmktpw9pe003tld04fjjgo4zt",
|
||||||
|
"name": "系统监控",
|
||||||
|
"nameEn": "System Monitoring",
|
||||||
|
"projects": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkuxtvzn002mjm04wdcx9wwi",
|
||||||
|
"name": "系统通知",
|
||||||
|
"nameEn": "System Notification",
|
||||||
|
"projects": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,305 @@
|
|||||||
|
{
|
||||||
|
"totalTags": 859,
|
||||||
|
"totalProjects": 454,
|
||||||
|
"activeProjects": 444,
|
||||||
|
"distributionAtLeast": {
|
||||||
|
"1": 859,
|
||||||
|
"2": 283,
|
||||||
|
"3": 169,
|
||||||
|
"4": 130,
|
||||||
|
"5": 100,
|
||||||
|
"6": 85,
|
||||||
|
"8": 67,
|
||||||
|
"10": 59,
|
||||||
|
"15": 43,
|
||||||
|
"20": 34,
|
||||||
|
"30": 26,
|
||||||
|
"50": 13
|
||||||
|
},
|
||||||
|
"top100CoverageProjects": 453,
|
||||||
|
"projectsWithoutTop100Tag": 1,
|
||||||
|
"top20": [
|
||||||
|
{
|
||||||
|
"name": "活跃维护",
|
||||||
|
"nameEn": "Active Maintenance",
|
||||||
|
"projects": 317
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Python",
|
||||||
|
"nameEn": "Python",
|
||||||
|
"projects": 268
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "AI开发工具",
|
||||||
|
"nameEn": "AI Development Tool",
|
||||||
|
"projects": 213
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "可商用",
|
||||||
|
"nameEn": null,
|
||||||
|
"projects": 171
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "需要配置",
|
||||||
|
"nameEn": "Requires Configuration",
|
||||||
|
"projects": 163
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "TypeScript",
|
||||||
|
"nameEn": "TypeScript",
|
||||||
|
"projects": 118
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "AI",
|
||||||
|
"nameEn": "AI",
|
||||||
|
"projects": 115
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "易上手",
|
||||||
|
"nameEn": "Easy to Start",
|
||||||
|
"projects": 107
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "框架",
|
||||||
|
"nameEn": "Framework",
|
||||||
|
"projects": 90
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "开发工具",
|
||||||
|
"nameEn": null,
|
||||||
|
"projects": 88
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "开箱即用",
|
||||||
|
"nameEn": "Ready to Use",
|
||||||
|
"projects": 72
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "文档完善",
|
||||||
|
"nameEn": "Well Documented",
|
||||||
|
"projects": 69
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "AI代理",
|
||||||
|
"nameEn": "AI Agents",
|
||||||
|
"projects": 52
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "自动化",
|
||||||
|
"nameEn": "Automation",
|
||||||
|
"projects": 46
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Docker",
|
||||||
|
"nameEn": "Docker",
|
||||||
|
"projects": 43
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Node.js",
|
||||||
|
"nameEn": "Node.js",
|
||||||
|
"projects": 40
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "本地部署",
|
||||||
|
"nameEn": null,
|
||||||
|
"projects": 38
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "有示例",
|
||||||
|
"nameEn": "Has Examples",
|
||||||
|
"projects": 36
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "需要Docker",
|
||||||
|
"nameEn": "Requires Docker",
|
||||||
|
"projects": 36
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "JavaScript",
|
||||||
|
"nameEn": "JavaScript",
|
||||||
|
"projects": 34
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"genericTagsTop30": [
|
||||||
|
{
|
||||||
|
"id": "cmkqimx520005jx04vyegybp9",
|
||||||
|
"name": "AI开发工具",
|
||||||
|
"nameEn": "AI Development Tool",
|
||||||
|
"projects": 213
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqk1div000qjr04ckie91k5",
|
||||||
|
"name": "AI",
|
||||||
|
"nameEn": "AI",
|
||||||
|
"projects": 115
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkoutftj0002jr04xqiuu0cb",
|
||||||
|
"name": "框架",
|
||||||
|
"nameEn": "Framework",
|
||||||
|
"projects": 90
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqk1d5o000kjr049wqf7i5o",
|
||||||
|
"name": "开发工具",
|
||||||
|
"nameEn": null,
|
||||||
|
"projects": 88
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks0yz0h002xl1040w65w47d",
|
||||||
|
"name": "AI代理",
|
||||||
|
"nameEn": "AI Agents",
|
||||||
|
"projects": 52
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkryxmob0003l80414uo5zqz",
|
||||||
|
"name": "CLI工具",
|
||||||
|
"nameEn": "CLI Tool",
|
||||||
|
"projects": 33
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks1fjqc0042l104cqi0dh9v",
|
||||||
|
"name": "工具",
|
||||||
|
"nameEn": "Tool",
|
||||||
|
"projects": 33
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqkaq140011jr04n5aqr763",
|
||||||
|
"name": "平台",
|
||||||
|
"nameEn": "Platform",
|
||||||
|
"projects": 29
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqlcukz000dl5044c6z8542",
|
||||||
|
"name": "多智能体系统",
|
||||||
|
"nameEn": "Multi-Agent System",
|
||||||
|
"projects": 28
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkrzlq0e0002l104jfnarvv8",
|
||||||
|
"name": "智能体",
|
||||||
|
"nameEn": "Agent",
|
||||||
|
"projects": 16
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmk9fpg560001kt04ab5369hg",
|
||||||
|
"name": "开发框架",
|
||||||
|
"nameEn": "Development Framework",
|
||||||
|
"projects": 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks05ci1001gl104lus7os2q",
|
||||||
|
"name": "企业级AI应用",
|
||||||
|
"nameEn": "Enterprise AI",
|
||||||
|
"projects": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks1s7p2004il104kpwprttq",
|
||||||
|
"name": "智能体框架",
|
||||||
|
"nameEn": "Agent Framework",
|
||||||
|
"projects": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmk9fpg580002kt04sslxarl5",
|
||||||
|
"name": "人工智能",
|
||||||
|
"nameEn": "Artificial Intelligence",
|
||||||
|
"projects": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmk9fpg5f0003kt04fcgbvj7y",
|
||||||
|
"name": "多智能体",
|
||||||
|
"nameEn": "Multi-Agent",
|
||||||
|
"projects": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqklwxr0019jr048wp0hds8",
|
||||||
|
"name": "AI 助手",
|
||||||
|
"nameEn": "AI Assistant",
|
||||||
|
"projects": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkshubus0004la04zjuh5nn5",
|
||||||
|
"name": "自主智能体",
|
||||||
|
"nameEn": "Autonomous Agents",
|
||||||
|
"projects": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks13z46003dl1047ba0entr",
|
||||||
|
"name": "AI代理框架",
|
||||||
|
"nameEn": "AI Agent Framework",
|
||||||
|
"projects": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmksf0ik1004ijv047hiusscw",
|
||||||
|
"name": "多模态AI",
|
||||||
|
"nameEn": "Multimodal AI",
|
||||||
|
"projects": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkrv2jvc0001l204703u5836",
|
||||||
|
"name": "开发者工具",
|
||||||
|
"nameEn": "Developer Tool",
|
||||||
|
"projects": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmksdlj9o0029jv04x9tdyc11",
|
||||||
|
"name": "科研工具",
|
||||||
|
"nameEn": "Research Tool",
|
||||||
|
"projects": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmksf2ncg004pjv0446rsr0e6",
|
||||||
|
"name": "生成式AI",
|
||||||
|
"nameEn": "Generative AI",
|
||||||
|
"projects": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks4tb3k000jl204z6oxfuyh",
|
||||||
|
"name": "终端工具",
|
||||||
|
"nameEn": "Terminal Tools",
|
||||||
|
"projects": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqjgpzv0003l104asyyc6dg",
|
||||||
|
"name": "低代码平台",
|
||||||
|
"nameEn": "Low-Code Platform",
|
||||||
|
"projects": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqlcuqx000fl504ywms66me",
|
||||||
|
"name": "AI开发框架",
|
||||||
|
"nameEn": "AI Development Framework",
|
||||||
|
"projects": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkujqy2l0007jx04w5zxt8be",
|
||||||
|
"name": "游戏AI",
|
||||||
|
"nameEn": "Game AI",
|
||||||
|
"projects": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkrxh1kc000hky04rpxvrj1w",
|
||||||
|
"name": "数据库工具",
|
||||||
|
"nameEn": "Database Tools",
|
||||||
|
"projects": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks05cng001il1049zztjibt",
|
||||||
|
"name": "Agent平台",
|
||||||
|
"nameEn": "Agent Platform",
|
||||||
|
"projects": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks3y6z0000hl9048h3bv62n",
|
||||||
|
"name": "记忆系统",
|
||||||
|
"nameEn": "Memory System",
|
||||||
|
"projects": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkjjb38300094j12lum5vwjj",
|
||||||
|
"name": "集成平台",
|
||||||
|
"nameEn": "Integration Platform",
|
||||||
|
"projects": 2
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
{
|
||||||
|
"totalTags": 100,
|
||||||
|
"totalProjectTags": 1888,
|
||||||
|
"zeroTags": 0,
|
||||||
|
"aiTagExists": false,
|
||||||
|
"aiTag": null,
|
||||||
|
"projectCoverage": {
|
||||||
|
"covered": 454,
|
||||||
|
"total": 454
|
||||||
|
},
|
||||||
|
"top30": [
|
||||||
|
{
|
||||||
|
"name": "Python",
|
||||||
|
"nameEn": "Python",
|
||||||
|
"projects": 268
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "AI开发工具",
|
||||||
|
"nameEn": "AI Development Tool",
|
||||||
|
"projects": 213
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "需要配置",
|
||||||
|
"nameEn": "Requires Configuration",
|
||||||
|
"projects": 163
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "TypeScript",
|
||||||
|
"nameEn": "TypeScript",
|
||||||
|
"projects": 118
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "AI代理",
|
||||||
|
"nameEn": "AI Agents",
|
||||||
|
"projects": 52
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "自动化",
|
||||||
|
"nameEn": "Automation",
|
||||||
|
"projects": 46
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Docker",
|
||||||
|
"nameEn": "Docker",
|
||||||
|
"projects": 43
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Node.js",
|
||||||
|
"nameEn": "Node.js",
|
||||||
|
"projects": 40
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "JavaScript",
|
||||||
|
"nameEn": "JavaScript",
|
||||||
|
"projects": 34
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "React",
|
||||||
|
"nameEn": "React",
|
||||||
|
"projects": 34
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "开源",
|
||||||
|
"nameEn": "Open Source",
|
||||||
|
"projects": 33
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "低学习成本",
|
||||||
|
"nameEn": "Low Learning Curve",
|
||||||
|
"projects": 31
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "PyTorch",
|
||||||
|
"nameEn": "PyTorch",
|
||||||
|
"projects": 30
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "LangChain",
|
||||||
|
"nameEn": "LangChain",
|
||||||
|
"projects": 29
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "多智能体系统",
|
||||||
|
"nameEn": "Multi-Agent System",
|
||||||
|
"projects": 28
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "大语言模型",
|
||||||
|
"nameEn": null,
|
||||||
|
"projects": 25
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "LLM",
|
||||||
|
"nameEn": "LLM",
|
||||||
|
"projects": 22
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Go",
|
||||||
|
"nameEn": "Go",
|
||||||
|
"projects": 20
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "企业级",
|
||||||
|
"nameEn": "Enterprise",
|
||||||
|
"projects": 20
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "需要基础",
|
||||||
|
"nameEn": "Requires Basics",
|
||||||
|
"projects": 20
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Library",
|
||||||
|
"nameEn": "Library",
|
||||||
|
"projects": 18
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "MCP",
|
||||||
|
"nameEn": "Model Context Protocol",
|
||||||
|
"projects": 18
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Markdown",
|
||||||
|
"nameEn": "Markdown",
|
||||||
|
"projects": 17
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Next.js",
|
||||||
|
"nameEn": "Next.js",
|
||||||
|
"projects": 17
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "RAG",
|
||||||
|
"nameEn": "RAG",
|
||||||
|
"projects": 17
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Java",
|
||||||
|
"nameEn": "Java",
|
||||||
|
"projects": 16
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "MIT许可",
|
||||||
|
"nameEn": "MIT License",
|
||||||
|
"projects": 16
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "LangGraph",
|
||||||
|
"nameEn": "LangGraph",
|
||||||
|
"projects": 15
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "FastAPI",
|
||||||
|
"nameEn": "FastAPI",
|
||||||
|
"projects": 14
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Rust",
|
||||||
|
"nameEn": "Rust",
|
||||||
|
"projects": 14
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
{
|
||||||
|
"generatedAt": "2026-02-21T03:47:31.204Z",
|
||||||
|
"mergeGroupCount": 6,
|
||||||
|
"mergePairCount": 6,
|
||||||
|
"mergePairs": [
|
||||||
|
{
|
||||||
|
"sourceId": "cmkoutg0o0006jr04goozjhft",
|
||||||
|
"targetId": "cmkqimx520005jx04vyegybp9",
|
||||||
|
"key": "ai development tool"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sourceId": "cmkjjb387000a4j12pdf123zn",
|
||||||
|
"targetId": "cmks0yz0h002xl1040w65w47d",
|
||||||
|
"key": "ai agent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sourceId": "cmkun0ao70048jx04atm2lwwq",
|
||||||
|
"targetId": "cmkryxmob0003l80414uo5zqz",
|
||||||
|
"key": "cli tool"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sourceId": "cmkrzqdlu000fl104qmy9gu2u",
|
||||||
|
"targetId": "cmkqlcukz000dl5044c6z8542",
|
||||||
|
"key": "multi agent system"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sourceId": "cmktk3biu000nl704iiohmv4i",
|
||||||
|
"targetId": "cmksdlj9o0029jv04x9tdyc11",
|
||||||
|
"key": "research tool"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sourceId": "cmktpotkp003eld04mv09ckde",
|
||||||
|
"targetId": "cmkshubus0004la04zjuh5nn5",
|
||||||
|
"key": "autonomous agent"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"before": {
|
||||||
|
"totalTags": 955,
|
||||||
|
"totalProjectTags": 4131,
|
||||||
|
"zeroTags": 90,
|
||||||
|
"missingNameEnAscii": null
|
||||||
|
},
|
||||||
|
"sqlPath": ".sisyphus/sql/tag-cleanup-2026-02-21.sql"
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
"after": {
|
||||||
|
"totalTags": 859,
|
||||||
|
"totalProjectTags": 4131,
|
||||||
|
"zeroTags": 0,
|
||||||
|
"missingNameEn": 17,
|
||||||
|
"missingNameEnAscii": 0
|
||||||
|
},
|
||||||
|
"deletedSourceTagsStillExists": [],
|
||||||
|
"mergedTargetProjectCounts": [
|
||||||
|
{
|
||||||
|
"id": "cmkshubus0004la04zjuh5nn5",
|
||||||
|
"name": "自主智能体",
|
||||||
|
"projects": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkryxmob0003l80414uo5zqz",
|
||||||
|
"name": "CLI工具",
|
||||||
|
"projects": 33
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqlcukz000dl5044c6z8542",
|
||||||
|
"name": "多智能体系统",
|
||||||
|
"projects": 28
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmkqimx520005jx04vyegybp9",
|
||||||
|
"name": "AI开发工具",
|
||||||
|
"projects": 213
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmks0yz0h002xl1040w65w47d",
|
||||||
|
"name": "AI代理",
|
||||||
|
"projects": 52
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmksdlj9o0029jv04x9tdyc11",
|
||||||
|
"name": "科研工具",
|
||||||
|
"projects": 4
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,132 @@
|
|||||||
|
{
|
||||||
|
"executedAt": "2026-02-21T04:05:08.555Z",
|
||||||
|
"totalTags": 83,
|
||||||
|
"totalProjectTags": 1547,
|
||||||
|
"zeroTags": 0,
|
||||||
|
"totalProjects": 454,
|
||||||
|
"projectsWithTags": 454,
|
||||||
|
"projectsWithoutTags": 0,
|
||||||
|
"aiTagExists": false,
|
||||||
|
"removedNoise": [
|
||||||
|
"开源",
|
||||||
|
"学术资源",
|
||||||
|
"复杂部署",
|
||||||
|
"用户通知",
|
||||||
|
"需要配置",
|
||||||
|
"低学习成本",
|
||||||
|
"云端服务",
|
||||||
|
"MIT许可",
|
||||||
|
"Apache-2.0",
|
||||||
|
"教程",
|
||||||
|
"企业级",
|
||||||
|
"需要基础",
|
||||||
|
"多语言支持",
|
||||||
|
"AI研究资源",
|
||||||
|
"跨平台",
|
||||||
|
"自托管",
|
||||||
|
"学术研究"
|
||||||
|
],
|
||||||
|
"removedNoiseStillExists": [],
|
||||||
|
"topTags": [
|
||||||
|
{
|
||||||
|
"name": "Python",
|
||||||
|
"nameEn": "Python",
|
||||||
|
"projectCount": 268
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "AI开发工具",
|
||||||
|
"nameEn": "AI Development Tool",
|
||||||
|
"projectCount": 215
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "TypeScript",
|
||||||
|
"nameEn": "TypeScript",
|
||||||
|
"projectCount": 118
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "AI代理",
|
||||||
|
"nameEn": "AI Agents",
|
||||||
|
"projectCount": 54
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "自动化",
|
||||||
|
"nameEn": "Automation",
|
||||||
|
"projectCount": 47
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Docker",
|
||||||
|
"nameEn": "Docker",
|
||||||
|
"projectCount": 43
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Node.js",
|
||||||
|
"nameEn": "Node.js",
|
||||||
|
"projectCount": 40
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "JavaScript",
|
||||||
|
"nameEn": "JavaScript",
|
||||||
|
"projectCount": 34
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "React",
|
||||||
|
"nameEn": "React",
|
||||||
|
"projectCount": 34
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "PyTorch",
|
||||||
|
"nameEn": "PyTorch",
|
||||||
|
"projectCount": 30
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "LangChain",
|
||||||
|
"nameEn": "LangChain",
|
||||||
|
"projectCount": 29
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "多智能体系统",
|
||||||
|
"nameEn": "Multi-Agent System",
|
||||||
|
"projectCount": 28
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "大语言模型",
|
||||||
|
"nameEn": null,
|
||||||
|
"projectCount": 25
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "LLM",
|
||||||
|
"nameEn": "LLM",
|
||||||
|
"projectCount": 22
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Go",
|
||||||
|
"nameEn": "Go",
|
||||||
|
"projectCount": 20
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Library",
|
||||||
|
"nameEn": "Library",
|
||||||
|
"projectCount": 18
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "MCP",
|
||||||
|
"nameEn": "Model Context Protocol",
|
||||||
|
"projectCount": 18
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "RAG",
|
||||||
|
"nameEn": "RAG",
|
||||||
|
"projectCount": 17
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Next.js",
|
||||||
|
"nameEn": "Next.js",
|
||||||
|
"projectCount": 17
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Markdown",
|
||||||
|
"nameEn": "Markdown",
|
||||||
|
"projectCount": 17
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
{
|
||||||
|
"executedAt": "2026-02-21T04:03:10.320Z",
|
||||||
|
"removedNames": [
|
||||||
|
"自托管",
|
||||||
|
"跨平台",
|
||||||
|
"企业级",
|
||||||
|
"需要基础",
|
||||||
|
"AI研究资源",
|
||||||
|
"多语言支持",
|
||||||
|
"教程",
|
||||||
|
"Apache-2.0",
|
||||||
|
"学术研究",
|
||||||
|
"MIT许可",
|
||||||
|
"云端服务",
|
||||||
|
"低学习成本"
|
||||||
|
],
|
||||||
|
"removedStillExists": [],
|
||||||
|
"totalTags": 88,
|
||||||
|
"totalProjectTags": 1754,
|
||||||
|
"zeroTags": 0,
|
||||||
|
"totalProjects": 454,
|
||||||
|
"projectsWithTags": 454,
|
||||||
|
"projectsWithoutTags": 0,
|
||||||
|
"aiTagExists": false,
|
||||||
|
"topTags": [
|
||||||
|
{
|
||||||
|
"name": "Python",
|
||||||
|
"nameEn": "Python",
|
||||||
|
"projectCount": 268
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "AI开发工具",
|
||||||
|
"nameEn": "AI Development Tool",
|
||||||
|
"projectCount": 213
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "需要配置",
|
||||||
|
"nameEn": "Requires Configuration",
|
||||||
|
"projectCount": 163
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "TypeScript",
|
||||||
|
"nameEn": "TypeScript",
|
||||||
|
"projectCount": 118
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "AI代理",
|
||||||
|
"nameEn": "AI Agents",
|
||||||
|
"projectCount": 52
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "自动化",
|
||||||
|
"nameEn": "Automation",
|
||||||
|
"projectCount": 46
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Docker",
|
||||||
|
"nameEn": "Docker",
|
||||||
|
"projectCount": 43
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Node.js",
|
||||||
|
"nameEn": "Node.js",
|
||||||
|
"projectCount": 40
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "JavaScript",
|
||||||
|
"nameEn": "JavaScript",
|
||||||
|
"projectCount": 34
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "React",
|
||||||
|
"nameEn": "React",
|
||||||
|
"projectCount": 34
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "开源",
|
||||||
|
"nameEn": "Open Source",
|
||||||
|
"projectCount": 33
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "PyTorch",
|
||||||
|
"nameEn": "PyTorch",
|
||||||
|
"projectCount": 30
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "LangChain",
|
||||||
|
"nameEn": "LangChain",
|
||||||
|
"projectCount": 29
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "多智能体系统",
|
||||||
|
"nameEn": "Multi-Agent System",
|
||||||
|
"projectCount": 28
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "大语言模型",
|
||||||
|
"nameEn": null,
|
||||||
|
"projectCount": 25
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "LLM",
|
||||||
|
"nameEn": "LLM",
|
||||||
|
"projectCount": 22
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Go",
|
||||||
|
"nameEn": "Go",
|
||||||
|
"projectCount": 20
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "MCP",
|
||||||
|
"nameEn": "Model Context Protocol",
|
||||||
|
"projectCount": 18
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Library",
|
||||||
|
"nameEn": "Library",
|
||||||
|
"projectCount": 18
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "RAG",
|
||||||
|
"nameEn": "RAG",
|
||||||
|
"projectCount": 17
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,211 @@
|
|||||||
|
BEGIN;
|
||||||
|
|
||||||
|
DELETE FROM "project_tags"
|
||||||
|
WHERE "tagId" NOT IN (
|
||||||
|
'cmk9fpg4h0000kt048l3a2nqu',
|
||||||
|
'cmkjjb37v00064j12b84dsrwg',
|
||||||
|
'cmkqimx520005jx04vyegybp9',
|
||||||
|
'cmks16lb0003ml104acf4zi7e',
|
||||||
|
'cmks0yz0h002xl1040w65w47d',
|
||||||
|
'cmkqjgpzq0002l104qknt9pho',
|
||||||
|
'cmkql448t0002l504m7bkovze',
|
||||||
|
'cmkqm1bzv0000jl04c3ks3w7w',
|
||||||
|
'cmkoutfvz0005jr04knetubim',
|
||||||
|
'cmks0rbk1002jl104uc7yg2xv',
|
||||||
|
'cmks3iu8m006wl104ndk8no10',
|
||||||
|
'cmkqk1dc5000ojr04jq0exq1z',
|
||||||
|
'cmkqimwxl0000jx04l8q0rdzo',
|
||||||
|
'cmks0yz1e002yl104nw4apfcq',
|
||||||
|
'cmkqjvuo5000ejr04y9ksio48',
|
||||||
|
'cmkjjb37v00074j122wpemyas',
|
||||||
|
'cmkqnvcbg0001jo04us31pcaj',
|
||||||
|
'cmkrzlq020001l104sosii49r',
|
||||||
|
'cmks1aoyn003yl104r4zspprz',
|
||||||
|
'cmks5ey0j001el204vjk69nwg',
|
||||||
|
'cmkshrboj0000la04h6bmugvj',
|
||||||
|
'cmkscgc0z0007jv04nwmzbn8c',
|
||||||
|
'cmkscxy490012jv04rf4k4h4h',
|
||||||
|
'cmkuxtw0m002ojm04pl9qazxs',
|
||||||
|
'cmkqklwxm0018jr04zyzlrmlv',
|
||||||
|
'cmkqk1dbi000ljr045f2rm18m',
|
||||||
|
'cmkrv2jpg0000l204swrx5xh4',
|
||||||
|
'cmkqlcukz000dl5044c6z8542',
|
||||||
|
'cmkqm1c6j0003jl04clkn9qyb',
|
||||||
|
'cmkqnn8hj0006l504j2uedblz',
|
||||||
|
'cmkrx8euz0007ky04hrgq5ryg',
|
||||||
|
'cmkqklwr20016jr04opezrtpo',
|
||||||
|
'cmks003y10015l104xruieqo9',
|
||||||
|
'cmks0c9ll0022l10436svssah',
|
||||||
|
'cmkqnn8gl0005l5042jdeuuur',
|
||||||
|
'cmkrx664z0000ky0481j124cq',
|
||||||
|
'cmkqjvuir0008jr048591dzwp',
|
||||||
|
'cmkjjb38l000c4j12oucpveph',
|
||||||
|
'cmkqnn8mm0007l5045q6z4zb0',
|
||||||
|
'cmkrzvdfk000tl104jsq77zig',
|
||||||
|
'cmks3d1wy006ll104gltxc3et',
|
||||||
|
'cmks1aoxo003xl104gap1drce',
|
||||||
|
'cmksczul60015jv04ft4n1cb2',
|
||||||
|
'cmks0m380002gl104o6f9cjtj',
|
||||||
|
'cmkrx8f0j000aky046uomv3rs',
|
||||||
|
'cmkqjs7e50004jr042mrpi3ac',
|
||||||
|
'cmkqk4i0d000vjr04ai18b8h3',
|
||||||
|
'cmkqk4i0g000wjr04t82lzvaq',
|
||||||
|
'cmkrzlq0q0003l10453qhsniu',
|
||||||
|
'cmks1ubzb004pl104mvs08pmu',
|
||||||
|
'cmks0eqvq0028l10426ogrvkb',
|
||||||
|
'cmks13z53003el104re4468xt',
|
||||||
|
'cmks05ci1001gl104lus7os2q',
|
||||||
|
'cmks1s7p2004il104kpwprttq',
|
||||||
|
'cmkqjs7dw0003jr04uy2bguck',
|
||||||
|
'cmkqklwxr0019jr048wp0hds8',
|
||||||
|
'cmkqjs77s0000jr041cypbjkn',
|
||||||
|
'cmkry59tc0001jy04kr21i343',
|
||||||
|
'cmk9fpg5f0003kt04fcgbvj7y',
|
||||||
|
'cmkrzxk4i0011l1045cubzwwr',
|
||||||
|
'cmks13z46003dl1047ba0entr',
|
||||||
|
'cmkry59u70003jy049eoxzkc7',
|
||||||
|
'cmks2a6r1005il104ormxbqud',
|
||||||
|
'cmks112wx0039l104zltcl2hp',
|
||||||
|
'cmkrzvdfe000sl104q0gge67f',
|
||||||
|
'cmks3w0g0000bl904u4lmk5d5',
|
||||||
|
'cmkt8m55q000nla04kgfld9x3',
|
||||||
|
'cmkqklwxj0017jr04czubkbbi',
|
||||||
|
'cmkshubus0004la04zjuh5nn5',
|
||||||
|
'cmkqk4i0d000ujr045d2njmc7',
|
||||||
|
'cmks02o6m001al104gi1jztpn',
|
||||||
|
'cmks4roac000el204paypouzl',
|
||||||
|
'cmkt7yhut0000la040ij92vgk',
|
||||||
|
'cmks5rr2e0020l204sn9ydvka',
|
||||||
|
'cmkjjb37z00084j121651lnjl',
|
||||||
|
'cmksg1wc90061jv04114izr2d',
|
||||||
|
'cmkuulu5d000jjc04u7hx67sz',
|
||||||
|
'cmkqimx3n0001jx043mvrmarx',
|
||||||
|
'cmksf0ik1004ijv047hiusscw',
|
||||||
|
'cmktoexz8001wld046f13barg',
|
||||||
|
'cmks2gqie005ol104v4d6nvck',
|
||||||
|
'cmks0equs0026l104sl4td34d',
|
||||||
|
'cmkjjb38j000b4j125a3fg282',
|
||||||
|
'cmktkj5to001hl704wtmqjft4',
|
||||||
|
'cmkrzxk4a0010l104u7irj9je',
|
||||||
|
'cmkto2g8o0019ld04b61pgj4y',
|
||||||
|
'cmktk5hid000sl7049a2bo5al',
|
||||||
|
'cmkv6j7uq000gk4043x1gfnp1',
|
||||||
|
'cmkti8i470000kz04bpsajkzk',
|
||||||
|
'cmks47ct90013l904x03rupwb',
|
||||||
|
'cmkql9bho0007l50406oflxr0',
|
||||||
|
'cmks112qf0034l104gkihwp10',
|
||||||
|
'cmks1s7ut004jl104gvlybgfs',
|
||||||
|
'cmks4pb54000bl204erpb9bbp',
|
||||||
|
'cmksfnrz1005qjv044pnraikr',
|
||||||
|
'cmkqimx480003jx04bcqob7pb',
|
||||||
|
'cmkqm1c690001jl04ge5elv46',
|
||||||
|
'cmks2a6l3005gl1040u4s0k4s',
|
||||||
|
'cmkth0k4x0026ju04t0euxxwk',
|
||||||
|
'cmkqnj1pt0001l504vmrywqeg'
|
||||||
|
);
|
||||||
|
|
||||||
|
DELETE FROM "tags"
|
||||||
|
WHERE "id" NOT IN (
|
||||||
|
'cmk9fpg4h0000kt048l3a2nqu',
|
||||||
|
'cmkjjb37v00064j12b84dsrwg',
|
||||||
|
'cmkqimx520005jx04vyegybp9',
|
||||||
|
'cmks16lb0003ml104acf4zi7e',
|
||||||
|
'cmks0yz0h002xl1040w65w47d',
|
||||||
|
'cmkqjgpzq0002l104qknt9pho',
|
||||||
|
'cmkql448t0002l504m7bkovze',
|
||||||
|
'cmkqm1bzv0000jl04c3ks3w7w',
|
||||||
|
'cmkoutfvz0005jr04knetubim',
|
||||||
|
'cmks0rbk1002jl104uc7yg2xv',
|
||||||
|
'cmks3iu8m006wl104ndk8no10',
|
||||||
|
'cmkqk1dc5000ojr04jq0exq1z',
|
||||||
|
'cmkqimwxl0000jx04l8q0rdzo',
|
||||||
|
'cmks0yz1e002yl104nw4apfcq',
|
||||||
|
'cmkqjvuo5000ejr04y9ksio48',
|
||||||
|
'cmkjjb37v00074j122wpemyas',
|
||||||
|
'cmkqnvcbg0001jo04us31pcaj',
|
||||||
|
'cmkrzlq020001l104sosii49r',
|
||||||
|
'cmks1aoyn003yl104r4zspprz',
|
||||||
|
'cmks5ey0j001el204vjk69nwg',
|
||||||
|
'cmkshrboj0000la04h6bmugvj',
|
||||||
|
'cmkscgc0z0007jv04nwmzbn8c',
|
||||||
|
'cmkscxy490012jv04rf4k4h4h',
|
||||||
|
'cmkuxtw0m002ojm04pl9qazxs',
|
||||||
|
'cmkqklwxm0018jr04zyzlrmlv',
|
||||||
|
'cmkqk1dbi000ljr045f2rm18m',
|
||||||
|
'cmkrv2jpg0000l204swrx5xh4',
|
||||||
|
'cmkqlcukz000dl5044c6z8542',
|
||||||
|
'cmkqm1c6j0003jl04clkn9qyb',
|
||||||
|
'cmkqnn8hj0006l504j2uedblz',
|
||||||
|
'cmkrx8euz0007ky04hrgq5ryg',
|
||||||
|
'cmkqklwr20016jr04opezrtpo',
|
||||||
|
'cmks003y10015l104xruieqo9',
|
||||||
|
'cmks0c9ll0022l10436svssah',
|
||||||
|
'cmkqnn8gl0005l5042jdeuuur',
|
||||||
|
'cmkrx664z0000ky0481j124cq',
|
||||||
|
'cmkqjvuir0008jr048591dzwp',
|
||||||
|
'cmkjjb38l000c4j12oucpveph',
|
||||||
|
'cmkqnn8mm0007l5045q6z4zb0',
|
||||||
|
'cmkrzvdfk000tl104jsq77zig',
|
||||||
|
'cmks3d1wy006ll104gltxc3et',
|
||||||
|
'cmks1aoxo003xl104gap1drce',
|
||||||
|
'cmksczul60015jv04ft4n1cb2',
|
||||||
|
'cmks0m380002gl104o6f9cjtj',
|
||||||
|
'cmkrx8f0j000aky046uomv3rs',
|
||||||
|
'cmkqjs7e50004jr042mrpi3ac',
|
||||||
|
'cmkqk4i0d000vjr04ai18b8h3',
|
||||||
|
'cmkqk4i0g000wjr04t82lzvaq',
|
||||||
|
'cmkrzlq0q0003l10453qhsniu',
|
||||||
|
'cmks1ubzb004pl104mvs08pmu',
|
||||||
|
'cmks0eqvq0028l10426ogrvkb',
|
||||||
|
'cmks13z53003el104re4468xt',
|
||||||
|
'cmks05ci1001gl104lus7os2q',
|
||||||
|
'cmks1s7p2004il104kpwprttq',
|
||||||
|
'cmkqjs7dw0003jr04uy2bguck',
|
||||||
|
'cmkqklwxr0019jr048wp0hds8',
|
||||||
|
'cmkqjs77s0000jr041cypbjkn',
|
||||||
|
'cmkry59tc0001jy04kr21i343',
|
||||||
|
'cmk9fpg5f0003kt04fcgbvj7y',
|
||||||
|
'cmkrzxk4i0011l1045cubzwwr',
|
||||||
|
'cmks13z46003dl1047ba0entr',
|
||||||
|
'cmkry59u70003jy049eoxzkc7',
|
||||||
|
'cmks2a6r1005il104ormxbqud',
|
||||||
|
'cmks112wx0039l104zltcl2hp',
|
||||||
|
'cmkrzvdfe000sl104q0gge67f',
|
||||||
|
'cmks3w0g0000bl904u4lmk5d5',
|
||||||
|
'cmkt8m55q000nla04kgfld9x3',
|
||||||
|
'cmkqklwxj0017jr04czubkbbi',
|
||||||
|
'cmkshubus0004la04zjuh5nn5',
|
||||||
|
'cmkqk4i0d000ujr045d2njmc7',
|
||||||
|
'cmks02o6m001al104gi1jztpn',
|
||||||
|
'cmks4roac000el204paypouzl',
|
||||||
|
'cmkt7yhut0000la040ij92vgk',
|
||||||
|
'cmks5rr2e0020l204sn9ydvka',
|
||||||
|
'cmkjjb37z00084j121651lnjl',
|
||||||
|
'cmksg1wc90061jv04114izr2d',
|
||||||
|
'cmkuulu5d000jjc04u7hx67sz',
|
||||||
|
'cmkqimx3n0001jx043mvrmarx',
|
||||||
|
'cmksf0ik1004ijv047hiusscw',
|
||||||
|
'cmktoexz8001wld046f13barg',
|
||||||
|
'cmks2gqie005ol104v4d6nvck',
|
||||||
|
'cmks0equs0026l104sl4td34d',
|
||||||
|
'cmkjjb38j000b4j125a3fg282',
|
||||||
|
'cmktkj5to001hl704wtmqjft4',
|
||||||
|
'cmkrzxk4a0010l104u7irj9je',
|
||||||
|
'cmkto2g8o0019ld04b61pgj4y',
|
||||||
|
'cmktk5hid000sl7049a2bo5al',
|
||||||
|
'cmkv6j7uq000gk4043x1gfnp1',
|
||||||
|
'cmkti8i470000kz04bpsajkzk',
|
||||||
|
'cmks47ct90013l904x03rupwb',
|
||||||
|
'cmkql9bho0007l50406oflxr0',
|
||||||
|
'cmks112qf0034l104gkihwp10',
|
||||||
|
'cmks1s7ut004jl104gvlybgfs',
|
||||||
|
'cmks4pb54000bl204erpb9bbp',
|
||||||
|
'cmksfnrz1005qjv044pnraikr',
|
||||||
|
'cmkqimx480003jx04bcqob7pb',
|
||||||
|
'cmkqm1c690001jl04ge5elv46',
|
||||||
|
'cmks2a6l3005gl1040u4s0k4s',
|
||||||
|
'cmkth0k4x0026ju04t0euxxwk',
|
||||||
|
'cmkqnj1pt0001l504vmrywqeg'
|
||||||
|
);
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
BEGIN;
|
||||||
|
|
||||||
|
-- Step 1: Merge duplicate tags by high-confidence canonicalized nameEn groups
|
||||||
|
WITH merge_map(source_id, target_id) AS (
|
||||||
|
VALUES
|
||||||
|
('cmkoutg0o0006jr04goozjhft', 'cmkqimx520005jx04vyegybp9'),
|
||||||
|
('cmkjjb387000a4j12pdf123zn', 'cmks0yz0h002xl1040w65w47d'),
|
||||||
|
('cmkun0ao70048jx04atm2lwwq', 'cmkryxmob0003l80414uo5zqz'),
|
||||||
|
('cmkrzqdlu000fl104qmy9gu2u', 'cmkqlcukz000dl5044c6z8542'),
|
||||||
|
('cmktk3biu000nl704iiohmv4i', 'cmksdlj9o0029jv04x9tdyc11'),
|
||||||
|
('cmktpotkp003eld04mv09ckde', 'cmkshubus0004la04zjuh5nn5')
|
||||||
|
)
|
||||||
|
INSERT INTO "project_tags" ("projectId", "tagId")
|
||||||
|
SELECT pt."projectId", mm.target_id
|
||||||
|
FROM "project_tags" pt
|
||||||
|
JOIN merge_map mm ON mm.source_id = pt."tagId"
|
||||||
|
ON CONFLICT ("projectId", "tagId") DO NOTHING;
|
||||||
|
|
||||||
|
WITH merge_map(source_id, target_id) AS (
|
||||||
|
VALUES
|
||||||
|
('cmkoutg0o0006jr04goozjhft', 'cmkqimx520005jx04vyegybp9'),
|
||||||
|
('cmkjjb387000a4j12pdf123zn', 'cmks0yz0h002xl1040w65w47d'),
|
||||||
|
('cmkun0ao70048jx04atm2lwwq', 'cmkryxmob0003l80414uo5zqz'),
|
||||||
|
('cmkrzqdlu000fl104qmy9gu2u', 'cmkqlcukz000dl5044c6z8542'),
|
||||||
|
('cmktk3biu000nl704iiohmv4i', 'cmksdlj9o0029jv04x9tdyc11'),
|
||||||
|
('cmktpotkp003eld04mv09ckde', 'cmkshubus0004la04zjuh5nn5')
|
||||||
|
)
|
||||||
|
DELETE FROM "tags" t
|
||||||
|
USING merge_map mm
|
||||||
|
WHERE t."id" = mm.source_id;
|
||||||
|
|
||||||
|
-- Step 2: Fill missing English names for ASCII-only tags
|
||||||
|
UPDATE "tags"
|
||||||
|
SET "nameEn" = "name"
|
||||||
|
WHERE ("nameEn" IS NULL OR "nameEn" = '')
|
||||||
|
AND "name" ~ '^[[:ascii:]]+$';
|
||||||
|
|
||||||
|
-- Step 3: Delete tags no longer referenced by any project
|
||||||
|
DELETE FROM "tags" t
|
||||||
|
WHERE NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM "project_tags" pt
|
||||||
|
WHERE pt."tagId" = t."id"
|
||||||
|
);
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
BEGIN;
|
||||||
|
|
||||||
|
WITH noise AS (
|
||||||
|
SELECT id
|
||||||
|
FROM tags
|
||||||
|
WHERE name IN (
|
||||||
|
'开源',
|
||||||
|
'学术资源',
|
||||||
|
'复杂部署',
|
||||||
|
'用户通知',
|
||||||
|
'需要配置'
|
||||||
|
)
|
||||||
|
),
|
||||||
|
projects_without_meaningful_tags AS (
|
||||||
|
SELECT p.id, p.name
|
||||||
|
FROM projects p
|
||||||
|
LEFT JOIN project_tags pt ON pt."projectId" = p.id
|
||||||
|
LEFT JOIN noise n ON n.id = pt."tagId"
|
||||||
|
GROUP BY p.id, p.name
|
||||||
|
HAVING COUNT(pt."tagId") > 0 AND COUNT(pt."tagId") = COUNT(n.id)
|
||||||
|
),
|
||||||
|
fallback_tags AS (
|
||||||
|
SELECT
|
||||||
|
(SELECT id FROM tags WHERE name = 'AI研究' LIMIT 1) AS research_id,
|
||||||
|
(SELECT id FROM tags WHERE name = 'AI 助手' LIMIT 1) AS assistant_id,
|
||||||
|
(SELECT id FROM tags WHERE name = 'AI代理' LIMIT 1) AS agent_id,
|
||||||
|
(SELECT id FROM tags WHERE name = '自动化' LIMIT 1) AS automation_id,
|
||||||
|
(SELECT id FROM tags WHERE name = 'AI开发工具' LIMIT 1) AS tool_id
|
||||||
|
),
|
||||||
|
assignments AS (
|
||||||
|
SELECT
|
||||||
|
p.id AS project_id,
|
||||||
|
CASE
|
||||||
|
WHEN p.name ~* '(论文|研究|paper|survey)' THEN (SELECT research_id FROM fallback_tags)
|
||||||
|
WHEN p.name ~* '(助手|assistant)' THEN (SELECT assistant_id FROM fallback_tags)
|
||||||
|
WHEN p.name ~* '(框架|agent|代理|智能体)' THEN (SELECT agent_id FROM fallback_tags)
|
||||||
|
WHEN p.name ~* '(通知|alert|quota|配额)' THEN (SELECT automation_id FROM fallback_tags)
|
||||||
|
ELSE (SELECT tool_id FROM fallback_tags)
|
||||||
|
END AS tag_id
|
||||||
|
FROM projects_without_meaningful_tags p
|
||||||
|
)
|
||||||
|
INSERT INTO project_tags ("projectId", "tagId")
|
||||||
|
SELECT project_id, tag_id
|
||||||
|
FROM assignments
|
||||||
|
WHERE tag_id IS NOT NULL
|
||||||
|
ON CONFLICT ("projectId", "tagId") DO NOTHING;
|
||||||
|
|
||||||
|
WITH noise AS (
|
||||||
|
SELECT id
|
||||||
|
FROM tags
|
||||||
|
WHERE name IN (
|
||||||
|
'开源',
|
||||||
|
'学术资源',
|
||||||
|
'复杂部署',
|
||||||
|
'用户通知',
|
||||||
|
'需要配置'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
DELETE FROM project_tags
|
||||||
|
WHERE "tagId" IN (SELECT id FROM noise);
|
||||||
|
|
||||||
|
WITH noise AS (
|
||||||
|
SELECT id
|
||||||
|
FROM tags
|
||||||
|
WHERE name IN (
|
||||||
|
'开源',
|
||||||
|
'学术资源',
|
||||||
|
'复杂部署',
|
||||||
|
'用户通知',
|
||||||
|
'需要配置'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
DELETE FROM tags
|
||||||
|
WHERE id IN (SELECT id FROM noise);
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
BEGIN;
|
||||||
|
|
||||||
|
WITH target_tags AS (
|
||||||
|
SELECT id FROM tags
|
||||||
|
WHERE name IN (
|
||||||
|
'自托管',
|
||||||
|
'跨平台',
|
||||||
|
'企业级',
|
||||||
|
'需要基础',
|
||||||
|
'AI研究资源',
|
||||||
|
'多语言支持',
|
||||||
|
'教程',
|
||||||
|
'Apache-2.0',
|
||||||
|
'学术研究',
|
||||||
|
'MIT许可',
|
||||||
|
'云端服务',
|
||||||
|
'低学习成本'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
DELETE FROM project_tags
|
||||||
|
WHERE "tagId" IN (SELECT id FROM target_tags);
|
||||||
|
|
||||||
|
WITH target_tags AS (
|
||||||
|
SELECT id FROM tags
|
||||||
|
WHERE name IN (
|
||||||
|
'自托管',
|
||||||
|
'跨平台',
|
||||||
|
'企业级',
|
||||||
|
'需要基础',
|
||||||
|
'AI研究资源',
|
||||||
|
'多语言支持',
|
||||||
|
'教程',
|
||||||
|
'Apache-2.0',
|
||||||
|
'学术研究',
|
||||||
|
'MIT许可',
|
||||||
|
'云端服务',
|
||||||
|
'低学习成本'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
DELETE FROM tags
|
||||||
|
WHERE id IN (SELECT id FROM target_tags);
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
+25
-24
@@ -69,48 +69,49 @@
|
|||||||
|
|
||||||
### 功能
|
### 功能
|
||||||
|
|
||||||
每日自动拉取标签,利用 LLM 生成 `merges + updates` 计划,并调用维护 API 执行标签合并。
|
每日自动拉取标签,按分桶分块交给 AI Agent 处理,并通过结构化输出解析器强制 JSON 格式,最终调用维护 API 执行合并与 `nameEn` 补全。
|
||||||
|
|
||||||
### 执行流程
|
### 执行流程
|
||||||
|
|
||||||
1. **Schedule Trigger**: 每日 UTC 03:00 触发
|
1. **Schedule Trigger**: 每日 UTC 03:00 触发
|
||||||
2. **Fetch Tags**: 调用 `GET /api/tags` 获取标签和项目计数
|
2. **Fetch Tags**: 调用 `GET /api/tags` 获取标签和项目计数
|
||||||
3. **Preprocess Tags**: 预处理排序(按 projectCount)
|
3. **Prepare Chunk Batches**: 预处理并按 bucket + chunk 拆分(默认每块 80)
|
||||||
4. **AI Analyze Tags**: 生成严格 JSON 计划(`merges`、`updates`)
|
4. **Tag Merge Agent + Structured Output Parser**: Agent 生成计划,Parser 强制输出 schema
|
||||||
5. **Validate JSON**: 校验输出结构和自合并风险
|
5. **Validate Plan**: 校验覆盖率(`reviewedTagIds` 必须覆盖整块)、ID 合法性、自合并
|
||||||
6. **Execute Maintenance**: 调用 `POST /api/tags/maintenance` 执行
|
6. **Retry Loop**: 校验失败会自动重试(默认 2 次,可配置)
|
||||||
7. **Summarize Results**: 输出日报(merged/deleted/updated)
|
7. **Execute Maintenance**: 仅对校验通过的块调用 `POST /api/tags/maintenance`
|
||||||
|
8. **Summarize Results**: 汇总 executed/noop/failed_validation/failed_execute
|
||||||
|
|
||||||
### API 端点
|
### API 端点
|
||||||
|
|
||||||
- `GET {API_URL}/api/tags` - 拉取标签列表(含 `_count.projects`)
|
- `GET {API_URL}/api/tags` - 拉取标签列表(含 `_count.projects`)
|
||||||
- `POST {API_URL}/api/tags/maintenance` - 执行批量 updates/merges
|
- `POST {API_URL}/api/tags/maintenance` - 执行批量 updates/merges
|
||||||
|
|
||||||
### 调试
|
### 环境变量
|
||||||
|
|
||||||
检查每个节点的输出,确保数据格式正确:
|
在 n8n 进程环境中配置:
|
||||||
|
|
||||||
- **Calculate Quarter**: 应输出 `quarter`, `startDate`, `endDate`
|
| 变量名 | 说明 | 默认值 |
|
||||||
- **Extract Keywords**: 应输出 `word`, `trendScore`, `quarter`
|
| --- | --- | --- |
|
||||||
- **Match Visual Rules**: 应添加 `visualConfig` 字段
|
| `SITE_BASE_URL` | 站点地址 | 无 |
|
||||||
- **Send to API**: 应返回 `success: true`
|
| `WEBHOOK_API_KEY` | 维护 API 鉴权密钥 | 无 |
|
||||||
|
| `TAG_JANITOR_CHUNK_SIZE` | 每块标签数量 | `80` |
|
||||||
|
| `TAG_JANITOR_MAX_RETRIES` | 校验失败自动重试次数 | `2` |
|
||||||
|
|
||||||
### 注意事项
|
### 注意事项
|
||||||
|
|
||||||
1. **Google Trends API 限制**:
|
1. **不要全量一次喂模型**:
|
||||||
- Google Trends 有请求频率限制
|
- 900+ 标签必须分块,否则大概率返回不完整。
|
||||||
- 建议在非高峰期执行工作流
|
|
||||||
|
|
||||||
2. **数据质量**:
|
2. **结构化输出是硬约束**:
|
||||||
- 当前工作流中的 `description` 和 `detailPoints` 是占位符
|
- 由 `Structured Output Parser` 保证格式。
|
||||||
- 需要添加 AI 节点(如 OpenAI)来生成真实内容
|
- `Validate Plan` 仍会做二次校验,防止“格式正确但内容不完整”。
|
||||||
|
|
||||||
3. **时区处理**:
|
3. **失败块不会误执行**:
|
||||||
- 确保时区设置正确,以便准确计算季度末日期
|
- 校验不通过的块不会调用维护 API,会在 summary 中归类为失败并给出原因。
|
||||||
|
|
||||||
### 扩展建议
|
### 扩展建议
|
||||||
|
|
||||||
- **AI 内容生成**:在 "Match Visual Rules" 后添加 OpenAI 节点,生成描述和要点
|
- 将 `failed_*` 摘要接入 Slack/Telegram 告警
|
||||||
- **数据验证**:添加验证节点检查数据完整性
|
- 对 `failed_validation` 块做二次人工审核队列
|
||||||
- **错误处理**:添加错误分支,记录失败的关键词
|
- 在预处理阶段先做字符串近似聚类,进一步减少 LLM token 消耗
|
||||||
- **通知**:工作流完成后发送通知(如 Slack、Email)
|
|
||||||
|
|||||||
@@ -30,65 +30,112 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"jsCode": "const response = $input.first().json;\nif (!response.success) {\n throw new Error('Failed to fetch tags: ' + JSON.stringify(response));\n}\n\nconst tags = response.tags;\nconst formatted = tags.map(t => ({\n id: t.id,\n name: t.name,\n nameEn: t.nameEn || '',\n projectCount: t._count?.projects || 0\n}));\n\n// Sort by project count descending\nformatted.sort((a, b) => b.projectCount - a.projectCount);\n\nreturn [{ json: { tags: formatted, total: formatted.length } }];"
|
"jsCode": "const response = $input.first().json;\nif (!response.success) {\n throw new Error('Failed to fetch tags: ' + JSON.stringify(response));\n}\n\nconst rawTags = Array.isArray(response.tags) ? response.tags : [];\nconst chunkSize = Math.max(20, Math.min(120, Number.parseInt($env.TAG_JANITOR_CHUNK_SIZE || '80', 10) || 80));\n\nconst normalizedTags = rawTags\n .map((tag) => ({\n id: tag.id,\n name: String(tag.name || '').trim(),\n nameEn: String(tag.nameEn || '').trim(),\n projectCount: Number(tag?._count?.projects || 0),\n }))\n .filter((tag) => tag.id && tag.name);\n\nnormalizedTags.sort((a, b) => b.projectCount - a.projectCount || a.name.localeCompare(b.name));\n\nif (normalizedTags.length === 0) {\n return [{\n json: {\n chunkId: 'empty-0',\n bucketKey: 'empty',\n tags: [],\n totalInChunk: 0,\n totalTags: 0,\n retryCount: 0,\n retryFeedback: '',\n status: 'noop',\n shouldExecute: false\n }\n }];\n}\n\nconst bucketMap = new Map();\nfor (const tag of normalizedTags) {\n const seed = (tag.nameEn || tag.name).toLowerCase().trim();\n const normalized = seed.replace(/[^a-z0-9\\u4e00-\\u9fa5]+/g, '');\n\n let bucketKey = 'misc';\n if (normalized.length > 0) {\n const first = normalized[0];\n if (/[a-z0-9]/.test(first)) {\n bucketKey = 'en-' + first;\n } else if (/[\\u4e00-\\u9fa5]/.test(first)) {\n bucketKey = 'zh-' + first;\n }\n }\n\n const list = bucketMap.get(bucketKey) || [];\n list.push(tag);\n bucketMap.set(bucketKey, list);\n}\n\nconst chunks = [];\nfor (const [bucketKey, tags] of bucketMap.entries()) {\n tags.sort((a, b) => b.projectCount - a.projectCount || a.name.localeCompare(b.name));\n for (let i = 0; i < tags.length; i += chunkSize) {\n chunks.push({\n bucketKey,\n tags: tags.slice(i, i + chunkSize)\n });\n }\n}\n\nchunks.sort((a, b) => b.tags.length - a.tags.length);\n\nreturn chunks.map((chunk, idx) => ({\n json: {\n chunkId: chunk.bucketKey + '-' + (idx + 1),\n bucketKey: chunk.bucketKey,\n tags: chunk.tags,\n totalInChunk: chunk.tags.length,\n totalTags: normalizedTags.length,\n retryCount: 0,\n retryFeedback: ''\n }\n}));"
|
||||||
},
|
},
|
||||||
"id": "preprocess",
|
"id": "prepare-chunks",
|
||||||
"name": "Preprocess Tags",
|
"name": "Prepare Chunk Batches",
|
||||||
"type": "n8n-nodes-base.code",
|
"type": "n8n-nodes-base.code",
|
||||||
"typeVersion": 2,
|
"typeVersion": 2,
|
||||||
"position": [440, 0]
|
"position": [460, 0]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"model": "gpt-4o",
|
"text": "=Chunk {{$json.chunkId}} from {{$json.bucketKey}}.\\nThis chunk contains {{$json.totalInChunk}} tags out of {{$json.totalTags}} total tags.\\nRetry count: {{$json.retryCount || 0}}.\\nRetry feedback: {{$json.retryFeedback || 'none'}}.\\n\\nOutput MUST be JSON and MUST follow the connected structured parser schema.\\nOnly use tag IDs from this chunk.\\n\\nChunk tags JSON:\\n{{JSON.stringify($json.tags)}}",
|
||||||
"messages": {
|
"options": {
|
||||||
"values": [
|
"systemMessage": "You are a strict tag-governance agent.\\n\\nFor the current chunk only:\\n1) Find semantic duplicates and output merges\\n2) Fill missing or low-quality English names and output updates\\n3) Return reviewedTagIds that fully cover every tag ID in this chunk\\n4) Put uncertain tags into unresolvedTagIds\\n\\nHard rules:\\n- Never reference tag IDs outside the current chunk\\n- Prefer existing target ID with higher projectCount\\n- Do not self-merge\\n- Return no markdown, no prose, JSON only"
|
||||||
{
|
},
|
||||||
"role": "system",
|
"promptType": "define",
|
||||||
"content": "You are a tag management expert. Analyze tags and identify semantic duplicates for merging and missing English names for completion. Output STRICT JSON only."
|
"hasOutputParser": true
|
||||||
},
|
},
|
||||||
{
|
"id": "tag-merge-agent",
|
||||||
"role": "user",
|
"name": "Tag Merge Agent",
|
||||||
"content": "Analyze these tags and identify:\n1. Semantic duplicates to merge (e.g., \"机器学习\" and \"ML\" → keep higher projectCount)\n2. Tags missing nameEn that need English names\n\nTags ({{$json.total}} total):\n{{JSON.stringify($json.tags)}}\n\nOutput STRICT JSON (no markdown):\n{\n \"merges\": [\n {\n \"target\": { \"name\": \"保留的标签\", \"nameEn\": \"Canonical Name\" },\n \"sourceTagIds\": [\"id1\", \"id2\"]\n }\n ],\n \"updates\": [\n { \"tagId\": \"id\", \"nameEn\": \"English Name\" }\n ]\n}\n\nRules:\n- Keep tag with higher projectCount as merge target\n- target can use { \"id\": \"existing\" } to keep existing tag\n- Only include tags needing action (empty arrays if none)\n- nameEn must be proper English, not pinyin"
|
"type": "@n8n/n8n-nodes-langchain.agent",
|
||||||
}
|
"typeVersion": 1.8,
|
||||||
]
|
"position": [700, 0]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"model": {
|
||||||
|
"__rl": true,
|
||||||
|
"mode": "list",
|
||||||
|
"value": "gpt-4.1-mini",
|
||||||
|
"cachedResultName": "gpt-4.1-mini"
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"temperature": 0.1
|
"temperature": 0.1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"id": "ai-analyze",
|
"id": "openai-model",
|
||||||
"name": "AI Analyze Tags",
|
"name": "OpenAI Chat Model",
|
||||||
"type": "@n8n/n8n-nodes-langchain.openAi",
|
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
|
||||||
"typeVersion": 1.8,
|
"typeVersion": 1.2,
|
||||||
"position": [660, 0]
|
"position": [700, 220]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"jsCode": "const response = $input.first().json;\nlet plan;\n\ntry {\n const content = response.message?.content || response.text || response;\n plan = typeof content === 'string' ? JSON.parse(content) : content;\n} catch (e) {\n throw new Error('Failed to parse AI response: ' + e.message);\n}\n\nif (!Array.isArray(plan.merges)) plan.merges = [];\nif (!Array.isArray(plan.updates)) plan.updates = [];\n\nfor (const merge of plan.merges) {\n if (merge.target.id && merge.sourceTagIds.includes(merge.target.id)) {\n throw new Error('Self-merge detected: ' + merge.target.id);\n }\n}\n\nif (plan.merges.length === 0 && plan.updates.length === 0) {\n return [{ json: { skipped: true, reason: 'No changes needed' } }];\n}\n\nreturn [{ json: plan }];"
|
"schemaType": "manual",
|
||||||
|
"inputSchema": "{\n \"type\": \"object\",\n \"properties\": {\n \"reviewedTagIds\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n }\n },\n \"unresolvedTagIds\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n }\n },\n \"merges\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"target\": {\n \"oneOf\": [\n {\n \"type\": \"object\",\n \"properties\": {\n \"id\": { \"type\": \"string\" }\n },\n \"required\": [\"id\"]\n },\n {\n \"type\": \"object\",\n \"properties\": {\n \"name\": { \"type\": \"string\" },\n \"nameEn\": { \"type\": \"string\" }\n },\n \"required\": [\"name\", \"nameEn\"]\n }\n ]\n },\n \"sourceTagIds\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n }\n }\n },\n \"required\": [\"target\", \"sourceTagIds\"]\n }\n },\n \"updates\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"tagId\": { \"type\": \"string\" },\n \"nameEn\": { \"type\": \"string\" }\n },\n \"required\": [\"tagId\", \"nameEn\"]\n }\n }\n },\n \"required\": [\"reviewedTagIds\", \"unresolvedTagIds\", \"merges\", \"updates\"]\n}",
|
||||||
|
"autoFix": true
|
||||||
},
|
},
|
||||||
"id": "validate-json",
|
"id": "structured-parser",
|
||||||
"name": "Validate JSON",
|
"name": "Structured Output Parser",
|
||||||
|
"type": "@n8n/n8n-nodes-langchain.outputParserStructured",
|
||||||
|
"typeVersion": 1.2,
|
||||||
|
"position": [960, 220]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "const maxRetries = Math.max(0, Number.parseInt($env.TAG_JANITOR_MAX_RETRIES || '2', 10) || 2);\n\nfunction normalizeIds(input) {\n if (!Array.isArray(input)) return [];\n return [...new Set(input.map((id) => String(id || '').trim()).filter(Boolean))];\n}\n\nfunction safeArray(input) {\n return Array.isArray(input) ? input : [];\n}\n\nreturn $input.all().map((item) => {\n const row = item.json || {};\n const parsed = row.output && typeof row.output === 'object' ? row.output : {};\n\n const tags = safeArray(row.tags);\n const chunkTagIds = normalizeIds(tags.map((tag) => tag.id));\n const chunkTagIdSet = new Set(chunkTagIds);\n\n const reviewedTagIds = normalizeIds(parsed.reviewedTagIds);\n const unresolvedTagIds = normalizeIds(parsed.unresolvedTagIds);\n\n const updates = safeArray(parsed.updates).flatMap((update) => {\n if (!update || typeof update !== 'object') return [];\n const tagId = String(update.tagId || '').trim();\n const nameEn = String(update.nameEn || '').trim();\n if (!tagId || !nameEn) return [];\n return [{ tagId, nameEn }];\n });\n\n const merges = safeArray(parsed.merges).flatMap((merge) => {\n if (!merge || typeof merge !== 'object') return [];\n const sourceTagIds = normalizeIds(merge.sourceTagIds);\n if (sourceTagIds.length === 0) return [];\n\n const target = merge.target;\n if (!target || typeof target !== 'object') return [];\n\n if (typeof target.id === 'string' && target.id.trim()) {\n return [{\n target: { id: target.id.trim() },\n sourceTagIds\n }];\n }\n\n const name = String(target.name || '').trim();\n const nameEn = String(target.nameEn || '').trim();\n if (!name || !nameEn) return [];\n\n return [{\n target: { name, nameEn },\n sourceTagIds\n }];\n });\n\n const errors = [];\n\n const reviewedSet = new Set(reviewedTagIds);\n const missingReviewed = chunkTagIds.filter((id) => !reviewedSet.has(id));\n if (missingReviewed.length > 0) {\n errors.push(`Missing reviewedTagIds coverage: ${missingReviewed.join(', ')}`);\n }\n\n for (const update of updates) {\n if (!chunkTagIdSet.has(update.tagId)) {\n errors.push(`Update references unknown tagId in chunk: ${update.tagId}`);\n }\n }\n\n for (const merge of merges) {\n for (const sourceTagId of merge.sourceTagIds) {\n if (!chunkTagIdSet.has(sourceTagId)) {\n errors.push(`Merge sourceTagId not in chunk: ${sourceTagId}`);\n }\n }\n\n if ('id' in merge.target) {\n if (!chunkTagIdSet.has(merge.target.id)) {\n errors.push(`Merge target.id not in chunk: ${merge.target.id}`);\n }\n if (merge.sourceTagIds.includes(merge.target.id)) {\n errors.push(`Self merge detected: ${merge.target.id}`);\n }\n }\n }\n\n const retryCount = Number(row.retryCount || 0);\n const hasValidationErrors = errors.length > 0;\n const needsRetry = hasValidationErrors && retryCount < maxRetries;\n const shouldExecute = !hasValidationErrors && (updates.length > 0 || merges.length > 0);\n\n let status = 'noop';\n if (needsRetry) status = 'retry';\n else if (hasValidationErrors) status = 'failed_validation';\n else if (shouldExecute) status = 'ready';\n\n return {\n json: {\n ...row,\n validated: {\n reviewedTagIds,\n unresolvedTagIds,\n updates,\n merges\n },\n validationErrors: errors,\n needsRetry,\n shouldExecute,\n status\n }\n };\n});"
|
||||||
|
},
|
||||||
|
"id": "validate-plan",
|
||||||
|
"name": "Validate Plan",
|
||||||
"type": "n8n-nodes-base.code",
|
"type": "n8n-nodes-base.code",
|
||||||
"typeVersion": 2,
|
"typeVersion": 2,
|
||||||
"position": [880, 0]
|
"position": [960, 0]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"conditions": {
|
"conditions": {
|
||||||
"boolean": [
|
"boolean": [
|
||||||
{
|
{
|
||||||
"value1": "={{$json.skipped}}",
|
"value1": "={{$json.needsRetry}}",
|
||||||
"value2": true
|
"value2": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"id": "check-skip",
|
"id": "retry-needed",
|
||||||
"name": "Check Skip",
|
"name": "Retry Needed?",
|
||||||
"type": "n8n-nodes-base.if",
|
"type": "n8n-nodes-base.if",
|
||||||
"typeVersion": 2,
|
"typeVersion": 2,
|
||||||
"position": [1100, 0]
|
"position": [1180, 0]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "return $input.all().map((item) => {\n const row = item.json || {};\n const nextRetryCount = Number(row.retryCount || 0) + 1;\n const retryFeedback = (row.validationErrors || []).join(' | ');\n\n return {\n json: {\n ...row,\n retryCount: nextRetryCount,\n retryFeedback\n }\n };\n});"
|
||||||
|
},
|
||||||
|
"id": "increment-retry",
|
||||||
|
"name": "Increment Retry",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"position": [1400, -120]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"conditions": {
|
||||||
|
"boolean": [
|
||||||
|
{
|
||||||
|
"value1": "={{$json.shouldExecute}}",
|
||||||
|
"value2": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": "execute-needed",
|
||||||
|
"name": "Execute Needed?",
|
||||||
|
"type": "n8n-nodes-base.if",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"position": [1400, 120]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
@@ -96,50 +143,225 @@
|
|||||||
"url": "={{$env.SITE_BASE_URL}}/api/tags/maintenance",
|
"url": "={{$env.SITE_BASE_URL}}/api/tags/maintenance",
|
||||||
"sendBody": true,
|
"sendBody": true,
|
||||||
"specifyBody": "json",
|
"specifyBody": "json",
|
||||||
"jsonBody": "={\n \"apiKey\": \"{{$env.WEBHOOK_API_KEY}}\",\n \"updates\": {{JSON.stringify($json.updates)}},\n \"merges\": {{JSON.stringify($json.merges)}}\n}",
|
"jsonBody": "={\n \"apiKey\": \"{{$env.WEBHOOK_API_KEY}}\",\n \"updates\": {{JSON.stringify($json.validated.updates || [])}},\n \"merges\": {{JSON.stringify($json.validated.merges || [])}}\n}",
|
||||||
"options": {}
|
"options": {}
|
||||||
},
|
},
|
||||||
"id": "execute-maintenance",
|
"id": "execute-maintenance",
|
||||||
"name": "Execute Maintenance",
|
"name": "Execute Maintenance",
|
||||||
"type": "n8n-nodes-base.httpRequest",
|
"type": "n8n-nodes-base.httpRequest",
|
||||||
"typeVersion": 4.2,
|
"typeVersion": 4.2,
|
||||||
"position": [1320, -100]
|
"position": [1620, 20],
|
||||||
|
"continueOnFail": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"jsCode": "const maintenanceResult = $('Execute Maintenance').first()?.json || {};\nconst skipped = $('Check Skip').first()?.json?.skipped;\n\nif (skipped) {\n return [{ json: {\n status: 'skipped',\n message: 'No changes needed',\n timestamp: new Date().toISOString()\n }}];\n}\n\nconst result = maintenanceResult.result || {};\n\nreturn [{ json: {\n status: maintenanceResult.success ? 'success' : 'failed',\n updatedCount: result.updatedCount || 0,\n mergedCount: result.mergedCount || 0,\n deletedTagCount: result.deletedTagCount || 0,\n timestamp: new Date().toISOString(),\n error: maintenanceResult.error || null\n}}];"
|
"jsCode": "return $input.all().map((item) => {\n const row = item.json || {};\n return {\n json: {\n status: row.status || 'noop',\n chunkId: row.chunkId || null,\n retryCount: Number(row.retryCount || 0),\n validationErrors: row.validationErrors || [],\n updatedCount: 0,\n mergedCount: 0,\n deletedTagCount: 0\n }\n };\n});"
|
||||||
},
|
},
|
||||||
"id": "summarize",
|
"id": "build-skip-result",
|
||||||
|
"name": "Build Skipped Result",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"position": [1620, 220]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "return $input.all().map((item) => {\n const row = item.json || {};\n\n if (row.success === true) {\n return {\n json: {\n status: 'executed',\n chunkId: null,\n retryCount: null,\n validationErrors: [],\n updatedCount: Number(row?.result?.updatedCount || 0),\n mergedCount: Number(row?.result?.mergedCount || 0),\n deletedTagCount: Number(row?.result?.deletedTagCount || 0)\n }\n };\n }\n\n const message = row?.error?.message || row?.message || row?.error || 'Unknown execution error';\n\n return {\n json: {\n status: 'failed_execute',\n chunkId: null,\n retryCount: null,\n validationErrors: [String(message)],\n updatedCount: 0,\n mergedCount: 0,\n deletedTagCount: 0\n }\n };\n});"
|
||||||
|
},
|
||||||
|
"id": "normalize-execution",
|
||||||
|
"name": "Normalize Execution Result",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"position": [1840, 20]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"mode": "append"
|
||||||
|
},
|
||||||
|
"id": "merge-results",
|
||||||
|
"name": "Merge Results",
|
||||||
|
"type": "n8n-nodes-base.merge",
|
||||||
|
"typeVersion": 3.2,
|
||||||
|
"position": [2060, 120]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "const rows = $input.all().map((item) => item.json || {});\n\nconst summary = {\n status: 'success',\n chunkRuns: rows.length,\n executedRuns: 0,\n noopRuns: 0,\n retryRuns: 0,\n failedValidationRuns: 0,\n failedExecuteRuns: 0,\n updatedCount: 0,\n mergedCount: 0,\n deletedTagCount: 0,\n failures: [],\n timestamp: new Date().toISOString()\n};\n\nfor (const row of rows) {\n if (row.status === 'executed') {\n summary.executedRuns += 1;\n summary.updatedCount += Number(row.updatedCount || 0);\n summary.mergedCount += Number(row.mergedCount || 0);\n summary.deletedTagCount += Number(row.deletedTagCount || 0);\n continue;\n }\n\n if (row.status === 'noop') {\n summary.noopRuns += 1;\n continue;\n }\n\n if (row.status === 'retry') {\n summary.retryRuns += 1;\n continue;\n }\n\n if (row.status === 'failed_validation') {\n summary.failedValidationRuns += 1;\n summary.failures.push({\n type: 'failed_validation',\n chunkId: row.chunkId || null,\n reasons: row.validationErrors || []\n });\n continue;\n }\n\n if (row.status === 'failed_execute') {\n summary.failedExecuteRuns += 1;\n summary.failures.push({\n type: 'failed_execute',\n chunkId: row.chunkId || null,\n reasons: row.validationErrors || []\n });\n }\n}\n\nif (summary.failedValidationRuns > 0 || summary.failedExecuteRuns > 0) {\n summary.status = 'partial_failed';\n}\n\nsummary.failures = summary.failures.slice(0, 20);\n\nreturn [{ json: summary }];"
|
||||||
|
},
|
||||||
|
"id": "summarize-results",
|
||||||
"name": "Summarize Results",
|
"name": "Summarize Results",
|
||||||
"type": "n8n-nodes-base.code",
|
"type": "n8n-nodes-base.code",
|
||||||
"typeVersion": 2,
|
"typeVersion": 2,
|
||||||
"position": [1540, 0]
|
"position": [2280, 120]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"connections": {
|
"connections": {
|
||||||
"Daily 3AM UTC": {
|
"Daily 3AM UTC": {
|
||||||
"main": [[{ "node": "Fetch Tags", "type": "main", "index": 0 }]]
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Fetch Tags",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"Fetch Tags": {
|
"Fetch Tags": {
|
||||||
"main": [[{ "node": "Preprocess Tags", "type": "main", "index": 0 }]]
|
|
||||||
},
|
|
||||||
"Preprocess Tags": {
|
|
||||||
"main": [[{ "node": "AI Analyze Tags", "type": "main", "index": 0 }]]
|
|
||||||
},
|
|
||||||
"AI Analyze Tags": {
|
|
||||||
"main": [[{ "node": "Validate JSON", "type": "main", "index": 0 }]]
|
|
||||||
},
|
|
||||||
"Validate JSON": {
|
|
||||||
"main": [[{ "node": "Check Skip", "type": "main", "index": 0 }]]
|
|
||||||
},
|
|
||||||
"Check Skip": {
|
|
||||||
"main": [
|
"main": [
|
||||||
[{ "node": "Execute Maintenance", "type": "main", "index": 0 }],
|
[
|
||||||
[{ "node": "Summarize Results", "type": "main", "index": 0 }]
|
{
|
||||||
|
"node": "Prepare Chunk Batches",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Prepare Chunk Batches": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Tag Merge Agent",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"OpenAI Chat Model": {
|
||||||
|
"ai_languageModel": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Tag Merge Agent",
|
||||||
|
"type": "ai_languageModel",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Structured Output Parser": {
|
||||||
|
"ai_outputParser": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Tag Merge Agent",
|
||||||
|
"type": "ai_outputParser",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Tag Merge Agent": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Validate Plan",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Validate Plan": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Retry Needed?",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Retry Needed?": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Increment Retry",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Execute Needed?",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Increment Retry": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Tag Merge Agent",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Execute Needed?": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Execute Maintenance",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Build Skipped Result",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"Execute Maintenance": {
|
"Execute Maintenance": {
|
||||||
"main": [[{ "node": "Summarize Results", "type": "main", "index": 0 }]]
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Normalize Execution Result",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Normalize Execution Result": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Merge Results",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Build Skipped Result": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Merge Results",
|
||||||
|
"type": "main",
|
||||||
|
"index": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Merge Results": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Summarize Results",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
|
|||||||
@@ -1,6 +1,166 @@
|
|||||||
import { prisma } from '@/lib/prisma'
|
import { prisma } from '@/lib/prisma'
|
||||||
import type { ProjectInput } from '@/lib/validations'
|
import type { ProjectInput } from '@/lib/validations'
|
||||||
import { generateSlug } from '@/lib/slug'
|
import { generateSlug } from '@/lib/slug'
|
||||||
|
import type { Prisma } from '@prisma/client'
|
||||||
|
|
||||||
|
const PLURAL_NORMALIZATION_MAP = new Map([
|
||||||
|
['agents', 'agent'],
|
||||||
|
['assistants', 'assistant'],
|
||||||
|
['tools', 'tool'],
|
||||||
|
['frameworks', 'framework'],
|
||||||
|
['models', 'model'],
|
||||||
|
['servers', 'server'],
|
||||||
|
['clients', 'client'],
|
||||||
|
['workflows', 'workflow'],
|
||||||
|
['plugins', 'plugin'],
|
||||||
|
['libraries', 'library'],
|
||||||
|
['datasets', 'dataset'],
|
||||||
|
['platforms', 'platform'],
|
||||||
|
['systems', 'system'],
|
||||||
|
['repositories', 'repository'],
|
||||||
|
['engines', 'engine'],
|
||||||
|
])
|
||||||
|
|
||||||
|
const NOISE_TAG_KEYS = new Set([
|
||||||
|
'ai',
|
||||||
|
'artificial intelligence',
|
||||||
|
'open source',
|
||||||
|
'requires configuration',
|
||||||
|
'requires basics',
|
||||||
|
'low learning curve',
|
||||||
|
'enterprise',
|
||||||
|
'complex deployment',
|
||||||
|
'cloud service',
|
||||||
|
'cross platform',
|
||||||
|
'tutorial',
|
||||||
|
'academic research',
|
||||||
|
'academic resource',
|
||||||
|
'ai research resource',
|
||||||
|
'multi language support',
|
||||||
|
'self hosted',
|
||||||
|
'user notification',
|
||||||
|
'mit license',
|
||||||
|
'apache 2 0',
|
||||||
|
'开源',
|
||||||
|
'需要配置',
|
||||||
|
'需要基础',
|
||||||
|
'低学习成本',
|
||||||
|
'企业级',
|
||||||
|
'复杂部署',
|
||||||
|
'云端服务',
|
||||||
|
'跨平台',
|
||||||
|
'教程',
|
||||||
|
'学术研究',
|
||||||
|
'学术资源',
|
||||||
|
'多语言支持',
|
||||||
|
'自托管',
|
||||||
|
'用户通知',
|
||||||
|
'许可',
|
||||||
|
])
|
||||||
|
|
||||||
|
const TAG_FALLBACK = {
|
||||||
|
name: 'AI开发工具',
|
||||||
|
nameEn: 'AI Development Tool',
|
||||||
|
} as const
|
||||||
|
|
||||||
|
function normalizeWhitespace(value: string): string {
|
||||||
|
return value.replace(/\s+/g, ' ').trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
function isAsciiText(value: string): boolean {
|
||||||
|
return /^[\x00-\x7f]+$/.test(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
function canonicalizeTagKey(value: string): string {
|
||||||
|
const normalized = normalizeWhitespace(value)
|
||||||
|
.normalize('NFKC')
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/[+/_&|-]+/g, ' ')
|
||||||
|
.replace(/[^a-z0-9\u4e00-\u9fa5\s]/g, ' ')
|
||||||
|
.replace(/\s+/g, ' ')
|
||||||
|
.trim()
|
||||||
|
|
||||||
|
if (!normalized) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalized
|
||||||
|
.split(' ')
|
||||||
|
.map((word) => PLURAL_NORMALIZATION_MAP.get(word) || word)
|
||||||
|
.join(' ')
|
||||||
|
.trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeIncomingTag(tag: ProjectInput['tags'][number]) {
|
||||||
|
const normalizedName = normalizeWhitespace(tag.name)
|
||||||
|
const normalizedNameEn = normalizeWhitespace(tag.nameEn || '')
|
||||||
|
const resolvedNameEn = normalizedNameEn || (isAsciiText(normalizedName) ? normalizedName : '')
|
||||||
|
const canonicalNameKey = canonicalizeTagKey(normalizedName)
|
||||||
|
const canonicalNameEnKey = canonicalizeTagKey(resolvedNameEn)
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: normalizedName,
|
||||||
|
nameEn: resolvedNameEn || null,
|
||||||
|
canonicalNameKey,
|
||||||
|
canonicalNameEnKey,
|
||||||
|
slug: generateSlug(normalizedName, resolvedNameEn || null),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isMeaningfulTag(tag: ReturnType<typeof normalizeIncomingTag>): boolean {
|
||||||
|
if (!tag.name) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NOISE_TAG_KEYS.has(tag.canonicalNameKey)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tag.canonicalNameEnKey && NOISE_TAG_KEYS.has(tag.canonicalNameEnKey)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
type TagWithProjectCount = Prisma.TagGetPayload<{
|
||||||
|
include: {
|
||||||
|
_count: { select: { projects: true } }
|
||||||
|
}
|
||||||
|
}>
|
||||||
|
|
||||||
|
function chooseBestTag(candidates: TagWithProjectCount[]): TagWithProjectCount {
|
||||||
|
return [...candidates].sort((a, b) => {
|
||||||
|
const projectDiff = b._count.projects - a._count.projects
|
||||||
|
if (projectDiff !== 0) {
|
||||||
|
return projectDiff
|
||||||
|
}
|
||||||
|
return a.createdAt.getTime() - b.createdAt.getTime()
|
||||||
|
})[0]!
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getFallbackTag(): Promise<TagWithProjectCount> {
|
||||||
|
const fallbackSlug = generateSlug(TAG_FALLBACK.name, TAG_FALLBACK.nameEn)
|
||||||
|
const fallbackTag = await prisma.tag.upsert({
|
||||||
|
where: { slug: fallbackSlug },
|
||||||
|
update: {
|
||||||
|
name: TAG_FALLBACK.name,
|
||||||
|
nameEn: TAG_FALLBACK.nameEn,
|
||||||
|
},
|
||||||
|
create: {
|
||||||
|
name: TAG_FALLBACK.name,
|
||||||
|
nameEn: TAG_FALLBACK.nameEn,
|
||||||
|
slug: fallbackSlug,
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
_count: {
|
||||||
|
select: { projects: true },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return fallbackTag
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 多级去重策略:查找已存在的项目
|
* 多级去重策略:查找已存在的项目
|
||||||
@@ -90,44 +250,159 @@ export async function findExistingProject(projectData: ProjectInput) {
|
|||||||
* @returns 标签连接对象数组
|
* @returns 标签连接对象数组
|
||||||
*/
|
*/
|
||||||
export async function upsertTags(tags: ProjectInput['tags']) {
|
export async function upsertTags(tags: ProjectInput['tags']) {
|
||||||
// 优化:批量查询所有已存在的标签,避免 N+1 问题
|
const meaningfulTags = tags
|
||||||
const allTagNames = tags.map((t) => t.name)
|
.map((tag) => normalizeIncomingTag(tag))
|
||||||
|
.filter((tag) => isMeaningfulTag(tag))
|
||||||
|
|
||||||
|
const normalizedIncomingTags = Array.from(
|
||||||
|
new Map(
|
||||||
|
meaningfulTags.map((normalized) => {
|
||||||
|
return [
|
||||||
|
normalized.canonicalNameEnKey || normalized.canonicalNameKey || normalized.name,
|
||||||
|
normalized,
|
||||||
|
] as const
|
||||||
|
})
|
||||||
|
).values()
|
||||||
|
)
|
||||||
|
|
||||||
|
if (normalizedIncomingTags.length === 0) {
|
||||||
|
return [await getFallbackTag()]
|
||||||
|
}
|
||||||
|
|
||||||
const existingTags = await prisma.tag.findMany({
|
const existingTags = await prisma.tag.findMany({
|
||||||
where: { name: { in: allTagNames } },
|
include: {
|
||||||
|
_count: {
|
||||||
|
select: { projects: true },
|
||||||
|
},
|
||||||
|
},
|
||||||
})
|
})
|
||||||
const existingTagNames = new Set(existingTags.map((t) => t.name))
|
|
||||||
|
|
||||||
// Upsert tags - 优化后只查询不存在的标签
|
const tagByExactName = new Map<string, TagWithProjectCount[]>()
|
||||||
return await Promise.all(
|
const tagBySlug = new Map<string, TagWithProjectCount>()
|
||||||
tags.map(async (tag) => {
|
const tagByCanonicalName = new Map<string, TagWithProjectCount[]>()
|
||||||
const tagSlug = generateSlug(tag.name, tag.nameEn)
|
const tagByCanonicalNameEn = new Map<string, TagWithProjectCount[]>()
|
||||||
|
|
||||||
// 首先从批量查询结果中查找
|
for (const tag of existingTags) {
|
||||||
if (existingTagNames.has(tag.name)) {
|
const exactNameKey = normalizeWhitespace(tag.name)
|
||||||
return existingTags.find((t) => t.name === tag.name)!
|
const existingCanonicalName = canonicalizeTagKey(tag.name)
|
||||||
|
const existingCanonicalNameEn = canonicalizeTagKey(tag.nameEn || '')
|
||||||
|
|
||||||
|
if (!tagByExactName.has(exactNameKey)) {
|
||||||
|
tagByExactName.set(exactNameKey, [])
|
||||||
|
}
|
||||||
|
tagByExactName.get(exactNameKey)!.push(tag)
|
||||||
|
|
||||||
|
if (existingCanonicalName) {
|
||||||
|
if (!tagByCanonicalName.has(existingCanonicalName)) {
|
||||||
|
tagByCanonicalName.set(existingCanonicalName, [])
|
||||||
}
|
}
|
||||||
|
tagByCanonicalName.get(existingCanonicalName)!.push(tag)
|
||||||
|
}
|
||||||
|
|
||||||
// 只有标签不存在时才尝试 upsert
|
if (existingCanonicalNameEn) {
|
||||||
try {
|
if (!tagByCanonicalNameEn.has(existingCanonicalNameEn)) {
|
||||||
return await prisma.tag.upsert({
|
tagByCanonicalNameEn.set(existingCanonicalNameEn, [])
|
||||||
where: { slug: tagSlug },
|
}
|
||||||
update: {},
|
tagByCanonicalNameEn.get(existingCanonicalNameEn)!.push(tag)
|
||||||
create: {
|
}
|
||||||
name: tag.name,
|
|
||||||
nameEn: tag.nameEn || null,
|
tagBySlug.set(tag.slug, tag)
|
||||||
slug: tagSlug,
|
}
|
||||||
},
|
|
||||||
|
const resolvedTags: TagWithProjectCount[] = []
|
||||||
|
|
||||||
|
for (const incomingTag of normalizedIncomingTags) {
|
||||||
|
const exactNameCandidates = tagByExactName.get(incomingTag.name) || []
|
||||||
|
let matchedTag = exactNameCandidates.length > 0 ? chooseBestTag(exactNameCandidates) : null
|
||||||
|
|
||||||
|
if (!matchedTag && incomingTag.canonicalNameKey) {
|
||||||
|
const canonicalNameCandidates = tagByCanonicalName.get(incomingTag.canonicalNameKey) || []
|
||||||
|
if (canonicalNameCandidates.length > 0) {
|
||||||
|
matchedTag = chooseBestTag(canonicalNameCandidates)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!matchedTag && incomingTag.canonicalNameEnKey) {
|
||||||
|
const canonicalNameEnCandidates = tagByCanonicalNameEn.get(incomingTag.canonicalNameEnKey) || []
|
||||||
|
if (canonicalNameEnCandidates.length > 0) {
|
||||||
|
matchedTag = chooseBestTag(canonicalNameEnCandidates)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!matchedTag) {
|
||||||
|
matchedTag = tagBySlug.get(incomingTag.slug) || null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matchedTag) {
|
||||||
|
if (incomingTag.nameEn && !matchedTag.nameEn) {
|
||||||
|
await prisma.tag.update({
|
||||||
|
where: { id: matchedTag.id },
|
||||||
|
data: { nameEn: incomingTag.nameEn },
|
||||||
})
|
})
|
||||||
} catch (error) {
|
matchedTag = {
|
||||||
// 如果 slug 冲突,查找并使用已存在的标签
|
...matchedTag,
|
||||||
const existingBySlug = await prisma.tag.findUnique({
|
nameEn: incomingTag.nameEn,
|
||||||
where: { slug: tagSlug },
|
|
||||||
})
|
|
||||||
if (existingBySlug) {
|
|
||||||
return existingBySlug
|
|
||||||
}
|
}
|
||||||
throw error
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
resolvedTags.push(matchedTag)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const createdTag = await prisma.tag.create({
|
||||||
|
data: {
|
||||||
|
name: incomingTag.name,
|
||||||
|
nameEn: incomingTag.nameEn,
|
||||||
|
slug: incomingTag.slug,
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
_count: {
|
||||||
|
select: { projects: true },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
resolvedTags.push(createdTag)
|
||||||
|
|
||||||
|
if (!tagByExactName.has(createdTag.name)) {
|
||||||
|
tagByExactName.set(createdTag.name, [])
|
||||||
|
}
|
||||||
|
tagByExactName.get(createdTag.name)!.push(createdTag)
|
||||||
|
tagBySlug.set(createdTag.slug, createdTag)
|
||||||
|
if (createdTag.nameEn) {
|
||||||
|
const canonicalNameEn = canonicalizeTagKey(createdTag.nameEn)
|
||||||
|
if (canonicalNameEn) {
|
||||||
|
if (!tagByCanonicalNameEn.has(canonicalNameEn)) {
|
||||||
|
tagByCanonicalNameEn.set(canonicalNameEn, [])
|
||||||
|
}
|
||||||
|
tagByCanonicalNameEn.get(canonicalNameEn)!.push(createdTag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
const fallbackTag = await prisma.tag.findFirst({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ name: incomingTag.name },
|
||||||
|
{ slug: incomingTag.slug },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
_count: {
|
||||||
|
select: { projects: true },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if (fallbackTag) {
|
||||||
|
resolvedTags.push(fallbackTag)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`Failed to resolve tag: ${incomingTag.name}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Array.from(
|
||||||
|
new Map(resolvedTags.map((tag) => [tag.id, tag])).values()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
type WebhookPayload,
|
type WebhookPayload,
|
||||||
type ProjectInput,
|
type ProjectInput,
|
||||||
} from '@/lib/validations'
|
} from '@/lib/validations'
|
||||||
import { findExistingProject } from '../../discovery/lib/discovery-service'
|
import { findExistingProject, upsertTags } from '../../discovery/lib/discovery-service'
|
||||||
import { generateSlug } from '@/lib/slug'
|
import { generateSlug } from '@/lib/slug'
|
||||||
|
|
||||||
export async function POST(request: NextRequest) {
|
export async function POST(request: NextRequest) {
|
||||||
@@ -90,46 +90,8 @@ export async function POST(request: NextRequest) {
|
|||||||
// 多级去重:查找已存在的项目
|
// 多级去重:查找已存在的项目
|
||||||
const existingProject = await findExistingProject(validProject)
|
const existingProject = await findExistingProject(validProject)
|
||||||
|
|
||||||
// 优化:批量查询所有已存在的标签,避免 N+1 问题
|
// 标签规范化与自动复用(防止同义标签持续膨胀)
|
||||||
const allTagNames = validProject.tags.map((t) => t.name)
|
const tagConnections = await upsertTags(validProject.tags)
|
||||||
const existingTags = await prisma.tag.findMany({
|
|
||||||
where: { name: { in: allTagNames } },
|
|
||||||
})
|
|
||||||
const existingTagNames = new Set(existingTags.map((t) => t.name))
|
|
||||||
|
|
||||||
// Upsert tags - 优化后只查询不存在的标签
|
|
||||||
const tagConnections = await Promise.all(
|
|
||||||
validProject.tags.map(async (tag) => {
|
|
||||||
const tagSlug = generateSlug(tag.name, tag.nameEn)
|
|
||||||
|
|
||||||
// 首先从批量查询结果中查找
|
|
||||||
if (existingTagNames.has(tag.name)) {
|
|
||||||
return existingTags.find((t) => t.name === tag.name)!
|
|
||||||
}
|
|
||||||
|
|
||||||
// 只有标签不存在时才尝试 upsert
|
|
||||||
try {
|
|
||||||
return await prisma.tag.upsert({
|
|
||||||
where: { slug: tagSlug },
|
|
||||||
update: {},
|
|
||||||
create: {
|
|
||||||
name: tag.name,
|
|
||||||
nameEn: tag.nameEn || null,
|
|
||||||
slug: tagSlug,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
} catch (error) {
|
|
||||||
// 如果 slug 冲突,查找并使用已存在的标签
|
|
||||||
const existingBySlug = await prisma.tag.findUnique({
|
|
||||||
where: { slug: tagSlug },
|
|
||||||
})
|
|
||||||
if (existingBySlug) {
|
|
||||||
return existingBySlug
|
|
||||||
}
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
// Generate slug for project
|
// Generate slug for project
|
||||||
const slug = generateSlug(validProject.name, validProject.nameEn)
|
const slug = generateSlug(validProject.name, validProject.nameEn)
|
||||||
|
|||||||
Reference in New Issue
Block a user