ZZZyx3587 commited on
Commit
03c63b9
ยท
verified ยท
1 Parent(s): b17c15d

Upload folder using huggingface_hub

Browse files
.claude/settings.local.json ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(pip install *)",
5
+ "Bash(python direction_analyzer.py)",
6
+ "Bash(python repo_evaluator.py)",
7
+ "Bash(python test_agents.py)",
8
+ "WebSearch",
9
+ "mcp__filesystem__read_media_file",
10
+ "Bash(python -c \"import run; print\\('run.py ๅฏผๅ…ฅๆˆๅŠŸ'\\)\")",
11
+ "Bash(python -c \"from app import format_report; print\\('app.py ๅฏผๅ…ฅๆˆๅŠŸ'\\)\")",
12
+ "Bash(python -c ' *)",
13
+ "Bash(ls -la *.txt *.md .gitignore)",
14
+ "Bash(cat requirements.txt)",
15
+ "Bash(python run.py https://arxiv.org/abs/2011.08785)",
16
+ "Bash(export GITHUB_TOKEN=\"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\")",
17
+ "Bash(timeout 15 python app.py.)",
18
+ "Bash(python -c \"import gradio; print\\(f'Gradio {gradio.__version__}'\\)\")",
19
+ "Bash(python run.py https://arxiv.org/abs/1706.03762)",
20
+ "Bash(python app.py)",
21
+ "Bash(taskkill //F //IM python.exe)",
22
+ "Read(//dev/**)",
23
+ "Bash(tasklist)",
24
+ "Bash(python -u app.py)",
25
+ "Bash(mkdir -p \"C:\\\\Users\\\\Yuan Xin\\\\.cache\\\\huggingface\\\\gradio\\\\frpc\")",
26
+ "Bash(curl -L -o \"C:\\\\Users\\\\Yuan Xin\\\\.cache\\\\huggingface\\\\gradio\\\\frpc\\\\frpc_windows_amd64_v0.3\" \"https://cdn-media.huggingface.co/frpc-gradio-0.3/frpc_windows_amd64.exe\")",
27
+ "Bash(where ngrok *)",
28
+ "Bash(export HF_ENDPOINT=https://hf-mirror.com)",
29
+ "Bash(curl -L -o \"C:\\\\Users\\\\Yuan Xin\\\\.cache\\\\huggingface\\\\gradio\\\\frpc\\\\frpc_windows_amd64_v0.3\" \"https://hf-mirror.com/frpc-gradio-0.3/resolve/main/frpc_windows_amd64.exe\")",
30
+ "Bash(python *)",
31
+ "Bash(curl -L -o \"C:\\\\Users\\\\Yuan Xin\\\\AppData\\\\Local\\\\Programs\\\\Python\\\\Python311\\\\Lib\\\\site-packages\\\\gradio\\\\frpc_windows_amd64_v0.3\" \"https://hf-mirror.com/frpc-gradio-0.3/resolve/main/frpc_windows_amd64.exe\")",
32
+ "Bash(curl *)",
33
+ "Bash(gradio deploy *)",
34
+ "Bash(pip freeze *)",
35
+ "Bash(huggingface-cli whoami *)",
36
+ "Bash(hf auth *)"
37
+ ]
38
+ }
39
+ }
.env.backup ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ GITHUB_TOKEN=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
2
+ NGROK_AUTH_TOKEN=3DHp7jn8WuHK6PhtgOTTAcmptnT_5ZdsV7VpfD6WQTKDeYD9r
.gitignore ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.pyo
5
+ *.egg-info/
6
+
7
+ # Environment
8
+ .env
9
+
10
+ # IDE
11
+ .vscode/
12
+ .idea/
13
+
14
+ # OS
15
+ .DS_Store
16
+ Thumbs.db
.gradio/certificate.pem ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
3
+ TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
4
+ cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
5
+ WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
6
+ ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
7
+ MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
8
+ h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
9
+ 0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
10
+ A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
11
+ T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
12
+ B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
13
+ B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
14
+ KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
15
+ OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
16
+ jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
17
+ qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
18
+ rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
19
+ HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
20
+ hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
21
+ ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
22
+ 3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
23
+ NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
24
+ ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
25
+ TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
26
+ jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
27
+ oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
28
+ 4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
29
+ mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
30
+ emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
31
+ -----END CERTIFICATE-----
.gradio/flagged/dataset1.csv ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ ่ฎบๆ–‡ arxiv ้“พๆŽฅ,็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ็ ”ๆŠฅ,timestamp
2
+ ,"## โš ๏ธ ่ฏท่พ“ๅ…ฅ arxiv ่ฎบๆ–‡้“พๆŽฅ
3
+
4
+ ็คบไพ‹: `https://arxiv.org/abs/2011.08785`",2026-05-05 10:05:48.041973
README.md CHANGED
@@ -1,13 +1,14 @@
1
  ---
2
  title: ResearchRadar
3
- emoji: ๐Ÿš€
4
- colorFrom: yellow
5
- colorTo: gray
6
  sdk: gradio
7
  sdk_version: 6.14.0
8
- python_version: '3.13'
9
- app_file: app.py
10
- pinned: false
11
  ---
12
 
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
1
  ---
2
  title: ResearchRadar
3
+ app_file: app.py
 
 
4
  sdk: gradio
5
  sdk_version: 6.14.0
 
 
 
6
  ---
7
 
8
+ # ResearchRadar โ€” ็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ + ๅผ€ๆบไปฃ็ ่ฏ„ไผฐ
9
+
10
+ ่พ“ๅ…ฅไธ€็ฏ‡ AI ่ฎบๆ–‡็š„ arxiv ้“พๆŽฅ๏ผŒ่‡ชๅŠจ็”Ÿๆˆ็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ็ ”ๆŠฅ๏ผš
11
+
12
+ 1. **็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ** โ€” ๅญ้ข†ๅŸŸๅฎšไฝ + ๆ–นๆณ•ๆ—่ฐฑ็ณป + ๅผ€ๆบ่ฆ†็›–ๅบฆ
13
+ 2. **ๅผ€ๆบๅฎž็Žฐ่ฏ„ไผฐ** โ€” ๅ…ญ็ปดๅบฆๆ‰“ๅˆ†๏ผˆ็Žฏๅขƒ้…็ฝฎใ€ๆ–‡ๆกฃใ€ไปฃ็ ใ€็คพๅŒบใ€ไพ่ต–ใ€ๅฏนๆฏ”ๅฎž้ชŒ้€‚้…ๅบฆ๏ผ‰
14
+ 3. **ๅฏนๆฏ”ๅฎž้ชŒๆŽจ่** โ€” ๆŒ‰ๅฐฑ็ปชๅบฆๅˆ†็ป„๏ผˆๅฏ็›ดๆŽฅ็”จ / ้œ€ไฟฎๆ”น / ไป…ๅ‚่€ƒ๏ผ‰
ResearchRadar_Week1_Demo_ๆ–นๆกˆ.md ADDED
@@ -0,0 +1,1225 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ResearchRadar ้กน็›ฎไปปๅŠกไนฆ
2
+
3
+ > ่ฏป่€…๏ผš็”ฒใ€ไน™๏ผˆ็ ”ไธ€๏ผŒๅ‡ไธบ Python ๅ…ฅ้—จๆฐดๅนณ๏ผ‰
4
+ > ็”จ้€”๏ผš็ฌฌไธ€ๅ‘จๆผ”็คบๆฑ‡ๆŠฅ + ๅŽ็ปญ 6 ๅ‘จๅผ€ๅ‘็š„ๆ‰ง่กŒไพๆฎ
5
+ > ๆฑ‡ๆŠฅๅฏน่ฑก๏ผšๅฏผๅธˆ + ๆŠ•่ต„ไบบ | ๆ—ถ้•ฟ๏ผš6 ๅˆ†้’Ÿ
6
+
7
+ ---
8
+
9
+ ## ไธ€ใ€้กน็›ฎๅฎšไน‰
10
+
11
+ ### 1.1 ๆˆ‘ไปฌ่ฆ่งฃๅ†ณไป€ไนˆ้—ฎ้ข˜
12
+
13
+ AI ็ ”็ฉถ่€…่ฏปๅˆฐไธ€็ฏ‡่ฎบๆ–‡ๅŽ๏ผŒๆƒณๅœจ่ฏฅๆ–นๅ‘ๅผ€ๅฑ•็ ”็ฉถใ€ๅ†™่ฎบๆ–‡่ท‘ๅฏนๆฏ”ๅฎž้ชŒ๏ผŒ้ขไธดไธ‰ไธช้€’่ฟ›็š„็—›็‚น๏ผš
14
+
15
+ | ็—›็‚น | ๅ…ทไฝ“ๆ่ฟฐ | ไบบๅทฅ่€—ๆ—ถ |
16
+ |------|----------|----------|
17
+ | **ๆ‘ธๆธ…ๆ–นๅ‘้šพ** | ่ฏปๅฎŒไธ€็ฏ‡่ฎบๆ–‡๏ผŒๆƒณ็Ÿฅ้“่ฟ™ไธช็ ”็ฉถๆ–นๅ‘ๆœ‰ๅ“ชไบ›ไธปๆตๆ–นๆณ•ๆ—ใ€ๅ„่‡ชๆœ‰ไป€ไนˆๅผ€ๆบๅฎž็Žฐใ€็›ฎๅ‰ๅ‰ๆฒฟๅœจๅ“ช้‡Œโ€”โ€”ไฟกๆฏๅˆ†ๆ•ฃๅœจ GitHubใ€PapersWithCodeใ€็ŸฅไนŽใ€ๅ„ๅฎž้ชŒๅฎคไธป้กต๏ผŒๆ‹ผๅ‡‘ๅ…จๆ™ฏๅ›พๆž่ดนๆ—ถ้—ด | 1~2 ๅคฉ |
18
+ | **ๆ‰พๅฏนไปฃ็ ้šพ** | ๅณไพฟๆ‰พๅˆฐไธ€ๅ †ไป“ๅบ“๏ผŒไนŸๅˆ†ไธๆธ…ๅ“ชไธชๆ˜ฏ"ๅฎ˜ๆ–นๅฎž็Žฐ"ใ€ๅ“ชไธชๆ˜ฏ"็คพๅŒบๆœ€็ƒญๅค็Žฐ"ใ€ๅ“ชไธชๅชๆ˜ฏ"ๅชๆœ‰ README ็š„็ฉบๅฃณ" | ๅŠๅคฉ~1 ๅคฉ |
19
+ | **่ฏ„ไผฐ่ƒฝไธ่ƒฝ่ท‘้šพ** | ็œ‹ไธญไธ€ไธชไป“ๅบ“ๆƒณๆ‹ฟๆฅ่ท‘ๅฏนๆฏ”ๅฎž้ชŒ๏ผŒ่ฃ…ไบ†ไธคๅคฉ็Žฏๅขƒๅ‘็Žฐไพ่ต–ๅดฉไบ†ใ€ๆ•ฐๆฎ้›†ๆฒกๅ…ฌๅผ€ใ€่ฎญ็ปƒ่„šๆœฌ่ท‘ไธ้€š | ๅŠๅคฉ~1 ๅคฉ |
20
+
21
+ **ResearchRadar ๅš็š„ไบ‹ๆƒ…**๏ผš่พ“ๅ…ฅไธ€็ฏ‡่ฎบๆ–‡็š„ arxiv ้“พๆŽฅ๏ผŒ่‡ชๅŠจๅฎŒๆˆใ€Œๆ‘ธๆธ…็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ โ†’ ๆ‰พๅˆฐ้ซ˜่ดจ้‡ๅผ€ๆบๅฎž็Žฐ โ†’ ่ฏ„ไผฐๅฏๅค็Žฐๆ€ง โ†’ ๆŽจ่ๆœ€้€‚ๅˆๆ‹ฟๆฅๅšๅฏนๆฏ”ๅฎž้ชŒ็š„ไป“ๅบ“ใ€ใ€‚
22
+
23
+ ### 1.2 ่พ“ๅ…ฅไธŽ่พ“ๅ‡บ
24
+
25
+ ```
26
+ ่พ“ๅ…ฅ๏ผšไธ€็ฏ‡่ฎบๆ–‡็š„ arxiv URL๏ผˆๅฆ‚ https://arxiv.org/abs/1706.03762๏ผ‰
27
+ โ†“
28
+ ่พ“ๅ‡บ๏ผšไธ€ไปฝ"็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ็ ”ๆŠฅ"๏ผŒๅŒ…ๅซ๏ผš
29
+ 1. ็ ”็ฉถๆ–นๅ‘่งฃ่ฏป๏ผš่ฟ™็ฏ‡่ฎบๆ–‡ๅฑžไบŽไป€ไนˆๅญ้ข†ๅŸŸใ€่ฏฅ้ข†ๅŸŸๆœ‰ๅ“ชไบ›ไธปๆตๆ–นๆณ•ๆ—
30
+ 2. ๅผ€ๆบๅฎž็Žฐๅ…จๆ™ฏๅ›พ๏ผšๆฏไธชๆ–นๆณ•ๆ—ไธ‹ๆœ‰ๅ“ชไบ›้ซ˜่ดจ้‡ไป“ๅบ“๏ผˆๅซ starใ€็ปดๆŠค็Šถๆ€ใ€ๆ–นๆณ•็ฑปๅž‹๏ผ‰
31
+ 3. ๆฏไธชไป“ๅบ“็š„ๅฏๅค็Žฐๆ€ง่ฏ„ๅˆ†๏ผˆ0-100๏ผ‰+ ไบ”็ปดๅบฆๆ˜Ž็ป† + ๅˆคๅฎš็†็”ฑ
32
+ 4. ๅฏนๆฏ”ๅฎž้ชŒๆŽจ่๏ผšๅ“ชไบ›ไป“ๅบ“ๅฏไปฅ็›ดๆŽฅๆ‹ฟๆฅ่ท‘ baseline / ๅฏนๆฏ”ๅฎž้ชŒ๏ผŒไธบไป€ไนˆ
33
+ ```
34
+
35
+ ### 1.3 ๆ ธๅฟƒไปทๅ€ผ้“พ๏ผˆไธ‰ๅฑ‚๏ผ‰
36
+
37
+ ```
38
+ ่ฎบๆ–‡่พ“ๅ…ฅ
39
+ โ†“
40
+ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
41
+ โ”‚ ็ฌฌไธ€ๅฑ‚๏ผš็ ”็ฉถๆ–นๅ‘่งฃๆž โ”‚
42
+ โ”‚ LLM ็†่งฃ่ฎบๆ–‡ โ†’ ่ฏ†ๅˆซๅญ้ข†ๅŸŸ โ†’ ๆขณ็†ๆ–นๆณ•ๆ—่ฐฑ็ณป โ”‚
43
+ โ”‚ ่พ“ๅ‡บ๏ผš่ฏฅๆ–นๅ‘็š„"็ ”็ฉถๅœฐๅ›พ" โ”‚
44
+ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
45
+ โ”‚ ็ฌฌไบŒๅฑ‚๏ผšๅผ€ๆบไปฃ็ ๅ‘็Žฐ โ”‚
46
+ โ”‚ ้’ˆๅฏนๆฏไธชๆ–นๆณ•ๆ—ๆœ็ดข GitHub โ†’ ๅŽป้‡ โ†’ ๅˆ็ญ› โ”‚
47
+ โ”‚ ่พ“ๅ‡บ๏ผšๆŒ‰ๆ–นๆณ•ๆ—ๅˆ†็ฑป็š„ๅ€™้€‰ไป“ๅบ“ๅˆ—่กจ โ”‚
48
+ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
49
+ โ”‚ ็ฌฌไธ‰ๅฑ‚๏ผšๅฏๅค็Žฐๆ€ง่ฏ„ไผฐ + ๅฏนๆฏ”ๅฎž้ชŒ้€‚้…ๅบฆ โ”‚
50
+ โ”‚ ๆŠ“ README + ไพ่ต– โ†’ LLM ๅคš็ปดๅบฆ่ฏ„ๅˆ† โ”‚
51
+ โ”‚ ่พ“ๅ‡บ๏ผšๆŽ’ๅ + ๆŽจ่ + ้ฃŽ้™ฉๆ็คบ โ”‚
52
+ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
53
+ โ†“
54
+ ็ ”ๆŠฅ่พ“ๅ‡บ๏ผˆ็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ + ไปฃ็ ๆŽจ่ + ๅค็Žฐ้ฃŽ้™ฉ่ฏ„ไผฐ๏ผ‰
55
+ ```
56
+
57
+ **ไธ‰ๅฑ‚็ผบไธ€ไธๅฏ**๏ผšไธ่งฃๆžๆ–นๅ‘ๅฐฑๆœไธๅ‡†ไปฃ็ ๏ผŒไธ่ฏ„ไผฐๅฐฑไธ็Ÿฅ้“่ƒฝไธ่ƒฝ่ท‘๏ผŒๆœ€็ปˆ็›ฎๆ ‡ๆ˜ฏไธบ็ ”็ฉถ่€…ๅ†™่ฎบๆ–‡่ท‘ๅฏนๆฏ”ๅฎž้ชŒๆไพ›ๅฏ้ ็š„ๅŽๅค‡ไปฃ็ ๅบ“ใ€‚
58
+
59
+ ---
60
+
61
+ ## ไบŒใ€็ณป็ปŸๆžถๆž„
62
+
63
+ ### 2.1 ๆ€ปไฝ“ๆžถๆž„ๅ›พ
64
+
65
+ ```
66
+ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
67
+ โ”‚ ResearchRadar ็ณป็ปŸ โ”‚
68
+ โ”‚ โ”‚
69
+ ็”จๆˆท่พ“ๅ…ฅ arxiv URL โ”€โ”€โ†’ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚
70
+ โ”‚ โ”‚ ็ฌฌไธ€ๅฑ‚๏ผš็ ”็ฉถๆ–นๅ‘่งฃๆž๏ผˆAgent๏ผ‰ โ”‚ โ”‚
71
+ โ”‚ โ”‚ โ”‚ โ”‚
72
+ โ”‚ โ”‚ 1. arxiv API โ†’ ่ฎบๆ–‡ๅ…ƒไฟกๆฏ โ”‚ โ”‚
73
+ โ”‚ โ”‚ 2. LLM โ†’ ๅญ้ข†ๅŸŸ่ฏ†ๅˆซ + ๆ–นๆณ•ๆ—ๆขณ็† โ”‚ โ”‚
74
+ โ”‚ โ”‚ 3. LLM โ†’ ไธบๆฏไธชๆ–นๆณ•ๆ—็”Ÿๆˆๆœ็ดข่ฏ โ”‚ โ”‚
75
+ โ”‚ โ”‚ ่พ“ๅ‡บ๏ผš็ ”็ฉถๆ–นๅ‘ "ๅœฐๅ›พ" โ”‚ โ”‚
76
+ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚
77
+ โ”‚ โ†“ โ”‚
78
+ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚
79
+ โ”‚ โ”‚ ็ฌฌไบŒๅฑ‚๏ผšๅผ€ๆบไปฃ็ ๅ‘็Žฐ๏ผˆWorkflow๏ผ‰ โ”‚ โ”‚
80
+ โ”‚ โ”‚ โ”‚ โ”‚
81
+ โ”‚ โ”‚ 1. GitHub Search โ†’ ๅคšๅ…ณ้”ฎ่ฏๆœ็ดข โ”‚ โ”‚
82
+ โ”‚ โ”‚ 2. ๅŽป้‡ + ๆŒ‰ๆ–นๆณ•ๆ—ๅฝ’็ฑป โ”‚ โ”‚
83
+ โ”‚ โ”‚ 3. ๅˆ็ญ›๏ผˆstar/ๆ›ดๆ–ฐๆ—ถ้—ด/่ฏญ่จ€๏ผ‰ โ”‚ โ”‚
84
+ โ”‚ โ”‚ ่พ“ๅ‡บ๏ผšๆŒ‰ๆ–นๆณ•ๆ—ๅˆ†็ฑป็š„ๅ€™้€‰ไป“ๅบ“ๅˆ—่กจ โ”‚ โ”‚
85
+ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚
86
+ โ”‚ โ†“ โ”‚
87
+ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚
88
+ โ”‚ โ”‚ ็ฌฌไธ‰ๅฑ‚๏ผš่ฏ„ไผฐไธŽๆŽจ่๏ผˆAgent๏ผ‰ โ”‚ โ”‚
89
+ โ”‚ โ”‚ โ”‚ โ”‚
90
+ โ”‚ โ”‚ ๅฏนๆฏไธชๅ€™้€‰ไป“ๅบ“๏ผš โ”‚ โ”‚
91
+ โ”‚ โ”‚ 1. ๆŠ“ README + ไพ่ต–ๆ–‡ไปถ๏ผˆWorkflow๏ผ‰โ”‚ โ”‚
92
+ โ”‚ โ”‚ 2. LLM โ†’ ๅฏๅค็Žฐๆ€งไบ”็ปดๅบฆ่ฏ„ๅˆ† โ”‚ โ”‚
93
+ โ”‚ โ”‚ 3. LLM โ†’ ๅฏนๆฏ”ๅฎž้ชŒ้€‚้…ๅบฆ่ฏ„ไผฐ โ”‚ โ”‚
94
+ โ”‚ โ”‚ 4. ็ปผๅˆๆŽ’ๅบ + ็”ŸๆˆๆŽจ่็†็”ฑ โ”‚ โ”‚
95
+ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚
96
+ โ”‚ โ†“ โ”‚
97
+ โ”‚ ็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ็ ”ๆŠฅ๏ผˆMarkdown๏ผ‰ โ”‚
98
+ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
99
+ ```
100
+
101
+ ### 2.2 ๆžถๆž„ไธญ็š„ไธค็ฑป็ป„ไปถ
102
+
103
+ ๆŒ‰็…ง่ทฏ็บฟๅคง็บฒ็š„็บฆๅฎš๏ผŒๆ•ดไธช็ณป็ปŸๅŒ…ๅซไธค็ฑป็ป„ไปถ๏ผš
104
+
105
+ | ็ฑปๅž‹ | ๅฎšไน‰ | ๆœฌ็ณป็ปŸไธญ็š„ๅฏนๅบ” |
106
+ |------|------|---------------|
107
+ | **Workflow**๏ผˆ่ทฏๅพ„ๅ†™ๆญป๏ผ‰ | ็กฎๅฎšๆ€งๆต็จ‹๏ผŒไธๆถ‰ๅŠ LLM ๅ†ณ็ญ– | ่ฎบๆ–‡ไฟกๆฏๆŠ“ๅ–ใ€GitHub ๆœ็ดขใ€ไพ่ต–ๆ–‡ไปถๆ‰ซๆใ€ไป“ๅบ“ๅ…ƒไฟกๆฏๆŠ“ๅ–ใ€ๅŽป้‡ๅฝ’็ฑป |
108
+ | **Agent**๏ผˆLLM ๅ†ณ็ญ–๏ผ‰ | ้œ€่ฆ LLM ็†่งฃๅ’Œๅˆคๆ–ญ็š„่Š‚็‚น | ็ ”็ฉถๆ–นๅ‘่งฃๆžใ€ๆ–นๆณ•ๆ—ๆขณ็†ใ€ๆœ็ดข่ฏ็”Ÿๆˆใ€ๅฏๅค็Žฐๆ€ง่ฏ„ๅˆ†ใ€ๅฏนๆฏ”ๅฎž้ชŒ้€‚้…ๅบฆ่ฏ„ไผฐ |
109
+
110
+ **Week 1 ๅชๅฎž็Žฐ Workflow + ๆœ€็ฎ€ๅ•็š„ Agent**๏ผˆbaseline ๆ–นๆกˆ๏ผŒๅ•ๆฌก LLM ่ฐƒ็”จๅ‡บ่ฏ„ๅˆ†๏ผŒไธๆถ‰ๅŠๅ›ž็Žฏ๏ผ‰ใ€‚ๅฏนไบŽไธ‰ๅฑ‚ๆžถๆž„๏ผŒWeek 1 ไธ‰ไธชๅฑ‚้ƒฝไผšๅฎž็Žฐไฝ†ๆฏๅฑ‚ๅชไฟ็•™ๆœ€็ฎ€็‰ˆๆœฌ๏ผš
111
+ - ็ฌฌไธ€ๅฑ‚๏ผš1 ๆฌก LLM ่ฐƒ็”จๅฎŒๆˆๆ–นๅ‘่งฃๆž + ๅ…ณ้”ฎ่ฏ็”Ÿๆˆ
112
+ - ็ฌฌไบŒๅฑ‚๏ผš็›ดๆŽฅ็”จๅ…ณ้”ฎ่ฏๆœ GitHub๏ผŒไธๅŠ ๆ–นๆณ•ๆ—ๅฝ’็ฑป๏ผˆๅฝ’็ฑป้€ป่พ‘ Week 3 ๅŠ ๏ผ‰
113
+ - ็ฌฌไธ‰ๅฑ‚๏ผš1 ๆฌก LLM ่ฐƒ็”จๅฎŒๆˆๅฏๅค็Žฐๆ€ง่ฏ„ๅˆ† + ๅฏนๆฏ”ๅฎž้ชŒ้€‚้…ๅบฆ๏ผˆๅˆๅนถไธบไธ€ไธช prompt๏ผ‰
114
+
115
+ ---
116
+
117
+ ## ไธ‰ใ€ๆŠ€ๆœฏ้€‰ๅž‹
118
+
119
+ | ้กน | ้€‰ๅž‹ | ็†็”ฑ |
120
+ |----|------|------|
121
+ | **็ผ–็จ‹่ฏญ่จ€** | Python 3.10+ | ไธคไบบ้ƒฝไผš๏ผŒ็”Ÿๆ€ๆœ€ๅฅฝ |
122
+ | **LLM ่ฐƒ็”จ** | DeepSeek API๏ผˆOpenAI SDK๏ผ‰ | ไฝ ไปฌๆœ‰ๆ— ้™้ขๅบฆ็š„ key๏ผŒไธญๆ–‡่ƒฝๅŠ›ๅผบ๏ผŒไพฟๅฎœ |
123
+ | **่ฎบๆ–‡ไฟกๆฏ** | arxiv REST API | ๅ…่ดนใ€ๆ— ้œ€่ฎค่ฏใ€่ฆ†็›–ๆ‰€ๆœ‰ AI ่ฎบๆ–‡ |
124
+ | **ไปฃ็ ๆœ็ดข** | GitHub Search REST API | ๅ…่ดน๏ผˆๅธฆ token 30ๆฌก/ๅˆ†้’Ÿ๏ผ‰๏ผŒๅฏๆŒ‰ๅ…ณ้”ฎ่ฏใ€่ฏญ่จ€ใ€star ็ญ›้€‰ |
125
+ | **ๆ•ฐๆฎๆŠ“ๅ–** | `requests` + ๆ ‡ๅ‡†ๅบ“ | ไธๅผ•ๅ…ฅ็ˆฌ่™ซๆก†ๆžถ |
126
+ | **UI** | Gradio | ่ทฏ็บฟๅคง็บฒๅทฒๅฎšๆญป๏ผŒ่ฝป้‡ๅคŸ็”จ |
127
+ | **็Šถๆ€็ฎก็†** | Python dict | ไธๅผ•ๅ…ฅไปปไฝ•็Šถๆ€็ฎก็†ๆก†ๆžถ |
128
+ | **ๅŒ…็ฎก็†** | pip + requirements.txt | ไธคไบบ้ƒฝๅœจ Windows ไธŠ๏ผŒ็ฎ€ๅ•็ฌฌไธ€ |
129
+
130
+ ---
131
+
132
+ ## ๅ››ใ€็ฌฌไธ€ๅ‘จ MVP ่Œƒๅ›ด
133
+
134
+ ### 4.1 MVP ็›ฎๆ ‡
135
+
136
+ **ไธ€ๅฅ่ฏ**๏ผš`python run.py <arxiv_url>` ่ƒฝ่ท‘้€šๅฎŒๆ•ด้“พ่ทฏโ€”โ€”ไปŽ่ฎบๆ–‡ๅˆฐๆŽจ่ไป“ๅบ“ใ€‚
137
+
138
+ ### 4.2 MVP ๅŒ…ๅซไป€ไนˆ๏ผˆ7 ไธชๆจกๅ—๏ผ‰
139
+
140
+ ```
141
+ ๆจกๅ— 1: ่ฎบๆ–‡ไฟกๆฏๆŠ“ๅ– arxiv API โ†’ ๆ ‡้ข˜ใ€ๆ‘˜่ฆใ€ไฝœ่€…ใ€ๅˆ†็ฑป
142
+ ๆจกๅ— 2: ็ ”็ฉถๆ–นๅ‘่งฃๆž + ๅ…ณ้”ฎ่ฏ LLM โ†’ ๅญ้ข†ๅŸŸ่ฏ†ๅˆซ + ๆ–นๆณ•ๆ—ๆขณ็† + ๆœ็ดข่ฏ็”Ÿๆˆ๏ผˆ1 ๆฌก่ฐƒ็”จๅฎŒๆˆ๏ผ‰
143
+ ๆจกๅ— 3: GitHub ไปฃ็ ๆœ็ดข GitHub Search API โ†’ ๅคšๅ…ณ้”ฎ่ฏๆœ็ดข๏ผŒๅŽป้‡๏ผŒๆŒ‰ star ๆŽ’ๅบ
144
+ ๆจกๅ— 4: ไป“ๅบ“่ฏฆๆƒ…ๆŠ“ๅ– ๅฏน top 5 ๅ€™้€‰ไป“ๅบ“๏ผšๆŠ“ README + ไพ่ต–ๆ–‡ไปถ + ๅ…ƒไฟกๆฏ
145
+ ๆจกๅ— 5: ๅฏๅค็Žฐๆ€ง + ้€‚้…ๅบฆ่ฏ„ไผฐ LLM โ†’ ๅ…ญ็ปดๅบฆ่ฏ„ๅˆ†๏ผˆๅฏๅค็Žฐๆ€ง 5 ็ปด + ๅฏนๆฏ”ๅฎž้ชŒ้€‚้…ๅบฆ๏ผ‰
146
+ ๆจกๅ— 6: ๆŽ’ๅบๆŽจ่ ็ปผๅˆๅŠ ๆƒๆŽ’ๅบ๏ผŒ็”ŸๆˆๆŽจ่็†็”ฑ
147
+ ๆจกๅ— 7: Gradio ็ ”ๆŠฅ่พ“ๅ‡บ ็ป“ๆž„ๅŒ– Markdown๏ผšๆ–นๅ‘ๅ…จๆ™ฏ + ไป“ๅบ“ๆŽ’่กŒ + ๆŽจ่ๅปบ่ฎฎ
148
+ ```
149
+
150
+ ### 4.3 MVP ไธๅŒ…ๅซไป€ไนˆ
151
+
152
+ - โŒ Issue ๆŠ“ๅ–ไธŽๅˆ†ๆž๏ผˆๆจกๅ—่ฟ‡ๅคง๏ผŒWeek 2 ๅŠ ๏ผ‰
153
+ - โŒ Agent ๅ›ž็Žฏๆœบๅˆถ๏ผˆWeek 4 ๅŠ ๏ผ‰
154
+ - โŒ ๆตๅผ่พ“ๅ‡บ๏ผˆWeek 5 ๅŠ ๏ผ‰
155
+ - โŒ ๅคš่ฝฎๅนถๅ‘๏ผˆๅง‹็ปˆไธฒ่กŒ๏ผŒๅ…ˆ่ท‘้€šๅ†ไผ˜ๅŒ–๏ผ‰
156
+ - โŒ ๅคๆ‚็š„้”™่ฏฏๆขๅค๏ผˆ็ฝ‘็ปœๆŒ‚ไบ†ๅฐฑๆŠฅ้”™๏ผŒไธๅžๅผ‚ๅธธ๏ผ‰
157
+
158
+ ---
159
+
160
+ ## ไบ”ใ€็”ฒ็š„ไปปๅŠก๏ผˆๆ•ฐๆฎๅฑ‚๏ผ‰
161
+
162
+ ็”ฒ่ดŸ่ดฃ **ๆ‰€ๆœ‰ไธŽๅค–้ƒจ API ๆ‰“ไบค้“** ็š„ๆจกๅ—๏ผš่ฎบๆ–‡ไฟกๆฏใ€ไปฃ็ ๆœ็ดขใ€ไป“ๅบ“่ฏฆๆƒ…ๆŠ“ๅ–ใ€่ฏ„ๆต‹้›†ๅปบ่ฎพใ€‚
163
+
164
+ ### 5.1 ๆจกๅ— 1๏ผš่ฎบๆ–‡ไฟกๆฏๆŠ“ๅ– `paper_fetcher.py`
165
+
166
+ **ๅšไป€ไนˆ**๏ผš่พ“ๅ…ฅ arxiv URL โ†’ ่พ“ๅ‡บ่ฎบๆ–‡ๆ ‡้ข˜ใ€ๆ‘˜่ฆใ€ไฝœ่€…ใ€ๅˆ†็ฑปๆ ‡็ญพใ€‚
167
+
168
+ **ๆŠ€ๆœฏ่ฆ็‚น**๏ผš
169
+ - arxiv API ็ซฏ็‚น๏ผš`http://export.arxiv.org/api/query?id_list={arxiv_id}&max_results=1`
170
+ - ่ฟ”ๅ›ž Atom XML ๆ ผๅผ๏ผŒ็”จๆ ‡ๅ‡†ๅบ“ `xml.etree.ElementTree` ่งฃๆž๏ผŒ**ไธๅผ•ๅ…ฅ้ขๅค–ไพ่ต–**
171
+ - **ๅฟ…้กปๅŠ  User-Agent ่ฏทๆฑ‚ๅคด**๏ผŒๅฆๅˆ™ arxiv ๅฏ่ƒฝๆ‹’็ป่ฏทๆฑ‚
172
+ - arxiv ID ไปŽ URL ไธญๆๅ–๏ผš`https://arxiv.org/abs/1706.03762` โ†’ `1706.03762`๏ผŒๅ…ผๅฎน `abs`ใ€`pdf` ็ญ‰ๅ‰็ผ€
173
+
174
+ ```python
175
+ # paper_fetcher.py
176
+ import urllib.request
177
+ import urllib.error
178
+ import xml.etree.ElementTree as ET
179
+ import re
180
+
181
+ def extract_arxiv_id(url: str) -> str:
182
+ """ไปŽ arxiv URL ๆๅ–่ฎบๆ–‡ ID
183
+ ๆ”ฏๆŒๆ ผๅผ:
184
+ - https://arxiv.org/abs/1706.03762
185
+ - https://arxiv.org/abs/1706.03762v2
186
+ - https://arxiv.org/pdf/1706.03762.pdf
187
+ - arxiv:1706.03762
188
+ - 1706.03762
189
+ """
190
+ # ๅฐ่ฏ•ๅŒน้… URL ๆ ผๅผ
191
+ match = re.search(r'arxiv\.org/(?:abs|pdf)/(\d{4}\.\d{4,5})(?:v\d+)?(?:\.pdf)?', url)
192
+ if match:
193
+ return match.group(1)
194
+ # ๅฐ่ฏ•ๅŒน้… arxiv:ID ๆˆ–็›ดๆŽฅ ID ๆ ผๅผ
195
+ match = re.search(r'(?:arxiv:)?(\d{4}\.\d{4,5})', url)
196
+ if match:
197
+ return match.group(1)
198
+ raise ValueError(f"ๆ— ๆณ•ไปŽ URL ๆๅ– arxiv ID: {url}")
199
+
200
+ def fetch_paper_info(arxiv_url: str) -> dict:
201
+ """ไปŽ arxiv ่Žทๅ–่ฎบๆ–‡ๅ…ƒไฟกๆฏ
202
+ ่ฟ”ๅ›ž: {"arxiv_id": str, "title": str, "abstract": str,
203
+ "authors": [str, ...], "categories": [str, ...], "published": str}
204
+ """
205
+ arxiv_id = extract_arxiv_id(arxiv_url)
206
+ api_url = f"http://export.arxiv.org/api/query?id_list={arxiv_id}&max_results=1"
207
+
208
+ req = urllib.request.Request(api_url, headers={"User-Agent": "ResearchRadar/1.0"})
209
+ try:
210
+ with urllib.request.urlopen(req, timeout=15) as resp:
211
+ xml_text = resp.read().decode("utf-8")
212
+ except urllib.error.HTTPError as e:
213
+ raise RuntimeError(f"arxiv API ่ฏทๆฑ‚ๅคฑ่ดฅ (HTTP {e.code})๏ผŒ่ฏทๆฃ€ๆŸฅ่ฎบๆ–‡ ID ๆ˜ฏๅฆๆญฃ็กฎ")
214
+ except urllib.error.URLError as e:
215
+ raise RuntimeError(f"ๆ— ๆณ•่ฟžๆŽฅ arxiv API๏ผŒ่ฏทๆฃ€ๆŸฅ็ฝ‘็ปœ: {e.reason}")
216
+
217
+ root = ET.fromstring(xml_text)
218
+ ns = {
219
+ "atom": "http://www.w3.org/2005/Atom",
220
+ "arxiv": "http://arxiv.org/schemas/atom",
221
+ }
222
+
223
+ entry = root.find("atom:entry", ns)
224
+ if entry is None:
225
+ raise ValueError(f"arxiv ๆœชๆ‰พๅˆฐ่ฎบๆ–‡ {arxiv_id}๏ผŒ่ฏทๆฃ€ๆŸฅ ID ๆ˜ฏๅฆๆญฃ็กฎ")
226
+
227
+ title_el = entry.find("atom:title", ns)
228
+ summary_el = entry.find("atom:summary", ns)
229
+ title = title_el.text.strip().replace("\n", " ") if title_el is not None and title_el.text else ""
230
+ abstract = summary_el.text.strip().replace("\n", " ") if summary_el is not None and summary_el.text else ""
231
+
232
+ authors = [
233
+ a.find("atom:name", ns).text
234
+ for a in entry.findall("atom:author", ns)
235
+ if a.find("atom:name", ns) is not None
236
+ ]
237
+ categories = [
238
+ c.get("term", "") for c in entry.findall("atom:category", ns)
239
+ ]
240
+ published_el = entry.find("atom:published", ns)
241
+ published = published_el.text if published_el is not None and published_el.text else ""
242
+
243
+ return {
244
+ "arxiv_id": arxiv_id,
245
+ "title": title,
246
+ "abstract": abstract,
247
+ "authors": authors,
248
+ "categories": categories,
249
+ "published": published,
250
+ }
251
+ ```
252
+
253
+ ### 5.2 ๆจกๅ— 3๏ผšGitHub ไปฃ็ ๆœ็ดข `repo_searcher.py`
254
+
255
+ **ๅšไป€ไนˆ**๏ผš็ป™ๅฎšไธ€ไธชๅ…ณ้”ฎ่ฏๅˆ—่กจ โ†’ ๆœ็ดข GitHub ่ฟ”ๅ›ž็›ธๅ…ณไป“ๅบ“ๅˆ—่กจใ€‚
256
+
257
+ **ๆŠ€ๆœฏ่ฆ็‚น**๏ผš
258
+ - GitHub Search API๏ผš`GET /search/repositories?q={query}&sort=stars&order=desc&per_page=5`
259
+ - ๆœ็ดข query ๆž„้€ ๏ผš็”จๅ…ณ้”ฎ่ฏ + ้™ๅฎšๆกไปถ๏ผˆlanguage:pythonใ€stars ้—จๆง›๏ผ‰
260
+ - **ๆœ็ดขๆŽฅๅฃ้™้€Ÿๆ›ดไธฅ**๏ผšๅธฆ token 30 ๆฌก/ๅˆ†้’Ÿ๏ผŒไธๅธฆ token 10 ๆฌก/ๅˆ†้’Ÿใ€‚ไฝ ไปฌไธคไบบๅ„็”ณ่ฏทไธ€ไธช token๏ผŒๅœจไปฃ็ ้‡Œ่ฝฎๆข
261
+ - ่ฟ”ๅ›ž็ป“ๆžœๅŽป้‡๏ผˆๅŒไธ€ไธช repo ๅฏ่ƒฝ่ขซไธๅŒๅ…ณ้”ฎ่ฏๆœๅˆฐๅคšๆฌก๏ผ‰
262
+
263
+ ```python
264
+ # repo_searcher.py
265
+ import requests
266
+ import os
267
+
268
+ GITHUB_TOKENS = [
269
+ os.getenv("GITHUB_TOKEN_1", ""),
270
+ os.getenv("GITHUB_TOKEN_2", ""),
271
+ ]
272
+ GITHUB_TOKENS = [t for t in GITHUB_TOKENS if t] # ่ฟ‡ๆปค็ฉบๅ€ผ
273
+ _token_index = 0
274
+
275
+ def _get_token():
276
+ """่ฝฎๆขไฝฟ็”จไธคไธช token๏ผŒ้™ไฝŽ้™้€Ÿ้ฃŽ้™ฉ"""
277
+ global _token_index
278
+ if not GITHUB_TOKENS:
279
+ return ""
280
+ token = GITHUB_TOKENS[_token_index]
281
+ _token_index = (_token_index + 1) % len(GITHUB_TOKENS)
282
+ return token
283
+
284
+ def _get_headers():
285
+ token = _get_token()
286
+ if token:
287
+ return {"Authorization": f"Bearer {token}", "Accept": "application/vnd.github.v3+json"}
288
+ return {"Accept": "application/vnd.github.v3+json"}
289
+
290
+ def search_repos(keywords: list[str], max_per_keyword: int = 5, language: str = "python") -> list[dict]:
291
+ """็”จๅ…ณ้”ฎ่ฏๅˆ—่กจๆœ็ดข GitHub ไป“ๅบ“
292
+ ่ฟ”ๅ›ž: ๅŽป้‡ๅŽ็š„ไป“ๅบ“ๅˆ—่กจ๏ผŒๆŒ‰ star ้™ๅบๆŽ’ๅˆ—
293
+ """
294
+ seen = set()
295
+ results = []
296
+
297
+ for kw in keywords:
298
+ # ๆž„้€ ๆŸฅ่ฏข๏ผšๅ…ณ้”ฎ่ฏ + Python + ่‡ณๅฐ‘ 5 ไธช star๏ผˆ่ฟ‡ๆปค็ฉบๅฃณไป“ๅบ“๏ผ‰
299
+ query = f"{kw} language:{language} stars:>=5"
300
+ url = "https://api.github.com/search/repositories"
301
+ params = {"q": query, "sort": "stars", "order": "desc", "per_page": max_per_keyword}
302
+
303
+ try:
304
+ resp = requests.get(url, headers=_get_headers(), params=params, timeout=15)
305
+ if resp.status_code == 403 and "rate limit" in resp.text.lower():
306
+ print(f"[่ญฆๅ‘Š] GitHub ๆœ็ดข้™้€Ÿ๏ผŒ่ทณ่ฟ‡ๅ…ณ้”ฎ่ฏ: {kw}")
307
+ continue
308
+ resp.raise_for_status()
309
+ data = resp.json()
310
+ except Exception as e:
311
+ print(f"[่ญฆๅ‘Š] ๆœ็ดขๅ…ณ้”ฎ่ฏ '{kw}' ๅคฑ่ดฅ: {e}")
312
+ continue
313
+
314
+ for item in data.get("items", []):
315
+ full_name = item["full_name"]
316
+ if full_name not in seen:
317
+ seen.add(full_name)
318
+ results.append({
319
+ "full_name": full_name,
320
+ "html_url": item["html_url"],
321
+ "description": item.get("description", ""),
322
+ "stars": item.get("stargazers_count", 0),
323
+ "language": item.get("language", ""),
324
+ "topics": item.get("topics", []),
325
+ "updated_at": item.get("updated_at", ""),
326
+ "match_keyword": kw,
327
+ })
328
+
329
+ # ๆŒ‰ star ้™ๅบ
330
+ results.sort(key=lambda r: r["stars"], reverse=True)
331
+ return results
332
+ ```
333
+
334
+ ### 5.3 ๆจกๅ— 5๏ผˆ็š„ไธ€้ƒจๅˆ†๏ผ‰๏ผšไป“ๅบ“่ฏฆๆƒ…ๆŠ“ๅ– `repo_fetcher.py`
335
+
336
+ **ๅšไป€ไนˆ**๏ผš็ป™ๅฎšไธ€ไธช GitHub ไป“ๅบ“็š„ owner/repo โ†’ ๆŠ“ READMEใ€ไพ่ต–ๆ–‡ไปถใ€ๅ…ƒไฟกๆฏใ€‚
337
+
338
+ ่ฟ™ไธŽไน‹ๅ‰ๆ–นๆกˆ็š„ๅ†…ๅฎนไธ€่‡ด๏ผŒไฝ†้œ€่ฆๆณจๆ„๏ผš็Žฐๅœจๆ˜ฏ**ๆ‰น้‡ๆŠ“ๅ–**๏ผˆไธ€ไธช่ฎบๆ–‡ๅฏนๅบ” 5 ไธชๅ€™้€‰ไป“ๅบ“๏ผ‰๏ผŒ้œ€่ฆๅค„็†้ƒจๅˆ†ไป“ๅบ“ๆŠ“ๅ–ๅคฑ่ดฅ็š„ๆƒ…ๅ†ตโ€”โ€”ไธ่ƒฝๅ› ไธบไธ€ไธชไป“ๅบ“ๅคฑ่ดฅไบ†ๅฐฑ่ฎฉๆ•ดไธชๆต็จ‹ๅดฉๆบƒใ€‚
339
+
340
+ ```python
341
+ # repo_fetcher.py
342
+ import os
343
+ import base64
344
+ import requests
345
+
346
+ GITHUB_TOKENS = [
347
+ os.getenv("GITHUB_TOKEN_1", ""),
348
+ os.getenv("GITHUB_TOKEN_2", ""),
349
+ ]
350
+ GITHUB_TOKENS = [t for t in GITHUB_TOKENS if t]
351
+ _token_index = 0
352
+
353
+ def _get_token():
354
+ global _token_index
355
+ if not GITHUB_TOKENS:
356
+ return ""
357
+ token = GITHUB_TOKENS[_token_index]
358
+ _token_index = (_token_index + 1) % len(GITHUB_TOKENS)
359
+ return token
360
+
361
+ API_BASE = "https://api.github.com"
362
+
363
+ DEPENDENCY_FILENAMES = [
364
+ "requirements.txt", "environment.yml", "environment.yaml",
365
+ "setup.py", "setup.cfg", "pyproject.toml",
366
+ "Pipfile", "Pipfile.lock", "Dockerfile", "Makefile", "CMakeLists.txt",
367
+ ]
368
+
369
+ def _github_get(owner: str, repo: str, endpoint: str) -> dict | None:
370
+ """็ปŸไธ€็š„ GitHub API GET ่ฏทๆฑ‚"""
371
+ headers = {"Accept": "application/vnd.github.v3+json"}
372
+ token = _get_token()
373
+ if token:
374
+ headers["Authorization"] = f"Bearer {token}"
375
+
376
+ url = f"{API_BASE}/repos/{owner}/{repo}/{endpoint}" if endpoint else f"{API_BASE}/repos/{owner}/{repo}"
377
+ resp = requests.get(url, headers=headers, timeout=15)
378
+ if resp.status_code == 404:
379
+ return None
380
+ if resp.status_code in (403, 429):
381
+ raise RuntimeError(f"GitHub API ้™้€Ÿๆˆ–ๆ‹’็ป่ฎฟ้—ฎ: {resp.status_code}")
382
+ resp.raise_for_status()
383
+ return resp.json()
384
+
385
+ def fetch_readme(owner: str, repo: str) -> str | None:
386
+ """ๆŠ“ๅ– README ๅ†…ๅฎน๏ผˆๅทฒ่งฃ็ ็š„็บฏๆ–‡ๆœฌ๏ผ‰"""
387
+ data = _github_get(owner, repo, "readme")
388
+ if not data:
389
+ return None
390
+ content = data.get("content", "")
391
+ try:
392
+ return base64.b64decode(content).decode("utf-8", errors="replace")
393
+ except Exception:
394
+ return None
395
+
396
+ def fetch_dependencies(owner: str, repo: str) -> dict[str, str]:
397
+ """ๆŠ“ๅ–ๆ‰€ๆœ‰ไพ่ต–ๆ–‡ไปถ๏ผŒ่ฟ”ๅ›ž {ๆ–‡ไปถๅ: ๅ†…ๅฎน}"""
398
+ result = {}
399
+ contents = _github_get(owner, repo, "contents") or []
400
+ for item in contents:
401
+ name = item.get("name", "")
402
+ if name in DEPENDENCY_FILENAMES:
403
+ file_data = _github_get(owner, repo, f"contents/{name}")
404
+ if file_data and file_data.get("content"):
405
+ try:
406
+ decoded = base64.b64decode(file_data["content"]).decode("utf-8", errors="replace")
407
+ result[name] = decoded
408
+ except Exception:
409
+ pass
410
+ return result
411
+
412
+ def fetch_issues(owner: str, repo: str, max_count: int = 30) -> list[dict]:
413
+ """ๆŠ“ๅ–ๆœ€่ฟ‘็š„ Issues๏ผˆไธบ Week 2 ้ข„็•™ๆŽฅๅฃ๏ผŒWeek 1 ๅฏไปฅไธ่ฐƒ็”จ๏ผ‰"""
414
+ issues = []
415
+ page = 1
416
+ while len(issues) < max_count:
417
+ params = {"state": "all", "sort": "created", "direction": "desc",
418
+ "per_page": min(30, max_count - len(issues)), "page": page}
419
+ resp = requests.get(
420
+ f"{API_BASE}/repos/{owner}/{repo}/issues",
421
+ headers=_get_headers(),
422
+ params=params,
423
+ timeout=15,
424
+ )
425
+ if resp.status_code != 200:
426
+ break
427
+ page_items = resp.json()
428
+ if not page_items:
429
+ break
430
+ for item in page_items:
431
+ if "pull_request" not in item: # GitHub Issues API ไผšๆททๅ…ฅ PR๏ผŒ้œ€่ฆ่ฟ‡ๆปค
432
+ issues.append({
433
+ "title": item.get("title", ""),
434
+ "state": item.get("state", ""),
435
+ "created_at": item.get("created_at", ""),
436
+ "body": (item.get("body") or "")[:500], # ๆˆชๆ–ญ๏ผŒๅช่ฆๅ‰ 500 ๅญ—็ฌฆ
437
+ "comments": item.get("comments", 0),
438
+ })
439
+ page += 1
440
+ return issues
441
+
442
+ def _get_headers():
443
+ token = _get_token()
444
+ headers = {"Accept": "application/vnd.github.v3+json"}
445
+ if token:
446
+ headers["Authorization"] = f"Bearer {token}"
447
+ return headers
448
+ ```
449
+
450
+ ### 5.4 ่ฏ„ๆต‹้›†ๅปบ่ฎพ
451
+
452
+ **่ฟ™ๆ˜ฏๆ•ดไธช้กน็›ฎๆœ€้‡่ฆ็š„่ต„ไบง**ใ€‚่ฏ„ๆต‹้›†ๅˆ†ไธค้ƒจๅˆ†๏ผš
453
+
454
+ **Part A๏ผšไปฃ็ ๅ‘็Žฐ่ฏ„ๆต‹้›†๏ผˆ10 ็ฏ‡่ฎบๆ–‡๏ผ‰**
455
+
456
+ ๆฏ็ฏ‡่ฎบๆ–‡ๆ ‡ๆณจ๏ผšๅทฒ็Ÿฅ็š„ๆœ€ไฝณๅผ€ๆบๅฎž็Žฐ๏ผˆground truth ไป“ๅบ“๏ผ‰ใ€‚
457
+
458
+ ```json
459
+ {
460
+ "paper_arxiv_id": "1706.03762",
461
+ "paper_title": "Attention Is All You Need",
462
+ "paper_keywords_human": ["transformer", "attention mechanism", "seq2seq", "neural machine translation"],
463
+ "known_implementations": [
464
+ {"full_name": "huggingface/transformers", "relevance": "official"},
465
+ {"full_name": "tensorflow/tensor2tensor", "relevance": "official"},
466
+ {"full_name": "jadore801120/attention-is-all-you-need-pytorch", "relevance": "community"}
467
+ ]
468
+ }
469
+ ```
470
+
471
+ **Part B๏ผšๅฏๅค็Žฐๆ€ง่ฏ„ๆต‹้›†๏ผˆ20 ไธชไป“ๅบ“๏ผ‰**
472
+
473
+ ๆฒฟ็”จ่ทฏ็บฟๅคง็บฒ็š„ๆ–นๆกˆ๏ผš10 ไธชๆญฃๆ ทๆœฌ + 10 ไธช่ดŸๆ ทๆœฌใ€‚
474
+
475
+ ```json
476
+ {
477
+ "repo_url": "https://github.com/xxx/yyy",
478
+ "ground_truth": "reproducible",
479
+ "evidence": ["่ฏๆฎ1", "่ฏๆฎ2"],
480
+ "rationale": "ไธบไป€ไนˆ่ฟ™ๆ ทๆ ‡ๆณจ"
481
+ }
482
+ ```
483
+
484
+ **Week 1 ๆœ€ไฝŽ่ฆๆฑ‚**๏ผšPart A ๆ ‡ๅฎŒ 3 ็ฏ‡่ฎบๆ–‡๏ผˆ็”จไบŽ demo๏ผ‰๏ผŒPart B ๆ ‡ๅฎŒ 6-8 ไธชไป“ๅบ“ใ€‚ไธ้œ€่ฆๅœจ demo ๆ—ถๅฑ•็คบ่ฏ„ๆต‹้›†๏ผŒไฝ†ๆ ‡ๆณจๅทฅไฝœๆ˜ฏๆ•ดไธช้กน็›ฎ็š„ๅŸบๅ‡†ใ€‚
485
+
486
+ ---
487
+
488
+ ## ๅ…ญใ€ไน™็š„ไปปๅŠก๏ผˆLLM + ้›†ๆˆๅฑ‚๏ผ‰
489
+
490
+ ไน™่ดŸ่ดฃ **ๆ‰€ๆœ‰ไธŽ LLM ไบคไบ’** ็š„ๆจกๅ—๏ผšๅ…ณ้”ฎ่ฏๆๅ–ใ€ๅฏๅค็Žฐๆ€ง่ฏ„ไผฐใ€ไธปๆต็จ‹็ผ–ๆŽ’ใ€Gradio ็•Œ้ขใ€‚
491
+
492
+ ### 6.1 ๆจกๅ— 2๏ผš็ ”็ฉถๆ–นๅ‘่งฃๆž + ๆœ็ดข่ฏ็”Ÿๆˆ `direction_analyzer.py`
493
+
494
+ **ๅšไป€ไนˆ**๏ผš่พ“ๅ…ฅ่ฎบๆ–‡ๆ ‡้ข˜ + ๆ‘˜่ฆ โ†’ LLM ไธ€ๆฌกๆ€งๅฎŒๆˆไธ‰ไปถไบ‹๏ผš(1) ่ฏ†ๅˆซ่ฏฅ่ฎบๆ–‡ๆ‰€ๅฑžๅญ้ข†ๅŸŸ๏ผŒ(2) ๆขณ็†่ฏฅ้ข†ๅŸŸ็š„ไธปๆตๆ–นๆณ•ๆ—๏ผŒ(3) ็”Ÿๆˆ้’ˆๅฏนๆฏไธชๆ–นๆณ•ๆ—็š„ GitHub ๆœ็ดข่ฏใ€‚
495
+
496
+ **ไธบไป€ไนˆๆฏ”ๅ•็บฏๆๅ–ๅ…ณ้”ฎ่ฏๆ›ดๅฅฝ**๏ผš
497
+ - ็ ”็ฉถ่€…ไธๅช้œ€่ฆ"ไธ€ไธชๅฎ˜ๆ–นๅฎž็Žฐ"๏ผŒไป–่ฆ็Ÿฅ้“่ฟ™ไธชๆ–นๅ‘ๆœ‰ๅ‡ ๆกๆŠ€ๆœฏ่ทฏ็บฟใ€ๆฏๆก่ทฏ็บฟๆœ‰ๅ“ชไบ›ไปฃ่กจๅฎž็Žฐ
498
+ - ๆฏ”ๅฆ‚่พ“ๅ…ฅ Transformer ่ฎบๆ–‡๏ผŒๅบ”่ฏฅ่พ“ๅ‡บใ€Œvanilla Transformer / BERT ็ณป / GPT ็ณป / T5 ็ณป / ViT ็ณปใ€็ญ‰ๆ–นๆณ•ๆ—
499
+ - ่ฟ™็›ดๆŽฅๅฏนๅบ”็ ”็ฉถ่€…็š„ๅฎž้™…้œ€ๆฑ‚โ€”โ€”ๅ†™่ฎบๆ–‡่ท‘ๅฏนๆฏ”ๅฎž้ชŒๆ—ถ้œ€่ฆ่ฆ†็›–ๅคšไธช baseline
500
+
501
+ ```python
502
+ # direction_analyzer.py
503
+ import json
504
+ import re
505
+ from openai import OpenAI
506
+
507
+ client = OpenAI(
508
+ api_key="your-deepseek-api-key",
509
+ base_url="https://api.deepseek.com",
510
+ )
511
+
512
+ DIRECTION_SYSTEM_PROMPT = """ไฝ ๆ˜ฏไธ€ไธช AI ็ ”็ฉถ้ข†ๅŸŸ็š„ๅฏผ่ˆชไธ“ๅฎถใ€‚ไฝ ็š„ไปปๅŠกๆ˜ฏ๏ผš็ป™ๅฎšไธ€็ฏ‡่ฎบๆ–‡๏ผŒๅธฎๅŠฉ็ ”็ฉถ่€…ๅฟซ้€Ÿไบ†่งฃ่ฏฅๆ–นๅ‘็š„ๅ‰ๆฒฟๅผ€ๆบ็”Ÿๆ€ใ€‚
513
+
514
+ ## ไฝ ็š„ๅˆ†ๆžๆก†ๆžถ
515
+
516
+ ่ฏทๅฎŒๆˆไปฅไธ‹ไธ‰ไธชๅฑ‚ๆฌก็š„ๅˆ†ๆž๏ผš
517
+
518
+ ### ๅฑ‚ๆฌก 1๏ผšๅญ้ข†ๅŸŸๅฎšไฝ
519
+ - ่ฟ™็ฏ‡่ฎบๆ–‡ๅฑžไบŽ AI/ML ็š„ๅ“ชไธชๅ…ทไฝ“ๅญ้ข†ๅŸŸ๏ผŸ๏ผˆๅฆ‚ "Transformer-based Neural Machine Translation" ่€Œ้ž็ฌผ็ปŸ็š„ "Deep Learning"๏ผ‰
520
+ - ่ฏฅๅญ้ข†ๅŸŸๅœจ 2024-2025 ๅนด็š„ไธปๆต่ถ‹ๅŠฟๆ˜ฏไป€ไนˆ๏ผŸ๏ผˆ1-2 ๅฅ่ฏ๏ผ‰
521
+
522
+ ### ๅฑ‚ๆฌก 2๏ผšๆ–นๆณ•ๆ—่ฐฑ็ณป
523
+ - ่ฏฅๅญ้ข†ๅŸŸ็›ฎๅ‰ๆœ‰ๅ“ชไบ›ไธปๆตๆ–นๆณ•ๆ—/ๅ˜ไฝ“๏ผŸ
524
+ - ๆฏไธชๆ–นๆณ•ๆ—็š„ไปฃ่กจๅทฅไฝœๆ˜ฏไป€ไนˆ๏ผŸ
525
+ - ่พ“ๅ‡บ 3-6 ไธชๆ–นๆณ•ๆ—๏ผŒๆฏไธชๅŒ…ๅซๆ–นๆณ•ๅ + ไธ€ๅฅ่ฏ่ฏดๆ˜Ž
526
+
527
+ ### ๅฑ‚ๆฌก 3๏ผšๆœ็ดข็ญ–็•ฅ
528
+ - ไธบๆฏไธชๆ–นๆณ•ๆ—็”Ÿๆˆ 2-3 ไธช GitHub ๆœ็ดขๆŸฅ่ฏข
529
+ - ๆŸฅ่ฏขๅบ”ไฝฟ็”จ่‹ฑๆ–‡๏ผŒๅŒ…ๅซ๏ผšๆ–นๆณ•ๅ + ๅ…ณ้”ฎ่ฏ + "implementation" ๆˆ– "pytorch" ๆˆ– "official"
530
+ - ้ขๅค–็”Ÿๆˆ 2-3 ไธช่ฆ†็›–ๅ…จ้ข†ๅŸŸ็š„ๅฎฝๆณ›ๆœ็ดข
531
+
532
+ ## ่พ“ๅ‡บๆ ผๅผ๏ผˆไธฅๆ ผ JSON๏ผŒไธ่ฆไปปไฝ•้ขๅค–ๆ–‡ๅญ—๏ผ‰
533
+ {
534
+ "subfield": "ๅญ้ข†ๅŸŸๅ็งฐ๏ผˆไธญ่‹ฑๆ–‡ๅ‡ๅฏ๏ผ‰",
535
+ "subfield_trend": "่ฏฅๅญ้ข†ๅŸŸๅฝ“ๅ‰ไธปๆต่ถ‹ๅŠฟ๏ผˆ1-2 ๅฅไธญๆ–‡๏ผ‰",
536
+ "method_families": [
537
+ {
538
+ "family_name": "ๆ–นๆณ•ๆ—ๅ็งฐ",
539
+ "description": "ไธ€ๅฅ่ฏ่ฏดๆ˜Ž",
540
+ "representative_work": "ไปฃ่กจ่ฎบๆ–‡ๆˆ–ๅทฅไฝœ",
541
+ "search_queries": ["ๆœ็ดข่ฏ1", "ๆœ็ดข่ฏ2"]
542
+ }
543
+ ],
544
+ "broad_queries": ["ๅ…จ้ข†ๅŸŸๅฎฝๆณ›ๆœ็ดข่ฏ1", "ๅ…จ้ข†ๅŸŸๅฎฝๆณ›ๆœ็ดข่ฏ2"]
545
+ }
546
+ """
547
+
548
+ def analyze_direction(title: str, abstract: str) -> dict:
549
+ """ไปŽ่ฎบๆ–‡ๆ ‡้ข˜ๅ’Œๆ‘˜่ฆไธญ่งฃๆž็ ”็ฉถๆ–นๅ‘ๅนถ็”Ÿๆˆๆœ็ดข็ญ–็•ฅ"""
550
+ user_prompt = f"""## ่ฎบๆ–‡ๆ ‡้ข˜
551
+ {title}
552
+
553
+ ## ่ฎบๆ–‡ๆ‘˜่ฆ
554
+ {abstract[:2000]}
555
+
556
+ ่ฏทๅˆ†ๆž่ฏฅ่ฎบๆ–‡็š„็ ”็ฉถๆ–นๅ‘ๅนถ็”Ÿๆˆๆœ็ดข็ญ–็•ฅใ€‚"""
557
+
558
+ response = client.chat.completions.create(
559
+ model="deepseek-chat",
560
+ messages=[
561
+ {"role": "system", "content": DIRECTION_SYSTEM_PROMPT},
562
+ {"role": "user", "content": user_prompt},
563
+ ],
564
+ temperature=0.4, # ็จ้ซ˜ๆธฉๅบฆ่ฎฉๆ–นๆณ•ๆ—ๆขณ็†ๆ›ดๆœ‰ๅˆ›้€ ๅŠ›
565
+ max_tokens=1500,
566
+ )
567
+ raw = response.choices[0].message.content.strip()
568
+ return _parse_json(raw)
569
+
570
+ def _parse_json(raw: str) -> dict:
571
+ """ๅฎ‰ๅ…จ่งฃๆž JSON๏ผŒๅฎนๅฟ LLM ็š„ๆ ผๅผ้—ฎ้ข˜"""
572
+ try:
573
+ return json.loads(raw)
574
+ except json.JSONDecodeError:
575
+ pass
576
+ match = re.search(r'```(?:json)?\s*([\s\S]*?)```', raw)
577
+ if match:
578
+ try:
579
+ return json.loads(match.group(1).strip())
580
+ except json.JSONDecodeError:
581
+ pass
582
+ match = re.search(r'\{[\s\S]*\}', raw)
583
+ if match:
584
+ try:
585
+ return json.loads(match.group(0))
586
+ except json.JSONDecodeError:
587
+ pass
588
+ raise ValueError(f"ๆ–นๅ‘่งฃๆž LLM ่ฟ”ๅ›žๆ ผๅผๆ— ๆณ•่งฃๆž: {raw[:300]}")
589
+ ```
590
+
591
+ ### 6.2 ๆจกๅ— 5๏ผšๅฏๅค็Žฐๆ€ง + ๅฏนๆฏ”ๅฎž้ชŒ้€‚้…ๅบฆ่ฏ„ไผฐ `repo_evaluator.py`
592
+
593
+ **ๅšไป€ไนˆ**๏ผš่พ“ๅ…ฅไธ€ไธชไป“ๅบ“็š„ README + ไพ่ต–ๆ–‡ไปถ + ๅ…ƒไฟกๆฏ + ๆ‰€ๅฑžๆ–นๆณ•ๆ— โ†’ LLM ่พ“ๅ‡บๅ…ญ็ปดๅบฆ่ฏ„ๅˆ†๏ผˆๅฏๅค็Žฐๆ€ง 5 ็ปด + ๅฏนๆฏ”ๅฎž้ชŒ้€‚้…ๅบฆ 1 ็ปด๏ผ‰ใ€‚
594
+
595
+ **ไธบไป€ไนˆๆ–ฐๅขžใ€Œๅฏนๆฏ”ๅฎž้ชŒ้€‚้…ๅบฆใ€็ปดๅบฆ**๏ผšๅพˆๅคšไป“ๅบ“่ƒฝ่ท‘๏ผˆๅฏๅค็Žฐ๏ผ‰๏ผŒไฝ†ไธ้€‚ๅˆ็›ดๆŽฅๆ‹ฟๆฅ่ท‘ๅฏนๆฏ”ๅฎž้ชŒโ€”โ€”ๆฏ”ๅฆ‚ๅฎƒๅชๅฎž็Žฐไบ†ๆŽจ็†ๆฒกๆœ‰่ฎญ็ปƒใ€ๆ•ฐๆฎ้›†็”จ็š„ๆ˜ฏ็งๆœ‰ๆ•ฐๆฎใ€ๆˆ–่€… benchmark ๆŒ‡ๆ ‡ไธๆ˜ฏ้ข†ๅŸŸๆ ‡ๅ‡†ๆŒ‡ๆ ‡ใ€‚็ ”็ฉถ่€…้œ€่ฆ็š„ๆ˜ฏ"่ƒฝ็›ดๆŽฅๅŠ ๅ…ฅ่ฎบๆ–‡ experiment section ็š„ไปฃ็ "ใ€‚
596
+
597
+ ```python
598
+ # repo_evaluator.py
599
+ import json
600
+ import re
601
+ from openai import OpenAI
602
+
603
+ client = OpenAI(
604
+ api_key="your-deepseek-api-key",
605
+ base_url="https://api.deepseek.com",
606
+ )
607
+
608
+ EVAL_SYSTEM_PROMPT = """ไฝ ๆ˜ฏไธ€ไธช AI ่ฎบๆ–‡ไปฃ็ ๅฏๅค็Žฐๆ€งไธŽๅฏนๆฏ”ๅฎž้ชŒ้€‚้…ๅบฆ่ฏ„ไผฐไธ“ๅฎถใ€‚
609
+
610
+ ไฝ ็š„ไปปๅŠกๆ˜ฏ่ฏ„ไผฐไธ€ไธช GitHub ไป“ๅบ“ๅœจไปฅไธ‹ไธคไธชๆ–น้ข็š„่กจ็Žฐ๏ผš
611
+ - **ๅฏๅค็Žฐๆ€ง**๏ผš่ฟ™ไธชไป“ๅบ“็š„ไปฃ็ ่ƒฝๅฆ่ขซไป–ไบบๆˆๅŠŸ่ท‘้€š๏ผŸ
612
+ - **ๅฏนๆฏ”ๅฎž้ชŒ้€‚้…ๅบฆ**๏ผš่ฟ™ไธชไป“ๅบ“้€‚ไธ้€‚ๅˆ็›ดๆŽฅๆ‹ฟๆฅๅ†™่ฎบๆ–‡ๅšๅฏนๆฏ”ๅฎž้ชŒ๏ผˆbaseline / comparison๏ผ‰๏ผŸ
613
+
614
+ ## ่ฏ„ไผฐๆก†ๆžถ๏ผˆๅ…ญ็ปดๅบฆ๏ผ‰
615
+
616
+ ### ๅฏๅค็Žฐๆ€ง๏ผˆไบ”็ปดๅบฆ๏ผŒๅˆ่ฎก 0-80 ๅˆ†๏ผ‰
617
+
618
+ 1. **็Žฏๅขƒ้…็ฝฎๅฎŒๆ•ดๆ€ง๏ผˆ0-15 ๅˆ†๏ผ‰**
619
+ - 15 ๅˆ†๏ผšๆไพ›ไบ†ๅฎŒๆ•ด็š„ไพ่ต–ๆ–‡ไปถ๏ผˆrequirements.txt / environment.yml๏ผ‰๏ผŒ็‰ˆๆœฌๅทๆ˜Ž็กฎ
620
+ - 8-14 ๅˆ†๏ผšๆœ‰ไพ่ต–ๆ–‡ไปถไฝ†้ƒจๅˆ†็‰ˆๆœฌๅท็ผบๅคฑๆˆ–ๆจก็ณŠ
621
+ - 0-7 ๅˆ†๏ผšๆฒกๆœ‰ไพ่ต–ๆ–‡ไปถๆˆ–ไธฅ้‡ไธๅฎŒๆ•ด
622
+
623
+ 2. **ๆ–‡ๆกฃ่ดจ้‡๏ผˆ0-20 ๅˆ†๏ผ‰**
624
+ - 16-20 ๅˆ†๏ผšๆœ‰ๆธ…ๆ™ฐ็š„ๅฎ‰่ฃ…ๆญฅ้ชคใ€่ฎญ็ปƒ/ๆŽจ็†ๅ‘ฝไปคใ€ๆ•ฐๆฎ้›†่Žทๅ–ๆ–นๅผ
625
+ - 8-15 ๅˆ†๏ผšๆœ‰ๅŸบๆœฌ่ฏดๆ˜Žไฝ†ๅ…ณ้”ฎๆญฅ้ชค็ผบๅคฑ
626
+ - 0-7 ๅˆ†๏ผšREADME ็ฉบๆดž๏ผŒๆฒกๆœ‰ๅฏๆ‰ง่กŒ็š„ๅ‘ฝไปค
627
+
628
+ 3. **ไปฃ็ ๅฏ็”จๆ€ง๏ผˆ0-20 ๅˆ†๏ผ‰**
629
+ - 16-20 ๅˆ†๏ผšๆœ‰ๅฎŒๆ•ด็š„่ฎญ็ปƒ/ๆŽจ็†่„šๆœฌใ€้…็ฝฎๆ–‡ไปถ๏ผŒๅ…ฅๅฃๆธ…ๆ™ฐ
630
+ - 8-15 ๅˆ†๏ผšๆœ‰ๆ ธๅฟƒไปฃ็ ไฝ†็ผบๅฐ‘ๅ…ณ้”ฎ็ป„ไปถ๏ผˆๅฆ‚ๅชๆœ‰ๆŽจ็†ๆฒกๆœ‰่ฎญ็ปƒ๏ผ‰
631
+ - 0-7 ๅˆ†๏ผšไปฃ็ ๆฎ‹็ผบ
632
+
633
+ 4. **็คพๅŒบๆดป่ทƒๅบฆ๏ผˆ0-10 ๅˆ†๏ผ‰**
634
+ - 8-10 ๅˆ†๏ผšStar > 500๏ผŒๆœ€่ฟ‘ๅŠๅนดๆœ‰ๆ›ดๆ–ฐ
635
+ - 4-7 ๅˆ†๏ผšๆœ‰ไธ€ๅฎšๅ…ณๆณจๅบฆไฝ†ๆ›ดๆ–ฐไธ้ข‘็น
636
+ - 0-3 ๅˆ†๏ผš้•ฟๆœŸไธๆ›ดๆ–ฐ๏ผŒStar < 10
637
+
638
+ 5. **ไพ่ต–ๅฅๅบทๅบฆ๏ผˆ0-15 ๅˆ†๏ผ‰**
639
+ - 12-15 ๅˆ†๏ผšไพ่ต–ๅ…จๆ˜ฏไธปๆตๆดป่ทƒๅŒ…๏ผŒๆ— ๅทฒ็Ÿฅๅ…ผๅฎนๆ€ง้—ฎ้ข˜
640
+ - 6-11 ๅˆ†๏ผšๆœ‰ๅฐ‘้‡่ฟ‡ๆ—ถไพ่ต–ไฝ†ๅฏๅ‡็บง
641
+ - 0-5 ๅˆ†๏ผšไพ่ต–ๅทฒๅœๆญข็ปดๆŠค๏ผˆๅฆ‚ TF 1.x๏ผ‰ๆˆ–ๅทฒ็Ÿฅๆ— ๆณ•ๅฎ‰่ฃ…
642
+
643
+ ### ๅฏนๆฏ”ๅฎž้ชŒ้€‚้…ๅบฆ๏ผˆไธ€็ปดๅบฆ๏ผŒ0-20 ๅˆ†๏ผ‰
644
+
645
+ 6. **ๅฏนๆฏ”ๅฎž้ชŒ้€‚้…ๅบฆ๏ผˆ0-20 ๅˆ†๏ผ‰**
646
+ - 16-20 ๅˆ†๏ผšๆไพ›ๆ ‡ๅ‡† benchmark ่ฏ„ไผฐ่„šๆœฌ + ้ข„่ฎญ็ปƒๆƒ้‡ + ๅ…ฌๅผ€ๆ•ฐๆฎ้›†๏ผŒๅฏไปฅ็›ดๆŽฅ่ท‘ๅ‡บ่ฎบๆ–‡ๅธธ็”จๆŒ‡ๆ ‡
647
+ - 10-15 ๅˆ†๏ผšๆœ‰่ฎญ็ปƒ่„šๆœฌๅ’Œ่ฏ„ไผฐไปฃ็ ๏ผŒไฝ†้œ€่ฆ่‡ชๅทฑๅ‡†ๅค‡ๆ•ฐๆฎๆˆ–ๅพฎ่ฐƒ้…ๆŽฅ
648
+ - 0-9 ๅˆ†๏ผšๅชๆœ‰ๆŽจ็†ใ€ไฝฟ็”จ็งๆœ‰ๆ•ฐๆฎ้›†ใ€ๆฒกๆœ‰ๆ ‡ๅ‡†ๅŒ–่ฏ„ไผฐๆต็จ‹
649
+
650
+ ## ่พ“ๅ‡บๆ ผๅผ๏ผˆไธฅๆ ผ JSON๏ผŒไธ่ฆไปปไฝ•้ขๅค–ๆ–‡ๅญ—๏ผ‰
651
+ {
652
+ "reproducibility_score": ๆ•ดๆ•ฐ๏ผˆ0-80๏ผŒๅฏๅค็Žฐๆ€งไบ”้กนๆฑ‚ๅ’Œ๏ผ‰,
653
+ "benchmark_fitness_score": ๆ•ดๆ•ฐ๏ผˆ0-20๏ผŒๅฏนๆฏ”ๅฎž้ชŒ้€‚้…ๅบฆ๏ผ‰,
654
+ "overall_score": ๆ•ดๆ•ฐ๏ผˆ0-100๏ผŒๅ…ญ้กนๆ€ปๅ’Œ๏ผ‰,
655
+ "verdict": "reproducible" | "partially" | "not_reproducible",
656
+ "env_score": ๆ•ดๆ•ฐ,
657
+ "doc_score": ๆ•ดๆ•ฐ,
658
+ "code_score": ๆ•ดๆ•ฐ,
659
+ "community_score": ๆ•ดๆ•ฐ,
660
+ "dep_score": ๆ•ดๆ•ฐ,
661
+ "benchmark_score": ๆ•ดๆ•ฐ,
662
+ "reasoning": "200ๅญ—ไปฅๅ†…็š„ไธญๆ–‡ๅˆ†ๆž๏ผŒๅฟ…้กปๅŒบๅˆ†'่ƒฝไธ่ƒฝ่ท‘'ๅ’Œ'้€‚ไธ้€‚ๅˆ่ท‘ๅฏนๆฏ”ๅฎž้ชŒ'ไธคไธชๅฑ‚้ข",
663
+ "risks": ["้ฃŽ้™ฉ1", "้ฃŽ้™ฉ2"],
664
+ "benchmark_readiness": "ready" | "partial" | "not_ready",
665
+ "suggested_use": "ๅฏไปฅ็›ดๆŽฅไฝœไธบๅฏนๆฏ”ๅฎž้ชŒ baseline" | "้œ€่ฆๅฐ‘้‡ไฟฎๆ”นๅŽๅฏ็”จไบŽๅฏนๆฏ”ๅฎž้ชŒ" | "ไป…้€‚ๅˆๅ‚่€ƒไปฃ็ ๅฎž็Žฐ๏ผŒไธ้€‚ๅˆ็›ดๆŽฅ่ท‘ๅฏนๆฏ”ๅฎž้ชŒ"
666
+ }
667
+
668
+ ## ๅˆคๅฎš่ง„ๅˆ™
669
+ - overall_score >= 70 โ†’ verdict = "reproducible"
670
+ - 40 <= overall_score < 70 โ†’ verdict = "partially"
671
+ - overall_score < 40 โ†’ verdict = "not_reproducible"
672
+
673
+ - benchmark_score >= 16 โ†’ benchmark_readiness = "ready"
674
+ - 10 <= benchmark_score < 16 โ†’ benchmark_readiness = "partial"
675
+ - benchmark_score < 10 โ†’ benchmark_readiness = "not_ready"
676
+ """
677
+
678
+ def evaluate_repo(repo: dict, readme: str | None, dependencies: dict[str, str],
679
+ method_family: str = "") -> dict:
680
+ """่ฏ„ไผฐๅ•ไธชไป“ๅบ“็š„ๅฏๅค็Žฐๆ€งๅ’Œๅฏนๆฏ”ๅฎž้ชŒ้€‚้…ๅบฆ"""
681
+ readme_text = (readme or "README ๆœชๆ‰พๅˆฐ")[:2500]
682
+ deps_text = "\n\n".join(
683
+ f"### {fname}\n```\n{content[:1000]}\n```"
684
+ for fname, content in dependencies.items()
685
+ ) or "ๆœชๆ‰พๅˆฐไพ่ต–ๆ–‡ไปถ"
686
+
687
+ family_hint = f"\n่ฏฅๆ–นๆณ•ๅฑžไบŽ: {method_family}" if method_family else ""
688
+
689
+ user_prompt = f"""## ไป“ๅบ“ไฟกๆฏ
690
+ - ๅ็งฐ: {repo.get('full_name', '')}
691
+ - URL: {repo.get('html_url', '')}
692
+ - Stars: {repo.get('stars', 0)}
693
+ - ๆ่ฟฐ: {repo.get('description', '')}
694
+ - ๆœ€ๅŽๆ›ดๆ–ฐ: {repo.get('updated_at', '')}
695
+ - ่ฏญ่จ€: {repo.get('language', '')}{family_hint}
696
+
697
+ ## README
698
+ {readme_text}
699
+
700
+ ## ไพ่ต–ๆ–‡ไปถ
701
+ {deps_text}
702
+
703
+ ่ฏท่ฏ„ไผฐ่ฏฅไป“ๅบ“็š„ๅฏๅค็Žฐๆ€งๅ’Œๅฏนๆฏ”ๅฎž้ชŒ้€‚้…ๅบฆใ€‚"""
704
+
705
+ response = client.chat.completions.create(
706
+ model="deepseek-chat",
707
+ messages=[
708
+ {"role": "system", "content": EVAL_SYSTEM_PROMPT},
709
+ {"role": "user", "content": user_prompt},
710
+ ],
711
+ temperature=0.3,
712
+ max_tokens=1500,
713
+ )
714
+ raw = response.choices[0].message.content.strip()
715
+ return _parse_json(raw)
716
+
717
+ def _parse_json(raw: str) -> dict:
718
+ """ๅฎ‰ๅ…จ JSON ่งฃๆž"""
719
+ try:
720
+ return json.loads(raw)
721
+ except json.JSONDecodeError:
722
+ pass
723
+ match = re.search(r'```(?:json)?\s*([\s\S]*?)```', raw)
724
+ if match:
725
+ try:
726
+ return json.loads(match.group(1).strip())
727
+ except json.JSONDecodeError:
728
+ pass
729
+ match = re.search(r'\{[\s\S]*\}', raw)
730
+ if match:
731
+ try:
732
+ return json.loads(match.group(0))
733
+ except json.JSONDecodeError:
734
+ pass
735
+ raise ValueError(f"่ฏ„ไผฐ LLM ่ฟ”ๅ›žๆ ผๅผๆ— ๆณ•่งฃๆž: {raw[:300]}")
736
+ ```
737
+
738
+ def evaluate_repo(repo: dict, readme: str | None, dependencies: dict[str, str]) -> dict:
739
+ """่ฏ„ไผฐๅ•ไธชไป“ๅบ“็š„ๅฏๅค็Žฐๆ€ง"""
740
+ readme_text = (readme or "README ๆœชๆ‰พๅˆฐ")[:2500]
741
+ deps_text = "\n\n".join(
742
+ f"### {fname}\n```\n{content[:1000]}\n```"
743
+ for fname, content in dependencies.items()
744
+ ) or "ๆœชๆ‰พๅˆฐไพ่ต–ๆ–‡ไปถ"
745
+
746
+ user_prompt = f"""## ไป“ๅบ“ไฟกๆฏ
747
+ - ๅ็งฐ: {repo.get('full_name', '')}
748
+ - URL: {repo.get('html_url', '')}
749
+ - Stars: {repo.get('stars', 0)}
750
+ - ๆ่ฟฐ: {repo.get('description', '')}
751
+ - ๆœ€ๅŽๆ›ดๆ–ฐ: {repo.get('updated_at', '')}
752
+ - ่ฏญ่จ€: {repo.get('language', '')}
753
+
754
+ ## README
755
+ {readme_text}
756
+
757
+ ## ไพ่ต–ๆ–‡ไปถ
758
+ {deps_text}
759
+
760
+ ่ฏท่ฏ„ไผฐ่ฏฅไป“ๅบ“็š„ๅฏๅค็Žฐๆ€งใ€‚"""
761
+
762
+ response = client.chat.completions.create(
763
+ model="deepseek-chat",
764
+ messages=[
765
+ {"role": "system", "content": EVAL_SYSTEM_PROMPT},
766
+ {"role": "user", "content": user_prompt},
767
+ ],
768
+ temperature=0.3,
769
+ max_tokens=1200,
770
+ )
771
+ raw = response.choices[0].message.content.strip()
772
+ return _parse_json(raw)
773
+
774
+ def _parse_json(raw: str) -> dict:
775
+ """ๅฎ‰ๅ…จ JSON ่งฃๆž๏ผŒๅŒ direction_analyzer ไธญ็š„ๅฎž็Žฐ"""
776
+ try:
777
+ return json.loads(raw)
778
+ except json.JSONDecodeError:
779
+ pass
780
+ match = re.search(r'```(?:json)?\s*([\s\S]*?)```', raw)
781
+ if match:
782
+ try:
783
+ return json.loads(match.group(1).strip())
784
+ except json.JSONDecodeError:
785
+ pass
786
+ match = re.search(r'\{[\s\S]*\}', raw)
787
+ if match:
788
+ try:
789
+ return json.loads(match.group(0))
790
+ except json.JSONDecodeError:
791
+ pass
792
+ raise ValueError(f"่ฏ„ไผฐ LLM ่ฟ”ๅ›žๆ ผๅผๆ— ๆณ•่งฃๆž: {raw[:300]}")
793
+ ```
794
+
795
+ ### 6.3 ๆจกๅ— 6๏ผšไธปๆต็จ‹ `run.py`
796
+
797
+ **่ฟ™ๆ˜ฏๆ•ดไธช้กน็›ฎ็š„ไธญๅคฎ่ฐƒๅบฆๅ™จ**ใ€‚ๆŒ‰ไธ‰ๅฑ‚ๆžถๆž„ไธฒ่”ๆ‰€ๆœ‰ๆจกๅ—ใ€‚
798
+
799
+ ```python
800
+ # run.py
801
+ import sys
802
+ from paper_fetcher import fetch_paper_info
803
+ from direction_analyzer import analyze_direction
804
+ from repo_searcher import search_repos
805
+ from repo_fetcher import fetch_readme, fetch_dependencies
806
+ from repo_evaluator import evaluate_repo
807
+
808
+ def run(arxiv_url: str, top_n: int = 5) -> dict:
809
+ """ไธปๅ…ฅๅฃ๏ผšไปŽ่ฎบๆ–‡ๅˆฐ"็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ + ไปฃ็ ๆŽจ่"็š„ๅฎŒๆ•ด้“พ่ทฏ
810
+
811
+ ่ฟ”ๅ›ž dict:
812
+ - paper: ่ฎบๆ–‡ไฟกๆฏ
813
+ - direction: ็ ”็ฉถๆ–นๅ‘่งฃๆž็ป“ๆžœ๏ผˆๅญ้ข†ๅŸŸ + ๆ–นๆณ•ๆ— + ๆœ็ดข่ฏ๏ผ‰
814
+ - repos: ่ฏ„ไผฐๅŽ็š„ไป“ๅบ“ๅˆ—่กจ๏ผˆๆŒ‰ overall_score ้™ๅบ๏ผ‰
815
+ """
816
+ # ===== ็ฌฌไธ€ๅฑ‚๏ผš็ ”็ฉถๆ–นๅ‘่งฃๆž =====
817
+ print(f"[1/5] ๆญฃๅœจ่Žทๅ–่ฎบๆ–‡ไฟกๆฏ...")
818
+ paper = fetch_paper_info(arxiv_url)
819
+ print(f" ่ฎบๆ–‡: {paper['title'][:80]}...")
820
+
821
+ print(f"[2/5] ๆญฃๅœจ่งฃๆž็ ”็ฉถๆ–นๅ‘...")
822
+ direction = analyze_direction(paper["title"], paper["abstract"])
823
+ print(f" ๅญ้ข†ๅŸŸ: {direction.get('subfield', 'ๆœช็Ÿฅ')}")
824
+ print(f" ๆ–นๆณ•ๆ—: {len(direction.get('method_families', []))} ไธช")
825
+
826
+ # ๆฑ‡ๆ€ปๆ‰€ๆœ‰ๆœ็ดข่ฏ๏ผˆๆ–นๆณ•ๆ—ๆœ็ดข + ๅฎฝๆณ›ๆœ็ดข๏ผ‰
827
+ all_queries = []
828
+ for mf in direction.get("method_families", []):
829
+ all_queries.extend(mf.get("search_queries", []))
830
+ all_queries.extend(direction.get("broad_queries", []))
831
+ print(f" ็”Ÿๆˆ {len(all_queries)} ไธชๆœ็ดขๆŸฅ่ฏข")
832
+
833
+ # ===== ็ฌฌไบŒๅฑ‚๏ผšๅผ€ๆบไปฃ็ ๅ‘็Žฐ =====
834
+ print(f"[3/5] ๆญฃๅœจๆœ็ดข GitHub ไป“ๅบ“...")
835
+ candidates = search_repos(all_queries, max_per_keyword=5)
836
+ candidates = candidates[:top_n]
837
+ print(f" ๅŽป้‡ๅŽๆ‰พๅˆฐ {len(candidates)} ไธชๅ€™้€‰ไป“ๅบ“")
838
+
839
+ if not candidates:
840
+ return {
841
+ "paper": paper,
842
+ "direction": direction,
843
+ "repos": [],
844
+ "error": "ๆœชๆ‰พๅˆฐ็›ธๅ…ณๅผ€ๆบไป“ๅบ“",
845
+ }
846
+
847
+ # ===== ็ฌฌไธ‰ๅฑ‚๏ผšๅฏๅค็Žฐๆ€ง + ๅฏนๆฏ”ๅฎž้ชŒ้€‚้…ๅบฆ่ฏ„ไผฐ =====
848
+ print(f"[4/5] ๆญฃๅœจ่ฏ„ไผฐ {len(candidates)} ไธชๅ€™้€‰ไป“ๅบ“...")
849
+ evaluated = []
850
+ for i, repo in enumerate(candidates):
851
+ full_name = repo["full_name"]
852
+ owner, name = full_name.split("/", 1)
853
+ # ๅฐ่ฏ•ๅฐ†ไป“ๅบ“ๅŒน้…ๅˆฐๅฏนๅบ”็š„ๆ–นๆณ•ๆ—๏ผˆ็ฎ€ๅ•่ง„ๅˆ™๏ผšๆ่ฟฐไธญๅŒ…ๅซๆ–นๆณ•ๆ—ๅ็งฐ๏ผ‰
854
+ matched_family = _match_family(repo, direction.get("method_families", []))
855
+ print(f" ({i+1}/{len(candidates)}) ่ฏ„ไผฐ {full_name} [{matched_family or 'ๆœชๅฝ’็ฑป'}]...")
856
+
857
+ try:
858
+ readme = fetch_readme(owner, name)
859
+ deps = fetch_dependencies(owner, name)
860
+ evaluation = evaluate_repo(repo, readme, deps, matched_family)
861
+ except Exception as e:
862
+ evaluation = {
863
+ "overall_score": 0,
864
+ "reproducibility_score": 0,
865
+ "benchmark_fitness_score": 0,
866
+ "verdict": "error",
867
+ "reasoning": f"่ฏ„ไผฐๅคฑ่ดฅ: {str(e)[:100]}",
868
+ "env_score": 0, "doc_score": 0, "code_score": 0,
869
+ "community_score": 0, "dep_score": 0, "benchmark_score": 0,
870
+ "risks": [], "benchmark_readiness": "not_ready",
871
+ "suggested_use": "่ฏ„ไผฐๅคฑ่ดฅ๏ผŒ่ฏทๆ‰‹ๅŠจๆฃ€ๆŸฅ",
872
+ }
873
+
874
+ evaluated.append({
875
+ **repo,
876
+ "method_family": matched_family,
877
+ "evaluation": evaluation,
878
+ })
879
+
880
+ # ๆŒ‰็ปผๅˆ่ฏ„ๅˆ†้™ๅบ
881
+ evaluated.sort(key=lambda r: r["evaluation"].get("overall_score", 0), reverse=True)
882
+
883
+ print(f"[5/5] ็”Ÿๆˆ็ ”ๆŠฅ...")
884
+ return {
885
+ "paper": paper,
886
+ "direction": direction,
887
+ "repos": evaluated,
888
+ }
889
+
890
+ def _match_family(repo: dict, families: list[dict]) -> str:
891
+ """็ฎ€ๅ•็š„ไป“ๅบ“โ†’ๆ–นๆณ•ๆ—ๅŒน้…๏ผšๅŸบไบŽๆ่ฟฐๅ’Œ topics"""
892
+ desc = (repo.get("description") or "").lower()
893
+ topics = " ".join(repo.get("topics", [])).lower()
894
+ text = desc + " " + topics
895
+
896
+ for mf in families:
897
+ family_name = mf.get("family_name", "").lower()
898
+ if family_name and family_name in text:
899
+ return mf["family_name"]
900
+ return ""
901
+
902
+ if __name__ == "__main__":
903
+ if len(sys.argv) < 2:
904
+ print("็”จๆณ•: python run.py <arxiv_url>")
905
+ print("็คบไพ‹: python run.py https://arxiv.org/abs/1706.03762")
906
+ sys.exit(1)
907
+
908
+ result = run(sys.argv[1])
909
+
910
+ if result.get("error"):
911
+ print(f"\nโŒ {result['error']}")
912
+ else:
913
+ # ไฝฟ็”จ app.py ไธญ็š„ format_report ่พ“ๅ‡บ
914
+ from app import format_report
915
+ print(format_report(result))
916
+ ```
917
+
918
+ ### 6.4 Gradio ็•Œ้ข `app.py`
919
+
920
+ ```python
921
+ # app.py
922
+ import gradio as gr
923
+ from run import run
924
+
925
+ def diagnose(arxiv_url: str) -> str:
926
+ """Gradio ๅ›ž่ฐƒ๏ผš่พ“ๅ…ฅ arxiv URL โ†’ ่พ“ๅ‡บ็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ็ ”ๆŠฅ"""
927
+ url = arxiv_url.strip()
928
+ if not url:
929
+ return "## ่ฏท่พ“ๅ…ฅ arxiv ่ฎบๆ–‡้“พๆŽฅ"
930
+
931
+ try:
932
+ result = run(url)
933
+ except ValueError as e:
934
+ return f"## ่พ“ๅ…ฅๆ ผๅผ้”™่ฏฏ\n\n{str(e)}"
935
+ except RuntimeError as e:
936
+ return f"## API ่ฏทๆฑ‚ๅคฑ่ดฅ\n\n{str(e)}"
937
+ except Exception as e:
938
+ return f"## ่ฟ่กŒๅ‡บ้”™\n\n```\n{str(e)}\n```"
939
+
940
+ if result.get("error"):
941
+ return f"## ๆœชๆ‰พๅˆฐ็›ธๅ…ณไปฃ็ \n\n{result['error']}"
942
+
943
+ return format_report(result)
944
+
945
+ def format_report(result: dict) -> str:
946
+ """ๅฐ† run() ็š„็ป“ๆžœๆ ผๅผๅŒ–ไธบ"็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ็ ”ๆŠฅ" Markdown"""
947
+ paper = result["paper"]
948
+ direction = result["direction"]
949
+ repos = result["repos"]
950
+
951
+ # ===== ็ ”ๆŠฅๅคด้ƒจ =====
952
+ md = f"""# ResearchRadar ็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ็ ”ๆŠฅ
953
+
954
+ ---
955
+
956
+ ## ไธ€ใ€่ฎบๆ–‡ไฟกๆฏ
957
+
958
+ | ้กน็›ฎ | ๅ†…ๅฎน |
959
+ |------|------|
960
+ | **ๆ ‡้ข˜** | {paper['title']} |
961
+ | **arXiv ID** | [{paper['arxiv_id']}](https://arxiv.org/abs/{paper['arxiv_id']}) |
962
+ | **ไฝœ่€…** | {', '.join(paper['authors'][:5])}{'...' if len(paper['authors']) > 5 else ''} |
963
+ | **ๅ‘่กจๆ—ถ้—ด** | {paper.get('published', 'N/A')[:10]} |
964
+ | **ๅˆ†็ฑป** | {', '.join(paper['categories']) if paper.get('categories') else 'N/A'} |
965
+
966
+ ---
967
+
968
+ ## ไบŒใ€็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ
969
+
970
+ ### ๅญ้ข†ๅŸŸๅฎšไฝ
971
+ **{direction.get('subfield', 'ๆœช็Ÿฅ')}**
972
+
973
+ ### ๅฝ“ๅ‰่ถ‹ๅŠฟ
974
+ {direction.get('subfield_trend', 'ๆš‚ๆ— ๅˆ†ๆž')}
975
+
976
+ ### ๆ–นๆณ•ๆ—่ฐฑ็ณป
977
+
978
+ | # | ๆ–นๆณ•ๆ— | ่ฏดๆ˜Ž | ไปฃ่กจๅทฅไฝœ |
979
+ |---|--------|------|----------|
980
+ """
981
+ for i, mf in enumerate(direction.get("method_families", [])):
982
+ md += f"| {i+1} | **{mf.get('family_name', '')}** | {mf.get('description', '')} | {mf.get('representative_work', '')} |\n"
983
+
984
+ md += """
985
+ ---
986
+
987
+ ## ไธ‰ใ€ๅผ€ๆบๅฎž็Žฐ่ฏ„ไผฐๆŽ’ๅ
988
+
989
+ """
990
+ # ===== ไป“ๅบ“ๆŽ’ๅ =====
991
+ for i, repo in enumerate(repos):
992
+ ev = repo["evaluation"]
993
+ verdict_icon = {
994
+ "reproducible": "โœ…", "partially": "โš ๏ธ",
995
+ "not_reproducible": "โŒ", "error": "๐Ÿ’ฅ"
996
+ }.get(ev.get("verdict", ""), "โ“")
997
+ readiness_icon = {
998
+ "ready": "๐ŸŸข", "partial": "๐ŸŸก", "not_ready": "๐Ÿ”ด"
999
+ }.get(ev.get("benchmark_readiness", ""), "โšช")
1000
+
1001
+ family_tag = f" `[{repo.get('method_family', 'ๆœชๅฝ’็ฑป')}]`" if repo.get("method_family") else ""
1002
+
1003
+ md += f"""### {i+1}. [{repo['full_name']}]({repo['html_url']}){family_tag}
1004
+
1005
+ {verdict_icon} ๅฏๅค็Žฐๆ€ง: **{ev.get('reproducibility_score', ev.get('overall_score', 'N/A'))}/80** | {readiness_icon} ๅฏนๆฏ”ๅฎž้ชŒ้€‚้…ๅบฆ: **{ev.get('benchmark_fitness_score', ev.get('benchmark_score', 'N/A'))}/20** | ็ปผๅˆ: **{ev.get('overall_score', 'N/A')}/100**
1006
+
1007
+ | ็ปดๅบฆ | ๅพ—ๅˆ† | ๆปกๅˆ† | ่ฏดๆ˜Ž |
1008
+ |------|:----:|:----:|------|
1009
+ | ็Žฏๅขƒ้…็ฝฎ | {ev.get('env_score', 'N/A')} | 15 | ไพ่ต–ๆ–‡ไปถๅฎŒๆ•ดๅบฆ |
1010
+ | ๆ–‡ๆกฃ่ดจ้‡ | {ev.get('doc_score', 'N/A')} | 20 | ๅฎ‰่ฃ…/่ฎญ็ปƒ/ๆ•ฐๆฎ่ฏดๆ˜Ž |
1011
+ | ไปฃ็ ๅฏ็”จๆ€ง | {ev.get('code_score', 'N/A')} | 20 | ่ฎญ็ปƒ+ๆŽจ็†่„šๆœฌๅฎŒๆ•ดๆ€ง |
1012
+ | ็คพๅŒบๆดป่ทƒ | {ev.get('community_score', 'N/A')} | 10 | Star + ๆ›ดๆ–ฐ้ข‘็އ |
1013
+ | ไพ่ต–ๅฅๅบท | {ev.get('dep_score', 'N/A')} | 15 | ไพ่ต–ๅŒ…ๅฏ็”จๆ€ง |
1014
+ | **ๅฏนๆฏ”ๅฎž้ชŒ** | **{ev.get('benchmark_score', 'N/A')}** | **20** | **ๆ ‡ๅ‡†ๅŒ–่ฏ„ไผฐ + ้ข„่ฎญ็ปƒๆƒ้‡** |
1015
+
1016
+ โญ Star: {repo.get('stars', 0)} | ๅŒน้…: `{repo.get('match_keyword', '')}`
1017
+
1018
+ **ๅˆ†ๆž**: {ev.get('reasoning', 'N/A')}
1019
+
1020
+ **ๅฏนๆฏ”ๅฎž้ชŒๅปบ่ฎฎ**: {ev.get('suggested_use', 'N/A')}
1021
+
1022
+ """
1023
+ if ev.get("risks"):
1024
+ md += "**้ฃŽ้™ฉ็‚น**: " + " | ".join(f"`{r}`" for r in ev["risks"]) + "\n"
1025
+ md += "\n---\n\n"
1026
+
1027
+ # ===== ๅฏนๆฏ”ๅฎž้ชŒๆŽจ่ๆ€ป็ป“ =====
1028
+ ready_repos = [r for r in repos if r["evaluation"].get("benchmark_readiness") == "ready"]
1029
+ partial_repos = [r for r in repos if r["evaluation"].get("benchmark_readiness") == "partial"]
1030
+
1031
+ md += """## ๅ››ใ€ๅฏนๆฏ”ๅฎž้ชŒๆŽจ่ๆ€ป็ป“
1032
+
1033
+ """
1034
+ if ready_repos:
1035
+ md += "### ๐ŸŸข ๅฏ็›ดๆŽฅ็”จไบŽๅฏนๆฏ”ๅฎž้ชŒ\n\n"
1036
+ for r in ready_repos:
1037
+ md += f"- **[{r['full_name']}]({r['html_url']})** โ€” {r['evaluation'].get('suggested_use', '')}\n"
1038
+ md += "\n"
1039
+
1040
+ if partial_repos:
1041
+ md += "### ๐ŸŸก ้œ€่ฆๅฐ‘้‡ไฟฎๆ”นๅŽๅฏ็”จ\n\n"
1042
+ for r in partial_repos:
1043
+ md += f"- **[{r['full_name']}]({r['html_url']})** โ€” {r['evaluation'].get('suggested_use', '')}\n"
1044
+ md += "\n"
1045
+
1046
+ not_ready = [r for r in repos if r["evaluation"].get("benchmark_readiness") not in ("ready", "partial")]
1047
+ if not_ready:
1048
+ md += "### ๐Ÿ”ด ไป…้€‚ๅˆๅ‚่€ƒไปฃ็ ๅฎž็Žฐ\n\n"
1049
+ for r in not_ready:
1050
+ md += f"- **[{r['full_name']}]({r['html_url']})** โ€” {r['evaluation'].get('suggested_use', r['evaluation'].get('reasoning', 'N/A')[:80])}...\n"
1051
+ md += "\n"
1052
+
1053
+ md += """
1054
+ ---
1055
+
1056
+ > ResearchRadar ยท ็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ + ๅผ€ๆบไปฃ็ ่ฏ„ไผฐ + ๅฏนๆฏ”ๅฎž้ชŒๆŽจ่
1057
+ """
1058
+
1059
+ return md
1060
+
1061
+ DEMO_EXAMPLES = [
1062
+ ["https://arxiv.org/abs/1706.03762"], # Attention Is All You Need (Transformer)
1063
+ ["https://arxiv.org/abs/2010.11929"], # An Image is Worth 16x16 Words (ViT)
1064
+ ["https://arxiv.org/abs/2006.11239"], # Denoising Diffusion Probabilistic Models (DDPM)
1065
+ ]
1066
+
1067
+ demo = gr.Interface(
1068
+ fn=diagnose,
1069
+ inputs=gr.Textbox(
1070
+ label="่ฎบๆ–‡ arxiv ้“พๆŽฅ",
1071
+ placeholder="https://arxiv.org/abs/1706.03762",
1072
+ lines=1,
1073
+ ),
1074
+ outputs=gr.Markdown(label="็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ็ ”ๆŠฅ"),
1075
+ title="ResearchRadar โ€” ็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ + ๅผ€ๆบไปฃ็ ่ฏ„ไผฐ",
1076
+ description="่พ“ๅ…ฅไธ€็ฏ‡ AI ่ฎบๆ–‡็š„ arxiv ้“พๆŽฅ๏ผŒAI ๅธฎไฝ ๆ‘ธๆธ…็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ๏ผŒๆ‰พๅˆฐๆœ€้€‚้…็š„ๅผ€ๆบๅฎž็Žฐ๏ผŒ่ฏ„ไผฐ่ƒฝๅฆ็›ดๆŽฅ็”จไบŽๅฏนๆฏ”ๅฎž้ชŒใ€‚",
1077
+ examples=DEMO_EXAMPLES,
1078
+ theme="soft",
1079
+ )
1080
+
1081
+ if __name__ == "__main__":
1082
+ demo.launch(server_name="0.0.0.0", share=False)
1083
+ ```
1084
+
1085
+ ---
1086
+
1087
+ ## ไธƒใ€ๆ•ฐๆฎๆตไธŽๆŽงๅˆถๆต
1088
+
1089
+ ### 7.1 ๅฎŒๆ•ด้“พ่ทฏ๏ผˆไธ‰ๅฑ‚ๆžถๆž„๏ผ‰
1090
+
1091
+ ```
1092
+ ็”จๆˆท่พ“ๅ…ฅ arxiv URL
1093
+ โ”‚
1094
+ โ–ผ
1095
+ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
1096
+ โ”‚ paper_fetcher.fetch_paper_info() โ”‚ โ† arxiv API
1097
+ โ”‚ ่พ“ๅ‡บ: {title, abstract, authors, ...} โ”‚
1098
+ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
1099
+ โ”‚
1100
+ โ–ผ
1101
+ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
1102
+ โ”‚ direction_analyzer.analyze_direction() โ”‚ โ† DeepSeek API (1 ๆฌก)
1103
+ โ”‚ ่พ“ๅ‡บ: {subfield, method_families[], โ”‚
1104
+ โ”‚ broad_queries[]} โ”‚
1105
+ โ”‚ โ€ป ๅŒๆ—ถๅฎŒๆˆๅญ้ข†ๅŸŸ่ฏ†ๅˆซ + ๆ–นๆณ•ๆ—ๆขณ็† + โ”‚
1106
+ โ”‚ ๆœ็ดข่ฏ็”Ÿๆˆ โ”‚
1107
+ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
1108
+ โ”‚
1109
+ โ–ผ
1110
+ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
1111
+ โ”‚ repo_searcher.search_repos() ๏ฟฝ๏ฟฝ โ† GitHub Search API (N ๆฌก)
1112
+ โ”‚ ่พ“ๅ…ฅ: ๆ‰€ๆœ‰ๆ–นๆณ•ๆ—ๆœ็ดข่ฏ + ๅฎฝๆณ›ๆœ็ดข่ฏ โ”‚
1113
+ โ”‚ ่พ“ๅ‡บ: ๅŽป้‡ๆŽ’ๅบๅŽ็š„ๅ€™้€‰ไป“ๅบ“ top 5 โ”‚
1114
+ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
1115
+ โ”‚
1116
+ โ–ผ (ๅฏนๆฏไธชๅ€™้€‰ไป“ๅบ“ๅพช็Žฏ)
1117
+ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
1118
+ โ”‚ repo_fetcher.fetch_readme() โ”‚ โ† GitHub Content API
1119
+ โ”‚ repo_fetcher.fetch_dependencies() โ”‚
1120
+ โ”‚ ่พ“ๅ‡บ: {readme, dependencies} โ”‚
1121
+ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
1122
+ โ”‚
1123
+ โ–ผ
1124
+ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
1125
+ โ”‚ repo_evaluator.evaluate_repo() โ”‚ โ† DeepSeek API (ๆฏไธชไป“ๅบ“ 1 ๆฌก)
1126
+ โ”‚ ่พ“ๅ‡บ: ๅ…ญ็ปดๅบฆ่ฏ„ๅˆ†๏ผˆๅฏๅค็Žฐๆ€ง 5 ็ปด + โ”‚
1127
+ โ”‚ ๅฏนๆฏ”ๅฎž้ชŒ้€‚้…ๅบฆ 1 ็ปด๏ผ‰ โ”‚
1128
+ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
1129
+ โ”‚
1130
+ โ–ผ
1131
+ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
1132
+ โ”‚ ๆŒ‰ overall_score ้™ๅบ โ”‚
1133
+ โ”‚ format_report() โ†’ ็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ็ ”ๆŠฅ โ”‚
1134
+ โ”‚ ๅŒ…ๅซ๏ผšๆ–นๅ‘่งฃ่ฏป + ๆ–นๆณ•ๆ—่ฐฑ็ณป + ไป“ๅบ“ๆŽ’่กŒ โ”‚
1135
+ โ”‚ + ๅฏนๆฏ”ๅฎž้ชŒๆŽจ่ โ”‚
1136
+ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
1137
+ ```
1138
+
1139
+ ### 7.2 API ่ฐƒ็”จๆฌกๆ•ฐไผฐ็ฎ—๏ผˆๅ•ๆฌก่ฟ่กŒ๏ผ‰
1140
+
1141
+ | ๆญฅ้ชค | API | ่ฐƒ็”จๆฌกๆ•ฐ |
1142
+ |------|-----|:--------:|
1143
+ | ่ฎบๆ–‡ไฟกๆฏ | arxiv API | 1 |
1144
+ | ็ ”็ฉถๆ–นๅ‘่งฃๆž | DeepSeek | 1 |
1145
+ | GitHub ๆœ็ดข | GitHub Search | N๏ผˆๆ–นๆณ•ๆ—ร—2 + ๅฎฝๆณ›ร—2 โ‰ˆ 10 ๆฌก๏ผ‰ |
1146
+ | ไป“ๅบ“่ฏฆๆƒ…๏ผˆร—5๏ผ‰ | GitHub Content | 5 |
1147
+ | ๅฏๅค็Žฐๆ€ง+้€‚้…ๅบฆ่ฏ„ไผฐ๏ผˆร—5๏ผ‰ | DeepSeek | 5 |
1148
+
1149
+ **ๅ•ๆฌก่ฟ่กŒๆ€ป่€—ๆ—ถไผฐ็ฎ—**๏ผš็บฆ 25-45 ็ง’๏ผˆไธฒ่กŒๆ‰ง่กŒ๏ผ‰ใ€‚ไธป่ฆ่€—ๆ—ถๅœจ GitHub ๆœ็ดข๏ผˆ10 ๆฌก ร— ๆฏๆฌก็บฆ 1-2 ็ง’๏ผ‰ๅ’Œ 5 ไธชไป“ๅบ“็š„่ฏ„ไผฐ๏ผˆ5 ๆฌก LLM ่ฐƒ็”จ ร— ๆฏๆฌก 3-5 ็ง’๏ผ‰ใ€‚
1150
+
1151
+ ---
1152
+
1153
+ ## ๅ…ซใ€ๅ…ณ้”ฎ่Š‚็‚นไธŽ้‡Œ็จ‹็ข‘
1154
+
1155
+ | ๅ‘จๆฌก | ็›ฎๆ ‡ | ็”ฒ๏ผˆๆ•ฐๆฎๅฑ‚๏ผ‰ | ไน™๏ผˆLLM+้›†ๆˆๅฑ‚๏ผ‰ | ไบคไป˜็‰ฉ |
1156
+ |:----:|------|-------------|-----------------|--------|
1157
+ | **Week 1** | ๅœฐๅŸบ + ไธ‰ๅฑ‚ MVP | `paper_fetcher.py` + `repo_searcher.py` + `repo_fetcher.py` + ่ฏ„ๆต‹้›† Part A ๆ ‡ 3 ็ฏ‡ + Part B ๆ ‡ 6 ไธช | `direction_analyzer.py` + `repo_evaluator.py` + `run.py` + `app.py` + ๆžถๆž„ๅ›พ | `python run.py <arxiv_url>` ๅฎŒๆ•ด้“พ่ทฏ + ็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ็ ”ๆŠฅ |
1158
+ | **Week 2** | ็ซฏๅˆฐ็ซฏ้“พ่ทฏ | Workflow ไผ˜ๅŒ–๏ผˆLLM ๅˆคๆ–ญไพ่ต–ๆ˜ฏๅฆ่ฟ˜่ƒฝ่ฃ…๏ผ‰ใ€ๅŠ  Tavily ็คพๅŒบๆœ็ดขใ€ๅŠ  Issue ๆŠ“ๅ– | Agent C ๅˆ็‰ˆ๏ผˆๆฐด่ดด/็œŸBug ๅˆ†็ฑป๏ผŒไธๅšๅ›ž็Žฏ๏ผ‰ใ€ๅฎŒๅ–„ๆ–นๆณ•ๆ—ๅฝ’็ฑป้€ป่พ‘ | ๅฎŒๆ•ด้“พ่ทฏ + ่ฏ„ๆต‹้›†ๅŸบ็บฟๅ‡†็กฎ็އ |
1159
+ | **Week 3** | ๅ‡†็กฎ็އๆ”ปๅ…ณ | ๆŒ็ปญๆ‰ฉๅ……่ฏ„ๆต‹้›†๏ผˆ่ฎบๆ–‡โ†’ไป“ๅบ“ ground truth๏ผ‰ใ€ไผ˜ๅŒ–ๆ•ฐๆฎ้‡‡้›†่ดจ้‡ | Prompt ่ฐƒไผ˜ใ€ๆ–นๆณ•ๆ—่ฏ†ๅˆซๅ‡†็กฎ็އใ€ๅ…ญ็ปดๆƒ้‡็”จ่ฏ„ๆต‹้›†ๅๆŽจ | ็ ”็ฉถๆ–นๅ‘่ฏ†ๅˆซๅ‡†็กฎ็އ โ‰ฅ 80%ใ€ๅฏๅค็Žฐๆ€งๅ‡†็กฎ็އ โ‰ฅ 75% |
1160
+ | **Week 4** | ๅ›ž็Žฏ + ๅขžๅผบๆœ็ดข | Gradio ็•Œ้ขๅ‡็บง๏ผˆๆ–นๆณ•ๆ—ๅˆ†ๅ—ๅฑ•็คบ๏ผ‰ใ€ๅขžๅŠ  PapersWithCode ๆ•ฐๆฎๆบ | Agent C ๅ›ž็Žฏๆœบๅˆถใ€Agent D ่พน็•Œๅˆ†ๆ•ฐๅ†่ฟญไปฃ | ๅธฆๅ›ž็Žฏ็‰ˆ๏ผŒๅ‡†็กฎ็އ โ‰ฅ 80% |
1161
+ | **Week 5** | UI ๅ‡็บง + ็ป“่ฅๅ‡†ๅค‡ | ๅฝ•ๅˆถ demo ่ง†้ข‘ใ€ๆ•ด็†ๆฑ‡ๆŠฅๆๆ–™๏ผˆๅ‡†ๅค‡ 2 ็ฏ‡่ฎบๆ–‡็š„ๅฎŒๆ•ดๅ…จๆ™ฏ็ ”ๆŠฅๅฏนๆฏ”๏ผ‰ | ๆตๅผ่พ“ๅ‡บใ€ๆŒ‰ๆ–นๆณ•ๆ—ๅˆ†ๅ—ๅฑ•็คบ | ็ป“่ฅ็ญ”่พฉๆๆ–™ |
1162
+ | **Week 6** | ็ผ“ๅ†ฒ | ๆŸฅๆผ่กฅ็ผบใ€่ฏ„ๆต‹้›†ๆ–ฐ้—ฎ้ข˜ไฟฎๅค | ๅŒไธŠ | ๆœ€็ปˆ็ญ”่พฉ |
1163
+
1164
+ ---
1165
+
1166
+ ## ไนใ€ๆ–‡ไปถ็ป“ๆž„
1167
+
1168
+ ```
1169
+ ResearchRadar/
1170
+ โ”œโ”€โ”€ paper_fetcher.py # ็”ฒ๏ผšarxiv ่ฎบๆ–‡ไฟกๆฏๆŠ“ๅ–
1171
+ โ”œโ”€โ”€ repo_searcher.py # ็”ฒ๏ผšGitHub ไปฃ็ ๆœ็ดข๏ผˆๅคšๅ…ณ้”ฎ่ฏๅŽป้‡๏ผ‰
1172
+ โ”œโ”€โ”€ repo_fetcher.py # ็”ฒ๏ผšไป“ๅบ“่ฏฆๆƒ…ๆŠ“ๅ–๏ผˆREADME + ไพ่ต– + Issues๏ผ‰
1173
+ โ”œโ”€โ”€ direction_analyzer.py # ไน™๏ผšLLM ็ ”็ฉถๆ–นๅ‘่งฃๆž + ๆ–นๆณ•ๆ—ๆขณ็† + ๆœ็ดข่ฏ็”Ÿๆˆ
1174
+ โ”œโ”€โ”€ repo_evaluator.py # ไน™๏ผšLLM ๅฏๅค็Žฐๆ€ง๏ผˆ5็ปด๏ผ‰+ ๅฏนๆฏ”ๅฎž้ชŒ้€‚้…ๅบฆ๏ผˆ1็ปด๏ผ‰่ฏ„ไผฐ
1175
+ โ”œโ”€โ”€ run.py # ไน™๏ผšไธ‰ๅฑ‚ไธปๆต็จ‹่ฐƒๅบฆ
1176
+ โ”œโ”€โ”€ app.py # ไน™๏ผšGradio ็•Œ้ข + ็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ็ ”ๆŠฅๆ ผๅผๅŒ–
1177
+ โ”œโ”€โ”€ eval.py # ็”ฒ๏ผš่ฏ„ๆต‹่„šๆœฌ๏ผˆๆ–นๅ‘่ฏ†ๅˆซๅ‡†็กฎ็އ + ๅฏๅค็Žฐๆ€งๅ‡†็กฎ็އ๏ผ‰
1178
+ โ”œโ”€โ”€ test_set/
1179
+ โ”‚ โ”œโ”€โ”€ papers.json # Part A๏ผš่ฎบๆ–‡โ†’็ ”็ฉถๆ–นๅ‘ ground truth + ๅทฒ็Ÿฅๅฎž็Žฐไป“ๅบ“
1180
+ โ”‚ โ””โ”€โ”€ repos.json # Part B๏ผšไป“ๅบ“โ†’ๅฏๅค็Žฐๆ€ง ground truth
1181
+ โ”œโ”€โ”€ requirements.txt # ้กน็›ฎไพ่ต–๏ผšrequests, gradio, openai
1182
+ โ””โ”€โ”€ README.md
1183
+ ```
1184
+
1185
+ **`requirements.txt`**๏ผš
1186
+ ```
1187
+ requests>=2.31
1188
+ gradio>=4.0
1189
+ openai>=1.0
1190
+ ```
1191
+
1192
+ ---
1193
+
1194
+ ## ๅใ€็ฌฌไธ€ๅ‘จไบ”ๅคฉๆ—ถ้—ด็บฟ
1195
+
1196
+ | ๅคฉ | ็”ฒ | ไน™ |
1197
+ |----|----|-----|
1198
+ | **ๅ‘จไธ€** | ๅ†™ `paper_fetcher.py`๏ผŒๅœจ 5 ็ฏ‡่ฎบๆ–‡ไธŠๆต‹่ฏ• arxiv APIใ€‚็”ณ่ฏท GitHub Token | ๅ†™ `direction_analyzer.py` prompt๏ผŒ็”จ 3-5 ็ฏ‡่ฎบๆ–‡ๆต‹่ฏ•๏ผšๅญ้ข†ๅŸŸ่ฏ†ๅˆซๆ˜ฏๅฆๅˆ็†๏ผŸๆ–นๆณ•ๆ—ๆ˜ฏๅฆ่ฆ†็›–ๅ…จ๏ผŸๆœ็ดข่ฏ่ดจ้‡ๅฆ‚ไฝ•๏ผŸ |
1199
+ | **ๅ‘จไบŒ** | ๅ†™ `repo_searcher.py` + `repo_fetcher.py`๏ผŒ่”่ฐƒๆœ็ดขโ†’ๆŠ“ๅ–้“พ่ทฏใ€‚ไธคไธช token ้ƒฝ้…ๅฅฝ่ฝฎๆข | ๅ†™ `repo_evaluator.py` prompt๏ผŒ็”จ 5 ไธชๅทฒ็Ÿฅไป“ๅบ“ๆต‹่ฏ•ๅ…ญ็ปด่ฏ„ๅˆ†ๆ˜ฏๅฆๅˆ็†๏ผŒ็‰นๅˆซๆณจๆ„"ๅฏๅค็Žฐ"ๅ’Œ"้€‚้…ๅบฆ"ๆœ‰ๆฒกๆœ‰ๅŒบๅˆ†ๅผ€ |
1200
+ | **ๅ‘จไธ‰** | ๅ‡†ๅค‡ 3 ็ฏ‡ demo ่ฎบๆ–‡ + ๆ ‡ Part A ่ฏ„ๆต‹้›†๏ผˆๆ–นๅ‘ + ๆ–นๆณ•ๆ— + ๅทฒ็Ÿฅๅฎž็Žฐไป“ๅบ“๏ผ‰๏ผŒ็กฎไฟๆฏ็ฏ‡ๆœๅˆฐ็š„ไป“ๅบ“ๆœ‰ไปฃ่กจๆ€ง | ๅ†™ `run.py` ๅฎŒๆ•ดไธ‰ๅฑ‚้“พ่ทฏ + `app.py` ๅ…จๆ™ฏ็ ”ๆŠฅๆ ผๅผๅŒ–๏ผŒไธ‹ๅˆไธŽ็”ฒ่”่ฐƒ |
1201
+ | **ๅ‘จๅ››** | ๆ ‡ Part B ่ฏ„ๆต‹้›†๏ผˆ6-8 ไธชไป“ๅบ“๏ผ‰๏ผŒๅค„็†่พน็•Œ case๏ผˆ่ฎบๆ–‡ๆœไธๅˆฐใ€ไป“ๅบ“ๆฒก READMEใ€ๆ–นๅ‘่ฏ†ๅˆซ้”™่ฏฏ๏ผ‰ | debug ๆ•ดๆก้“พ่ทฏ๏ผšๆ—ถ้—ดๆถˆ่€—ใ€JSON ่งฃๆžๅคฑ่ดฅใ€API ๅผ‚ๅธธๅค„็†ใ€‚ๅ‡†ๅค‡ PPT ไธ‰ๅฑ‚ๆžถๆž„ๅ›พ |
1202
+ | **ๅ‘จไบ”ไธŠๅˆ** | ๅฎŒๆ•ด่ตฐไธ€้ demo ๆต็จ‹ + ๅ‡†ๅค‡่ฎฒ็จฟ๏ผˆ3 ็ฏ‡่ฎบๆ–‡ๅ„่ฎฒไป€ไนˆๆ•…ไบ‹๏ผšๆ–นๅ‘ๅ…จๆ™ฏโ†’ไปฃ็ ๅ‘็Žฐโ†’ๅฏนๆฏ”ๅฎž้ชŒๆŽจ่๏ผ‰ | ๅŒไธŠ๏ผŒ่”่ฐƒ + ็ญ”่พฉ้ข„ๆผ” |
1203
+ | **ๅ‘จไบ”ไธ‹ๅˆ** | **ๆฑ‡ๆŠฅ** | **ๆฑ‡ๆŠฅ** |
1204
+
1205
+ ---
1206
+
1207
+ ## ๅไธ€ใ€ไฝ ไปฌ้œ€่ฆๅ†ณๅฎš็š„ไบ‹ๆƒ…
1208
+
1209
+ ๅœจๅผ€ๅง‹ๅ†™ไปฃ็ ไน‹ๅ‰๏ผŒไธ‹้ขๅ‡ ไธช้—ฎ้ข˜้œ€่ฆ็”ฒๅ’Œไน™ๅไธ‹ๆฅๅฏนๆธ…ๆฅšใ€‚ไธ็”จไธ€ๆฌกๅ›ž็ญ”ๅฎŒ๏ผŒๅฏไปฅ่พนๅ†™่พนๅ†ณๅฎš๏ผš
1210
+
1211
+ 1. **็”ฒๅ’Œไน™ๅ„่‡ช็”ณ่ฏท GitHub Token ไบ†ๅ—๏ผŸ** ๆœ็ดขๆŽฅๅฃ้™้€Ÿๅพˆไธฅ๏ผˆ30ๆฌก/ๅˆ†้’Ÿ๏ผ‰๏ผŒไธคไธช token ่ฝฎๆขๆ˜ฏๆœ€ไฝŽ้…็ฝฎใ€‚ๅฆ‚ๆžœๆฒกๆœ‰๏ผŒๅ‘จไธ€็ฌฌไธ€ไปถไบ‹ๅฐฑๅŽป [github.com/settings/tokens](https://github.com/settings/tokens) ๅ„็”ณ่ฏทไธ€ไธช Fine-grained token๏ผˆๅชๅ‹พ้€‰ public repo ่ฏปๆƒ้™๏ผ‰ใ€‚
1212
+
1213
+ 2. **DeepSeek API key ็Žฐๅœจๅฐฑ่ƒฝ็”จๅ—๏ผŸ** ่ฐๆ‹ฟ็€ key๏ผŸๅ‘จไธ€ไธŠๅˆๅ…ˆ็”จๆœ€็ฎ€ๅ•็š„ `curl` ๆˆ– Python ่„šๆœฌ้ชŒ่ฏ key ๆœ‰ๆ•ˆใ€‚่ฟ™ไปถไบ‹้˜ปๅกžๆ‰€ๆœ‰ LLM ๆจกๅ—ใ€‚
1214
+
1215
+ 3. **3 ็ฏ‡ demo ่ฎบๆ–‡้€‰ไป€ไนˆ๏ผŸ** ๅปบ่ฎฎ้€‰ไธ€็ฏ‡็ปๅ…ธ่ฎบๆ–‡๏ผˆๅฆ‚ Transformerใ€ResNetใ€DDPM๏ผŒๆ–นๆณ•ๆ—ไธฐๅฏŒใ€ๅผ€ๆบๅฎž็Žฐๅคšใ€ๆผ”็คบๆ•ˆๆžœๅฅฝ๏ผ‰๏ผŒๅ†ไปŽไฝ ไปฌๅ„่‡ชๆ–นๅ‘ๅ„้€‰ไธ€็ฏ‡ใ€‚ๅ…ณ้”ฎๆ˜ฏ่ฆ็กฎไฟๆฏ็ฏ‡่ฎบๆ–‡้ƒฝ่ƒฝๆœๅˆฐ 3+ ไธชไป“ๅบ“๏ผŒๅฆๅˆ™ demo ๆ—ถๆฒกไธœ่ฅฟๅฑ•็คบใ€‚
1216
+
1217
+ 4. **ๆ–นๅ‘่งฃๆž๏ผˆdirection_analyzer๏ผ‰็š„่ฏ„ๆต‹ๆ ‡ๅ‡†ๆ€Žไนˆๅฎš๏ผŸ** ่ฟ™ๆ˜ฏๆ•ดไธช็ณป็ปŸๆœ€"ไธป่ง‚"็š„ๆจกๅ—ใ€‚ๅญ้ข†ๅŸŸๅฎšไฝๅฏนไธๅฏน๏ผŸๆ–นๆณ•ๆ—ๅ…จไธๅ…จ๏ผŸๆœ็ดข่ฏๅฅฝไธๅฅฝ๏ผŸไฝ ไปฌ้œ€่ฆๅฎšไธ€ไธช็ฎ€ๅ•็š„ๅˆคๅฎš่ง„ๅˆ™๏ผŒๆฏ”ๅฆ‚"ๅญ้ข†ๅŸŸ็”ฑไบบๅทฅ็กฎ่ฎคๆญฃ็กฎ"ใ€"ๆ–นๆณ•ๆ—่‡ณๅฐ‘่ฆ†็›– 3 ไธช่ฏฅ้ข†ๅŸŸ็š„ๅ…ฌ่ฎคไธปๆตๆ–นๅ‘"ใ€‚
1218
+
1219
+ 5. **"ๅฏนๆฏ”ๅฎž้ชŒ้€‚้…ๅบฆ"็š„ ground truth ๆ€Žไนˆๆ ‡๏ผŸ** ่ฟ™ไธชๆฏ”"ๅฏๅค็Žฐๆ€ง"ๆ›ด้šพๆ ‡ๆณจใ€‚ๅฏๅค็Žฐๆ€งๅฏไปฅ่ฏ•็€่ฃ…็Žฏๅขƒ่ท‘ไธ€ไธ‹๏ผŒ้€‚้…ๅบฆ้œ€่ฆๅˆคๆ–ญ"่ฟ™ไธชไป“ๅบ“่ƒฝ็›ดๆŽฅ่ท‘ๅ‡บ่ฎบๆ–‡้‡Œ็š„ๆŒ‡ๆ ‡ๅ—"ใ€‚ๅปบ่ฎฎๅ…ˆ็”จไฝ ไปฌ่‡ชๅทฑ็†Ÿๆ‚‰็š„ไป“ๅบ“ๆ ‡ 5 ไธช๏ผŒๅ†ๆ‰ฉๅฑ•ๅˆฐ 20 ไธชใ€‚
1220
+
1221
+ 6. **่ฎบๆ–‡ๅคชๆ–ฐๆœไธๅˆฐไปฃ็ ๆ€Žไนˆๅค„็†๏ผŸ** ๆ–ฐ่ฎบๆ–‡๏ผˆๅฆ‚ 2025 ๅนด็š„๏ผ‰ๅพ€ๅพ€ๆฒกๆœ‰ๅผ€ๆบๅฎž็Žฐ๏ผŒไฝ†่ฟ™ๆฐๆฐๆ˜ฏๅทฅๅ…ท็š„ไปทๅ€ผโ€”โ€”ๆๅ‰ๅ‘Š่ฏ‰ไฝ "่ฟ™ไธชๆ–นๅ‘่ฟ˜ๆฒกๆœ‰ๆˆ็†Ÿไปฃ็ ๏ผŒไฝ ๅฏ่ƒฝ่ฆไปŽๅคดๅฎž็Žฐ"ใ€‚ๆผ”็คบๆ—ถๅฆ‚ๆžœ้‡ๅˆฐ่ฟ™็งๆƒ…ๅ†ต๏ผŒไฝ ๅบ”่ฏฅๆ€Žไนˆ่ฎฒ่ฟ™ไธชๆ•…ไบ‹๏ผŸ
1222
+
1223
+ ---
1224
+
1225
+ > **ๆœ€ๅŽไธ€ๅฅ**๏ผšไฝ ไปฌๆ˜ฏ็ ”ไธ€ๅฐ็™ฝ๏ผŒ่ฟ™ๅฎŒๅ…จไธไธขไบบใ€‚6 ๅˆ†้’Ÿๆผ”็คบ๏ผŒ่ฏ„ๅง”ไธไผš่ขซ่Šฑๅ“จ UI ๆ‰“ๅŠจ๏ผŒไฝ†ไผš่ขซใ€Œๆธ…ๆฅš็š„้€ป่พ‘ + ็œŸๅฎž็š„ๆ•ฐๆฎ + ่ฏšๅฎž็š„ๅฑ€้™ใ€่ฏดๆœใ€‚ๅˆซไผช่ฃ…๏ผŒๅˆซ้€ ๅ‡ใ€‚ไฝ ไปฌ็š„ไปทๅ€ผๆ˜ฏ"ๅธฎ็ ”็ฉถ่€…่Š‚็œไปŽ่ฎบๆ–‡ๅˆฐ่ท‘้€šๅฏนๆฏ”ๅฎž้ชŒ็š„ๆ—ถ้—ด"๏ผŒ่ฟ™ไธชไปทๅ€ผไธ€ๅฅ่ฏๅฐฑ่ƒฝ่ฎฒๆธ…ๆฅšใ€‚
ResearchRadar_ไน™_ไปปๅŠกไนฆ.md ADDED
@@ -0,0 +1,1575 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ResearchRadar ไน™๏ผˆๅ†ณ็ญ–+้›†ๆˆๅฑ‚๏ผ‰ไปปๅŠกไนฆ
2
+
3
+ > ่ง’่‰ฒ๏ผšไน™ โ€” LLM ๅ†ณ็ญ–ๆจกๅ— + ไธปๆต็จ‹่ฐƒๅบฆ + Gradio ็•Œ้ข
4
+ > ็›ฎๆ ‡๏ผš็ฌฌไธ€ๅ‘จ็ป“ๆŸๅ‰๏ผŒ`python run.py <arxiv_url>` ่ƒฝ่ท‘้€šๅฎŒๆ•ดไธ‰ๅฑ‚้“พ่ทฏ๏ผŒ่พ“ๅ‡บ็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ็ ”ๆŠฅ
5
+ > ๅ‰็ฝฎๆกไปถ๏ผšDeepSeek API key ๅทฒ้ชŒ่ฏๅฏ็”จ
6
+
7
+ ---
8
+
9
+ ## ใ€‡ใ€ๅ…ˆ็†่งฃไฝ ่ฆๆญๅปบ็š„ๆ˜ฏไป€ไนˆโ€”โ€”Agent ไธŽ Workflow ็š„ๆœฌ่ดจ
10
+
11
+ > ไฝ ๆ˜ฏๅฐ็™ฝ๏ผŒ่ฟ™ไธไธขไบบใ€‚ๅพˆๅคšๅ†™ไบ†ๅฅฝๅ‡ ๅนดไปฃ็ ็š„ไบบไนŸๆฒกๆžๆ‡‚ Agent ๅˆฐๅบ•ๆ˜ฏไป€ไนˆใ€‚่ฟ™ไธ€่Š‚ๅฏ่ƒฝๆ˜ฏๆ•ดไธชไปปๅŠกไนฆๆœ€้‡่ฆ็š„้ƒจๅˆ†ใ€‚
12
+
13
+ ### 0.1 Agent ๆ˜ฏไป€ไนˆ๏ผˆ็”จไฝ ่ƒฝๅฌๆ‡‚็š„่ฏ๏ผ‰
14
+
15
+ **Agent = ไธ€ไธชๆœ‰ๅˆคๆ–ญ่ƒฝๅŠ›็š„ๅ‡ฝๆ•ฐ**ใ€‚่ทŸๆ™ฎ้€šๅ‡ฝๆ•ฐ็š„ๅ”ฏไธ€ๅŒบๅˆซๆ˜ฏ๏ผšๅฎƒไธ้ ๅ†™ๆญป็š„่ง„ๅˆ™ๅšๅˆคๆ–ญ๏ผŒ่€Œๆ˜ฏๆŠŠไฟกๆฏไธข็ป™ LLM๏ผŒ่ฎฉ LLM ๆฅ"ๅธฎไฝ ็œ‹ไธ€็œผ็„ถๅŽๅšๅ†ณๅฎš"ใ€‚
16
+
17
+ ็”จไฝ ็†Ÿๆ‚‰็š„ๅœบๆ™ฏไธพไพ‹๏ผš
18
+
19
+ - **ไธ้œ€่ฆ Agent ็š„ไบ‹**๏ผšไฝ ๅŽป GitHub ็œ‹ไธ€ไธชไป“ๅบ“ๆœ‰ๅคšๅฐ‘ starใ€‚่ฟ™ๅฐฑๆ˜ฏ็”จ็œผ็›็œ‹๏ผŒไธ้œ€่ฆๅŠจ่„‘ๅญใ€‚ๅ†™ๆˆไปฃ็ ๅฐฑๆ˜ฏ `requests.get(url) โ†’ resp.json()["stargazers_count"]`ใ€‚่ฟ™ๆ˜ฏ **Workflow**ใ€‚
20
+
21
+ - **้œ€่ฆ Agent ็š„ไบ‹**๏ผš็ป™ไฝ  100 ๆก GitHub Issue๏ผŒ่ฎฉไฝ ๅˆคๆ–ญๅ“ชไบ›ๆ˜ฏ็œŸ bug ๅ“ชไบ›ๆ˜ฏ"ๅคงไฝฌๆฑ‚ไปฃ็ "็š„ๆฐด่ดดใ€‚่ฟ™้œ€่ฆ็†่งฃ Issue ็š„ๅ†…ๅฎน๏ผŒ้œ€่ฆๅŠจ่„‘ๅญใ€‚ไฝ† 100 ๆกไฝ ไบบๅทฅ็œ‹ไธ่ฟ‡ๆฅ๏ผŒๆ‰€ไปฅๅ†™ไธ€ไธชๅ‡ฝๆ•ฐ๏ผŒๆŠŠ Issue ๆ–‡ๆœฌไธข็ป™ DeepSeek๏ผŒ่ฎฉๅฎƒๅธฎไฝ ๅˆ†็ฑปใ€‚่ฟ™ๅฐฑๆ˜ฏ **Agent**ใ€‚
22
+
23
+ **Agent ็š„ๆœฌ่ดจๅฐฑๆ˜ฏไธ€ไธช Python ๅ‡ฝๆ•ฐ๏ผŒๅ‡ฝๆ•ฐไฝ“้‡Œๆœ‰่ฟ™ไธ€่กŒ**๏ผš
24
+
25
+ ```python
26
+ response = client.chat.completions.create(
27
+ model="deepseek-chat",
28
+ messages=[{"role": "system", "content": "ไฝ ๆ˜ฏXXไธ“ๅฎถ..."},
29
+ {"role": "user", "content": "ๅธฎๆˆ‘ๅˆคๆ–ญ..."}],
30
+ )
31
+ # LLM ่ฟ”ๅ›ž็š„ๅˆคๆ–ญ็ป“ๆžœๅฐฑๆ˜ฏ Agent ็š„"ๅ†ณ็ญ–"
32
+ return response.choices[0].message.content
33
+ ```
34
+
35
+ **ไธ้œ€่ฆ LangChain๏ผŒไธ้œ€่ฆ LangGraph๏ผŒไธ้œ€่ฆไปปไฝ•ๆก†ๆžถใ€‚** ไฝ ไปฌ่ทฏ็บฟๅคง็บฒ้‡Œ่ฏด็š„"4 ไธชๅ‡ฝๆ•ฐ + ไธ€ไธช dict + if/else๏ผŒ300 ่กŒ่ƒฝๆžๅฎš"ๅฐฑๆ˜ฏ่ฟ™ไธชๆ„ๆ€ใ€‚
36
+
37
+ ### 0.2 Workflow ๆ˜ฏไป€ไนˆ
38
+
39
+ **Workflow = ไธ€ๆกๅ†™ๆญป็š„ๆตๆฐด็บฟ**ใ€‚ๆฏไธ€ๆญฅๅšไป€ไนˆๆ˜ฏ็กฎๅฎš็š„๏ผŒไธ้œ€่ฆ LLM ๆฅๅš้€‰ๆ‹ฉใ€‚
40
+
41
+ ```python
42
+ # ่ฟ™ๅฐฑๆ˜ฏไธ€ไธช Workflow๏ผš3 ๆญฅ๏ผŒๆฏไธ€ๆญฅๅšไป€ไนˆๆ˜ฏๅ†™ๆญป็š„
43
+ def workflow_example(url):
44
+ data = requests.get(url).json() # ๆญฅ้ชค 1๏ผšๆŠ“ๆ•ฐๆฎ
45
+ cleaned = clean_data(data) # ๆญฅ้ชค 2๏ผšๆธ…ๆด—
46
+ return cleaned # ๆญฅ้ชค 3๏ผš่ฟ”ๅ›ž
47
+ ```
48
+
49
+ Workflow ้‡Œๆฒกๆœ‰ `if LLM่ฏดAๅฐฑ่ตฐB` ่ฟ™็ง้€ป่พ‘ใ€‚่ทฏๅพ„ๆ˜ฏ็กฎๅฎš็š„ใ€‚
50
+
51
+ ### 0.3 ไฝ ็š„้กน็›ฎ้‡Œ๏ผšๅ“ชไธชๆ˜ฏ Agent๏ผŒๅ“ชไธชๆ˜ฏ Workflow
52
+
53
+ ๆ•ดไธช ResearchRadar ็ณป็ปŸๅฏไปฅๆŒ‰่ฟ™ไธชๆ ‡ๅ‡†้‡ๆ–ฐๆ ‡ๆณจ๏ผš
54
+
55
+ ```
56
+ ็”จๆˆท่พ“ๅ…ฅ arxiv URL
57
+ โ”‚
58
+ โ–ผ
59
+ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
60
+ โ”‚ paper_fetcher.fetch_paper_info() โ”‚ โ† WORKFLOW๏ผˆ็”ฒ๏ผ‰
61
+ โ”‚ ๅš็š„ไบ‹ๆƒ…๏ผš่ฐƒ arxiv API๏ผŒ่งฃๆž XML๏ผŒ่ฟ”ๅ›ž dict โ”‚
62
+ โ”‚ ไธ้œ€่ฆ LLM๏ผŒ่ทฏๅพ„ๅ†™ๆญป โ”‚
63
+ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
64
+ โ”‚
65
+ โ–ผ
66
+ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
67
+ โ”‚ direction_analyzer.analyze_direction() โ”‚ โ† AGENT๏ผˆไน™ โ˜… ไฝ ่ดŸ่ดฃ๏ผ‰
68
+ โ”‚ ๅš็š„ไบ‹ๆƒ…๏ผšLLM ็†่งฃ่ฎบๆ–‡ โ†’ ๅˆคๆ–ญๅญ้ข†ๅŸŸ โ†’ โ”‚
69
+ โ”‚ ๆขณ็†ๆ–นๆณ•ๆ— โ†’ ็”Ÿๆˆๆœ็ดข่ฏ โ”‚
70
+ โ”‚ ้œ€่ฆ LLM๏ผšๆ˜ฏ็š„๏ผŒLLM ๅœจๅš"ๅˆคๆ–ญ" โ”‚
71
+ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
72
+ โ”‚
73
+ โ–ผ
74
+ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
75
+ โ”‚ repo_searcher.search_repos() โ”‚ โ† WORKFLOW๏ผˆ็”ฒ๏ผ‰
76
+ โ”‚ ๅš็š„ไบ‹ๆƒ…๏ผš็”จๆœ็ดข่ฏ่ฐƒ GitHub API๏ผŒ โ”‚
77
+ โ”‚ ๅŽป้‡๏ผŒๆŒ‰ star ๆŽ’ๅบ โ”‚
78
+ โ”‚ ไธ้œ€่ฆ LLM๏ผŒๅฐฑๆ˜ฏๅพช็Žฏ่ฐƒ API โ”‚
79
+ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
80
+ โ”‚
81
+ โ–ผ
82
+ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
83
+ โ”‚ repo_fetcher.fetch_readme() โ”‚ โ† WORKFLOW๏ผˆ็”ฒ๏ผ‰
84
+ โ”‚ repo_fetcher.fetch_dependencies() โ”‚
85
+ โ”‚ ๅš็š„ไบ‹ๆƒ…๏ผš่ฐƒ GitHub API๏ผŒbase64 ่งฃ็ ๏ผŒ โ”‚
86
+ โ”‚ ่ฟ”ๅ›žๆ–‡ไปถๅ†…ๅฎน โ”‚
87
+ โ”‚ ไธ้œ€่ฆ LLM โ”‚
88
+ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
89
+ โ”‚
90
+ โ–ผ
91
+ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
92
+ โ”‚ repo_evaluator.evaluate_repo() โ”‚ โ† AGENT๏ผˆไน™ โ˜… ไฝ ่ดŸ่ดฃ๏ผ‰
93
+ โ”‚ ๅš็š„ไบ‹ๆƒ…๏ผšLLM ้˜…่ฏป README + ไพ่ต–ๆ–‡ไปถ โ†’ โ”‚
94
+ โ”‚ ๅˆคๆ–ญๅฏๅค็Žฐๆ€ง + ๅฏนๆฏ”ๅฎž้ชŒ้€‚้…ๅบฆ โ”‚
95
+ โ”‚ ้œ€่ฆ LLM๏ผšๆ˜ฏ็š„๏ผŒLLM ๅœจๅš"่ฏ„ๅˆ†ๅˆคๆ–ญ" โ”‚
96
+ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
97
+ โ”‚
98
+ โ–ผ
99
+ Markdown ็ ”ๆŠฅ
100
+ ```
101
+
102
+ **ไธ€ๅฅ่ฏๆ€ป็ป“**๏ผšๆ•ดไธช็ณป็ปŸ = ็”ฒ็š„ 3 ไธช Workflow + ไฝ ็š„ 2 ไธช Agent + 1 ไธช่ฐƒๅบฆๅ™จ๏ผˆrun.py๏ผ‰ๆŠŠๅฎƒไปฌไธฒ่ตทๆฅใ€‚
103
+
104
+ ### 0.4 Agent ๅ’Œ Workflow ็š„ๆญๅปบๆ€่ทฏๅฏนๆฏ”
105
+
106
+ | | Workflow๏ผˆ็”ฒ่ดŸ่ดฃ๏ผ‰ | Agent๏ผˆไฝ ่ดŸ่ดฃ๏ผ‰ |
107
+ |------|------|------|
108
+ | **ๆ ธๅฟƒๅทฅไฝœ** | ๆžๆธ…ๆฅš API ๆ€Žไนˆ่ฐƒใ€่ฟ”ๅ›žไป€ไนˆๆ ผๅผใ€ๆ€Žไนˆ่งฃๆž | ่ฎพ่ฎกไธ€ๆฎตๆ–‡ๅญ—๏ผˆprompt๏ผ‰๏ผŒๅ‘Š่ฏ‰ LLM ๅฎƒๆ˜ฏไป€ไนˆ่ง’่‰ฒใ€่ฆๅˆคๆ–ญไป€ไนˆใ€่พ“ๅ‡บไป€ไนˆๆ ผๅผ |
109
+ | **้šพๅบฆๅœจๅ“ช** | ๅ„็งๅผ‚ๅธธๅค„็†๏ผˆAPI ๆŒ‚ไบ†ใ€้™้€Ÿไบ†ใ€ๆ•ฐๆฎๆ ผๅผๅ˜ไบ†๏ผ‰ | LLM ่ฟ”ๅ›ž็š„ JSON ๆ ผๅผไธๅฏนใ€่ฏ„ๅˆ†ไธ็ฌฆๅˆๅธธ่ฏ†ใ€ไธๅŒ่ฎบๆ–‡็š„่พ“ๅ‡บ่ดจ้‡ไธ็จณๅฎš |
110
+ | **่ฐƒ่ฏ•ๆ–นๅผ** | `print()` ็œ‹ API ่ฟ”ๅ›žไบ†ไป€ไนˆ | ๆ‰‹ๅทฅๆข 3-5 ็ฏ‡่ฎบๆ–‡/ไป“ๅบ“ๆต‹่ฏ•๏ผŒ็œ‹ LLM ็š„ๅˆคๆ–ญๆ˜ฏๅฆๅˆ็† |
111
+ | **ไฝ ้œ€่ฆ็š„ๆŠ€่ƒฝ** | ไผš็”จ `requests` ๅบ“ใ€ไผš็œ‹ API ๆ–‡ๆกฃ | ไผšๅ†™ๆธ…ๆ™ฐ็š„ promptใ€ไผšๅค„็† LLM ่พ“ๅ‡บๆ ผๅผ |
112
+
113
+ ### 0.5 ไฝ ็š„ 4 ไธชๆ–‡ไปถๅ„่‡ชๆ˜ฏไป€ไนˆ
114
+
115
+ | ไฝ ็š„ๆ–‡ไปถ | ็ฑปๅž‹ | ็ฎ€็Ÿญ่ฏดๆ˜Ž |
116
+ |----------|------|----------|
117
+ | `direction_analyzer.py` | **Agent** | ้‡Œ้ขๅชๆœ‰ไธ€ไธชๆ ธๅฟƒๅ‡ฝๆ•ฐ `analyze_direction()`๏ผŒๅฎƒ่ฐƒ LLM ๅšๆ–นๅ‘ๅˆคๆ–ญ |
118
+ | `repo_evaluator.py` | **Agent** | ้‡Œ้ขๅชๆœ‰ไธ€ไธชๆ ธๅฟƒๅ‡ฝๆ•ฐ `evaluate_repo()`๏ผŒๅฎƒ่ฐƒ LLM ๅš่ฏ„ๅˆ†ๅˆคๆ–ญ |
119
+ | `run.py` | **่ฐƒๅบฆๅ™จ** | ๆ—ขไธๆ˜ฏ Agent ไนŸไธๆ˜ฏ Workflow๏ผŒๆ˜ฏไธ€ๆฎต"่ƒถๆฐดไปฃ็ "๏ผŒๆŒ‰้กบๅบ่ฐƒ็”จ Workflow ๅ’Œ Agent๏ผŒๆŠŠๆ•ฐๆฎๅœจๅฎƒไปฌไน‹้—ดไผ ้€’ |
120
+ | `app.py` | **็•Œ้ข** | ๅŒ…่ฃ…ไธ€ไธช Gradio ๅฃณ๏ผŒ่ฎฉ็”จๆˆท่ƒฝ่พ“ๅ…ฅ URL ็œ‹ๅˆฐ็ ”ๆŠฅ |
121
+
122
+ ### 0.6 ไฝ ๅ’Œ็”ฒ็š„ๆŽฅๅฃ็บฆๅฎš
123
+
124
+ ็”ฒ่ดŸ่ดฃๆญ Workflow ็ป™ไฝ ๆไพ›ๆ•ฐๆฎ๏ผŒไฝ ่ดŸ่ดฃๆญ Agent ๅšๅˆคๆ–ญใ€‚ไฝ ๅช้œ€่ฆ็Ÿฅ้“็”ฒ็š„ๅ‡ฝๆ•ฐ็ญพๅ๏ผš
125
+
126
+ | ็”ฒ็š„ๆ–‡ไปถ | ไฝ ่ฐƒ็”จ็š„ๅ‡ฝๆ•ฐ | ่พ“ๅ…ฅ | ่พ“ๅ‡บ |
127
+ |----------|-------------|------|------|
128
+ | `paper_fetcher.py` | `fetch_paper_info(url)` | arxiv URL ๅญ—็ฌฆไธฒ | `{"arxiv_id", "title", "abstract", "authors", "categories", "published"}` |
129
+ | `repo_searcher.py` | `search_repos(keywords, max_per_keyword=5)` | ๅ…ณ้”ฎ่ฏๅญ—็ฌฆไธฒๅˆ—่กจ | `[{"full_name", "html_url", "description", "stars", "topics", ...}, ...]` |
130
+ | `repo_fetcher.py` | `fetch_readme(owner, repo)` | owner ๅญ—็ฌฆไธฒ, repo ๅญ—็ฌฆไธฒ | README ๆ–‡ๆœฌ ๆˆ– None |
131
+ | `repo_fetcher.py` | `fetch_dependencies(owner, repo)` | owner ๅญ—็ฌฆไธฒ, repo ๅญ—็ฌฆไธฒ | `{"requirements.txt": "...", "environment.yml": "...", ...}` |
132
+
133
+ **ไฝ ่ฆๅ‘็”ฒ็กฎ่ฎค็š„**๏ผšไปฅไธŠ 4 ไธชๅ‡ฝๆ•ฐๅๅ’Œๅ‚ๆ•ฐๅไธ่ฆๆ”น๏ผŒๅฆๅˆ™ `run.py` ่ฐƒไธ้€šใ€‚
134
+
135
+ ### 0.7 ๅทฅไธš็ผบ้™ทๅผ‚ๅธธๆฃ€ๆต‹้ข†ๅŸŸ้€‚้…
136
+
137
+ ไฝ ไปฌๆš‚ๅฎš็š„้ข†ๅŸŸๆ˜ฏ**ๅทฅไธš็ผบ้™ทๅผ‚ๅธธๆฃ€ๆต‹**๏ผˆIndustrial Defect Anomaly Detection๏ผ‰ใ€‚่ฟ™ไธช้ข†ๅŸŸๅพˆ้€‚ๅˆ ResearchRadar๏ผŒๅ› ไธบ๏ผš
138
+
139
+ - ๆœ‰ๆ˜Ž็กฎ็š„ๆ–นๆณ•ๆ—่ฐฑ็ณป๏ผšๅŸบไบŽ้‡ๆž„็š„๏ผˆAutoEncoder/VAE๏ผ‰ใ€ๅŸบไบŽๅตŒๅ…ฅ็š„๏ผˆPaDiM/PatchCore๏ผ‰ใ€ๅŸบไบŽๅธˆ็”Ÿ็ฝ‘็ปœ็š„๏ผˆSTFPM/EfficientAD๏ผ‰ใ€ๅŸบไบŽๅˆๆˆ็ผบ้™ท็š„๏ผˆDRAEM/Defect-GAN๏ผ‰
140
+ - ๆœ‰ๅ…ฌ่ฎค็š„ๆ•ฐๆฎ้›†๏ผšMVTec ADใ€BTADใ€KSDD2
141
+ - ๆœ‰้ซ˜่ดจ้‡ๅผ€ๆบ้กน็›ฎ๏ผšOpenVINO anomalibใ€PaDiMใ€PatchCoreใ€EfficientAD
142
+ - ๆ˜ฏ CV ้ข†ๅŸŸ็š„ๆดป่ทƒๅญๆ–นๅ‘๏ผŒGitHub ไธŠ่ƒฝๆ‰พๅˆฐ่ถณๅคŸๅคš็š„ๅ€™้€‰ไป“ๅบ“
143
+
144
+ ไปฅไธ‹ๆ˜ฏไฝ ่ฐƒ่ฏ• prompt ๆ—ถๅปบ่ฎฎ็”จ็š„ๆต‹่ฏ•่ฎบๆ–‡๏ผˆ้€‰่ฟ™ไธช้ข†ๅŸŸ็š„็ปๅ…ธ่ฎบๆ–‡๏ผ‰๏ผš
145
+
146
+ | ่ฎบๆ–‡ | arxiv ID | ไธบไป€ไนˆ้€‚ๅˆๆต‹่ฏ• |
147
+ |------|----------|---------------|
148
+ | PaDiM (2021) | `2011.08785` | ๅตŒๅ…ฅๆ–นๆณ•ๆ—ไปฃ่กจ๏ผŒGitHub ๅฎž็Žฐๅคš |
149
+ | PatchCore (2022) | `2106.08265` | ๅตŒๅ…ฅๆ–นๆณ•ๆ—๏ผŒๆœ‰ anomalib ๅฎ˜ๆ–นๅฎž็Žฐ |
150
+ | EfficientAD (2023) | `2303.14535` | ๅธˆ็”Ÿ็ฝ‘็ปœ๏ผŒ่พƒๆ–ฐ๏ผŒๅฎž็Žฐ็›ธๅฏนๅฐ‘๏ผˆๆต‹่ฏ•"ๆœไธๅˆฐไปฃ็ "็š„ๆƒ…ๅ†ต๏ผ‰ |
151
+ | DRAEM (2021) | `2108.07610` | ๅˆๆˆ็ผบ้™ทๆ–นๆณ•๏ผŒๆœ‰ๅฎ˜ๆ–นไปฃ็  |
152
+ | DeepSVDD (2018) | ้ž arxiv๏ผŒICML 2018 | ็ปๅ…ธๅ•็ฑปๅˆ†็ฑปๆ–นๆณ•๏ผŒๅคšไธช็คพๅŒบๅฎž็Žฐ |
153
+
154
+ ---
155
+
156
+ ## ไธ€ใ€Agent 1๏ผš`direction_analyzer.py` โ€” ็ ”็ฉถๆ–นๅ‘่งฃๆž
157
+
158
+ > **็ฑปๅž‹๏ผšAgent** | ่ฟ™ๆ˜ฏไฝ ่ดŸ่ดฃ็š„็ฌฌไธ€ไธช Agent
159
+ > **ๆ ธๅฟƒๅˆคๆ–ญ**๏ผš็ป™ๅฎš่ฎบๆ–‡ๆ ‡้ข˜+ๆ‘˜่ฆ๏ผŒLLM ๅˆคๆ–ญๅฎƒๅฑžไบŽไป€ไนˆๅญ้ข†ๅŸŸใ€่ฏฅ้ข†ๅŸŸๆœ‰ๅ“ชไบ›ๆ–นๆณ•ๆ—ใ€ๅบ”่ฏฅ็”จไป€ไนˆๆœ็ดข่ฏ
160
+
161
+ ### 1.1 ่ฟ™ไธช Agent ่ฆ่งฃๅ†ณไป€ไนˆ้—ฎ้ข˜
162
+
163
+ ไปฅๅพ€็š„ๆ–นๆกˆๆ˜ฏ"ไปŽ่ฎบๆ–‡ๆ ‡้ข˜+ๆ‘˜่ฆๆๅ–ๅ…ณ้”ฎ่ฏๆœ GitHub"ใ€‚ไฝ†่ฟ™ไธๅคŸโ€”โ€”็ ”็ฉถ่€…้œ€่ฆ็š„ไธๆ˜ฏไธ€ๅ †ๅ…ณ้”ฎ่ฏ๏ผŒ่€Œๆ˜ฏ**ๅฏน่ฏฅ็ ”็ฉถๆ–นๅ‘็š„็ณป็ปŸ็†่งฃ**ใ€‚
164
+
165
+ ๆฏ”ๅฆ‚่พ“ๅ…ฅไธ€็ฏ‡ๅทฅไธš็ผบ้™ทๆฃ€ๆต‹็š„่ฎบๆ–‡๏ผˆๅฆ‚ PaDiM๏ผ‰๏ผŒ่ฟ™ไธช Agent ๅบ”่ฏฅ่พ“ๅ‡บ๏ผš
166
+ - ๅญ้ข†ๅŸŸ๏ผšUnsupervised Industrial Defect Detection / Anomaly Detection
167
+ - ๆ–นๆณ•ๆ—๏ผšๅŸบไบŽๅตŒๅ…ฅ็š„๏ผˆPaDiM/PatchCore๏ผ‰ใ€ๅŸบไบŽ้‡ๆž„็š„๏ผˆAutoEncoder/VAE๏ผ‰ใ€ๅŸบไบŽๅธˆ็”Ÿ็ฝ‘็ปœ็š„๏ผˆSTFPM/EfficientAD๏ผ‰ใ€ๅŸบไบŽๅˆๆˆ็ผบ้™ท็š„๏ผˆDRAEM๏ผ‰
168
+ - ๆœ็ดข่ฏ๏ผšๆฏไธชๆ–น๏ฟฝ๏ฟฝ๏ฟฝๆ—็š„ GitHub ๆœ็ดข่ฏ
169
+
170
+ **่ฟ™ๆ•ดไธชๅˆคๆ–ญ่ฟ‡็จ‹ๅฐฑๆ˜ฏ Agent ็š„"ๅ†ณ็ญ–"**โ€”โ€”ๅฎƒไธๆ˜ฏไปŽๆ•ฐๆฎๅบ“ๆŸฅๅ‡บๆฅ็š„๏ผŒๆ˜ฏ LLM ๆ นๆฎ่ฎบๆ–‡ๅ†…ๅฎน็†่งฃๅŽๅšๅ‡บ็š„ๅˆคๆ–ญใ€‚
171
+
172
+ ### 1.2 ่ฟ™ไธช Agent ็š„ๆญๅปบๆ€่ทฏ
173
+
174
+ ```
175
+ ๆญฅ้ชค 1๏ผšๅ†™ System Prompt๏ผˆๅ‘Š่ฏ‰ LLM ๅฎƒๆ˜ฏไป€ไนˆ่ง’่‰ฒใ€่ฆ่พ“ๅ‡บไป€ไนˆ๏ผ‰
176
+ ๆญฅ้ชค 2๏ผšๅ†™ User Prompt ๆจกๆฟ๏ผˆๆŠŠ่ฎบๆ–‡ๆ ‡้ข˜ๅ’Œๆ‘˜่ฆๅกซๅ…ฅๆจกๆฟ๏ผ‰
177
+ ๆญฅ้ชค 3๏ผš่ฐƒ OpenAI SDK ๅ‘้€็ป™ DeepSeek
178
+ ๆญฅ้ชค 4๏ผš่งฃๆž LLM ่ฟ”ๅ›ž็š„ JSON
179
+ ๆญฅ้ชค 5๏ผš็”จ 3-5 ็ฏ‡่ฎบๆ–‡ๆต‹่ฏ•๏ผŒ็œ‹ LLM ็š„ๆ–นๅ‘ๅˆคๆ–ญๆ˜ฏๅฆๅˆ็†
180
+ ```
181
+
182
+ **ๆญๅปบ็š„ๅ…ณ้”ฎไธๅœจไบŽไปฃ็ ๏ผˆไปฃ็ ไธๅˆฐ 50 ่กŒ๏ผ‰๏ผŒๅœจไบŽ Prompt ๅ†™ๅพ—ๅฅฝไธๅฅฝใ€‚** Prompt ๅ†ณๅฎšไบ† Agent ็š„ๅˆคๆ–ญ่ดจ้‡ใ€‚
183
+
184
+ ### 1.3 ๅฎŒๆ•ดไปฃ็ 
185
+
186
+ ```python
187
+ # direction_analyzer.py
188
+ # ============================================================
189
+ # ็ฑปๅž‹๏ผšAGENT๏ผˆไน™่ดŸ่ดฃ๏ผ‰
190
+ # ๅŠŸ่ƒฝ๏ผšLLM ่งฃๆž็ ”็ฉถๆ–นๅ‘ โ†’ ๅญ้ข†ๅŸŸ + ๆ–นๆณ•ๆ—่ฐฑ็ณป + GitHub ๆœ็ดข่ฏ
191
+ # ============================================================
192
+ import json
193
+ import re
194
+ from openai import OpenAI
195
+
196
+ # ้…็ฝฎ
197
+ DEEPSEEK_API_KEY = "sk-a115c4388d444cf688d323855edb36da"
198
+ DEEPSEEK_BASE_URL = "https://api.deepseek.com"
199
+ MODEL = "deepseek-chat"
200
+
201
+ client = OpenAI(api_key=DEEPSEEK_API_KEY, base_url=DEEPSEEK_BASE_URL)
202
+
203
+ # ============================================================
204
+ # System Prompt โ€”โ€” ่ฟ™ๆ˜ฏๆ•ดไธชๆจกๅ—็š„็ต้ญ‚๏ผŒ่ฐƒไผ˜ไธป่ฆ่ฐƒ่ฟ™้‡Œ
205
+ # ============================================================
206
+ DIRECTION_SYSTEM_PROMPT = """ไฝ ๆ˜ฏไธ€ไธช AI ็ ”็ฉถ้ข†ๅŸŸ็š„ๅฏผ่ˆชไธ“ๅฎถใ€‚ไฝ ็š„ไปปๅŠกๆ˜ฏ๏ผš็ป™ๅฎšไธ€็ฏ‡่ฎบๆ–‡็š„ๆ ‡้ข˜ๅ’Œๆ‘˜่ฆ๏ผŒๅธฎๅŠฉ็ ”็ฉถ่€…ๅฟซ้€Ÿไบ†่งฃ่ฏฅๆ–นๅ‘็š„ๅ‰ๆฒฟๅผ€ๆบ็”Ÿๆ€ใ€‚
207
+
208
+ ## ไฝ ็š„ๅˆ†ๆžๆก†ๆžถ
209
+
210
+ ### ๅฑ‚ๆฌก 1๏ผšๅญ้ข†ๅŸŸๅฎšไฝ
211
+ - ่ฟ™็ฏ‡่ฎบๆ–‡ๅฑžไบŽ AI/ML/CV/NLP ็š„ๅ“ชไธชๅ…ทไฝ“ๅญ้ข†ๅŸŸ๏ผŸ
212
+ - ไธ่ฆ็ป™ๅ‡บ็ฌผ็ปŸ็š„็ญ”ๆกˆ๏ผˆๅฆ‚ "Deep Learning"๏ผ‰๏ผŒ่ฆ็ป™ๅ‡บๅ…ทไฝ“ๅญ้ข†ๅŸŸ๏ผˆๅฆ‚ "Transformer-based Neural Machine Translation"๏ผ‰
213
+ - ่ฏดๆ˜Ž่ฏฅๅญ้ข†ๅŸŸๅœจ 2024-2025 ๅนด็š„ไธปๆต่ถ‹ๅŠฟ๏ผˆ1-2 ๅฅ่ฏ๏ผŒไธญๆ–‡๏ผ‰
214
+
215
+ ### ๅฑ‚ๆฌก 2๏ผšๆ–นๆณ•ๆ—่ฐฑ็ณป
216
+ - ่ฏฅๅญ้ข†ๅŸŸ็›ฎๅ‰ๆœ‰ๅ“ชไบ›ไธปๆตๆ–นๆณ•ๆ—/ๅ˜ไฝ“/ๆŠ€ๆœฏ่ทฏ็บฟ๏ผŸ
217
+ - ๆฏไธชๆ–นๆณ•ๆ—็š„ไปฃ่กจๅทฅไฝœๆ˜ฏไป€ไนˆ๏ผˆ่ฎบๆ–‡ๆˆ–็Ÿฅๅ้กน็›ฎๅ๏ผ‰๏ผŸ
218
+ - ่พ“ๅ‡บ 3-6 ไธชๆ–นๆณ•ๆ—๏ผŒไธ่ฆๅคชๅคš๏ผˆๅคชๅคšๆœ็ดขไผšๆ…ข๏ผ‰๏ผŒไนŸไธ่ฆๅคชๅฐ‘๏ผˆๅคชๅฐ‘่ฆ†็›–้ขไธๅคŸ๏ผ‰
219
+ - ๆฏไธชๆ–นๆณ•ๆ—ไธ€ๅฅ่ฏ่ฏดๆ˜Žๅ…ถๆ ธๅฟƒ็‰น็‚น
220
+
221
+ ### ๅฑ‚ๆฌก 3๏ผšๆœ็ดข็ญ–็•ฅ
222
+ - ไธบๆฏไธชๆ–นๆณ•ๆ—็”Ÿๆˆ 2-3 ไธช GitHub ๆœ็ดขๆŸฅ่ฏข
223
+ - ๆœ็ดขๆŸฅ่ฏขไฝฟ็”จ่‹ฑๆ–‡๏ผŒๆ ผๅผ๏ผšๆ–นๆณ•ๅ + ๆŠ€ๆœฏๅ…ณ้”ฎ่ฏ + ๆœ็ดข้™ๅฎš่ฏ
224
+ - ๆœ็ดข้™ๅฎš่ฏไฝฟ็”จ๏ผšimplementation, pytorch, official, tensorflow, code
225
+ - ้ขๅค–็”Ÿๆˆ 3 ไธช่ฆ†็›–ๅ…จ้ข†ๅŸŸ็š„ๅฎฝๆณ›ๆœ็ดข
226
+ - ๅฎฝๆณ›ๆœ็ดขไธ้™ๅฎšๅ…ทไฝ“ๆ–นๆณ•ๅ๏ผŒ็”จๅญ้ข†ๅŸŸๅ+้™ๅฎš่ฏ
227
+
228
+ ## ๆœ็ดขๆŸฅ่ฏข่ดจ้‡่ฆๆฑ‚
229
+ - ๅฅฝ็š„ๆŸฅ่ฏข็คบไพ‹๏ผš"vision transformer implementation pytorch"
230
+ - ๅฅฝ็š„ๆŸฅ่ฏข็คบไพ‹๏ผš"denoising diffusion probabilistic model official code"
231
+ - ๅ็š„ๆŸฅ่ฏข็คบไพ‹๏ผš"transformer"๏ผˆๅคชๅฎฝๆณ›๏ผ‰
232
+ - ๅ็š„ๆŸฅ่ฏข็คบไพ‹๏ผš"a novel approach for image classification using deep learning"๏ผˆๅคชๅคšๅบŸ่ฏ๏ผ‰
233
+
234
+ ## ่พ“ๅ‡บๆ ผๅผ๏ผˆไธฅๆ ผ JSON๏ผŒไธ่ฆไปปไฝ•้ขๅค–ๆ–‡ๅญ—๏ผŒไธ่ฆ markdown ไปฃ็ ๅ—ๆ ‡่ฎฐ๏ผ‰
235
+ {
236
+ "subfield": "ๅ…ทไฝ“ๅญ้ข†ๅŸŸๅ็งฐ",
237
+ "subfield_trend": "่ฏฅๅญ้ข†ๅŸŸ 2024-2025 ็š„ไธปๆต่ถ‹ๅŠฟ๏ผˆ1-2 ๅฅไธญๆ–‡๏ผ‰",
238
+ "method_families": [
239
+ {
240
+ "family_name": "ๆ–นๆณ•ๆ—็ฎ€็งฐ",
241
+ "description": "ไธ€ๅฅ่ฏๆ ธๅฟƒ็‰น็‚น",
242
+ "representative_work": "ไปฃ่กจ่ฎบๆ–‡ๆˆ–็Ÿฅๅ้กน็›ฎ",
243
+ "search_queries": ["ๆœ็ดข่ฏ1", "ๆœ็ดข่ฏ2", "ๆœ็ดข่ฏ3"]
244
+ }
245
+ ],
246
+ "broad_queries": ["ๅฎฝๆณ›ๆœ็ดข1", "ๅฎฝๆณ›ๆœ็ดข2", "ๅฎฝๆณ›ๆœ็ดข3"]
247
+ }
248
+ """
249
+
250
+ def analyze_direction(title: str, abstract: str) -> dict:
251
+ """ไปŽ่ฎบๆ–‡ๆ ‡้ข˜ๅ’Œๆ‘˜่ฆไธญ่งฃๆž็ ”็ฉถๆ–นๅ‘ๅนถ็”Ÿๆˆๆœ็ดข็ญ–็•ฅใ€‚
252
+
253
+ Args:
254
+ title: ่ฎบๆ–‡ๆ ‡้ข˜
255
+ abstract: ่ฎบๆ–‡ๆ‘˜่ฆ๏ผˆไผš่‡ชๅŠจๆˆชๆ–ญๅˆฐ 2500 ๅญ—็ฌฆ๏ผ‰
256
+
257
+ Returns:
258
+ dict: ๅŒ…ๅซ subfield, subfield_trend, method_families, broad_queries
259
+ """
260
+ # ๆˆชๆ–ญๆ‘˜่ฆ๏ผŒ้˜ฒๆญข่ถ…ๅ‡บ token ้™ๅˆถ
261
+ abstract_truncated = abstract[:2500] if abstract else "๏ผˆๆ‘˜่ฆไธๅฏ็”จ๏ผ‰"
262
+ title_clean = title.strip() if title else "๏ผˆๆ ‡้ข˜ไธๅฏ็”จ๏ผ‰"
263
+
264
+ user_prompt = f"""## ่ฎบๆ–‡ๆ ‡้ข˜
265
+ {title_clean}
266
+
267
+ ## ่ฎบๆ–‡ๆ‘˜่ฆ
268
+ {abstract_truncated}
269
+
270
+ ่ฏทๅˆ†ๆž่ฏฅ่ฎบๆ–‡็š„็ ”็ฉถๆ–นๅ‘ๅนถ็”Ÿๆˆๆœ็ดข็ญ–็•ฅใ€‚"""
271
+
272
+ response = client.chat.completions.create(
273
+ model=MODEL,
274
+ messages=[
275
+ {"role": "system", "content": DIRECTION_SYSTEM_PROMPT},
276
+ {"role": "user", "content": user_prompt},
277
+ ],
278
+ temperature=0.4, # 0.4 ็ป™ๆ–นๆณ•ๆ—ๆขณ็†ไฟ็•™ไธ€ไบ›ๅคšๆ ทๆ€ง
279
+ max_tokens=2000,
280
+ # ๅฆ‚ๆžœ DeepSeek ๆ”ฏๆŒ response_format๏ผŒๅ–ๆถˆไธ‹้ข่ฟ™่กŒ็š„ๆณจ้‡Šๅฏไปฅๆๅ‡ JSON ้ตๅฎˆ็އ
281
+ # response_format={"type": "json_object"},
282
+ )
283
+ raw = response.choices[0].message.content.strip()
284
+ return _parse_json_safe(raw)
285
+
286
+
287
+ def _parse_json_safe(raw: str) -> dict:
288
+ """ๅฎ‰ๅ…จ่งฃๆž LLM ่ฟ”ๅ›ž็š„ JSON๏ผŒๅค„็†ๅ„็งๅธธ่ง็š„ๆ ผๅผ้—ฎ้ข˜ใ€‚
289
+
290
+ LLM ๅฏ่ƒฝ่ฟ”ๅ›ž๏ผš
291
+ 1. ็บฏ JSON๏ผš{"subfield": "..."}
292
+ 2. markdown ๅŒ…๏ฟฝ๏ฟฝ๏ฟฝ๏ผš```json\n{...}\n```
293
+ 3. ๅธฆๅ‰็ผ€ๆ–‡ๅญ—๏ผš่ฟ™ๆ˜ฏๅˆ†ๆž็ป“ๆžœ๏ผš\n{...}
294
+ 4. ๆ ผๅผ้”™่ฏฏ๏ผš็ผบๅฐ‘ๅผ•ๅทใ€ๅฐพ้ƒจ้€—ๅท็ญ‰
295
+ """
296
+ # ็ญ–็•ฅ 1๏ผš็›ดๆŽฅ่งฃๆž
297
+ try:
298
+ return json.loads(raw)
299
+ except json.JSONDecodeError:
300
+ pass
301
+
302
+ # ็ญ–็•ฅ 2๏ผšๆๅ– markdown ไปฃ็ ๅ—
303
+ match = re.search(r'```(?:json)?\s*([\s\S]*?)```', raw)
304
+ if match:
305
+ try:
306
+ return json.loads(match.group(1).strip())
307
+ except json.JSONDecodeError:
308
+ pass
309
+
310
+ # ็ญ–็•ฅ 3๏ผšๆๅ–็ฌฌไธ€ไธช { ... } ๅฏน่ฑก๏ผˆ่ดชๅฉชๅŒน้…ๆœ€ๅค–ๅฑ‚๏ผ‰
311
+ match = re.search(r'\{[\s\S]*\}', raw)
312
+ if match:
313
+ try:
314
+ return json.loads(match.group(0))
315
+ except json.JSONDecodeError:
316
+ pass
317
+
318
+ # ็ญ–็•ฅ 4๏ผšๅ…จ้ƒจๅคฑ่ดฅ๏ผŒๆŠ›ๅ‡บๅผ‚ๅธธ๏ผˆๅธฆไธŠๅŽŸๅง‹ๅ†…ๅฎนๆ–นไพฟ่ฐƒ่ฏ•๏ผ‰
319
+ raise ValueError(
320
+ f"direction_analyzer: ๆ— ๆณ•่งฃๆž LLM ่ฟ”ๅ›ž็š„ JSONใ€‚\n"
321
+ f"ๅŽŸๅง‹่ฟ”ๅ›ž๏ผˆๅ‰ 500 ๅญ—็ฌฆ๏ผ‰:\n{raw[:500]}"
322
+ )
323
+
324
+
325
+ # ============================================================
326
+ # ่‡ชๆต‹ไปฃ็  โ€”โ€” ๅ†™ๅฎŒๅŽ่ฟ่กŒ python direction_analyzer.py ้ชŒ่ฏ
327
+ # ============================================================
328
+ if __name__ == "__main__":
329
+ # ๆต‹่ฏ•็”จไพ‹ 1๏ผšTransformer๏ผˆ็ปๅ…ธ่ฎบๆ–‡๏ผŒๆ–นๆณ•ๆ—ไธฐๅฏŒ๏ผ‰
330
+ print("=" * 60)
331
+ print("ๆต‹่ฏ• 1๏ผšTransformer ่ฎบๆ–‡")
332
+ print("=" * 60)
333
+ result = analyze_direction(
334
+ "Attention Is All You Need",
335
+ "The dominant sequence transduction models are based on complex recurrent "
336
+ "or convolutional neural networks... We propose a new simple network "
337
+ "architecture, the Transformer, based solely on attention mechanisms."
338
+ )
339
+ print(f"ๅญ้ข†ๅŸŸ: {result.get('subfield')}")
340
+ print(f"ๆ–นๆณ•ๆ—: {[mf.get('family_name') for mf in result.get('method_families', [])]}")
341
+ print(f"ๆ€ปๆœ็ดข่ฏๆ•ฐ: {sum(len(mf.get('search_queries', [])) for mf in result.get('method_families', [])) + len(result.get('broad_queries', []))}")
342
+
343
+ print()
344
+
345
+ # ๆต‹่ฏ•็”จไพ‹ 2๏ผšPaDiM๏ผˆๅทฅไธš็ผบ้™ทๆฃ€ๆต‹๏ผŒไฝ ็š„้ข†ๅŸŸ๏ผ‰
346
+ print("=" * 60)
347
+ print("ๆต‹่ฏ• 2๏ผšPaDiM ่ฎบๆ–‡๏ผˆๅทฅไธš็ผบ้™ทๆฃ€ๆต‹๏ผ‰")
348
+ print("=" * 60)
349
+ result = analyze_direction(
350
+ "PaDiM: a Patch Distribution Modeling Framework for Anomaly Detection and Localization",
351
+ "We present a new framework for anomaly detection and localization based on "
352
+ "patch distribution modeling. PaDiM uses pretrained convolutional neural networks "
353
+ "to extract patch features and models their distribution using multivariate "
354
+ "Gaussian distributions. It achieves state-of-the-art results on MVTec AD and "
355
+ "STC datasets for industrial defect detection."
356
+ )
357
+ print(f"ๅญ้ข†ๅŸŸ: {result.get('subfield')}")
358
+ print(f"่ถ‹ๅŠฟ: {result.get('subfield_trend')}")
359
+ print(f"ๆ–นๆณ•ๆ—ๆ•ฐ้‡: {len(result.get('method_families', []))}")
360
+ for mf in result.get("method_families", []):
361
+ print(f" - {mf.get('family_name')}: {mf.get('description')[:60]}...")
362
+ print(f"ๅฎฝๆณ›ๆœ็ดข่ฏ: {result.get('broad_queries', [])}")
363
+
364
+ print()
365
+
366
+ # ๆต‹่ฏ•็”จไพ‹ 3๏ผšEfficientAD๏ผˆ่พƒๆ–ฐ่ฎบๆ–‡๏ผŒๆœไธๅˆฐไปฃ็ ็š„ๆต‹่ฏ•๏ผ‰
367
+ print("=" * 60)
368
+ print("ๆต‹่ฏ• 3๏ผšEfficientAD ่ฎบๆ–‡๏ผˆ2023๏ผŒ่พƒๆ–ฐ๏ผ‰")
369
+ print("=" * 60)
370
+ result = analyze_direction(
371
+ "EfficientAD: Accurate Visual Anomaly Detection at Millisecond-Level Latencies",
372
+ "We propose EfficientAD, a lightweight anomaly detection method that achieves "
373
+ "millisecond-level latency. It uses a teacher-student architecture with a simple "
374
+ "student network that learns to mimic the teacher's features on normal images only."
375
+ )
376
+ print(f"ๅญ้ข†ๅŸŸ: {result.get('subfield')}")
377
+ print(f"ๆ–นๆณ•ๆ—: {[mf.get('family_name') for mf in result.get('method_families', [])]}")
378
+ ```
379
+
380
+ ### 1.3 ่ฐƒ่ฏ•ๆŒ‡ๅ—
381
+
382
+ **้—ฎ้ข˜ 1๏ผšLLM ่ฟ”ๅ›ž็š„ไธๆ˜ฏ JSON**
383
+ - ็—‡็Šถ๏ผš`ValueError: direction_analyzer: ๆ— ๆณ•่งฃๆž LLM ่ฟ”ๅ›ž็š„ JSON`
384
+ - ๆŽ’ๆŸฅ๏ผšๆ‰“ๅฐ `raw[:500]` ็œ‹็œ‹ LLM ๅˆฐๅบ•ๅ›žไบ†ไป€ไนˆ
385
+ - ไฟฎๅค๏ผšๅฆ‚ๆžœ LLM ้ข‘็น่ฟ”ๅ›ž้ž JSON๏ผŒๅœจ system prompt ็ฌฌไธ€ๅฅๅŠ  `ไฝ ๅฟ…้กปๅช่ฟ”ๅ›žไธ€ไธช JSON ๅฏน่ฑก๏ผŒไธ่ฆๆœ‰ไปปไฝ•่งฃ้‡Šๆ–‡ๅญ—ใ€‚`
386
+ - ๅค‡้€‰๏ผšๅฆ‚ๆžœ DeepSeek ๆ”ฏๆŒ `response_format={"type": "json_object"}` ๅฐฑๅผ€ๅฏ๏ผˆ่งไปฃ็ ไธญ็š„ๆณจ้‡Š่กŒ๏ผ‰
387
+
388
+ **้—ฎ้ข˜ 2๏ผšๆ–นๆณ•ๆ—ๅคชๅฐ‘๏ผˆๆฏ”ๅฆ‚ๅชๆœ‰ 2 ไธช๏ผ‰**
389
+ - ็—‡็Šถ๏ผšๆœ็ดข่ฆ†็›–้ขไธๅคŸ๏ผŒๆ‰พไธๅˆฐ่ถณๅคŸ็š„ไป“ๅบ“
390
+ - ไฟฎๅค๏ผšๅœจ system prompt ้‡ŒๆŠŠ"่พ“ๅ‡บ 3-6 ไธชๆ–นๆณ•ๆ—"ๆ”นๆˆ"ๅฟ…้กป่พ“ๅ‡บ่‡ณๅฐ‘ 4 ไธชๆ–นๆณ•ๆ—"
391
+
392
+ **้—ฎ้ข˜ 3๏ผšๆœ็ดข่ฏ่ดจ้‡ๅทฎ๏ผˆๆœไธๅˆฐไป“ๅบ“๏ผ‰**
393
+ - ็—‡็Šถ๏ผš็”Ÿๆˆ็š„ๆœ็ดข่ฏๅœจ GitHub ไธŠ่ฟ”ๅ›ž็ฉบ็ป“ๆžœๆˆ–ๆ— ๅ…ณ็ป“ๆžœ
394
+ - ๆต‹่ฏ•ๆ–นๆณ•๏ผšๅคๅˆถ็”Ÿๆˆ็š„ๆœ็ดข่ฏ๏ผŒๆ‰‹ๅŠจๅœจ GitHub ๆœ็ดขๆก†้‡Œๆœ๏ผŒ็œ‹็ป“ๆžœ่ดจ้‡
395
+ - ไฟฎๅค๏ผšๅœจ system prompt ็š„ๆœ็ดข็คบไพ‹้ƒจๅˆ†ๅขžๅŠ ๆ›ดๅคšๅฅฝ/ๅไพ‹ๅญ
396
+
397
+ **้—ฎ้ข˜ 4๏ผšๆ–นๆณ•ๆ—ๅ็งฐๅคชๅญฆๆœฏๅŒ–๏ผŒGitHub ๆœไธๅˆฐ**
398
+ - ็—‡็Šถ๏ผšๆ–นๆณ•ๆ—ๅซ "Gated Linear Unit Enhanced Multi-Head Self-Attention"๏ผŒGitHub ๆฒก็ป“ๆžœ
399
+ - ไฟฎๅค๏ผšๅœจ prompt ้‡Œ่ฆๆฑ‚ๆœ็ดข่ฏไฝฟ็”จๅธธ็”จ๏ฟฝ๏ฟฝ็ฎ€็งฐ่€Œ้ž่ฎบๆ–‡ๅ…จๅ
400
+
401
+ ---
402
+
403
+ ## ไบŒใ€Agent 2๏ผš`repo_evaluator.py` โ€” ๅฏๅค็Žฐๆ€ง + ้€‚้…ๅบฆ่ฏ„ไผฐ
404
+
405
+ > **็ฑปๅž‹๏ผšAgent** | ่ฟ™ๆ˜ฏไฝ ่ดŸ่ดฃ็š„็ฌฌไบŒไธช Agent
406
+ > **ๆ ธๅฟƒๅˆคๆ–ญ**๏ผš็ป™ๅฎšไธ€ไธชไป“ๅบ“็š„ README + ไพ่ต–ๆ–‡ไปถ๏ผŒLLM ๅˆคๆ–ญๅฎƒ่ƒฝไธ่ƒฝ่ท‘ใ€้€‚ไธ้€‚ๅˆๆ‹ฟๆฅๅšๅฏนๆฏ”ๅฎž้ชŒ
407
+
408
+ ### 2.1 ่ฟ™ไธช Agent ่ฆ่งฃๅ†ณไป€ไนˆ้—ฎ้ข˜
409
+
410
+ ๅฝ“ไฝ ้€š่ฟ‡ Agent 1๏ผˆๆ–นๅ‘่งฃๆž๏ผ‰ๅ’Œ็”ฒ็š„ไธคไธช Workflow๏ผˆๆœ็ดข+ๆŠ“ๅ–๏ผ‰ๆ‹ฟๅˆฐไบ† 5 ไธชๅ€™้€‰ไป“ๅบ“ๅŽ๏ผŒ้œ€่ฆๅˆคๆ–ญไธคไธชๅฑ‚้ข๏ผš
411
+ 1. **่ƒฝไธ่ƒฝ่ท‘๏ผŸ**๏ผˆๅฏๅค็Žฐๆ€ง 5 ็ปดๅบฆ๏ผŒ80 ๅˆ†๏ผ‰โ€”โ€” ่ฟ™ๆ˜ฏ"ๆŠ€ๆœฏๅฏ่กŒๆ€ง"
412
+ 2. **้€‚ไธ้€‚ๅˆ็›ดๆŽฅๆ‹ฟๆฅๅšๅฏนๆฏ”ๅฎž้ชŒ๏ผŸ**๏ผˆ้€‚้…ๅบฆ 1 ็ปดๅบฆ๏ผŒ20 ๅˆ†๏ผ‰โ€”โ€” ่ฟ™ๆ˜ฏ"่ฎบๆ–‡ๅฎž็”จๆ€ง"
413
+
414
+ ไธคไธชๅฑ‚้ขๅฟ…้กปๅˆ†ๅผ€ๆ‰“ๅˆ†ใ€‚ๆฏ”ๅฆ‚ anomalib๏ผˆOpenVINO ็š„ๅผ‚ๅธธๆฃ€ๆต‹ๅบ“๏ผ‰๏ผš
415
+ - ๅฏๅค็Žฐๆ€ง่ฏ„ๅˆ†้ซ˜๏ผˆREADME ๅฎŒๅ–„ใ€ๆœ‰ requirements.txtใ€pip install ๅณ็”จ๏ผ‰
416
+ - ้€‚้…ๅบฆ่ฏ„ๅˆ†้ซ˜๏ผˆๆœ‰ๆ ‡ๅ‡† benchmark ่„šๆœฌใ€ๆ”ฏๆŒ MVTec AD ๆ•ฐๆฎ้›†ใ€ๆœ‰้ข„่ฎญ็ปƒๆƒ้‡๏ผ‰
417
+
418
+ ่€Œไธ€ไธช"ๅชๆœ‰ๆŽจ็†่„šๆœฌ็š„ไธชไบบ้กน็›ฎ"๏ผš
419
+ - ๅฏๅค็Žฐๆ€งๅฏ่ƒฝ่ฟ˜่กŒ๏ผˆ่ƒฝ่ท‘้€š demo๏ผ‰
420
+ - ้€‚้…ๅบฆไธ€ๅฎšไฝŽ๏ผˆๆฒกๆœ‰่ฎญ็ปƒ่„šๆœฌๅ’Œๆ ‡ๅ‡†ๅŒ–่ฏ„ไผฐ๏ผŒไธ่ƒฝ็”จไบŽ่ท‘ๅฏนๆฏ”ๅฎž้ชŒ๏ผ‰
421
+
422
+ ### 2.2 ่ฟ™ไธช Agent ็š„ๆญๅปบๆ€่ทฏ
423
+
424
+ ```
425
+ ๆญฅ้ชค 1๏ผšๅ†™ System Prompt๏ผŒๅฎšไน‰ๅ…ญ็ปดๅบฆ่ฏ„ๅˆ†ๆก†ๆžถ + ๆฏไธช็ปดๅบฆ็š„ๅˆ†ๆ•ฐ้”š็‚น
426
+ ๆญฅ้ชค 2๏ผšๅ†™ User Prompt ๆจกๆฟ๏ผˆๅกซๅ…ฅ README + ไพ่ต–ๆ–‡ไปถ + ไป“ๅบ“ๅ…ƒไฟกๆฏ๏ผ‰
427
+ ๆญฅ้ชค 3๏ผš่ฐƒ OpenAI SDK ๅ‘้€็ป™ DeepSeek
428
+ ๆญฅ้ชค 4๏ผš่งฃๆž LLM ่ฟ”ๅ›ž็š„ JSON
429
+ ๆญฅ้ชค 5๏ผš็”จ 5 ไธชไฝ ่‡ชๅทฑ็†Ÿๆ‚‰็š„ไป“ๅบ“ๆต‹่ฏ•๏ผˆ2 ๅฅฝ + 2 ๅทฎ + 1 ไธญ๏ผ‰๏ผŒๅฏนๆฏ” LLM ่ฏ„ๅˆ† vs ไฝ ็š„ไบบๅทฅ่ฏ„ๅˆ†
430
+ ```
431
+
432
+ **่ฟ™ไธช Agent ๆœ€้šพ็š„้ƒจๅˆ†**๏ผš่ฎฉ LLM ๅŒบๅˆ†"่ƒฝ่ท‘"ๅ’Œ"้€‚ๅˆ่ท‘ๅฏนๆฏ”ๅฎž้ชŒ"ใ€‚ๅพˆๅคšๅˆๅญฆ่€…ไผšๆŠŠ่ฟ™ไธคไธชๆฆ‚ๅฟตๆททๆท†ใ€‚ๅฆ‚ๆžœไฝ ็š„ prompt ๆฒกๅ†™ๆธ…ๆฅš๏ผŒLLM ไนŸไผšๆททๆท†ใ€‚่ฐƒ่ฏ•ๆ—ถ้‡็‚นๆฃ€ๆŸฅ๏ผšREADME ๅฅฝไฝ†ๅชๆœ‰ๆŽจ็†็š„ไป“ๅบ“๏ผŒ้€‚้…ๅบฆๆ˜ฏๅฆ็œŸ็š„ไฝŽไบŽ 10 ๅˆ†ใ€‚
433
+
434
+ ### 2.3 ๅฎŒๆ•ดไปฃ็ 
435
+
436
+ ```python
437
+ # repo_evaluator.py
438
+ # ============================================================
439
+ # ็ฑปๅž‹๏ผšAGENT๏ผˆไน™่ดŸ่ดฃ๏ผ‰
440
+ # ๅŠŸ่ƒฝ๏ผšLLM ๅ…ญ็ปดๅบฆ่ฏ„ๅˆ†๏ผˆๅฏๅค็Žฐๆ€ง 5 ็ปด + ๅฏนๆฏ”ๅฎž้ชŒ้€‚้…ๅบฆ 1 ็ปด๏ผ‰
441
+ # ============================================================
442
+ import json
443
+ import re
444
+ from openai import OpenAI
445
+
446
+ DEEPSEEK_API_KEY = "sk-a115c4388d444cf688d323855edb36da"
447
+ DEEPSEEK_BASE_URL = "https://api.deepseek.com"
448
+ MODEL = "deepseek-chat"
449
+
450
+ client = OpenAI(api_key=DEEPSEEK_API_KEY, base_url=DEEPSEEK_BASE_URL)
451
+
452
+ # ============================================================
453
+ # System Prompt
454
+ # ============================================================
455
+ EVAL_SYSTEM_PROMPT = """ไฝ ๆ˜ฏไธ€ไธช AI ่ฎบๆ–‡ไปฃ็ ่ฏ„ไผฐไธ“ๅฎถใ€‚ไฝ ้œ€่ฆๅŒๆ—ถ่ฏ„ไผฐไธ€ไธช GitHub ไป“ๅบ“็š„ไธคไธชๆ–น้ข๏ผš
456
+ - **ๅฏๅค็Žฐๆ€ง**๏ผš่ฟ™ไธชไป“ๅบ“็š„ไปฃ็ ่ƒฝไธ่ƒฝ่ขซไป–ไบบๆˆๅŠŸ่ท‘้€š๏ผŸ
457
+ - **ๅฏนๆฏ”ๅฎž้ชŒ้€‚้…ๅบฆ**๏ผš่ฟ™ไธชไป“ๅบ“้€‚ไธ้€‚ๅˆ็›ดๆŽฅๆ‹ฟๆฅๅ†™่ฎบๆ–‡ๅšๅฏนๆฏ”ๅฎž้ชŒ๏ผˆbaseline / comparison๏ผ‰๏ผŸ
458
+
459
+ ## ่ฏ„ไผฐๆก†ๆžถ๏ผˆๅ…ญ็ปดๅบฆ๏ผŒๆ€ปๅˆ† 100๏ผ‰
460
+
461
+ ### ๅฏๅค็Žฐๆ€ง๏ผˆไบ”็ปดๅบฆ๏ผŒๅˆ่ฎก 80 ๅˆ†๏ผ‰
462
+
463
+ 1. **็Žฏๅขƒ้…็ฝฎๅฎŒๆ•ดๆ€ง๏ผˆ0-15 ๅˆ†๏ผ‰**
464
+ - 15 ๅˆ†๏ผšๆไพ›ไบ† requirements.txt / environment.yml๏ผŒๆ‰€ๆœ‰ไพ่ต–็‰ˆๆœฌๅทๆ˜Ž็กฎ
465
+ - 8-14 ๅˆ†๏ผšๆœ‰ไพ่ต–ๆ–‡ไปถไฝ†้ƒจๅˆ†็‰ˆๆœฌๅทไฝฟ็”จ่Œƒๅ›ด๏ผˆ>=, ~=๏ผ‰ๆˆ–็ผบๅคฑ
466
+ - 0-7 ๅˆ†๏ผšๆฒกๆœ‰ไพ่ต–ๆ–‡ไปถๆˆ–ไธฅ้‡ไธๅฎŒๆ•ด
467
+
468
+ 2. **ๆ–‡ๆกฃ่ดจ้‡๏ผˆ0-20 ๅˆ†๏ผ‰**
469
+ - 16-20 ๅˆ†๏ผšๆœ‰ๆธ…ๆ™ฐ็š„ๅฎ‰่ฃ…ๆญฅ้ชคใ€่ฎญ็ปƒๅ‘ฝไปคใ€ๆŽจ็†ๅ‘ฝไปคใ€ๆ•ฐๆฎ้›†่Žทๅ–ๆ–นๅผ
470
+ - 8-15 ๅˆ†๏ผšๆœ‰ๅŸบๆœฌ่ฏดๆ˜Žไฝ†ๅ…ณ้”ฎๆญฅ้ชค็ผบๅคฑ๏ผˆๅฆ‚ๆฒก่ฏดๆ•ฐๆฎ้›†ๅœจๅ“ชไธ‹่ฝฝ๏ผ‰
471
+ - 0-7 ๅˆ†๏ผšREADME ๅชๆœ‰้กน็›ฎ็ฎ€ไป‹๏ผŒๆฒกๆœ‰ๅฏๆ‰ง่กŒๅ‘ฝไปค
472
+
473
+ 3. **ไปฃ็ ๅฏ็”จๆ€ง๏ผˆ0-20 ๅˆ†๏ผ‰**
474
+ - 16-20 ๅˆ†๏ผš่ฎญ็ปƒ่„šๆœฌ + ๆŽจ็†่„šๆœฌ + ้…็ฝฎๆ–‡ไปถ + ๆจกๅž‹ๅฎšไน‰ๅ…ฅๅฃๅฎŒๆ•ด
475
+ - 8-15 ๅˆ†๏ผšๆœ‰ๆ ธๅฟƒไปฃ็ ไฝ†็ผบๅฐ‘ๅ…ณ้”ฎ็ป„ไปถ๏ผˆๅฆ‚ๅชๆœ‰ๆŽจ็†ๆฒกๆœ‰่ฎญ็ปƒ๏ผ‰
476
+ - 0-7 ๅˆ†๏ผšไปฃ็ ๆฎ‹็ผบ๏ผŒๅชๆœ‰้ชจๆžถๆˆ– shell ่„šๆœฌ
477
+
478
+ 4. **็คพๅŒบๆดป่ทƒๅบฆ๏ผˆ0-10 ๅˆ†๏ผ‰**
479
+ - 8-10 ๅˆ†๏ผšStar > 500๏ผŒๆœ€่ฟ‘ๅŠๅนดๆœ‰ commit๏ผŒIssue ๆœ‰ไบบๅ›žๅค
480
+ - 4-7 ๅˆ†๏ผšๆœ‰ไธ€ๅฎšๅ…ณๆณจๅบฆไฝ†ๆ›ดๆ–ฐไธ้ข‘็น
481
+ - 0-3 ๅˆ†๏ผš้•ฟๆœŸไธๆ›ดๆ–ฐ๏ผŒStar < 10
482
+
483
+ 5. **ไพ่ต–ๅฅๅบทๅบฆ๏ผˆ0-15 ๅˆ†๏ผ‰**
484
+ - 12-15 ๅˆ†๏ผšไพ่ต–ๅ…จๆ˜ฏไธปๆตๆดป่ทƒๅŒ…๏ผŒ็‰ˆๆœฌ็›ธไบ’ๅ…ผๅฎน
485
+ - 6-11 ๅˆ†๏ผšๆœ‰ๅฐ‘้‡่ฟ‡ๆ—ถไพ่ต–ไฝ†ไธๅฝฑๅ“ๅฎ‰่ฃ…
486
+ - 0-5 ๅˆ†๏ผšไพ่ต–ๆถ‰ๅŠๅทฒๅœๆญข็ปดๆŠค็š„ๅŒ…๏ผˆๅฆ‚ TensorFlow 1.xใ€Python 2๏ผ‰๏ผŒๅทฒ็Ÿฅๆ— ๆณ•ๅœจ็Žฐไปฃ็Žฏๅขƒๅฎ‰่ฃ…
487
+
488
+ ### ๅฏนๆฏ”ๅฎž้ชŒ้€‚้…ๅบฆ๏ผˆไธ€็ปดๅบฆ๏ผŒ20 ๅˆ†๏ผ‰
489
+
490
+ 6. **ๅฏนๆฏ”ๅฎž้ชŒ้€‚้…ๅบฆ๏ผˆ0-20 ๅˆ†๏ผ‰**
491
+ - 16-20 ๅˆ†๏ผšๆไพ›ๆ ‡ๅ‡† benchmark ่ฏ„ไผฐ่„šๆœฌ + ้ข„่ฎญ็ปƒๆƒ้‡ไธ‹่ฝฝ + ๅ…ฌๅผ€ๆ•ฐๆฎ้›†ๆ”ฏๆŒ๏ผŒๅฏ็›ดๆŽฅ่ท‘ๅ‡บ่ฎบๆ–‡ๅธธ็”จๆŒ‡ๆ ‡๏ผŒๅฏไปฅๆ— ็ผๆ’ๅ…ฅ่ฎบๆ–‡็š„ Experiment section
492
+ - 10-15 ๅˆ†๏ผšๆœ‰่ฎญ็ปƒ่„šๆœฌๅ’Œ่ฏ„ไผฐไปฃ็ ๏ผŒไฝ†้œ€่ฆ่‡ชๅทฑๅ‡†ๅค‡ๆ•ฐๆฎๆˆ–้€‚้…ๆ•ฐๆฎ้›†ๆ ผๅผ
493
+ - 0-9 ๅˆ†๏ผšๅชๆœ‰ๆŽจ็† demoใ€ไฝฟ็”จ็งๆœ‰ๆ•ฐๆฎ้›†ใ€ๆฒกๆœ‰ๆ ‡ๅ‡†ๅŒ–่ฏ„ไผฐๆต็จ‹ใ€ๆˆ–ๆฒกๆœ‰้ข„่ฎญ็ปƒๆƒ้‡
494
+
495
+ ## ่พ“ๅ‡บๆ ผๅผ๏ผˆไธฅๆ ผ JSON๏ผ‰
496
+ {
497
+ "reproducibility_score": 80,
498
+ "benchmark_fitness_score": 20,
499
+ "overall_score": 100,
500
+ "verdict": "reproducible",
501
+ "env_score": 15,
502
+ "doc_score": 20,
503
+ "code_score": 20,
504
+ "community_score": 10,
505
+ "dep_score": 15,
506
+ "benchmark_score": 20,
507
+ "reasoning": "200ๅญ—ไปฅๅ†…็š„ไธญๆ–‡ๅˆ†ๆž๏ผŒๅฟ…้กปๅŒบๅˆ†'่ƒฝไธ่ƒฝ่ท‘'ๅ’Œ'้€‚ไธ้€‚ๅˆ่ท‘ๅฏนๆฏ”ๅฎž้ชŒ'ไธคไธชๅฑ‚้ข",
508
+ "risks": ["้ฃŽ้™ฉ1", "้ฃŽ้™ฉ2"],
509
+ "benchmark_readiness": "ready",
510
+ "suggested_use": "ๅฏไปฅ็›ดๆŽฅไฝœไธบๅฏนๆฏ”ๅฎž้ชŒ baseline"
511
+ }
512
+
513
+ ## ๅˆคๅฎš่ง„ๅˆ™
514
+ - overall_score >= 70 โ†’ verdict = "reproducible"
515
+ - 40 <= overall_score < 70 โ†’ verdict = "partially"
516
+ - overall_score < 40 โ†’ verdict = "not_reproducible"
517
+
518
+ - benchmark_score >= 16 โ†’ benchmark_readiness = "ready"
519
+ - 10 <= benchmark_score < 16 โ†’ benchmark_readiness = "partial"
520
+ - benchmark_score < 10 โ†’ benchmark_readiness = "not_ready"
521
+
522
+ ## bench_readiness ไธŽ suggested_use ็š„ๅฏนๅบ”ๅ…ณ็ณป
523
+ - "ready" โ†’ "ๅฏไปฅ็›ดๆŽฅไฝœไธบๅฏนๆฏ”ๅฎž้ชŒ baseline"
524
+ - "partial" โ†’ "้œ€่ฆๅฐ‘้‡ไฟฎๆ”นๅŽๅฏ็”จไบŽๅฏนๆฏ”ๅฎž้ชŒ"
525
+ - "not_ready" โ†’ "ไป…้€‚ๅˆๅ‚่€ƒไปฃ็ ๅฎž็Žฐ๏ผŒไธ้€‚ๅˆ็›ดๆŽฅ่ท‘ๅฏนๆฏ”ๅฎž้ชŒ"
526
+ """
527
+
528
+
529
+ def evaluate_repo(
530
+ repo: dict,
531
+ readme: str | None,
532
+ dependencies: dict[str, str],
533
+ method_family: str = "",
534
+ ) -> dict:
535
+ """่ฏ„ไผฐๅ•ไธชไป“ๅบ“็š„ๅฏๅค็Žฐๆ€งๅ’Œๅฏนๆฏ”ๅฎž้ชŒ้€‚้…ๅบฆใ€‚
536
+
537
+ Args:
538
+ repo: ไป“ๅบ“ๅ…ƒไฟกๆฏ dict๏ผˆๆฅ่‡ช repo_searcher๏ผ‰๏ผŒๅŒ…ๅซ full_name, html_url, stars ็ญ‰
539
+ readme: README ๆ–‡ไปถๅ…จๆ–‡๏ผˆๆˆ– None๏ผ‰
540
+ dependencies: {ๆ–‡ไปถๅ: ๆ–‡ไปถๅ†…ๅฎน} ็š„ dict
541
+ method_family: ่ฏฅไป“ๅบ“ๆ‰€ๅฑž็š„ๆ–นๆณ•ๆ—ๅ็งฐ๏ผˆ็ฉบๅญ—็ฌฆไธฒ่กจ็คบๆœชๅฝ’็ฑป๏ผ‰
542
+
543
+ Returns:
544
+ dict: ๅ…ญ็ปดๅบฆ่ฏ„ๅˆ†็ป“ๆžœ
545
+ """
546
+ # ๆž„้€  README ่พ“ๅ…ฅ
547
+ readme_text = (readme or "README ๆœชๆ‰พๅˆฐ")[:2500]
548
+
549
+ # ๆž„้€ ไพ่ต–ๆ–‡ไปถ่พ“ๅ…ฅ
550
+ if dependencies:
551
+ deps_parts = []
552
+ for fname, content in dependencies.items():
553
+ truncated = content[:1000] if content else "(็ฉบๆ–‡ไปถ)"
554
+ deps_parts.append(f"### {fname}\n```\n{truncated}\n```")
555
+ deps_text = "\n\n".join(deps_parts)
556
+ else:
557
+ deps_text = "ๆœชๆ‰พๅˆฐไปปไฝ•ไพ่ต–ๆ–‡ไปถ๏ผˆrequirements.txt / environment.yml ็ญ‰๏ผ‰"
558
+
559
+ # ๆ–นๆณ•ๆ—ๆ็คบ
560
+ family_hint = f"\n- ๆ–นๆณ•ๆ—: {method_family}" if method_family else ""
561
+
562
+ user_prompt = f"""## ไป“ๅบ“ๅŸบๆœฌไฟกๆฏ
563
+ - ๅ็งฐ: {repo.get('full_name', 'ๆœช็Ÿฅ')}
564
+ - URL: {repo.get('html_url', '')}
565
+ - Stars: {repo.get('stars', 0)}
566
+ - ๆ่ฟฐ: {repo.get('description', 'ๆ— ๆ่ฟฐ')}
567
+ - ๆœ€ๅŽๆ›ดๆ–ฐ: {repo.get('updated_at', 'ๆœช็Ÿฅ')}
568
+ - ไธป่ฆ่ฏญ่จ€: {repo.get('language', 'ๆœช็Ÿฅ')}{family_hint}
569
+
570
+ ## README ๅ†…ๅฎน
571
+ {readme_text}
572
+
573
+ ## ไพ่ต–ๆ–‡ไปถ
574
+ {deps_text}
575
+
576
+ ่ฏท่ฏ„ไผฐ่ฏฅไป“ๅบ“็š„ๅฏๅค็Žฐๆ€งๅ’Œๅฏนๆฏ”ๅฎž้ชŒ้€‚้…ๅบฆใ€‚"""
577
+
578
+ response = client.chat.completions.create(
579
+ model=MODEL,
580
+ messages=[
581
+ {"role": "system", "content": EVAL_SYSTEM_PROMPT},
582
+ {"role": "user", "content": user_prompt},
583
+ ],
584
+ temperature=0.3, # ่ฏ„ไผฐไปปๅŠก็”จไฝŽๆธฉๅบฆ
585
+ max_tokens=2000,
586
+ )
587
+ raw = response.choices[0].message.content.strip()
588
+ return _parse_json_safe(raw)
589
+
590
+
591
+ def _parse_json_safe(raw: str) -> dict:
592
+ """ๅฎ‰ๅ…จ JSON ่งฃๆž๏ผŒๅคš็ง็ญ–็•ฅๅฎน้”™"""
593
+ try:
594
+ return json.loads(raw)
595
+ except json.JSONDecodeError:
596
+ pass
597
+
598
+ match = re.search(r'```(?:json)?\s*([\s\S]*?)```', raw)
599
+ if match:
600
+ try:
601
+ return json.loads(match.group(1).strip())
602
+ except json.JSONDecodeError:
603
+ pass
604
+
605
+ match = re.search(r'\{[\s\S]*\}', raw)
606
+ if match:
607
+ try:
608
+ return json.loads(match.group(0))
609
+ except json.JSONDecodeError:
610
+ pass
611
+
612
+ raise ValueError(
613
+ f"repo_evaluator: ๆ— ๆณ•่งฃๆž LLM ่ฟ”ๅ›ž็š„ JSONใ€‚\n"
614
+ f"ๅŽŸๅง‹่ฟ”ๅ›ž๏ผˆๅ‰ 500 ๅญ—็ฌฆ๏ผ‰:\n{raw[:500]}"
615
+ )
616
+
617
+
618
+ # ============================================================
619
+ # ่‡ชๆต‹ไปฃ็  โ€”โ€” ๅ†™ๅฎŒๅŽ่ฟ่กŒ python repo_evaluator.py ้ชŒ่ฏ
620
+ # ============================================================
621
+ if __name__ == "__main__":
622
+ # ๆต‹่ฏ• 1๏ผšanomalib๏ผˆๅทฅไธš็ผบ้™ทๆฃ€ๆต‹้ข†ๅŸŸ็š„ๆ˜Žๆ˜Ÿ้กน็›ฎ๏ผŒๅบ”่ฏฅๆ˜ฏ้ซ˜ๅˆ†๏ผ‰
623
+ print("=" * 60)
624
+ print("ๆต‹่ฏ• 1๏ผšOpenVINO anomalib๏ผˆ้ข„ๆœŸ๏ผšๅฏๅค็Žฐๆ€ง้ซ˜ + ้€‚้…ๅบฆ้ซ˜๏ผ‰")
625
+ print("=" * 60)
626
+ mock_repo = {
627
+ "full_name": "openvinotoolkit/anomalib",
628
+ "html_url": "https://github.com/openvinotoolkit/anomalib",
629
+ "stars": 4000,
630
+ "description": "An anomaly detection library comprising state-of-the-art algorithms and features such as experiment management, hyper-parameter optimization, and edge inference.",
631
+ "updated_at": "2026-01-15T00:00:00Z",
632
+ "language": "Python",
633
+ }
634
+ mock_readme = """# Anomalib
635
+ A library for benchmarking, developing and deploying deep learning based anomaly detection algorithms.
636
+
637
+ ## Installation
638
+ ```
639
+ pip install anomalib
640
+ ```
641
+
642
+ ## Training
643
+ ```python
644
+ from anomalib.engine import Engine
645
+ engine = Engine()
646
+ engine.train()
647
+ ```
648
+
649
+ ## Supported Datasets
650
+ - MVTec AD
651
+ - BTAD
652
+ - KSDD2
653
+
654
+ ## Benchmarking
655
+ Use `tools/benchmark.py` for standardized evaluation across all models.
656
+ """
657
+ mock_deps = {"requirements.txt": "torch>=1.10\npytorch-lightning>=1.7\nopencv-python\nnumpy\nscikit-learn"}
658
+ result = evaluate_repo(mock_repo, mock_readme, mock_deps, "Embedding-based")
659
+ print(f" ็ปผๅˆ: {result.get('overall_score')}/100")
660
+ print(f" ๅฏๅค็Žฐๆ€ง: {result.get('reproducibility_score')}/80")
661
+ print(f" ้€‚้…ๅบฆ: {result.get('benchmark_fitness_score')}/20")
662
+ print(f" ๅˆคๅฎš: {result.get('verdict')} | ๅฎž้ชŒๅฐฑ็ปช: {result.get('benchmark_readiness')}")
663
+
664
+ print()
665
+
666
+ # ๆต‹่ฏ• 2๏ผšไธ€ไธชๅชๆœ‰ๆŽจ็† demo ็š„ไธชไบบ้กน็›ฎ๏ผˆๅบ”่ฏฅๆ˜ฏไฝŽ้€‚้…ๅบฆ๏ผ‰
667
+ print("=" * 60)
668
+ print("ๆต‹่ฏ• 2๏ผšไป…ๆŽจ็†็š„ไธชไบบ้กน็›ฎ๏ผˆ้ข„ๆœŸ๏ผšๅฏๅค็Žฐๆ€งไธญ็ญ‰ + ้€‚้…ๅบฆไฝŽ๏ผ‰")
669
+ print("=" * 60)
670
+ mock_repo2 = {
671
+ "full_name": "someone/anomaly-detection-demo",
672
+ "html_url": "https://github.com/someone/anomaly-detection-demo",
673
+ "stars": 15,
674
+ "description": "A simple demo of anomaly detection using pretrained models.",
675
+ "updated_at": "2024-03-01T00:00:00Z",
676
+ "language": "Python",
677
+ }
678
+ mock_readme2 = """# Anomaly Detection Demo
679
+ A simple inference demo.
680
+
681
+ ## Usage
682
+ ```
683
+ python demo.py --image path/to/image.jpg
684
+ ```
685
+
686
+ Pretrained weights: download from Google Drive (link may expire).
687
+ """
688
+ mock_deps2 = {}
689
+ result2 = evaluate_repo(mock_repo2, mock_readme2, mock_deps2, "Embedding-based")
690
+ print(f" ็ปผๅˆ: {result2.get('overall_score')}/100")
691
+ print(f" ้€‚้…ๅบฆ: {result2.get('benchmark_fitness_score')}/20")
692
+ print(f" ๅˆคๅฎš: {result2.get('verdict')} | ๅฎž้ชŒๅฐฑ็ปช: {result2.get('benchmark_readiness')}")
693
+ print(f" ๅปบ่ฎฎ: {result2.get('suggested_use')}")
694
+ ```
695
+
696
+ ### 2.3 ่ฐƒ่ฏ•ๆŒ‡ๅ—
697
+
698
+ **้—ฎ้ข˜ 1๏ผšๅฏๅค็Žฐๆ€งๅ’Œ้€‚้…ๅบฆๅˆ†ๆ•ฐๆ€ปๆ˜ฏ้ซ˜ๅบฆ็›ธๅ…ณ๏ผˆๆฒกๅˆ†ๅผ€๏ผ‰**
699
+ - ็—‡็Šถ๏ผš`reproducibility_score` ้ซ˜ โ†’ `benchmark_fitness_score` ไนŸ้ซ˜๏ผŒไฝŽๅŒ็†
700
+ - ๅŽŸๅ› ๏ผšLLM ๆฒก็†่งฃ่ฟ™ไธคไธชๆ˜ฏ็‹ฌ็ซ‹็š„็ปดๅบฆ
701
+ - ไฟฎๅค๏ผšๅœจ system prompt ไธญๅŠ ไธ€ๅฅๆ˜Ž็กฎ็š„ๅไพ‹๏ผšใ€Œไพ‹ๅฆ‚๏ผŒไธ€ไธชไป“ๅบ“ๅฏไปฅ README ๅพˆๅฎŒๅ–„๏ผˆๆ–‡ๆกฃ 20 ๅˆ†๏ผ‰ไฝ†ๅชๆไพ›ๆŽจ็† demo ๆฒกๆœ‰่ฎญ็ปƒ๏ผˆ้€‚้…ๅบฆ 5 ๅˆ†๏ผ‰ใ€‚ไธคไธช็ปดๅบฆๅฟ…้กป็‹ฌ็ซ‹ๅˆคๆ–ญใ€‚ใ€
702
+
703
+ **้—ฎ้ข˜ 2๏ผš่ฏ„ๅˆ†ๅคช้ซ˜ๆˆ–ๅคชไฝŽ๏ผˆไธ็ฌฆๅˆๅธธ่ฏ†๏ผ‰**
704
+ - ็—‡็Šถ๏ผšๆ‰€ๆœ‰ไป“ๅบ“้ƒฝๅœจ 80+ ๆˆ– 30-
705
+ - ๆต‹่ฏ•๏ผšๆ‰‹้€‰ 3 ไธชไฝ ่‡ชๅทฑ็†Ÿๆ‚‰็š„ไป“ๅบ“๏ผˆไธ€ไธชๅฅฝไธ€ไธชๅไธ€ไธชไธญ้—ด๏ผ‰๏ผŒไบบๅทฅๆ‰“ๅˆ†๏ผŒๅฏนๆฏ” LLM ๆ‰“ๅˆ†
706
+ - ไฟฎๅค๏ผšๅœจ system prompt ไธญ่ฐƒๆ•ดๅˆ†ๆ•ฐๆฎต็š„ๆ่ฟฐๆˆ–ๅขžๅŠ ๅ‚่€ƒ้”š็‚น
707
+
708
+ **้—ฎ้ข˜ 3๏ผšreasoning ๅคช็ฌผ็ปŸ๏ผˆ"่ฏฅไป“ๅบ“่ดจ้‡่พƒ้ซ˜"๏ผ‰**
709
+ - ็—‡็Šถ๏ผšๅˆ†ๆžๆฒกๆœ‰ๅผ•็”จๅ…ทไฝ“ๅ†…ๅฎน
710
+ - ไฟฎๅค๏ผšๅœจ system prompt ็š„ reasoning ๆ่ฟฐไธญๅŠ ใ€Œๅฟ…้กปๅผ•็”จ README ๆˆ–ไพ่ต–ๆ–‡ไปถไธญ็š„ๅ…ทไฝ“ๅ†…ๅฎนไฝœไธบ่ฏๆฎใ€
711
+
712
+ ---
713
+
714
+ ## ไธ‰ใ€่ฐƒๅบฆๅ™จ๏ผš`run.py` โ€” ๆŠŠ Agent ๅ’Œ Workflow ไธฒ่ตทๆฅ
715
+
716
+ > **็ฑปๅž‹๏ผš่ฐƒๅบฆๅ™จ๏ผˆ่ƒถๆฐดไปฃ็ ๏ผ‰** | ๆ—ขไธๆ˜ฏ Agent ไนŸไธๆ˜ฏ Workflow
717
+ > **ๆ ธๅฟƒ้€ป่พ‘**๏ผšๆŒ‰้กบๅบ่ฐƒ็”จ Workflow๏ผˆ็”ฒ๏ผ‰ๅ’Œ Agent๏ผˆไฝ ๏ผ‰๏ผŒๅœจๅฎƒไปฌไน‹้—ดไผ ้€’ๆ•ฐๆฎ
718
+
719
+ ### 3.1 ่ฟ™ไธช่ฐƒๅบฆๅ™จ่ฆ่งฃๅ†ณไป€ไนˆ้—ฎ้ข˜
720
+
721
+ ไฝ ็Žฐๅœจๆœ‰ไบ†๏ผš
722
+ - ็”ฒ็š„ๅทฅไฝœ๏ผš3 ไธช Workflow๏ผˆ`fetch_paper_info` โ†’ `search_repos` โ†’ `fetch_readme`/`fetch_dependencies`๏ผ‰
723
+ - ไฝ ็š„ๅทฅไฝœ๏ผš2 ไธช Agent๏ผˆ`analyze_direction` โ†’ `evaluate_repo`๏ผ‰
724
+
725
+ ไฝ†่ฟ™ไบ›ๆ˜ฏไบ’็›ธ็‹ฌ็ซ‹็š„ๅ‡ฝๆ•ฐใ€‚่ฐƒๅบฆๅ™จ็š„่Œ่ดฃๅฐฑๆ˜ฏ**ๆŒ‰ๆญฃ็กฎ็š„้กบๅบ่ฐƒ็”จๅฎƒไปฌ๏ผŒๆŠŠไธŠไธ€ไธช็š„่พ“ๅ‡บไผ ็ป™ไธ‹ไธ€ไธช็š„่พ“ๅ…ฅ**ใ€‚
726
+
727
+ ```
728
+ ่ฐƒๅบฆๅ™จ = ไธ€ไธชๅ‡ฝๆ•ฐ run(url)๏ผŒ้‡Œ้ขๆŒ‰้กบๅบๅšไบ†๏ผš
729
+ 1. ่ฐƒ็”ฒ็š„ Workflow: fetch_paper_info(url) โ†’ ๆ‹ฟๅˆฐ่ฎบๆ–‡ไฟกๆฏ
730
+ 2. ่ฐƒไฝ ็š„ Agent: analyze_direction(...) โ†’ ๆ‹ฟๅˆฐๆ–นๅ‘+ๆœ็ดข่ฏ
731
+ 3. ่ฐƒ็”ฒ็š„ Workflow: search_repos(...) โ†’ ๆ‹ฟๅˆฐๅ€™้€‰ไป“ๅบ“
732
+ 4. ่ฐƒ็”ฒ็š„ Workflow: fetch_readme/deps(...) โ†’ ๆ‹ฟๅˆฐๆฏไธชไป“ๅบ“็š„่ฏฆๆƒ…
733
+ 5. ่ฐƒไฝ ็š„ Agent: evaluate_repo(...) โ†’ ๆ‹ฟๅˆฐๆฏไธชไป“ๅบ“็š„่ฏ„ๅˆ†
734
+ 6. ๆฑ‡ๆ€ป่ฟ”ๅ›ž
735
+ ```
736
+
737
+ **ๅฐฑ่ฟ™ไนˆ็ฎ€ๅ•ใ€‚** ๆฒกๆœ‰ๅ›ž็Žฏใ€ๆฒกๆœ‰ๅนถๅ‘ใ€ๆฒกๆœ‰ๆตๅผใ€‚่ฟ™ 6 ๆญฅๅฐฑๆ˜ฏ run.py ็š„ๅ…จ้ƒจๅ†…ๅฎนใ€‚
738
+
739
+ ### 3.2 ๅฎŒๆ•ดไปฃ็ 
740
+
741
+ ```python
742
+ # run.py
743
+ import sys
744
+
745
+ # ============================================================
746
+ # ๅฏผๅ…ฅๆ‰€ๆœ‰ๆจกๅ—
747
+ # ============================================================
748
+ from paper_fetcher import fetch_paper_info # ็”ฒ็š„ๆจกๅ—
749
+ from direction_analyzer import analyze_direction # ไฝ ็š„ๆจกๅ—
750
+ from repo_searcher import search_repos # ็”ฒ็š„ๆจกๅ—
751
+ from repo_fetcher import fetch_readme, fetch_dependencies # ็”ฒ็š„ๆจกๅ—
752
+ from repo_evaluator import evaluate_repo # ไฝ ็š„ๆจกๅ—
753
+
754
+
755
+ def run(arxiv_url: str, top_n: int = 5) -> dict:
756
+ """ไธปๅ…ฅๅฃ๏ผšไปŽ่ฎบๆ–‡ arxiv URL ๅˆฐ็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ + ไปฃ็ ๆŽจ่็š„ๅฎŒๆ•ด้“พ่ทฏใ€‚
757
+
758
+ Args:
759
+ arxiv_url: arxiv ่ฎบๆ–‡ URL๏ผŒๅฆ‚ https://arxiv.org/abs/1706.03762
760
+ top_n: ๆœ€็ปˆไฟ็•™็š„ๅ€™้€‰ไป“ๅบ“ๆ•ฐ้‡
761
+
762
+ Returns:
763
+ dict: {
764
+ "paper": {...},
765
+ "direction": {...},
766
+ "repos": [{...evaluation...}, ...],
767
+ "error": "..." # ไป…ๅœจๅฎŒๅ…จๅคฑ่ดฅๆ—ถๅญ˜ๅœจ
768
+ }
769
+ """
770
+ # ================================================================
771
+ # ็ฌฌไธ€ๅฑ‚๏ผš็ ”็ฉถๆ–นๅ‘่งฃๆž
772
+ # ================================================================
773
+ print("=" * 60)
774
+ print("[1/5] ๆญฃๅœจ่Žทๅ–่ฎบๆ–‡ไฟกๆฏ...")
775
+ try:
776
+ paper = fetch_paper_info(arxiv_url)
777
+ except Exception as e:
778
+ return {"error": f"่Žทๅ–่ฎบๆ–‡ไฟกๆฏๅคฑ่ดฅ: {e}"}
779
+
780
+ title = paper.get("title", "")
781
+ abstract = paper.get("abstract", "")
782
+ print(f" ๆ ‡้ข˜: {title[:100]}...")
783
+ print(f" ไฝœ่€…: {', '.join(paper.get('authors', [])[:3])}")
784
+ print(f" ๅˆ†็ฑป: {', '.join(paper.get('categories', []))}")
785
+
786
+ print()
787
+ print("[2/5] ๆญฃๅœจ่งฃๆž็ ”็ฉถๆ–นๅ‘๏ผˆLLM ่ฐƒ็”จ #1๏ผ‰...")
788
+ try:
789
+ direction = analyze_direction(title, abstract)
790
+ except Exception as e:
791
+ return {
792
+ "paper": paper,
793
+ "error": f"็ ”็ฉถๆ–นๅ‘่งฃๆžๅคฑ่ดฅ: {e}",
794
+ }
795
+
796
+ subfield = direction.get("subfield", "ๆœช็Ÿฅ")
797
+ families = direction.get("method_families", [])
798
+ print(f" ๅญ้ข†ๅŸŸ: {subfield}")
799
+ print(f" ่ถ‹ๅŠฟ: {direction.get('subfield_trend', '')[:80]}...")
800
+ print(f" ๆ–นๆณ•ๆ— ({len(families)} ไธช):")
801
+ for mf in families:
802
+ print(f" - {mf.get('family_name', '?')}: {mf.get('description', '')[:60]}...")
803
+
804
+ # ๆฑ‡ๆ€ปๆ‰€ๆœ‰ๆœ็ดขๆŸฅ่ฏข
805
+ all_queries = []
806
+ for mf in families:
807
+ all_queries.extend(mf.get("search_queries", []))
808
+ all_queries.extend(direction.get("broad_queries", []))
809
+ print(f" ็”Ÿๆˆ {len(all_queries)} ไธชๆœ็ดขๆŸฅ่ฏข")
810
+
811
+ # ================================================================
812
+ # ็ฌฌไบŒๅฑ‚๏ผšๅผ€ๆบไปฃ็ ๅ‘็Žฐ
813
+ # ================================================================
814
+ print()
815
+ print(f"[3/5] ๆญฃๅœจๆœ็ดข GitHub ไป“ๅบ“๏ผˆ{len(all_queries)} ไธชๆŸฅ่ฏข๏ผ‰...")
816
+ try:
817
+ candidates = search_repos(all_queries, max_per_keyword=5)
818
+ except Exception as e:
819
+ return {
820
+ "paper": paper,
821
+ "direction": direction,
822
+ "error": f"GitHub ๆœ็ดขๅคฑ่ดฅ: {e}",
823
+ }
824
+
825
+ candidates = candidates[:top_n]
826
+ print(f" ๅŽป้‡ๅŽ่Žทๅพ— {len(candidates)} ไธชๅ€™้€‰ไป“ๅบ“")
827
+
828
+ if not candidates:
829
+ return {
830
+ "paper": paper,
831
+ "direction": direction,
832
+ "repos": [],
833
+ "error": "ๆœชๆ‰พๅˆฐ็›ธๅ…ณๅผ€ๆบไป“ๅบ“ใ€‚่ฏฅๆ–นๅ‘ๅฏ่ƒฝๅคชๆ–ฐๆˆ–ๅคชๅ†ท้—จ๏ผŒๆš‚ๆ— ้ซ˜่ดจ้‡ๅผ€ๆบๅฎž็Žฐใ€‚",
834
+ }
835
+
836
+ print(" ๅ€™้€‰ๅˆ—่กจ:")
837
+ for i, c in enumerate(candidates):
838
+ print(f" {i+1}. {c['full_name']} โญ{c.get('stars', 0)}")
839
+
840
+ # ================================================================
841
+ # ็ฌฌไธ‰ๅฑ‚๏ผšๅฏๅค็Žฐๆ€ง + ๅฏนๆฏ”ๅฎž้ชŒ้€‚้…ๅบฆ่ฏ„ไผฐ
842
+ # ================================================================
843
+ print()
844
+ print(f"[4/5] ๆญฃๅœจ่ฏ„ไผฐ {len(candidates)} ไธชไป“ๅบ“...")
845
+ evaluated = []
846
+
847
+ for i, repo in enumerate(candidates):
848
+ full_name = repo["full_name"]
849
+ try:
850
+ owner, name = full_name.split("/", 1)
851
+ except ValueError:
852
+ print(f" ({i+1}/{len(candidates)}) ่ทณ่ฟ‡๏ผˆๆ— ๆณ•่งฃๆžไป“ๅบ“ๅ๏ผ‰: {full_name}")
853
+ continue
854
+
855
+ # ๅฐ่ฏ•ๅŒน้…ๆ–นๆณ•ๆ—
856
+ matched_family = _match_family(repo, families)
857
+ family_tag = f"[{matched_family}]" if matched_family else "[ๆœชๅฝ’็ฑป]"
858
+ print(f" ({i+1}/{len(candidates)}) ่ฏ„ไผฐ {full_name} {family_tag}...")
859
+
860
+ try:
861
+ readme = fetch_readme(owner, name)
862
+ deps = fetch_dependencies(owner, name)
863
+ evaluation = evaluate_repo(repo, readme, deps, matched_family)
864
+ except Exception as e:
865
+ print(f" โš  ่ฏ„ไผฐๅคฑ่ดฅ: {e}")
866
+ evaluation = _make_error_evaluation(str(e))
867
+
868
+ evaluated.append({
869
+ "full_name": repo.get("full_name", ""),
870
+ "html_url": repo.get("html_url", ""),
871
+ "description": repo.get("description", ""),
872
+ "stars": repo.get("stars", 0),
873
+ "language": repo.get("language", ""),
874
+ "updated_at": repo.get("updated_at", ""),
875
+ "topics": repo.get("topics", []),
876
+ "match_keyword": repo.get("match_keyword", ""),
877
+ "method_family": matched_family,
878
+ "evaluation": evaluation,
879
+ })
880
+
881
+ # ๆŒ‰็ปผๅˆ่ฏ„ๅˆ†้™ๅบๆŽ’ๅˆ—
882
+ evaluated.sort(key=lambda r: r["evaluation"].get("overall_score", 0), reverse=True)
883
+
884
+ # ================================================================
885
+ # ๆฑ‡ๆ€ป่ฟ”ๅ›ž
886
+ # ================================================================
887
+ print()
888
+ print(f"[5/5] ๅฎŒๆˆ๏ผ")
889
+ best = evaluated[0] if evaluated else None
890
+ if best:
891
+ print(f" ๆœ€้ซ˜ๅˆ†: {best['full_name']} ({best['evaluation'].get('overall_score', 0)}/100)")
892
+ print("=" * 60)
893
+
894
+ return {
895
+ "paper": paper,
896
+ "direction": direction,
897
+ "repos": evaluated,
898
+ }
899
+
900
+
901
+ def _match_family(repo: dict, families: list[dict]) -> str:
902
+ """็ฎ€ๆ˜“็š„ไป“ๅบ“โ†’ๆ–นๆณ•ๆ—ๅŒน้…ใ€‚
903
+
904
+ ็ญ–็•ฅ๏ผš
905
+ 1. ๆฃ€ๆŸฅไป“ๅบ“ topics ๆ˜ฏๅฆๅŒ…ๅซๆ–นๆณ•ๆ—ๅ…ณ้”ฎ่ฏ
906
+ 2. ๆฃ€ๆŸฅไป“ๅบ“ๆ่ฟฐๆ˜ฏๅฆๅŒ…ๅซๆ–นๆณ•ๆ—ๅ็งฐ
907
+ 3. ้ƒฝๆฒกๅŒน้…ๅˆฐ่ฟ”ๅ›ž็ฉบๅญ—็ฌฆไธฒ๏ผˆๆœชๅฝ’็ฑป๏ผ‰
908
+ """
909
+ desc = (repo.get("description") or "").lower()
910
+ topics = " ".join(repo.get("topics", [])).lower()
911
+ combined = desc + " " + topics
912
+
913
+ for mf in families:
914
+ family_name = mf.get("family_name", "")
915
+ if not family_name:
916
+ continue
917
+
918
+ # ๅฐ่ฏ•็ฒพ็กฎๅŒน้… + ้ƒจๅˆ†ๅŒน้…
919
+ name_lower = family_name.lower()
920
+ # ไพ‹ๅฆ‚ "Vision Transformer" -> ["vision", "transformer"]
921
+ name_parts = name_lower.split()
922
+
923
+ # ๆฃ€ๆŸฅ่‡ณๅฐ‘ 2/3 ็š„ๅ็งฐ่ฏๅ‡บ็Žฐๅœจ combined ไธญ
924
+ if len(name_parts) >= 2:
925
+ match_count = sum(1 for part in name_parts if part in combined)
926
+ if match_count >= max(2, len(name_parts) * 2 // 3):
927
+ return family_name
928
+ else:
929
+ # ๅ•่ฏๆ–นๆณ•ๅ็›ดๆŽฅๅŒน้…
930
+ if name_lower in combined:
931
+ return family_name
932
+
933
+ return ""
934
+
935
+
936
+ def _make_error_evaluation(error_msg: str) -> dict:
937
+ """ๆž„้€ ไธ€ไธช่กจ็คบ่ฏ„ไผฐๅคฑ่ดฅ็š„ evaluation dict"""
938
+ return {
939
+ "reproducibility_score": 0,
940
+ "benchmark_fitness_score": 0,
941
+ "overall_score": 0,
942
+ "verdict": "error",
943
+ "env_score": 0,
944
+ "doc_score": 0,
945
+ "code_score": 0,
946
+ "community_score": 0,
947
+ "dep_score": 0,
948
+ "benchmark_score": 0,
949
+ "reasoning": f"่ฏ„ไผฐๅคฑ่ดฅ: {error_msg[:100]}",
950
+ "risks": ["่ฏ„ไผฐ่ฟ‡็จ‹ๅ‡บ้”™๏ผŒ่ฏทๆ‰‹ๅŠจๆฃ€ๆŸฅ่ฏฅไป“ๅบ“"],
951
+ "benchmark_readiness": "not_ready",
952
+ "suggested_use": "่ฏ„ไผฐๅคฑ่ดฅ๏ผŒ่ฏทๆ‰‹ๅŠจๆฃ€ๆŸฅ",
953
+ }
954
+
955
+
956
+ # ============================================================
957
+ # ๅ‘ฝไปค่กŒๅ…ฅๅฃ
958
+ # ============================================================
959
+ if __name__ == "__main__":
960
+ if len(sys.argv) < 2:
961
+ print("็”จๆณ•: python run.py <arxiv_url>")
962
+ print("็คบไพ‹: python run.py https://arxiv.org/abs/1706.03762")
963
+ print("็คบไพ‹: python run.py https://arxiv.org/abs/2010.11929")
964
+ sys.exit(1)
965
+
966
+ url = sys.argv[1]
967
+ result = run(url)
968
+
969
+ if result.get("error"):
970
+ print(f"\n{'='*60}")
971
+ print(f"่ฟ่กŒๆœชๅฎŒๅ…จๆˆๅŠŸ")
972
+ print(f"{'='*60}")
973
+ print(f" {result['error']}")
974
+
975
+ # ๅณไฝฟๆœ‰ error๏ผŒๅฆ‚ๆžœๆœ‰้ƒจๅˆ†็ป“ๆžœไนŸๆ‰“ๅฐๅ‡บๆฅ
976
+ if result.get("paper"):
977
+ print(f"\n ่ฎบๆ–‡ไฟกๆฏๅทฒ่Žทๅ–: {result['paper'].get('title', '')[:80]}")
978
+ if result.get("direction"):
979
+ print(f" ๆ–นๅ‘่งฃๆžๅทฒๅฎŒๆˆ: {result['direction'].get('subfield', '')}")
980
+ else:
981
+ # ๅฏผๅ…ฅ format_report ๅนถๆ‰“ๅฐ
982
+ from app import format_report
983
+ print(format_report(result))
984
+ ```
985
+
986
+ ### 3.3 ่ฐƒ่ฏ•ๆŒ‡ๅ—
987
+
988
+ **้—ฎ้ข˜ 1๏ผšๆ•ดไธช้“พ่ทฏ่ท‘ๅˆฐๆŸไธ€ๆญฅๅกไฝไธๅŠจ**
989
+ - ๅŽŸๅ› ๏ผšๆŸไธช API ่ฐƒ็”จ่ถ…ๆ—ถๆˆ–้™้€Ÿ
990
+ - ๆŽ’ๆŸฅ๏ผš็œ‹็ปˆ็ซฏๆœ€ๅŽๆ‰“ๅฐ็š„ๆ˜ฏ `[1/5]` `[2/5]` `[3/5]` ่ฟ˜ๆ˜ฏ `[4/5]`
991
+ - ๅกๅœจ `[1/5]` โ†’ arxiv API ้—ฎ้ข˜
992
+ - ๅกๅœจ `[2/5]` โ†’ DeepSeek API ้—ฎ้ข˜
993
+ - ๅกๅœจ `[3/5]` โ†’ GitHub Search API ้™้€Ÿ
994
+ - ๅกๅœจ `[4/5]` โ†’ GitHub Content API ๆˆ– repo_evaluator ็š„ DeepSeek ่ฐƒ็”จ้—ฎ้ข˜
995
+
996
+ **้—ฎ้ข˜ 2๏ผš`[3/5]` ่ฟ”ๅ›ž 0 ไธชๅ€™้€‰ไป“ๅบ“**
997
+ - ๆŽ’ๆŸฅ๏ผšๆ‰“ๅฐ `all_queries`๏ผŒๅคๅˆถๅ‡ ไธชๅŽป GitHub ๆ‰‹ๅŠจๆœ๏ผŒ็œ‹ๆ˜ฏไธๆ˜ฏๆœ็ดข่ฏๆœฌ่บซ็š„้—ฎ้ข˜
998
+ - ไฟฎๅคๆ–นๅ‘๏ผš่ฐƒ direction_analyzer ็š„ prompt๏ผˆ่ง 1.3 ่Š‚๏ผ‰
999
+
1000
+ **้—ฎ้ข˜ 3๏ผš`_match_family` ๅŒน้…ไธๅˆฐๆ–นๆณ•ๆ—**
1001
+ - ่ฟ™ๆ˜ฏ Week 1 ็š„ๅทฒ็Ÿฅ้™ๅˆถใ€‚็ฎ€ๆ˜“ๅŒน้…ๅŸบไบŽๅญ—็ฌฆไธฒๅŒ…ๅซ๏ผŒ็ฒพๅ‡†ๅบฆไธ้ซ˜
1002
+ - Week 1 ๆŽฅๅ—"ๆœชๅฝ’็ฑป"็š„็ป“ๆžœ๏ผŒWeek 3 ๅ†็”จ LLM ๅš็ฒพๅ‡†ๅฝ’็ฑป
1003
+
1004
+ ---
1005
+
1006
+ ## ๅ››ใ€็•Œ้ข๏ผš`app.py` โ€” Gradio ็•Œ้ข + ็ ”ๆŠฅๆ ผๅผๅŒ–
1007
+
1008
+ > **็ฑปๅž‹๏ผš็•Œ้ขๅฑ‚** | ไธๆ˜ฏ Agent ไนŸไธๆ˜ฏ Workflow
1009
+ > **ๆ ธๅฟƒ่Œ่ดฃ**๏ผšๆŠŠ run.py ็š„ dict ่พ“ๅ‡บๅ˜ๆˆไบบ็ฑปๅฏ่ฏป็š„ Markdown ็ ”ๆŠฅ๏ผŒๅŒ…่ฃ…ไธ€ไธช Gradio ๅฃณ
1010
+
1011
+ ### 4.1 ่ฟ™ไธชๆจกๅ—่ฆ่งฃๅ†ณไป€ไนˆ้—ฎ้ข˜
1012
+
1013
+ ไธคไธช่Œ่ดฃ๏ผš
1014
+ 1. **`format_report()`**๏ผšๆŠŠ `run.py` ่ฟ”ๅ›ž็š„ dict ๅ˜ๆˆ"็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ็ ”ๆŠฅ"๏ผˆไฝ ๅทฒ็ปๅœจๅ‰้ข็œ‹ๅˆฐ่ฟ‡็ป“ๆž„ไบ†๏ผ‰
1015
+ 2. **`diagnose()`**๏ผšGradio ็š„ๅ›ž่ฐƒๅ‡ฝๆ•ฐ๏ผŒ็”จๆˆท่พ“ๅ…ฅ arxiv URL โ†’ ่ฐƒ `run()` โ†’ ่ฐƒ `format_report()` โ†’ ๆ˜พ็คบ
1016
+
1017
+ ### 4.2 ๅฎŒๆ•ดไปฃ็ 
1018
+
1019
+ ```python
1020
+ # app.py
1021
+ import gradio as gr
1022
+ from run import run
1023
+
1024
+
1025
+ def diagnose(arxiv_url: str) -> str:
1026
+ """Gradio ๅ›ž่ฐƒ๏ผš็”จๆˆท่พ“ๅ…ฅ arxiv URL โ†’ ่ฟ”ๅ›ž Markdown ็ ”ๆŠฅ"""
1027
+ url = arxiv_url.strip()
1028
+ if not url:
1029
+ return "## โš ๏ธ ่ฏท่พ“ๅ…ฅ arxiv ่ฎบๆ–‡้“พๆŽฅ\n\n็คบไพ‹: `https://arxiv.org/abs/1706.03762`"
1030
+
1031
+ # ๆ‰ง่กŒๅฎŒๆ•ด้“พ่ทฏ
1032
+ try:
1033
+ result = run(url)
1034
+ except ValueError as e:
1035
+ return f"## โŒ ่พ“ๅ…ฅๆ ผๅผ้”™่ฏฏ\n\n{str(e)}\n\n่ฏท็กฎ่ฎค่พ“ๅ…ฅ็š„ๆ˜ฏๆœ‰ๆ•ˆ็š„ arxiv ้“พๆŽฅใ€‚"
1036
+ except RuntimeError as e:
1037
+ return f"## โŒ API ่ฏทๆฑ‚ๅคฑ่ดฅ\n\n{str(e)}\n\n่ฏทๆฃ€ๆŸฅ็ฝ‘็ปœ่ฟžๆŽฅๆˆ–็จๅŽ้‡่ฏ•ใ€‚"
1038
+ except Exception as e:
1039
+ return f"## โŒ ่ฟ่กŒๅ‡บ้”™\n\n```\n{type(e).__name__}: {str(e)}\n```\n\n่ฏทๅฐ†ไปฅไธŠ้”™่ฏฏไฟกๆฏๅ้ฆˆ็ป™ๅผ€ๅ‘่€…ใ€‚"
1040
+
1041
+ # ๅค„็†้ƒจๅˆ†ๅคฑ่ดฅ็š„ๆƒ…ๅ†ต
1042
+ if result.get("error") and not result.get("repos"):
1043
+ paper = result.get("paper", {})
1044
+ direction = result.get("direction", {})
1045
+ title = paper.get("title", "ๆœช็Ÿฅ") if paper else "ๆœช็Ÿฅ"
1046
+
1047
+ md = f"""# ResearchRadar ็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ็ ”ๆŠฅ
1048
+
1049
+ ---
1050
+
1051
+ ## ่ฎบๆ–‡ไฟกๆฏ
1052
+
1053
+ **{title}**
1054
+
1055
+ ---
1056
+
1057
+ ## โš ๏ธ ๆœช่ƒฝๅฎŒๆˆๅฎŒๆ•ด่ฏ„ไผฐ
1058
+
1059
+ **ๅŽŸๅ› **: {result['error']}
1060
+
1061
+ """
1062
+ if direction:
1063
+ md += f"""## ็ ”็ฉถๆ–นๅ‘่งฃๆž๏ผˆ้ƒจๅˆ†็ป“ๆžœ๏ผ‰
1064
+
1065
+ **ๅญ้ข†ๅŸŸ**: {direction.get('subfield', 'N/A')}
1066
+
1067
+ **่ถ‹ๅŠฟ**: {direction.get('subfield_trend', 'N/A')}
1068
+ """
1069
+ md += """
1070
+ ---
1071
+ > ่ฏฅ่ฎบๆ–‡ๆ–นๅ‘ๅฏ่ƒฝๅคชๆ–ฐๆˆ–ๅคชๅ†ท้—จ๏ผŒๆš‚ๆ— ้ซ˜่ดจ้‡ๅผ€ๆบๅฎž็Žฐใ€‚่ฟ™ๆฐๅฅฝๅธฎๅŠฉไฝ ๅˆคๆ–ญโ€”โ€”ๅœจ่ฟ™ไธชๆ–นๅ‘ๅผ€ๅฑ•็ ”็ฉถ้œ€่ฆไปŽๅคดๅฎž็Žฐใ€‚
1072
+ """
1073
+ return md
1074
+
1075
+ # ๆญฃๅธธๆƒ…ๅ†ต
1076
+ return format_report(result)
1077
+
1078
+
1079
+ def format_report(result: dict) -> str:
1080
+ """ๅฐ† run() ็š„็ป“ๆžœๆ ผๅผๅŒ–ไธบ Markdown ็ ”ๆŠฅใ€‚
1081
+
1082
+ ็ ”ๆŠฅ็ป“ๆž„๏ผš
1083
+ 1. ่ฎบๆ–‡ไฟกๆฏ
1084
+ 2. ็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ๏ผˆๅญ้ข†ๅŸŸ + ่ถ‹ๅŠฟ + ๆ–นๆณ•ๆ—่ฐฑ็ณป๏ผ‰
1085
+ 3. ๅผ€ๆบๅฎž็Žฐ่ฏ„ไผฐๆŽ’ๅ๏ผˆๅ…ญ็ปดๅบฆ่ฏ„ๅˆ†ๆ˜Ž็ป†๏ผ‰
1086
+ 4. ๅฏนๆฏ”ๅฎž้ชŒๆŽจ่ๆ€ป็ป“๏ผˆๆŒ‰ๅฐฑ็ปชๅบฆๅˆ†็ป„๏ผ‰
1087
+ """
1088
+ paper = result.get("paper", {})
1089
+ direction = result.get("direction", {})
1090
+ repos = result.get("repos", [])
1091
+
1092
+ # ================================================================
1093
+ # ็ฌฌไธ€้ƒจๅˆ†๏ผš่ฎบๆ–‡ไฟกๆฏ
1094
+ # ================================================================
1095
+ arxiv_id = paper.get("arxiv_id", "")
1096
+ arxiv_url = f"https://arxiv.org/abs/{arxiv_id}" if arxiv_id else ""
1097
+
1098
+ md = "# ResearchRadar ็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ็ ”ๆŠฅ\n\n---\n\n## ไธ€ใ€่ฎบๆ–‡ไฟกๆฏ\n\n"
1099
+ md += "| ้กน็›ฎ | ๅ†…ๅฎน |\n|------|------|\n"
1100
+ md += f"| **ๆ ‡้ข˜** | {paper.get('title', 'N/A')} |\n"
1101
+ if arxiv_url:
1102
+ md += f"| **arXiv** | [{arxiv_id}]({arxiv_url}) |\n"
1103
+ else:
1104
+ md += f"| **arXiv ID** | {arxiv_id or 'N/A'} |\n"
1105
+ md += f"| **ไฝœ่€…** | {', '.join(paper.get('authors', ['N/A'])[:5])}"
1106
+ if len(paper.get("authors", [])) > 5:
1107
+ md += f" ็ญ‰ {len(paper.get('authors', []))} ไบบ"
1108
+ md += " |\n"
1109
+ published = paper.get("published", "")
1110
+ if published:
1111
+ md += f"| **ๅ‘่กจๆ—ถ้—ด** | {published[:10]} |\n"
1112
+ categories = paper.get("categories", [])
1113
+ if categories:
1114
+ md += f"| **ๅˆ†็ฑป** | {', '.join(categories)} |\n"
1115
+
1116
+ # ================================================================
1117
+ # ็ฌฌไบŒ้ƒจๅˆ†๏ผš็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ
1118
+ # ================================================================
1119
+ md += "\n---\n\n## ไบŒใ€็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ\n\n"
1120
+ md += f"### ๅญ้ข†ๅŸŸๅฎšไฝ\n\n**{direction.get('subfield', 'ๆœช็Ÿฅ')}**\n\n"
1121
+ md += f"### ๅฝ“ๅ‰่ถ‹ๅŠฟ\n\n{direction.get('subfield_trend', 'ๆš‚ๆ— ๅˆ†ๆž')}\n\n"
1122
+ md += "### ๆ–นๆณ•ๆ—่ฐฑ็ณป\n\n"
1123
+ md += "| # | ๆ–นๆณ•ๆ— | ๆ ธๅฟƒ็‰น็‚น | ไปฃ่กจๅทฅไฝœ |\n|---|--------|----------|----------|\n"
1124
+
1125
+ families = direction.get("method_families", [])
1126
+ for i, mf in enumerate(families):
1127
+ family_name = mf.get("family_name", f"ๆ–นๆณ•ๆ— {i+1}")
1128
+ desc = mf.get("description", "")[:80]
1129
+ rep = mf.get("representative_work", "")[:60]
1130
+ md += f"| {i+1} | **{family_name}** | {desc} | {rep} |\n"
1131
+
1132
+ if not families:
1133
+ md += "| - | ๆœช่ƒฝ่ฏ†ๅˆซๅ‡บๆ˜Ž็กฎ็š„ๆ–นๆณ•ๆ— | - | - |\n"
1134
+
1135
+ # ================================================================
1136
+ # ็ฌฌไธ‰้ƒจๅˆ†๏ผšๅผ€ๆบๅฎž็Žฐ่ฏ„ไผฐๆŽ’ๅ
1137
+ # ================================================================
1138
+ md += "\n---\n\n## ไธ‰ใ€ๅผ€ๆบๅฎž็Žฐ่ฏ„ไผฐๆŽ’ๅ\n\n"
1139
+
1140
+ if not repos:
1141
+ md += "**ๆœชๆ‰พๅˆฐ็›ธๅ…ณๅผ€ๆบไป“ๅบ“ใ€‚**\n\n"
1142
+ return md
1143
+
1144
+ for i, repo in enumerate(repos):
1145
+ full_name = repo.get("full_name", f"ไป“ๅบ“ {i+1}")
1146
+ html_url = repo.get("html_url", "")
1147
+ ev = repo.get("evaluation", {})
1148
+ mf = repo.get("method_family", "")
1149
+
1150
+ # ๅ›พๆ ‡ๆ˜ ๅฐ„
1151
+ verdict_icon = {
1152
+ "reproducible": "โœ…",
1153
+ "partially": "โš ๏ธ",
1154
+ "not_reproducible": "โŒ",
1155
+ "error": "๐Ÿ’ฅ",
1156
+ }.get(ev.get("verdict", ""), "โ“")
1157
+
1158
+ readiness_icon = {
1159
+ "ready": "๐ŸŸข",
1160
+ "partial": "๐ŸŸก",
1161
+ "not_ready": "๐Ÿ”ด",
1162
+ }.get(ev.get("benchmark_readiness", ""), "โšช")
1163
+
1164
+ family_tag = f" `[{mf}]`" if mf else ""
1165
+
1166
+ md += f"### {i+1}. [{full_name}]({html_url}){family_tag}\n\n"
1167
+
1168
+ repro_score = ev.get("reproducibility_score", ev.get("overall_score", "N/A"))
1169
+ bench_score = ev.get("benchmark_fitness_score", ev.get("benchmark_score", "N/A"))
1170
+ overall = ev.get("overall_score", "N/A")
1171
+
1172
+ md += f"{verdict_icon} **ๅฏๅค็Žฐๆ€ง: {repro_score}/80** | "
1173
+ md += f"{readiness_icon} **ๅฏนๆฏ”ๅฎž้ชŒ้€‚้…ๅบฆ: {bench_score}/20** | "
1174
+ md += f"**็ปผๅˆ: {overall}/100**\n\n"
1175
+
1176
+ # ๅ…ญ็ปดๅบฆๅพ—ๅˆ†่กจ
1177
+ md += "| ็ปดๅบฆ | ๅพ—ๅˆ† | ๆปกๅˆ† | ่ฏดๆ˜Ž |\n"
1178
+ md += "|------|:----:|:----:|------|\n"
1179
+ md += f"| ็Žฏๅขƒ้…็ฝฎ | {_fmt(ev.get('env_score'))} | 15 | ไพ่ต–ๆ–‡ไปถๅฎŒๆ•ดๆ€ง |\n"
1180
+ md += f"| ๆ–‡ๆกฃ่ดจ้‡ | {_fmt(ev.get('doc_score'))} | 20 | ๅฎ‰่ฃ…/่ฎญ็ปƒ/ๆ•ฐๆฎ่ฏดๆ˜Ž |\n"
1181
+ md += f"| ไปฃ็ ๅฏ็”จๆ€ง | {_fmt(ev.get('code_score'))} | 20 | ่ฎญ็ปƒ+ๆŽจ็†่„šๆœฌ |\n"
1182
+ md += f"| ็คพๅŒบๆดป่ทƒๅบฆ | {_fmt(ev.get('community_score'))} | 10 | Star + ๆ›ดๆ–ฐ้ข‘็އ |\n"
1183
+ md += f"| ไพ่ต–ๅฅๅบทๅบฆ | {_fmt(ev.get('dep_score'))} | 15 | ไพ่ต–ๅ…ผๅฎนๆ€ง |\n"
1184
+ md += f"| **ๅฏนๆฏ”ๅฎž้ชŒ** | **{_fmt(ev.get('benchmark_score'))}** | **20** | **benchmark + ้ข„่ฎญ็ปƒๆƒ้‡** |\n"
1185
+
1186
+ md += f"\nโญ Star: {repo.get('stars', 0)}"
1187
+ if repo.get("match_keyword"):
1188
+ md += f" | ๅŒน้…่ฏ: `{repo.get('match_keyword', '')}`"
1189
+ md += "\n\n"
1190
+
1191
+ # ๅˆ†ๆžๅ’Œๅปบ่ฎฎ
1192
+ reasoning = ev.get("reasoning", "N/A")
1193
+ md += f"**ๅˆ†ๆž**: {reasoning}\n\n"
1194
+ md += f"**ๅฏนๆฏ”ๅฎž้ชŒๅปบ่ฎฎ**: {ev.get('suggested_use', 'N/A')}\n\n"
1195
+
1196
+ # ้ฃŽ้™ฉ
1197
+ risks = ev.get("risks", [])
1198
+ if risks:
1199
+ md += "**้ฃŽ้™ฉ็‚น**: " + " ยท ".join(f"`{r}`" for r in risks) + "\n\n"
1200
+
1201
+ md += "---\n\n"
1202
+
1203
+ # ================================================================
1204
+ # ็ฌฌๅ››้ƒจๅˆ†๏ผšๅฏนๆฏ”ๅฎž้ชŒๆŽจ่ๆ€ป็ป“
1205
+ # ================================================================
1206
+ md += "## ๅ››ใ€ๅฏนๆฏ”ๅฎž้ชŒๆŽจ่ๆ€ป็ป“\n\n"
1207
+
1208
+ ready = [r for r in repos if r["evaluation"].get("benchmark_readiness") == "ready"]
1209
+ partial = [r for r in repos if r["evaluation"].get("benchmark_readiness") == "partial"]
1210
+ not_ready = [
1211
+ r for r in repos
1212
+ if r["evaluation"].get("benchmark_readiness") not in ("ready", "partial")
1213
+ ]
1214
+
1215
+ if ready:
1216
+ md += "### ๐ŸŸข ๅฏ็›ดๆŽฅ็”จไบŽๅฏนๆฏ”ๅฎž้ชŒ\n\n"
1217
+ for r in ready:
1218
+ url = r.get("html_url", "#")
1219
+ name = r.get("full_name", "ๆœช็Ÿฅ")
1220
+ suggestion = r["evaluation"].get("suggested_use", "")
1221
+ md += f"- **[{name}]({url})** โ€” {suggestion}\n"
1222
+ md += "\n"
1223
+
1224
+ if partial:
1225
+ md += "### ๐ŸŸก ้œ€่ฆๅฐ‘้‡ไฟฎๆ”นๅŽๅฏ็”จ\n\n"
1226
+ for r in partial:
1227
+ url = r.get("html_url", "#")
1228
+ name = r.get("full_name", "ๆœช็Ÿฅ")
1229
+ suggestion = r["evaluation"].get("suggested_use", "")
1230
+ md += f"- **[{name}]({url})** โ€” {suggestion}\n"
1231
+ md += "\n"
1232
+
1233
+ if not_ready:
1234
+ md += "### ๐Ÿ”ด ไป…้€‚ๅˆๅ‚่€ƒไปฃ็ ๅฎž็Žฐ\n\n"
1235
+ for r in not_ready:
1236
+ url = r.get("html_url", "#")
1237
+ name = r.get("full_name", "ๆœช็Ÿฅ")
1238
+ reason = r["evaluation"].get("suggested_use") or r["evaluation"].get("reasoning", "")
1239
+ md += f"- **[{name}]({url})** โ€” {reason[:120]}...\n"
1240
+ md += "\n"
1241
+
1242
+ md += "---\n\n> ResearchRadar ยท ่ฎบๆ–‡็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ + ๅผ€ๆบไปฃ็ ่ฏ„ไผฐ + ๅฏนๆฏ”ๅฎž้ชŒๆŽจ่\n"
1243
+
1244
+ return md
1245
+
1246
+
1247
+ def _fmt(value) -> str:
1248
+ """ๆ ผๅผๅŒ–ๆ•ฐๅ€ผ๏ผŒNone ๆ˜พ็คบไธบ N/A"""
1249
+ if value is None:
1250
+ return "N/A"
1251
+ return str(value)
1252
+
1253
+
1254
+ # ============================================================
1255
+ # Gradio ๅฏๅŠจ
1256
+ # ============================================================
1257
+ DEMO_EXAMPLES = [
1258
+ ["https://arxiv.org/abs/2011.08785"], # PaDiM๏ผˆๅทฅไธš็ผบ้™ทๆฃ€ๆต‹๏ผŒไฝ ็š„้ข†ๅŸŸ๏ผ‰
1259
+ ["https://arxiv.org/abs/1706.03762"], # Attention Is All You Need๏ผˆ็ปๅ…ธ๏ผ‰
1260
+ ["https://arxiv.org/abs/2108.07610"], # DRAEM๏ผˆ็ผบ้™ทๆฃ€ๆต‹๏ผŒๅˆๆˆ็ผบ้™ทๆ–นๆณ•๏ผ‰
1261
+ ]
1262
+
1263
+ demo = gr.Interface(
1264
+ fn=diagnose,
1265
+ inputs=gr.Textbox(
1266
+ label="่ฎบๆ–‡ arxiv ้“พๆŽฅ",
1267
+ placeholder="https://arxiv.org/abs/1706.03762",
1268
+ lines=1,
1269
+ ),
1270
+ outputs=gr.Markdown(label="็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ็ ”ๆŠฅ"),
1271
+ title="ResearchRadar โ€” ็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ + ๅผ€ๆบไปฃ็ ่ฏ„ไผฐ",
1272
+ description=(
1273
+ "่พ“ๅ…ฅไธ€็ฏ‡ AI ่ฎบๆ–‡็š„ arxiv ้“พๆŽฅ๏ผŒAI ๅธฎไฝ ๏ผš\n"
1274
+ "1. ๆ‘ธๆธ…่ฏฅ็ ”็ฉถๆ–นๅ‘็š„ๅ‰ๆฒฟๅ…จๆ™ฏ๏ผˆๅญ้ข†ๅŸŸ + ๆ–นๆณ•ๆ—่ฐฑ็ณป๏ผ‰\n"
1275
+ "2. ๆ‰พๅˆฐๆœ€้€‚้…็š„ๅผ€ๆบๅฎž็Žฐ\n"
1276
+ "3. ่ฏ„ไผฐๆฏไธชไป“ๅบ“่ƒฝๅฆ็›ดๆŽฅ็”จไบŽๅ†™่ฎบๆ–‡่ท‘ๅฏนๆฏ”ๅฎž้ชŒ"
1277
+ ),
1278
+ examples=DEMO_EXAMPLES,
1279
+ theme="soft",
1280
+ )
1281
+
1282
+ if __name__ == "__main__":
1283
+ # share=False: ไป…ๆœฌๅœฐ่ฎฟ้—ฎ
1284
+ # share=True: ็”Ÿๆˆๅ…ฌ็ฝ‘้“พๆŽฅ๏ผˆๆผ”็คบๆ—ถๅฆ‚ๆžœ็ฝ‘็ปœๅ…่ฎธๅฏไปฅ็”จ๏ผ‰
1285
+ demo.launch(server_name="0.0.0.0", share=False)
1286
+ ```
1287
+
1288
+ ---
1289
+
1290
+ ## ไธญ้—ดๆ€ป็ป“๏ผšไฝ ๆญๅปบ็š„ 4 ไธชๆ–‡ไปถไธŽ Agent/Workflow ็š„ๅ…ณ็ณป
1291
+
1292
+ ๅœจไฝ ่ขซไปฃ็ ็ป†่Š‚ๆทนๆฒกไน‹ๅ‰๏ผŒๅ…ˆ้€€ๅŽไธ€ๆญฅ็œ‹ๅ…จๆ™ฏ๏ผš
1293
+
1294
+ ```
1295
+ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
1296
+ โ”‚ run.py๏ผˆ่ฐƒๅบฆๅ™จ๏ผŒไฝ ๅ†™๏ผ‰ โ”‚
1297
+ โ”‚ โ”‚
1298
+ โ”‚ ็ฌฌ 1 ๆญฅ๏ผšpaper = fetch_paper_info(url) โ† ็”ฒ็š„ Workflow โ”‚
1299
+ โ”‚ โ”‚ โ”‚
1300
+ โ”‚ ็ฌฌ 2 ๆญฅ๏ผšdirection = analyze_direction() โ† ไฝ ็š„ Agent 1 โ”‚
1301
+ โ”‚ โ”‚ โ”‚
1302
+ โ”‚ ็ฌฌ 3 ๆญฅ๏ผšrepos = search_repos(queries) โ† ็”ฒ็š„ Workflow โ”‚
1303
+ โ”‚ โ”‚ โ”‚
1304
+ โ”‚ ็ฌฌ 4 ๆญฅ๏ผšdetails = fetch_readme/deps() โ† ็”ฒ็š„ Workflow โ”‚
1305
+ โ”‚ โ”‚ โ”‚
1306
+ โ”‚ ็ฌฌ 5 ๆญฅ๏ผšscores = evaluate_repo() โ† ไฝ ็š„ Agent 2 โ”‚
1307
+ โ”‚ โ”‚ โ”‚
1308
+ โ”‚ ็ฌฌ 6 ๆญฅ๏ผšreturn ๆฑ‡ๆ€ป็ป“ๆžœ โ”‚
1309
+ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
1310
+ โ”‚
1311
+ โ–ผ
1312
+ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
1313
+ โ”‚ app.py๏ผˆ็•Œ้ข๏ผŒไฝ ๅ†™๏ผ‰ โ”‚
1314
+ โ”‚ format_report(ๆฑ‡ๆ€ป็ป“ๆžœ) โ†’ Markdown ็ ”ๆŠฅ โ†’ ็”จๆˆท็œ‹ๅˆฐ โ”‚
1315
+ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
1316
+ ```
1317
+
1318
+ **ไฝ ้œ€่ฆๅฎž้™…ๅ†™็š„ไธœ่ฅฟๆ‹†่งฃไธ‹ๆฅๅฐฑๆ˜ฏ**๏ผš
1319
+
1320
+ | ๆ–‡ไปถ | ๆ ธๅฟƒๅทฅไฝœ้‡ | ไปฃ็ ่กŒๆ•ฐ๏ผˆไผฐ็ฎ—๏ผ‰ |
1321
+ |------|-----------|:--------:|
1322
+ | `direction_analyzer.py` | ๅ†™ 1 ๆฎต System Prompt + 1 ๆฎต User Prompt ๆจกๆฟ + JSON ่งฃๆž | ~90 ่กŒ |
1323
+ | `repo_evaluator.py` | ๅ†™ 1 ๆฎต System Prompt + 1 ๆฎต User Prompt ๆจกๆฟ + JSON ่งฃๆž | ~110 ่กŒ |
1324
+ | `run.py` | 6 ๆญฅ้กบๅบ่ฐƒ็”จ + try/except + `_match_family` + print ่ฟ›ๅบฆ | ~120 ่กŒ |
1325
+ | `app.py` | `format_report()` ๅญ—็ฌฆไธฒๆ‹ผๆŽฅ + Gradio ๅŒ…่ฃ… | ~180 ่กŒ |
1326
+
1327
+ **ๅ…ฑ่ฎก็บฆ 500 ่กŒ Python๏ผŒๆ ธๅฟƒๆŒ‘ๆˆ˜ไธๆ˜ฏไปฃ็ ้‡๏ผŒ่€Œๆ˜ฏ Prompt ๅทฅ็จ‹ใ€‚**
1328
+
1329
+ ---
1330
+
1331
+ ## ไบ”ใ€็ฌฌไธ€ๅ‘จไบ”ๅคฉๆ‰ง่กŒ่ฎกๅˆ’
1332
+
1333
+ ### ไฝ ็š„ 5 ๅคฉๆ—ถ้—ด็บฟ
1334
+
1335
+ ```
1336
+ ๅ‘จไธ€ โ”€โ”€โ†’ ๅ†™ direction_analyzer.py + ่ฐƒ prompt
1337
+ ๅ‘จไบŒ โ”€โ”€โ†’ ๅ†™ repo_evaluator.py + ่ฐƒๅ…ญ็ปด่ฏ„ๅˆ†
1338
+ ๅ‘จไธ‰ โ”€โ”€โ†’ ๅ†™ run.py + app.py + ไธ‹ๅˆไธŽ็”ฒ่”่ฐƒ
1339
+ ๅ‘จๅ›› โ”€โ”€โ†’ debug ๆ•ดๆก้“พ่ทฏ + PPT ไธ‰ๅฑ‚ๆžถๆž„ๅ›พ + ็Šถๆ€ schema
1340
+ ๅ‘จไบ” โ”€โ”€โ†’ ่”่ฐƒๅฝฉๆŽ’ + ไธ‹ๅˆๆฑ‡ๆŠฅ
1341
+ ```
1342
+
1343
+ ### ๆฏๆ—ฅ่ฏฆ็ป†ไปปๅŠก
1344
+
1345
+ #### ๅ‘จไธ€๏ผšๆ–นๅ‘่งฃๆžๆจกๅ—
1346
+
1347
+ | ๆ—ถ้—ด | ไปปๅŠก | ้ชŒๆ”ถๆ ‡ๅ‡† |
1348
+ |------|------|----------|
1349
+ | ไธŠๅˆ | ๅปบ `direction_analyzer.py`๏ผŒๅคๅˆถไธŠๆ–‡ๅฎŒๆ•ดไปฃ็  | ่ฟ่กŒ่‡ชๆต‹๏ผŒๆˆๅŠŸๆ‰“ๅฐ Transformer ๅ’Œ PaDiM ็š„ๆ–นๅ‘่งฃๆž็ป“ๆžœ |
1350
+ | ไธ‹ๅˆ | ็”จ 3-5 ็ฏ‡่ฎบๆ–‡ๆต‹่ฏ•๏ผšTransformer + PaDiM(2011.08785) + PatchCore(2106.08265) + EfficientAD(2303.14535) + DRAEM(2108.07610) | ๆฏ็ฏ‡่ฎบๆ–‡็š„ๆ–นๆณ•ๆ— โ‰ฅ 3 ไธช๏ผŒๆœ็ดข่ฏๅœจ GitHub ๆ‰‹ๅŠจๆœๆœ‰็ป“ๆžœใ€‚็‰นๅˆซๅ…ณๆณจ๏ผšๅทฅไธš็ผบ้™ทๆฃ€ๆต‹็š„่ฎบๆ–‡ๆ˜ฏๅฆ่ขซๆญฃ็กฎๅฝ’็ฑปๅˆฐ anomaly detection/defect detection ๅญ้ข†ๅŸŸ |
1351
+ | ๆ™šไธŠ | ๆ นๆฎๆต‹่ฏ•็ป“ๆžœ่ฐƒ system prompt๏ผˆๆ–นๆณ•ๆ—ๅคชๅฐ‘๏ผŸๆœ็ดข่ฏๅคชๅญฆๆœฏๅŒ–๏ผŸๅญ้ข†ๅŸŸๅฎšไฝไธๅ‡†๏ผŸ๏ผ‰ | 5 ็ฏ‡่ฎบๆ–‡็š„ๆ–นๅ‘่งฃๆž็ป“ๆžœไฝ ้ƒฝ่ง‰ๅพ—"ๅˆ็†" |
1352
+
1353
+ #### ๅ‘จไบŒ๏ผš่ฏ„ไผฐๆจกๅ—
1354
+
1355
+ | ๆ—ถ้—ด | ไปปๅŠก | ้ชŒๆ”ถๆ ‡ๅ‡† |
1356
+ |------|------|----------|
1357
+ | ไธŠๅˆ | ๅปบ `repo_evaluator.py`๏ผŒๅคๅˆถๅฎŒๆ•ดไปฃ็  | ่ฟ่กŒ่‡ชๆต‹๏ผŒanomalib ่ฏ„ๅˆ†ๅœจ 70-95 ไน‹้—ด๏ผŒไป…ๆŽจ็†็š„ไป“ๅบ“้€‚้…ๅบฆ < 10 |
1358
+ | ไธ‹ๅˆ | ๆ‰‹ๆŒ‘ 5 ไธชๅทฅไธš็ผบ้™ทๆฃ€ๆต‹้ข†ๅŸŸ็š„ไป“ๅบ“ๆต‹่ฏ•๏ผˆๅฆ‚ anomalib / PaDiM / PatchCore / 2 ไธชไธชไบบ้กน็›ฎ๏ผ‰๏ผŒไบบๅทฅ่ฏ„ๅˆ† vs LLM ่ฏ„ๅˆ†ๅฏนๆฏ” | ๅฏๅค็Žฐๆ€งๅ’Œ้€‚้…ๅบฆไธคไธช็ปดๅบฆ่ƒฝ็œ‹ๅ‡บ็‹ฌ็ซ‹ๆ€งโ€”โ€”anomalib ไธค็ปด้ƒฝ้ซ˜๏ผŒๅชๆœ‰ๆŽจ็† demo ็š„้กน็›ฎ้€‚้…ๅบฆ่ฟœไฝŽไบŽๅฏๅค็Žฐๆ€ง |
1359
+ | ๆ™šไธŠ | ่ฐƒ prompt๏ผŒ้‡็‚นๅ…ณๆณจ"้€‚้…ๅบฆ"่ฏ„ๅˆ†ๆ˜ฏๅฆๅˆ็† | ๆœ‰ๆ ‡ๅ‡† benchmark ่„šๆœฌ็š„ไป“ๅบ“้€‚้…ๅบฆ > 15๏ผŒๅชๆœ‰ๆŽจ็† demo ็š„ < 10 |
1360
+
1361
+ #### ๅ‘จไธ‰๏ผšไธฒ่” + UI
1362
+
1363
+ | ๆ—ถ้—ด | ไปปๅŠก | ้ชŒๆ”ถๆ ‡ๅ‡† |
1364
+ |------|------|----------|
1365
+ | ไธŠๅˆ | ๅปบ `run.py`๏ผŒๅคๅˆถๅฎŒๆ•ดไปฃ็  | `python run.py https://arxiv.org/abs/2011.08785`๏ผˆPaDiM๏ผ‰่ƒฝ่ท‘้€šๅฎŒๆ•ดไธ‰ๅฑ‚้“พ่ทฏ |
1366
+ | ไธ‹ๅˆ | ๅปบ `app.py`๏ผŒๅคๅˆถๅฎŒๆ•ดไปฃ็ ๏ผŒไธŽ็”ฒ่”่ฐƒ | ๆต่งˆๅ™จๆ‰“ๅผ€ `http://127.0.0.1:7860`๏ผŒ่พ“ๅ…ฅ PaDiM arxiv URL๏ผŒ็œ‹ๅˆฐๅฎŒๆ•ด็ ”ๆŠฅ๏ผŒๆ–นๆณ•ๆ—ๅŒ…ๅซ Embedding-based / Reconstruction-based ็ญ‰ |
1367
+ | ๆ™šไธŠ | ๅค„็†ๅ„็ง่พน็•Œๆƒ…ๅ†ต๏ผˆ่งไธ‹ๆ–นๅ‘จไธ‰ๆ—ฅ่ฝๅ‰ checklist๏ผ‰ | ่ง checklist |
1368
+
1369
+ **ๅ‘จไธ‰ๆ—ฅ่ฝๅ‰ checklist**๏ผš
1370
+ - [ ] `python run.py <arxiv_url>` ่ƒฝ่ท‘้€š๏ผŒ็ปˆ็ซฏๆ‰“ๅฐ `[5/5] ๅฎŒๆˆ๏ผ`
1371
+ - [ ] ๆœไธๅˆฐไป“ๅบ“ๆ—ถ๏ผšไธๅดฉๆบƒ๏ผŒ่ฟ”ๅ›ž `error` ๅญ—ๆฎต + ๆ–นๅ‘่งฃๆž็ป“ๆžœ
1372
+ - [ ] ๆŸไธชไป“ๅบ“ๆŠ“ๅ–ๅคฑ่ดฅๆ—ถ๏ผš่ฏฅไป“ๅบ“ๆ ‡่ฎฐไธบ error๏ผŒๅ…ถไป–ไป“ๅบ“ๆญฃๅธธ่ฏ„ไผฐ
1373
+ - [ ] ๅฎŒๆ•ด็ ”ๆŠฅๅœจ Gradio ไธญๆ˜พ็คบๆญฃๅธธ๏ผŒMarkdown ๆธฒๆŸ“ๆญฃ็กฎ
1374
+ - [ ] ็ซฏๅˆฐ็ซฏ่€—ๆ—ถ < 60 ็ง’
1375
+
1376
+ #### ๅ‘จๅ››๏ผšdebug + PPT
1377
+
1378
+ | ๆ—ถ้—ด | ไปปๅŠก | ไบงๅ‡บ |
1379
+ |------|------|------|
1380
+ | ไธŠๅˆ | ็”จ 3 ็ฏ‡ demo ่ฎบๆ–‡ๅ„่ท‘ 3 ้๏ผŒ่ฎฐๅฝ•ๆฏๆฌก็ป“ๆžœ็š„ไธ€่‡ดๆ€ง | ๅ‘็Žฐๆœ€้ข‘็น็š„ๅคฑ่ดฅๆจกๅผ๏ผŒไฟฎๅคๆˆ–่ฎฐๅฝ•ไธบ"ๅทฒ็Ÿฅ้™ๅˆถ" |
1381
+ | ไธ‹ๅˆ | ็”ป PPT ไธญ็š„ไธ‰ๅฑ‚ๆžถๆž„ๅ›พ + ็Šถๆ€ schema + Agent I/O schema | 3 ้กต PPT ๅˆ็จฟ |
1382
+ | ๆ™šไธŠ | ๅ‡†ๅค‡็ญ”่พฉๅฏ่ƒฝ่ขซ้—ฎๅˆฐ็š„ๆŠ€ๆœฏ้—ฎ้ข˜๏ผˆ่ง้™„ๅฝ• A๏ผ‰ | ็ญ”่พฉ Q&A ๆ–‡ๆกฃ |
1383
+
1384
+ #### ๅ‘จไบ”๏ผšๅฝฉๆŽ’ + ๆฑ‡ๆŠฅ
1385
+
1386
+ | ๆ—ถ้—ด | ไปปๅŠก |
1387
+ |------|------|
1388
+ | ไธŠๅˆ | ๅฎŒๆ•ด่ตฐไธ€้ demo ๆต็จ‹๏ผˆๆญฃๆ ทๆœฌ่ฎบๆ–‡ โ†’ ่ดŸๆ ทๆœฌ่ฎบๆ–‡ โ†’ ็ฐ่‰ฒๆ ทๆœฌ๏ผ‰๏ผŒๆฏๆญฅ้…ไธŠ่ฎฒ่งฃ่ฏ |
1389
+ | ไธ‹ๅˆ | **ๆฑ‡ๆŠฅ** |
1390
+
1391
+ ---
1392
+
1393
+ ## ๅ…ญใ€ไฝ ็š„็Šถๆ€ schema ่ฎพ่ฎก
1394
+
1395
+ ไฝ ้œ€่ฆๅฎšไน‰้กน็›ฎไธญ็š„ๆ ธๅฟƒๆ•ฐๆฎ็ป“ๆž„ใ€‚่ฟ™้ƒจๅˆ†ๅฏไปฅ็”จๅœจ PPT ไธญๅฑ•็คบไฝ ็š„ๆžถๆž„่ฎพ่ฎก่ƒฝๅŠ›ใ€‚
1396
+
1397
+ ```python
1398
+ # state.py โ€”โ€” ๅฎšไน‰ๆ•ดไธช็ณป็ปŸ็š„ๆ•ฐๆฎๆจกๅž‹๏ผˆPPT ็”จ๏ผŒWeek 1 ไธๅผบๅˆถ่ฆๆฑ‚ๅ†™ๆˆไปฃ็ ๏ผ‰
1399
+
1400
+ from dataclasses import dataclass, field
1401
+ from typing import Optional
1402
+
1403
+ # ---- ็ฌฌไธ€ๅฑ‚๏ผš็ ”็ฉถๆ–นๅ‘ ----
1404
+
1405
+ @dataclass
1406
+ class MethodFamily:
1407
+ """ๆ–นๆณ•ๆ—"""
1408
+ family_name: str # ๅฆ‚ "Vanilla Transformer"
1409
+ description: str # ไธ€ๅฅ่ฏๆ ธๅฟƒ็‰น็‚น
1410
+ representative_work: str # ไปฃ่กจ่ฎบๆ–‡
1411
+ search_queries: list[str] # GitHub ๆœ็ดข่ฏ
1412
+
1413
+
1414
+ @dataclass
1415
+ class DirectionAnalysis:
1416
+ """็ ”็ฉถๆ–นๅ‘่งฃๆž็ป“ๆžœ"""
1417
+ subfield: str # ๅญ้ข†ๅŸŸๅ
1418
+ subfield_trend: str # ๅฝ“ๅ‰่ถ‹ๅŠฟ
1419
+ method_families: list[MethodFamily]
1420
+ broad_queries: list[str] # ๅฎฝๆณ›ๆœ็ดข่ฏ
1421
+
1422
+
1423
+ # ---- ็ฌฌไบŒๅฑ‚๏ผšๅ€™้€‰ไป“ๅบ“ ----
1424
+
1425
+ @dataclass
1426
+ class CandidateRepo:
1427
+ """ๅ€™้€‰ไป“ๅบ“"""
1428
+ full_name: str # "huggingface/transformers"
1429
+ html_url: str
1430
+ description: str
1431
+ stars: int
1432
+ language: str
1433
+ updated_at: str
1434
+ topics: list[str]
1435
+ match_keyword: str # ๅ‘ฝไธญๅ“ชไธชๆœ็ดข่ฏ
1436
+ method_family: str # ๅฝ’็ฑปๅˆฐๅ“ชไธชๆ–นๆณ•ๆ—๏ผˆๅฏไธบ็ฉบ๏ผ‰
1437
+ readme: Optional[str] = None
1438
+ dependencies: dict[str, str] = field(default_factory=dict)
1439
+
1440
+
1441
+ # ---- ็ฌฌไธ‰ๅฑ‚๏ผš่ฏ„ไผฐ็ป“ๆžœ ----
1442
+
1443
+ @dataclass
1444
+ class EvalResult:
1445
+ """ๅ•ไธชไป“ๅบ“็š„่ฏ„ไผฐ็ป“ๆžœ"""
1446
+ reproducibility_score: int # ๅฏๅค็Žฐๆ€งๆ€ปๅˆ† (0-80)
1447
+ benchmark_fitness_score: int # ๅฏนๆฏ”ๅฎž้ชŒ้€‚้…ๅบฆๆ€ปๅˆ† (0-20)
1448
+ overall_score: int # ็ปผๅˆๆ€ปๅˆ† (0-100)
1449
+ verdict: str # reproducible / partially / not_reproducible
1450
+ env_score: int # 0-15
1451
+ doc_score: int # 0-20
1452
+ code_score: int # 0-20
1453
+ community_score: int # 0-10
1454
+ dep_score: int # 0-15
1455
+ benchmark_score: int # 0-20
1456
+ reasoning: str # ๅˆ†ๆžๆ–‡ๆœฌ
1457
+ risks: list[str] # ้ฃŽ้™ฉๅˆ—่กจ
1458
+ benchmark_readiness: str # ready / partial / not_ready
1459
+ suggested_use: str # ๅปบ่ฎฎ็”จ้€”
1460
+
1461
+
1462
+ # ---- ้กถๅฑ‚๏ผšๅฎŒๆ•ด็ ”ๆŠฅ ----
1463
+
1464
+ @dataclass
1465
+ class ResearchReport:
1466
+ """ๅฎŒๆ•ด็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ็ ”ๆŠฅ"""
1467
+ paper_info: dict
1468
+ direction: DirectionAnalysis
1469
+ repos: list[tuple[CandidateRepo, EvalResult]]
1470
+ ```
1471
+
1472
+ ---
1473
+
1474
+ ## ไธƒใ€ๆ–‡ไปถ็ป“ๆž„๏ผˆไฝ ่ดŸ่ดฃ็š„ๆ–‡ไปถ็”จ โ˜… ๆ ‡่ฎฐ๏ผ‰
1475
+
1476
+ ```
1477
+ ResearchRadar/
1478
+ โ”œโ”€โ”€ paper_fetcher.py # ็”ฒ
1479
+ โ”œโ”€โ”€ repo_searcher.py # ็”ฒ
1480
+ โ”œโ”€โ”€ repo_fetcher.py # ็”ฒ
1481
+ โ”œโ”€โ”€ direction_analyzer.py # โ˜… ไฝ ๏ผšLLM ็ ”็ฉถๆ–นๅ‘่งฃๆž + ๆ–นๆณ•ๆ— + ๆœ็ดข่ฏ
1482
+ โ”œโ”€โ”€ repo_evaluator.py # โ˜… ไฝ ๏ผšLLM ๅ…ญ็ปดๅบฆ่ฏ„ๅˆ†
1483
+ โ”œโ”€โ”€ run.py # โ˜… ไฝ ๏ผšไธ‰ๅฑ‚่ฐƒๅบฆๅ™จ
1484
+ โ”œโ”€โ”€ app.py # โ˜… ไฝ ๏ผšGradio + ็ ”ๆŠฅๆ ผๅผๅŒ–
1485
+ โ”œโ”€โ”€ state.py # โ˜… ไฝ ๏ผš็Šถๆ€ schema๏ผˆPPT ็”จ๏ผŒๅฏ้€‰๏ผ‰
1486
+ โ”œโ”€โ”€ eval.py # ็”ฒ
1487
+ โ”œโ”€โ”€ test_set/ # ็”ฒ
1488
+ โ”œโ”€โ”€ requirements.txt
1489
+ โ””โ”€โ”€ README.md
1490
+ ```
1491
+
1492
+ **ไฝ ็š„ `requirements.txt` ้œ€ๆฑ‚้กน**๏ผš
1493
+ ```
1494
+ openai>=1.0 # DeepSeek ้€š่ฟ‡ OpenAI SDK ่ฐƒ็”จ
1495
+ gradio>=4.0 # UI
1496
+ ```
1497
+
1498
+ ---
1499
+
1500
+ ## ๅ…ซใ€PPT ไธญไฝ ้œ€่ฆๅ‡†ๅค‡็š„ๅ†…ๅฎน
1501
+
1502
+ 6 ๅˆ†้’Ÿๆผ”็คบไธญ๏ผŒไฝ ็š„ PPT ้ƒจๅˆ†ๅปบ่ฎฎๅ  2-3 ้กต๏ผš
1503
+
1504
+ ### Page 1๏ผšไธ‰ๅฑ‚ๆžถๆž„ๅ›พ
1505
+ ```
1506
+ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
1507
+ โ”‚ ResearchRadar ไธ‰ๅฑ‚ๆžถๆž„ โ”‚
1508
+ โ”‚ โ”‚
1509
+ โ”‚ โ‘  ็ ”็ฉถๆ–นๅ‘่งฃๆž (Agent) โ”‚
1510
+ โ”‚ arxiv API โ†’ LLM โ†’ ๅญ้ข†ๅŸŸ+ๆ–นๆณ•ๆ— โ”‚
1511
+ โ”‚ โ†“ โ”‚
1512
+ โ”‚ โ‘ก ๅผ€ๆบไปฃ็ ๅ‘็Žฐ (Workflow) โ”‚
1513
+ โ”‚ GitHub Search โ†’ ๅŽป้‡ โ†’ ๅ€™้€‰ไป“ๅบ“ โ”‚
1514
+ โ”‚ โ†“ โ”‚
1515
+ โ”‚ โ‘ข ๅฏๅค็Žฐๆ€ง+้€‚้…ๅบฆ่ฏ„ไผฐ (Agent) โ”‚
1516
+ โ”‚ GitHub API โ†’ LLM โ†’ ๅ…ญ็ปด่ฏ„ๅˆ† โ”‚
1517
+ โ”‚ โ†“ โ”‚
1518
+ โ”‚ ็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ็ ”ๆŠฅ โ”‚
1519
+ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
1520
+ ```
1521
+
1522
+ ### Page 2๏ผš็Šถๆ€ๆต่ฝฌ
1523
+ ```
1524
+ arxiv URL โ†’ paper_info dict
1525
+ โ†“
1526
+ direction dict (subfield + method_families + queries)
1527
+ โ†“
1528
+ [repo dict, repo dict, ...] (top 5)
1529
+ โ†“
1530
+ ๆฏไธช repo ้™„ๅŠ  evaluation dict (ๅ…ญ็ปด่ฏ„ๅˆ†)
1531
+ โ†“
1532
+ ResearchReport (ๆฑ‡ๆ€ป โ†’ format_report โ†’ Markdown)
1533
+ ```
1534
+
1535
+ ### Page 3๏ผš6 ๅ‘จ้‡Œ็จ‹็ข‘๏ผˆไธŽ็”ฒๅ…ฑ็”จ๏ผ‰
1536
+ ๅฑ•็คบ่ทฏ็บฟๅ›พ๏ผŒๅผบ่ฐƒ Week 1 ๅทฒ็ปๅฎž็Žฐไธ‰ๅฑ‚ๅ…จ้“พ่ทฏใ€‚
1537
+
1538
+ ---
1539
+
1540
+ ## ไนใ€ๅธธ่งๆ•…้šœๆจกๅผ้€ŸๆŸฅ่กจ
1541
+
1542
+ | ็Žฐ่ฑก | ๆŽ’ๆŸฅๅ‘ฝไปค | ๅฏ่ƒฝๅŽŸๅ›  | ไฟฎๅค |
1543
+ |------|----------|----------|------|
1544
+ | `direction_analyzer` JSON ่งฃๆžๅคฑ่ดฅ | ๅœจไปฃ็ ไธญ `print(raw[:300])` | DeepSeek ่ฟ”ๅ›žไบ† markdown ๅŒ…่ฃนๆˆ–็บฏๆ–‡ๆœฌ | ๆฃ€ๆŸฅ `_parse_json_safe` ็š„ไธ‰็ง็ญ–็•ฅๆ˜ฏๅฆ่ฆ†็›–๏ผŒๆˆ–ๅŠ  prompt ็บฆๆŸ |
1545
+ | GitHub ๆœไธๅˆฐไป“ๅบ“ | `print(all_queries)` ็„ถๅŽๆ‰‹ๅŠจๅŽป GitHub ๆœ | ๆœ็ดข่ฏๅคชๅญฆๆœฏๅŒ–๏ผŒGitHub ไธๅŒน้… | ่ฐƒ direction_analyzer prompt๏ผŒๅขžๅŠ "ๆœ็ดข่ฏ็”จๆ—ฅๅธธๆŠ€ๆœฏๆœฏ่ฏญ"็š„็บฆๆŸ |
1546
+ | ๆœๅˆฐ็š„ไป“ๅบ“ๅคชๅคš๏ผˆ100+๏ผ‰ | ๆญฃๅธธ๏ผŒ`search_repos` ๅ†…้ƒจๆŒ‰ star ๆŽ’ๅบๅ– top | ๆฒก้—ฎ้ข˜ | ไธ้œ€่ฆไฟฎๅค |
1547
+ | `repo_evaluator` ่ฏ„ๅˆ†ๅ…จๆ˜ฏ 70+ | ๆขไธ€ไธชๅ…ฌ่ฎค็ƒ‚ไป“ๅบ“ๆต‹่ฏ• | LLM ่ฏ„ๅˆ†ๅๅฎฝๅฎน | ๅœจ prompt ไธญๅŠ "ๅคšๆ•ฐไป“ๅบ“็š„ๅˆ†ๆ•ฐๅบ”่ฏฅๅœจ 40-70 ไน‹้—ด"็š„้”šๅฎš |
1548
+ | Gradio ็™ฝๅฑ | ็ปˆ็ซฏ็œ‹้”™่ฏฏๆ—ฅๅฟ— | `app.py` import ๆŠฅ้”™ๆˆ–็ซฏๅฃๅ†ฒ็ช | ๆฃ€ๆŸฅๆ‰€ๆœ‰ import๏ผŒๆˆ–ๆข็ซฏๅฃ `server_port=7861` |
1549
+ | ๆ•ดๆก้“พ่ทฏ > 2 ๅˆ†้’Ÿ | ๅˆ†ๅˆซ่ฎกๆ—ถๆฏไธชๆญฅ้ชค | GitHub ๆœ็ดขไธฒ่กŒ 10+ ๆฌก | Week 2 ไผ˜ๅŒ–๏ผŒๅ…ˆ็”จ `top_n=3` ๅ‡ๅฐๅ€™้€‰ๆ•ฐ |
1550
+
1551
+ ---
1552
+
1553
+ ## ้™„ๅฝ• A๏ผš็ญ”่พฉๅฏ่ƒฝ่ขซ้—ฎๅˆฐ็š„้—ฎ้ข˜
1554
+
1555
+ 1. **"ไธบไป€ไนˆไธ็”จ LangChain/LangGraph๏ผŸ"**
1556
+ โ†’ ๆˆ‘ไปฌ็š„็ณป็ปŸ่ง„ๆจกไธ้œ€่ฆ็ผ–ๆŽ’ๆก†ๆžถใ€‚Agent ๅฐฑๆ˜ฏไธ€ไธช่ฐƒไบ† LLM ็š„ Python ๅ‡ฝๆ•ฐ๏ผŒWorkflow ๅฐฑๆ˜ฏไธ€ไธชๆฒก่ฐƒ LLM ็š„ Python ๅ‡ฝๆ•ฐ๏ผŒrun.py ๅฐฑๆ˜ฏไธ€ๆฎต่ƒถๆฐดไปฃ็ ๆŒ‰้กบๅบ่ฐƒ็”จๅฎƒไปฌใ€‚ๆ€ปๅ…ฑไธๅˆฐ 500 ่กŒ๏ผŒๆก†ๆžถ็š„ๅญฆไน ๆˆๆœฌๅ’ŒๆŠฝ่ฑกๅผ€้”€่ฟœ่ถ…ๅฎƒๅธฆๆฅ็š„ๆ”ถ็›Šใ€‚
1557
+
1558
+ 2. **"ๆ–นๅ‘่งฃๆž็š„ๅ‡†็กฎ็އๆ€Žไนˆไฟ่ฏ๏ผŸ"**
1559
+ โ†’ Week 1 ๆ˜ฏ baseline๏ผŒๆˆ‘ไปฌ็”จไบบ็œผ้ชŒ่ฏไบ† 5 ็ฏ‡่ฎบๆ–‡๏ผˆๅซๅทฅไธš็ผบ้™ทๆฃ€ๆต‹้ข†ๅŸŸ็š„ PaDiMใ€PatchCore ็ญ‰๏ผ‰็š„ๆ–นๅ‘่งฃๆž็ป“ๆžœใ€‚Week 3 ไผšๅปบ็ซ‹ๆ–นๅ‘่งฃๆž็š„่ฏ„ๆต‹้›†๏ผŒ็”จๅฏ้‡ๅŒ–ๆŒ‡ๆ ‡่ฐƒไผ˜ใ€‚
1560
+
1561
+ 3. **"Agent ๅˆฐๅบ•ๆ˜ฏๆ€Žไนˆๆญๅปบ็š„๏ผŸ"**
1562
+ โ†’ ไธ‰ๆญฅ๏ผšๅ†™ System Prompt ๅฎšไน‰่ง’่‰ฒๅ’Œ่พ“ๅ‡บๆ ผๅผ โ†’ ๅ†™ User Prompt ๅกซๅ…ฅๅ…ทไฝ“ๆ•ฐๆฎ โ†’ ่ฐƒ DeepSeek API ๆ‹ฟๅˆฐ JSON ็ป“ๆžœใ€‚Agent ไธๆ˜ฏไธ€ไธชไผš่‡ชๅทฑ่ท‘็š„็จ‹ๅบ๏ผŒๅฐฑๆ˜ฏไธ€ไธชๅ‡ฝๆ•ฐ้‡Œๆœ‰ไธ€่กŒ `client.chat.completions.create(...)`ใ€‚
1563
+
1564
+ 4. **"ๅฏนๆฏ”ๅฎž้ชŒ้€‚้…ๅบฆๅ’Œๅฏๅค็Žฐๆ€งๆœ‰ไป€ไนˆๅŒบๅˆซ๏ผŸ"**
1565
+ โ†’ ๅฏๅค็Žฐๆ€งๅ›ž็ญ”"่ƒฝไธ่ƒฝ่ท‘"๏ผŒ้€‚้…ๅบฆๅ›ž็ญ”"่ท‘ไบ†่ƒฝไธ่ƒฝๅ†™่ฟ›ๅฎž้ชŒ้ƒจๅˆ†"ใ€‚ไปฅ anomalib ไธบไพ‹๏ผšๅฏๅค็Žฐๆ€ง 78/80๏ผˆREADME ๅฎŒๅ–„ใ€pip install ๅณ็”จ๏ผ‰๏ผŒ้€‚้…ๅบฆ 19/20๏ผˆๆœ‰ๆ ‡ๅ‡† benchmark ่„šๆœฌใ€ๆ”ฏๆŒ MVTec AD ๆ•ฐๆฎ้›†ใ€ๆœ‰้ข„่ฎญ็ปƒๆƒ้‡๏ผ‰ใ€‚่€Œไธ€ไธชๅชๆœ‰ๆŽจ็† demo ็š„้กน็›ฎๅฏไปฅๅฏๅค็Žฐๆ€ง 55/80 ไฝ†้€‚้…ๅบฆ 5/20ใ€‚
1566
+
1567
+ 5. **"ไฝ ๅ’Œ็”ฒๆ€Žไนˆๅˆ†ๅทฅ๏ผŸ"**
1568
+ โ†’ ไป–่ดŸ่ดฃๆ‰€ๆœ‰ Workflow๏ผˆarxiv APIใ€GitHub ๆœ็ดขใ€ๆ–‡ไปถๆŠ“ๅ–่ฟ™ไบ›็กฎๅฎšๆ€งๆต็จ‹๏ผ‰๏ผŒๆˆ‘่ดŸ่ดฃๆ‰€ๆœ‰ Agent๏ผˆๆ–นๅ‘่งฃๆžใ€ๅฏๅค็Žฐๆ€ง่ฏ„ไผฐ่ฟ™ไบ›้œ€่ฆ LLM ๅšๅˆคๆ–ญ็š„ๆจกๅ—๏ผ‰๏ผŒไปฅๅŠๆŠŠไธค่€…ไธฒ่ตทๆฅ็š„่ฐƒๅบฆๅ™จใ€‚ๆŒ‰ๅŠŸ่ƒฝๅฑ‚ๅˆ‡่€Œไธๆ˜ฏๆŒ‰ๆจกๅ—ๅˆ‡๏ผŒไฟ่ฏๆŽฅๅฃๆธ…ๆ™ฐใ€‚
1569
+
1570
+ 6. **"ๅทฅไธš็ผบ้™ทๆฃ€ๆต‹่ฟ™ไธชๆ–นๅ‘ๆœ‰ไป€ไนˆ็‰นๆฎŠๆ€ง๏ผŸ"**
1571
+ โ†’ ่ฟ™ไธชๆ–นๅ‘ๆ–นๆณ•ๆ—ๆธ…ๆ™ฐ๏ผˆEmbedding-based / Reconstruction-based / Teacher-Student / Synthetic Defect๏ผ‰๏ผŒๆœ‰ๅ…ฌ่ฎค benchmark๏ผˆMVTec AD๏ผ‰๏ผŒๆœ‰ anomalib ่ฟ™ๆ ท็š„ไธ€็ซ™ๅผๅผ€ๆบๅบ“ใ€‚้žๅธธ้€‚ๅˆ็”จ ResearchRadar ๅšๆ–นๅ‘ๅ…จๆ™ฏๅฑ•็คบโ€”โ€”่พ“ๅ…ฅไธ€็ฏ‡่ฎบๆ–‡ๅฐฑ่ƒฝ็œ‹ๅˆฐๆ•ดไธช้ข†ๅŸŸ็š„ๆ–นๆณ•ๆ—ๅœฐๅ›พใ€‚
1572
+
1573
+ ---
1574
+
1575
+ > **ๆœ€ๅŽไธ€ไธชๅปบ่ฎฎ**๏ผšๅ‘จไธ€ๅˆฐๅ‘จไบ”๏ผŒๆฏๅคฉไธ‹ๅˆ 5 ็‚นๅ’Œ็”ฒ่Šฑ 10 ๅˆ†้’Ÿๅฏนไธ€ไธ‹่ฟ›ๅบฆใ€‚ไฝ ไปฌ็š„ๅทฅไฝœๆœ‰ไบค็•Œ้ข๏ผˆไฝ ็š„ run.py ่ฐƒไป–็š„ fetcher/searcher๏ผ‰๏ผŒๆŽฅๅฃ็บฆๅฎšๆธ…ๆฅšๅฐฑไธไผšๅœจๅ‘จไธ‰่”่ฐƒๆ—ถๅดฉใ€‚
app.py ADDED
@@ -0,0 +1,317 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app.py
2
+ # ============================================================
3
+ # ็ฑปๅž‹๏ผš็•Œ้ขๅฑ‚๏ผˆไน™่ดŸ่ดฃ๏ผ‰
4
+ # ๅŠŸ่ƒฝ๏ผšGradio ็•Œ้ข + ็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ็ ”ๆŠฅๆ ผๅผๅŒ–
5
+ # ็”จๆณ•๏ผšpython app.py โ†’ ๆต่งˆๅ™จๆ‰“ๅผ€ http://127.0.0.1:7860
6
+ # ๅ…ฌ็ฝ‘๏ผš่ฎพ็ฝฎ NGROK_AUTH_TOKEN ็Žฏๅขƒๅ˜้‡ๅŽ่‡ชๅŠจ็”Ÿๆˆๅ…ฌ็ฝ‘้“พๆŽฅ
7
+ # ============================================================
8
+ import sys
9
+ import os
10
+ import gradio as gr
11
+
12
+ # ่‡ชๅŠจๅŠ ่ฝฝ .env ๆ–‡ไปถ
13
+ _ENV_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), ".env")
14
+ if os.path.isfile(_ENV_PATH):
15
+ with open(_ENV_PATH, "r", encoding="utf-8") as _f:
16
+ for _line in _f:
17
+ _line = _line.strip()
18
+ if _line and not _line.startswith("#") and "=" in _line:
19
+ _key, _val = _line.split("=", 1)
20
+ if _key not in os.environ:
21
+ os.environ[_key] = _val.strip().strip('"').strip("'")
22
+
23
+ try:
24
+ from run import run
25
+ except (ImportError, ModuleNotFoundError) as e:
26
+ print(f"[้”™่ฏฏ] ๆ— ๆณ•ๅฏผๅ…ฅ run.py๏ผŒ่ฏท็กฎ่ฎค็”ฒๅทฒไบคไป˜ๆ‰€ๆœ‰ Workflow ๆจกๅ—ใ€‚")
27
+ print(f" {e}")
28
+ sys.exit(1)
29
+
30
+
31
+ def diagnose(arxiv_url: str) -> str:
32
+ """Gradio ๅ›ž่ฐƒ๏ผš็”จๆˆท่พ“ๅ…ฅ arxiv URL โ†’ ่ฟ”ๅ›ž Markdown ็ ”ๆŠฅ"""
33
+ url = arxiv_url.strip()
34
+ if not url:
35
+ return "## โš ๏ธ ่ฏท่พ“ๅ…ฅ arxiv ่ฎบๆ–‡้“พๆŽฅ\n\n็คบไพ‹: `https://arxiv.org/abs/2011.08785`"
36
+
37
+ try:
38
+ result = run(url)
39
+ except ValueError as e:
40
+ return f"## โŒ ่พ“ๅ…ฅๆ ผๅผ้”™่ฏฏ\n\n{str(e)}\n\n่ฏท็กฎ่ฎค่พ“ๅ…ฅ็š„ๆ˜ฏๆœ‰ๆ•ˆ็š„ arxiv ้“พๆŽฅใ€‚"
41
+ except RuntimeError as e:
42
+ return f"## โŒ API ่ฏทๆฑ‚ๅคฑ่ดฅ\n\n{str(e)}\n\n่ฏทๆฃ€ๆŸฅ็ฝ‘็ปœ่ฟžๆŽฅๆˆ–็จๅŽ้‡่ฏ•ใ€‚"
43
+ except Exception as e:
44
+ return f"## โŒ ่ฟ่กŒๅ‡บ้”™\n\n```\n{type(e).__name__}: {str(e)}\n```\n\n่ฏทๅฐ†ไปฅไธŠ้”™่ฏฏไฟกๆฏๅ้ฆˆ็ป™ๅผ€ๅ‘่€…ใ€‚"
45
+
46
+ # ๅค„็†้ƒจๅˆ†ๅคฑ่ดฅ็š„ๆƒ…ๅ†ต
47
+ if result.get("error") and not result.get("repos"):
48
+ paper = result.get("paper", {})
49
+ direction = result.get("direction", {})
50
+ title = paper.get("title", "ๆœช็Ÿฅ") if paper else "ๆœช็Ÿฅ"
51
+
52
+ md = f"""# ResearchRadar ็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ็ ”ๆŠฅ
53
+
54
+ ---
55
+
56
+ ## ่ฎบๆ–‡ไฟกๆฏ
57
+
58
+ **{title}**
59
+
60
+ ---
61
+
62
+ ## โš ๏ธ ๆœช่ƒฝๅฎŒๆˆๅฎŒๆ•ด่ฏ„ไผฐ
63
+
64
+ **ๅŽŸๅ› **: {result['error']}
65
+
66
+ """
67
+ if direction:
68
+ md += f"""## ็ ”็ฉถๆ–นๅ‘่งฃๆž๏ผˆ้ƒจๅˆ†็ป“ๆžœ๏ผ‰
69
+
70
+ **ๅญ้ข†ๅŸŸ**: {direction.get('subfield', 'N/A')}
71
+
72
+ **่ถ‹ๅŠฟ**: {direction.get('subfield_trend', 'N/A')}
73
+ """
74
+ md += """
75
+ ---
76
+ > ่ฏฅ่ฎบๆ–‡ๆ–นๅ‘ๅฏ่ƒฝๅคชๆ–ฐๆˆ–ๅคชๅ†ท้—จ๏ผŒๆš‚ๆ— ้ซ˜่ดจ้‡ๅผ€ๆบๅฎž็Žฐใ€‚่ฟ™ๆฐๅฅฝๅธฎๅŠฉไฝ ๅˆคๆ–ญโ€”โ€”ๅœจ่ฟ™ไธชๆ–นๅ‘ๅผ€ๅฑ•็ ”็ฉถ้œ€่ฆไปŽๅคดๅฎž็Žฐใ€‚
77
+ """
78
+ return md
79
+
80
+ return format_report(result)
81
+
82
+
83
+ def format_report(result: dict) -> str:
84
+ """ๅฐ† run() ็š„็ป“ๆžœๆ ผๅผๅŒ–ไธบ"็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ็ ”ๆŠฅ" Markdownใ€‚
85
+
86
+ ็ ”ๆŠฅ็ป“ๆž„๏ผš
87
+ 1. ่ฎบๆ–‡ไฟกๆฏ
88
+ 2. ็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ๏ผˆๅญ้ข†ๅŸŸ + ่ถ‹ๅŠฟ + ๆ–นๆณ•ๆ—่ฐฑ็ณป + ๅผ€ๆบ่ฆ†็›–ๅบฆ๏ผ‰
89
+ 3. ๆ–นๆณ•ๆ—ๅผ€ๆบ่ฆ†็›–ๅบฆ๏ผˆๅ“ชไบ›ๅทฒๆœ‰ๅฎž็Žฐใ€ๅ“ชไบ›ๆ˜ฏ็ฉบ็™ฝ๏ผ‰
90
+ 4. ๅผ€ๆบๅฎž็Žฐ่ฏ„ไผฐๆŽ’ๅ๏ผˆๅ…ญ็ปดๅบฆ่ฏ„ๅˆ†ๆ˜Ž็ป†๏ผ‰
91
+ 5. ๅฏนๆฏ”ๅฎž้ชŒๆŽจ่ๆ€ป็ป“๏ผˆๆŒ‰ๅฐฑ็ปชๅบฆๅˆ†็ป„๏ผ‰
92
+ """
93
+ paper = result.get("paper", {})
94
+ direction = result.get("direction", {})
95
+ repos = result.get("repos", [])
96
+
97
+ # ===== ็ฌฌไธ€้ƒจๅˆ†๏ผš่ฎบๆ–‡ไฟกๆฏ =====
98
+ arxiv_id = paper.get("arxiv_id", "")
99
+ arxiv_url = f"https://arxiv.org/abs/{arxiv_id}" if arxiv_id else ""
100
+
101
+ md = "# ResearchRadar ็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ็ ”ๆŠฅ\n\n---\n\n## ไธ€ใ€่ฎบๆ–‡ไฟกๆฏ\n\n"
102
+ md += "| ้กน็›ฎ | ๅ†…ๅฎน |\n|------|------|\n"
103
+ md += f"| **ๆ ‡้ข˜** | {paper.get('title', 'N/A')} |\n"
104
+ if arxiv_url:
105
+ md += f"| **arXiv** | [{arxiv_id}]({arxiv_url}) |\n"
106
+ else:
107
+ md += f"| **arXiv ID** | {arxiv_id or 'N/A'} |\n"
108
+ md += f"| **ไฝœ่€…** | {', '.join(paper.get('authors', ['N/A'])[:5])}"
109
+ if len(paper.get("authors", [])) > 5:
110
+ md += f" ็ญ‰ {len(paper.get('authors', []))} ไบบ"
111
+ md += " |\n"
112
+ published = paper.get("published", "")
113
+ if published:
114
+ md += f"| **ๅ‘่กจๆ—ถ้—ด** | {published[:10]} |\n"
115
+ categories = paper.get("categories", [])
116
+ if categories:
117
+ md += f"| **ๅˆ†็ฑป** | {', '.join(categories)} |\n"
118
+
119
+ # ===== ็ฌฌไบŒ้ƒจๅˆ†๏ผš็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ =====
120
+ md += "\n---\n\n## ไบŒใ€็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ\n\n"
121
+ md += f"### ๅญ้ข†ๅŸŸๅฎšไฝ\n\n**{direction.get('subfield', 'ๆœช็Ÿฅ')}**\n\n"
122
+ md += f"### ๅฝ“ๅ‰่ถ‹ๅŠฟ\n\n{direction.get('subfield_trend', 'ๆš‚ๆ— ๅˆ†ๆž')}\n\n"
123
+ md += "### ๆ–นๆณ•ๆ—่ฐฑ็ณป\n\n"
124
+ md += "| # | ๆ–นๆณ•ๆ— | ๆ ธๅฟƒ็‰น็‚น | ไปฃ่กจๅทฅไฝœ |\n|---|--------|----------|----------|\n"
125
+
126
+ families = direction.get("method_families", [])
127
+ for i, mf in enumerate(families):
128
+ family_name = mf.get("family_name", f"ๆ–นๆณ•ๆ— {i+1}")
129
+ desc = mf.get("description", "")[:80]
130
+ rep = mf.get("representative_work", "")[:60]
131
+ md += f"| {i+1} | **{family_name}** | {desc} | {rep} |\n"
132
+
133
+ if not families:
134
+ md += "| - | ๆœช่ƒฝ่ฏ†ๅˆซๅ‡บๆ˜Ž็กฎ็š„ๆ–นๆณ•๏ฟฝ๏ฟฝ๏ฟฝ | - | - |\n"
135
+
136
+ # ๆ–นๆณ•ๆ—ๅผ€ๆบ่ฆ†็›–ๅบฆ
137
+ md += "\n### ๆ–นๆณ•ๆ—ๅผ€ๆบ่ฆ†็›–ๅบฆ\n\n"
138
+ covered = []
139
+ uncovered = []
140
+ for mf in families:
141
+ name = mf.get("family_name", "ๆœช็Ÿฅ")
142
+ matched = mf.get("matched_repos", [])
143
+ if matched:
144
+ covered.append((name, matched))
145
+ else:
146
+ uncovered.append(name)
147
+
148
+ if covered:
149
+ md += "| ๆ–นๆณ•ๆ— | ๅผ€ๆบไป“ๅบ“ๆ•ฐ | ่ฆ†็›–็Šถๆ€ |\n"
150
+ md += "|--------|:----------:|:--------:|\n"
151
+ for name, matched in covered:
152
+ md += f"| **{name}** | {len(matched)} | ๐ŸŸข ๅทฒๆœ‰ๅฎž็Žฐ |\n"
153
+ if uncovered:
154
+ for name in uncovered:
155
+ md += f"| **{name}** | 0 | ๐Ÿ”ด ๆš‚ๆ— ๅผ€ๆบๅฎž็Žฐ |\n"
156
+ md += "\n> ๐Ÿ’ก ่ฟ™ไบ›ๆ–นๆณ•ๆ—ๆš‚ๆ— ๅผ€ๆบๅฎž็Žฐโ€”โ€”ๅฏ่ƒฝๆ˜ฏ็ ”็ฉถ็ฉบ็™ฝๆˆ–ๅฏนๆฏ”ๅฎž้ชŒ็š„ๅทฎๅผ‚ๅŒ–ๆ–นๅ‘ใ€‚\n"
157
+ md += "\n"
158
+
159
+ # ===== ็ฌฌไธ‰้ƒจๅˆ†๏ผšไป“ๅบ“่ฏ„ไผฐๆŽ’ๅ =====
160
+ md += "\n---\n\n## ๅ››ใ€ๅผ€ๆบๅฎž็Žฐ่ฏ„ไผฐๆŽ’ๅ\n\n"
161
+
162
+ if not repos:
163
+ md += "**ๆœชๆ‰พๅˆฐ็›ธๅ…ณๅผ€ๆบไป“ๅบ“ใ€‚**\n\n"
164
+ return md
165
+
166
+ for i, repo in enumerate(repos):
167
+ full_name = repo.get("full_name", f"ไป“ๅบ“ {i+1}")
168
+ html_url = repo.get("html_url", "")
169
+ ev = repo.get("evaluation", {})
170
+ mf = repo.get("method_family", "")
171
+
172
+ verdict_icon = {
173
+ "reproducible": "โœ…", "partially": "โš ๏ธ",
174
+ "not_reproducible": "โŒ", "error": "๐Ÿ’ฅ",
175
+ }.get(ev.get("verdict", ""), "โ“")
176
+
177
+ readiness_icon = {
178
+ "ready": "๐ŸŸข", "partial": "๐ŸŸก", "not_ready": "๐Ÿ”ด",
179
+ }.get(ev.get("benchmark_readiness", ""), "โšช")
180
+
181
+ family_tag = f" `[{mf}]`" if mf else ""
182
+
183
+ md += f"### {i+1}. [{full_name}]({html_url}){family_tag}\n\n"
184
+
185
+ repro_score = ev.get("reproducibility_score", ev.get("overall_score", "N/A"))
186
+ bench_score = ev.get("benchmark_fitness_score", ev.get("benchmark_score", "N/A"))
187
+ overall = ev.get("overall_score", "N/A")
188
+
189
+ md += f"{verdict_icon} **ๅฏๅค็Žฐๆ€ง: {repro_score}/80** | "
190
+ md += f"{readiness_icon} **ๅฏนๆฏ”ๅฎž้ชŒ้€‚้…ๅบฆ: {bench_score}/20** | "
191
+ md += f"**็ปผๅˆ: {overall}/100**\n\n"
192
+
193
+ md += "| ็ปดๅบฆ | ๅพ—ๅˆ† | ๆปกๅˆ† | ่ฏดๆ˜Ž |\n"
194
+ md += "|------|:----:|:----:|------|\n"
195
+ md += f"| ็Žฏๅขƒ้…็ฝฎ | {_fmt(ev.get('env_score'))} | 15 | ไพ่ต–ๆ–‡ไปถๅฎŒๆ•ดๆ€ง |\n"
196
+ md += f"| ๆ–‡ๆกฃ่ดจ้‡ | {_fmt(ev.get('doc_score'))} | 20 | ๅฎ‰่ฃ…/่ฎญ็ปƒ/ๆ•ฐๆฎ่ฏดๆ˜Ž |\n"
197
+ md += f"| ไปฃ็ ๅฏ็”จๆ€ง | {_fmt(ev.get('code_score'))} | 20 | ่ฎญ็ปƒ+ๆŽจ็†่„šๆœฌ |\n"
198
+ md += f"| ็คพๅŒบๆดป่ทƒๅบฆ | {_fmt(ev.get('community_score'))} | 10 | Star + ๆ›ดๆ–ฐ้ข‘็އ |\n"
199
+ md += f"| ไพ่ต–ๅฅๅบทๅบฆ | {_fmt(ev.get('dep_score'))} | 15 | ไพ่ต–ๅ…ผๅฎนๆ€ง |\n"
200
+ md += f"| **ๅฏนๆฏ”ๅฎž้ชŒ** | **{_fmt(ev.get('benchmark_score'))}** | **20** | **benchmark + ้ข„่ฎญ็ปƒๆƒ้‡** |\n"
201
+
202
+ md += f"\nโญ Star: {repo.get('stars', 0)}"
203
+ if repo.get("match_keyword"):
204
+ md += f" | ๅŒน้…่ฏ: `{repo.get('match_keyword', '')}`"
205
+ md += "\n\n"
206
+
207
+ reasoning = ev.get("reasoning", "N/A")
208
+ md += f"**ๅˆ†ๆž**: {reasoning}\n\n"
209
+ md += f"**ๅฏนๆฏ”ๅฎž้ชŒๅปบ่ฎฎ**: {ev.get('suggested_use', 'N/A')}\n\n"
210
+
211
+ risks = ev.get("risks", [])
212
+ if risks:
213
+ md += "**้ฃŽ้™ฉ็‚น**: " + " ยท ".join(f"`{r}`" for r in risks) + "\n\n"
214
+
215
+ md += "---\n\n"
216
+
217
+ # ===== ็ฌฌๅ››้ƒจๅˆ†๏ผšๅฏนๆฏ”ๅฎž้ชŒๆŽจ่ๆ€ป็ป“ =====
218
+ md += "## ไบ”ใ€ๅฏนๆฏ”ๅฎž้ชŒๆŽจ่ๆ€ป็ป“\n\n"
219
+
220
+ ready = [r for r in repos if r["evaluation"].get("benchmark_readiness") == "ready"]
221
+ partial = [r for r in repos if r["evaluation"].get("benchmark_readiness") == "partial"]
222
+ not_ready = [
223
+ r for r in repos
224
+ if r["evaluation"].get("benchmark_readiness") not in ("ready", "partial")
225
+ ]
226
+
227
+ if ready:
228
+ md += "### ๐ŸŸข ๅฏ็›ดๆŽฅ็”จไบŽๅฏนๆฏ”ๅฎž้ชŒ\n\n"
229
+ for r in ready:
230
+ url = r.get("html_url", "#")
231
+ name = r.get("full_name", "ๆœช็Ÿฅ")
232
+ suggestion = r["evaluation"].get("suggested_use", "")
233
+ md += f"- **[{name}]({url})** โ€” {suggestion}\n"
234
+ md += "\n"
235
+
236
+ if partial:
237
+ md += "### ๐ŸŸก ้œ€่ฆๅฐ‘้‡ไฟฎๆ”นๅŽๅฏ็”จ\n\n"
238
+ for r in partial:
239
+ url = r.get("html_url", "#")
240
+ name = r.get("full_name", "ๆœช็Ÿฅ")
241
+ suggestion = r["evaluation"].get("suggested_use", "")
242
+ md += f"- **[{name}]({url})** โ€” {suggestion}\n"
243
+ md += "\n"
244
+
245
+ if not_ready:
246
+ md += "### ๐Ÿ”ด ไป…้€‚ๅˆๅ‚่€ƒไปฃ็ ๅฎž็Žฐ\n\n"
247
+ for r in not_ready:
248
+ url = r.get("html_url", "#")
249
+ name = r.get("full_name", "ๆœช็Ÿฅ")
250
+ reason = r["evaluation"].get("suggested_use") or r["evaluation"].get("reasoning", "")
251
+ md += f"- **[{name}]({url})** โ€” {reason[:120]}...\n"
252
+ md += "\n"
253
+
254
+ md += "---\n\n> ResearchRadar ยท ่ฎบๆ–‡็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ + ๅผ€ๆบไปฃ็ ่ฏ„ไผฐ + ๅฏนๆฏ”ๅฎž้ชŒๆŽจ่\n"
255
+
256
+ return md
257
+
258
+
259
+ def _fmt(value) -> str:
260
+ """ๆ ผๅผๅŒ–ๆ•ฐๅ€ผ๏ผŒNone ๆ˜พ็คบไธบ N/A"""
261
+ if value is None:
262
+ return "N/A"
263
+ return str(value)
264
+
265
+
266
+ # ============================================================
267
+ # Gradio ๅฏๅŠจ
268
+ # ============================================================
269
+ DEMO_EXAMPLES = [
270
+ ["https://arxiv.org/abs/2011.08785"], # PaDiM๏ผˆๅทฅไธš็ผบ้™ทๆฃ€ๆต‹๏ผ‰
271
+ ["https://arxiv.org/abs/1706.03762"], # Attention Is All You Need
272
+ ["https://arxiv.org/abs/2108.07610"], # DRAEM๏ผˆ็ผบ้™ทๆฃ€ๆต‹๏ผŒๅˆๆˆ็ผบ้™ทๆ–นๆณ•๏ผ‰
273
+ ]
274
+
275
+ demo = gr.Interface(
276
+ fn=diagnose,
277
+ inputs=gr.Textbox(
278
+ label="่ฎบๆ–‡ arxiv ้“พๆŽฅ",
279
+ placeholder="https://arxiv.org/abs/2011.08785",
280
+ lines=1,
281
+ ),
282
+ outputs=gr.Markdown(label="็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ็ ”ๆŠฅ"),
283
+ title="ResearchRadar โ€” ็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ + ๅผ€ๆบไปฃ็ ่ฏ„ไผฐ",
284
+ description=(
285
+ "่พ“ๅ…ฅไธ€็ฏ‡ AI ่ฎบๆ–‡็š„ arxiv ้“พๆŽฅ๏ผŒAI ๅธฎไฝ ๏ผš\n"
286
+ "1. ๆ‘ธๆธ…่ฏฅ็ ”็ฉถๆ–นๅ‘็š„ๅ‰ๆฒฟๅ…จๆ™ฏ๏ผˆๅญ้ข†ๅŸŸ + ๆ–นๆณ•ๆ—่ฐฑ็ณป๏ผ‰\n"
287
+ "2. ๆ‰พๅˆฐๆœ€้€‚้…็š„ๅผ€ๆบๅฎž็Žฐ\n"
288
+ "3. ่ฏ„ไผฐๆฏไธชไป“ๅบ“่ƒฝๅฆ็›ดๆŽฅ็”จไบŽๅ†™่ฎบๆ–‡่ท‘ๅฏนๆฏ”ๅฎž้ชŒ"
289
+ ),
290
+ examples=DEMO_EXAMPLES,
291
+ )
292
+
293
+ if __name__ == "__main__":
294
+ public_url = None
295
+
296
+ # ๅฐ่ฏ• ngrok ๅ…ฌ็ฝ‘้šง้“๏ผˆๅ›ฝๅ†…ๅฏ็”จ๏ผ‰
297
+ ngrok_token = os.getenv("NGROK_AUTH_TOKEN", "")
298
+ if ngrok_token:
299
+ try:
300
+ from pyngrok import ngrok, conf
301
+ conf.get_default().auth_token = ngrok_token
302
+ tunnel = ngrok.connect(7860, "http")
303
+ public_url = tunnel.public_url
304
+ print(f"\n{'='*60}")
305
+ print(f" ๅ…ฌ็ฝ‘้“พๆŽฅ: {public_url}")
306
+ print(f" ไปปไฝ•ไบบๅฏไปฅๆ‰“ๅผ€ๆญค้“พๆŽฅไฝฟ็”จ ResearchRadar")
307
+ print(f"{'='*60}\n")
308
+ except Exception as e:
309
+ print(f" [WARN] ngrok ๅฏๅŠจๅคฑ่ดฅ: {e}")
310
+ print(f" ้™็บงไธบไป…ๆœฌๅœฐ่ฎฟ้—ฎ")
311
+
312
+ if not public_url:
313
+ print("\n ๆœช้…็ฝฎๅ…ฌ็ฝ‘้šง้“๏ผŒไป…ๆœฌๅœฐ่ฎฟ้—ฎ: http://127.0.0.1:7860")
314
+ print(" ๅฆ‚้œ€ๅ…ฌ็ฝ‘่ฎฟ้—ฎ๏ผŒ่ฏท่ฎพ็ฝฎ NGROK_AUTH_TOKEN ็Žฏๅขƒๅ˜้‡")
315
+ print(" ๅ…่ดน่Žทๅ– token: https://dashboard.ngrok.com/get-started/your-authtoken\n")
316
+
317
+ demo.launch(server_name="0.0.0.0", share=False, theme="soft", show_error=True)
direction_analyzer.py ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # direction_analyzer.py
2
+ # ============================================================
3
+ # ็ฑปๅž‹๏ผšAGENT๏ผˆไน™่ดŸ่ดฃ๏ผ‰
4
+ # ๅŠŸ่ƒฝ๏ผšๅŸบไบŽ GitHub ๆœ็ดข็ป“ๆžœ + ่ฎบๆ–‡ไฟกๆฏ โ†’ ๅฝ’็บณๆ–นๆณ•ๆ—่ฐฑ็ณป
5
+ # ็”จๆณ•๏ผšpython direction_analyzer.py๏ผˆ่‡ชๆต‹๏ผ‰
6
+ # ============================================================
7
+ from llm_utils import call_llm_json, parse_json_safe, validate_direction_output, fix_windows_encoding
8
+
9
+ DIRECTION_SYSTEM_PROMPT = """ไฝ ๆ˜ฏไธ€ไธช AI ็ ”็ฉถ้ข†ๅŸŸ็š„ๅผ€ๆบ็”Ÿๆ€ๅˆ†ๆžๅธˆใ€‚ไฝ ็š„ไปปๅŠกๆ˜ฏ๏ผšๆ‹ฟๅˆฐไธ€็ฏ‡่ฎบๆ–‡ๅ’Œ GitHub ไธŠๆœๅˆฐ็š„็›ธๅ…ณไป“ๅบ“ๅˆ—่กจ๏ผŒๅธฎ็ ”็ฉถ่€…็†ๆธ…โ€”โ€”่ฟ™ไบ›ไป“ๅบ“ๅˆ†ๅˆซๅฑžไบŽไป€ไนˆๆ–นๆณ•ๆ—ใ€ๅ“ชไบ›ๆ–นๆณ•ๆ—ๆœ‰ๆˆ็†Ÿๅผ€ๆบๅฎž็Žฐใ€ๅ“ชไบ›่ฟ˜ๆ˜ฏ็ฉบ็™ฝใ€‚
10
+
11
+ ## ๅˆ†ๆžๆต็จ‹
12
+
13
+ ### ๆญฅ้ชค 1๏ผšๅฎšไฝๅญ้ข†ๅŸŸ
14
+ - ็ป“ๅˆ่ฎบๆ–‡ๅ†…ๅฎนๅ’Œไป“ๅบ“ๆ่ฟฐ๏ผŒๅˆคๆ–ญ่ฏฅ่ฎบๆ–‡ๅฑžไบŽๅ“ชไธชๅ…ทไฝ“ๅญ้ข†ๅŸŸ
15
+ - ไธ่ฆ็ฌผ็ปŸ๏ผˆๅฆ‚"ๆทฑๅบฆๅญฆไน "๏ผ‰๏ผŒ่ฆๅ…ทไฝ“๏ผˆๅฆ‚"ๅทฅไธšๅ›พๅƒๅผ‚ๅธธๆฃ€ๆต‹"๏ผ‰
16
+ - ่ฏดๆ˜Ž่ฏฅๅญ้ข†ๅŸŸๅœจ 2024-2025 ๅนด็š„ไธปๆต่ถ‹ๅŠฟ๏ผˆ1-2 ๅฅไธญๆ–‡๏ผ‰
17
+
18
+ ### ๆญฅ้ชค 2๏ผšๅฝ’็บณๆ–นๆณ•ๆ—
19
+ - ๆต่งˆๆ‰€ๆœ‰ไป“ๅบ“็š„ description ๅ’Œ topics๏ผŒๅฐ†ๅŒ็ฑปๆ–นๆณ•็š„ไป“ๅบ“ๅฝ’ไธบไธ€ๆ—
20
+ - ๆฏไธชๆ–นๆณ•ๆ—็ป™ๅ‡บ๏ผšๅ็งฐใ€ไธ€ๅฅ่ฏๆ ธๅฟƒ็‰น็‚นใ€ไปฃ่กจ่ฎบๆ–‡/้กน็›ฎ
21
+ - ่พ“ๅ‡บ 3-6 ไธชๆ–นๆณ•ๆ—
22
+ - ๅฆ‚ๆžœไธ€ไธชไป“ๅบ“ๆ˜Žๆ˜พไธๅฑžไบŽไปปไฝ•ไธ€ๆ—๏ผˆๅฆ‚ๅชๆ˜ฏๆ•™็จ‹ใ€่ฎบๆ–‡ๅˆ—่กจ๏ผ‰๏ผŒไธ่ฆๅผบ่กŒๅฝ’็ฑป
23
+ - ้‡่ฆ๏ผšๆ–นๆณ•ๆ—ๅ็งฐๅฐฝ้‡ไฝฟ็”จ่‹ฑๆ–‡๏ผˆๅฆ‚"Patch Distribution Modeling"๏ผ‰๏ผŒไพฟไบŽๅŽ็ปญๆœ็ดข
24
+
25
+ ### ๆญฅ้ชค 3๏ผšไธบๆฏไธชๆ–นๆณ•ๆ—ๅˆ—ๅ‡บๅฝ’ๅฑžไป“ๅบ“
26
+ - matched_repos ๅกซ่ฏฅๆ—ๅŒ…ๅซ็š„ไป“ๅบ“ full_name ๅˆ—่กจ
27
+ - ๅฆ‚ๆžœไธ€ไธชไป“ๅบ“ๅฏไปฅๅฑžไบŽๅคšไธชๆ—๏ผˆๅฆ‚ๅŒๆ—ถๅฎž็Žฐไบ†ๆ–นๆณ•Aๅ’Œๆ–นๆณ•B๏ผ‰๏ผŒ้€‰ๆœ€ๅŒน้…็š„้‚ฃไธช
28
+
29
+ ### ๆญฅ้ชค 4๏ผš็”Ÿๆˆ็ฒพๅ‡†ๆœ็ดข่ฏ
30
+ - ไธบๆฏไธชๆ–นๆณ•ๆ—็”Ÿๆˆ 2-3 ไธช GitHub ๆœ็ดขๆŸฅ่ฏข๏ผˆ่‹ฑๆ–‡๏ผ‰
31
+ - ๆ ผๅผ๏ผšๆ–นๆณ•ๅ + ๆŠ€ๆœฏๅ…ณ้”ฎ่ฏ + ้™ๅฎš่ฏ๏ผˆimplementation / pytorch / official / code๏ผ‰
32
+ - ้ขๅค–็”Ÿๆˆ 3 ไธชๅฎฝๆณ›ๆœ็ดข่ฏ๏ผŒ็”จไบŽๆŸฅๆ‰พๆœช่ขซๅฝ“ๅ‰ๆœ็ดข่ฆ†็›–็š„ๆ–นๆณ•ๆ—
33
+
34
+ ## ๆœ็ดขๆŸฅ่ฏข่ดจ้‡่ฆๆฑ‚
35
+ - ๅฅฝ็š„ๆŸฅ่ฏข๏ผš"patch distribution modeling anomaly detection pytorch"
36
+ - ๅ็š„ๆŸฅ่ฏข๏ผš"anomaly"๏ผˆๅคชๅฎฝๆณ›๏ผ‰
37
+ - ๅ็š„ๆŸฅ่ฏข๏ผš"a novel approach for industrial defect detection using deep learning"๏ผˆๅคชๅคšๅบŸ่ฏ๏ผ‰
38
+
39
+ ## ่พ“ๅ‡บๆ ผๅผ๏ผˆไธฅๆ ผ JSON๏ผŒ็ดงๅ‡‘ๆ ผๅผไธ่ฆ็ผฉ่ฟ›ๅ’Œๅคšไฝ™็ฉบๆ ผ๏ผ‰
40
+ {
41
+ "subfield": "ๅ…ทไฝ“ๅญ้ข†ๅŸŸๅ็งฐ",
42
+ "subfield_trend": "่ฏฅๅญ้ข†ๅŸŸ 2024-2025 ็š„ไธปๆต่ถ‹ๅŠฟ๏ผˆ1-2 ๅฅไธญๆ–‡๏ผ‰",
43
+ "method_families": [
44
+ {
45
+ "family_name": "ๆ–นๆณ•ๆ—็ฎ€็งฐ",
46
+ "description": "ไธ€ๅฅ่ฏๆ ธๅฟƒ็‰น็‚น",
47
+ "representative_work": "ไปฃ่กจ่ฎบๆ–‡ๆˆ–็Ÿฅๅ้กน็›ฎ",
48
+ "matched_repos": ["owner/repo1", "owner/repo2"],
49
+ "search_queries": ["ๆœ็ดข่ฏ1", "ๆœ็ดข่ฏ2"]
50
+ }
51
+ ],
52
+ "broad_queries": ["ๅฎฝๆณ›ๆœ็ดข1", "ๅฎฝๆณ›ๆœ็ดข2", "ๅฎฝๆณ›ๆœ็ดข3"]
53
+ }
54
+ """
55
+
56
+
57
+ def analyze_direction(title: str, abstract: str, repos: list[dict] | None = None) -> dict:
58
+ """ๅŸบไบŽ่ฎบๆ–‡ไฟกๆฏๅ’Œ GitHub ๆœ็ดข็ป“ๆžœ๏ผŒๅฝ’็บณๆ–นๆณ•ๆ—่ฐฑ็ณปใ€‚
59
+
60
+ Args:
61
+ title: ่ฎบๆ–‡ๆ ‡้ข˜
62
+ abstract: ่ฎบๆ–‡ๆ‘˜่ฆ๏ผˆไผš่‡ชๅŠจๆˆชๆ–ญ๏ผ‰
63
+ repos: GitHub ๆœ็ดข็ป“ๆžœๅˆ—่กจ๏ผŒๆฏไธช dict ๅŒ…ๅซ full_name, description,
64
+ stars, language, topics, html_urlใ€‚ๅฆ‚ๆžœไธบ None ๆˆ–็ฉบๅˆ—่กจ๏ผŒ
65
+ ๅˆ™ไป…ๅŸบไบŽ LLM ่‡ช่บซ็Ÿฅ่ฏ†ๅˆ†ๆž๏ผˆ้™็บงๆจกๅผ๏ผ‰ใ€‚
66
+
67
+ Returns:
68
+ dict: ๅŒ…ๅซ subfield, subfield_trend, method_families, broad_queries
69
+ """
70
+ abstract_truncated = abstract[:2000] if abstract else "๏ผˆๆ‘˜่ฆไธๅฏ็”จ๏ผ‰"
71
+ title_clean = title.strip() if title else "๏ผˆๆ ‡้ข˜ไธๅฏ็”จ๏ผ‰"
72
+
73
+ # ๆž„ๅปบไป“ๅบ“ๅˆ—่กจๆ–‡ๆœฌ
74
+ if repos:
75
+ repo_lines = []
76
+ for r in repos[:20]: # ๆœ€ๅคšไผ  20 ไธชไป“ๅบ“็ป™ LLM
77
+ full_name = r.get("full_name", "")
78
+ desc = r.get("description", "")[:100]
79
+ stars = r.get("stars", 0)
80
+ topics = ", ".join(r.get("topics", [])[:5])
81
+ language = r.get("language", "")
82
+ repo_lines.append(
83
+ f"- **{full_name}** (โญ{stars}, {language}) โ€” {desc}\n"
84
+ f" topics: {topics}"
85
+ )
86
+ repos_text = "\n".join(repo_lines)
87
+ else:
88
+ repos_text = "๏ผˆๆœชๆไพ› GitHub ๆœ็ดข็ป“ๆžœ๏ผŒ่ฏทๅŸบไบŽไฝ ็š„่ฎญ็ปƒ็Ÿฅ่ฏ†่ฟ›่กŒๅˆ†ๆž๏ผ‰"
89
+
90
+ user_prompt = f"""## ่ฎบๆ–‡ไฟกๆฏ
91
+ **ๆ ‡้ข˜**: {title_clean}
92
+
93
+ **ๆ‘˜่ฆ**: {abstract_truncated}
94
+
95
+ ## GitHub ๆœ็ดข็ป“ๆžœ๏ผˆๅ…ฑ {len(repos) if repos else 0} ไธชไป“ๅบ“๏ผ‰
96
+
97
+ {repos_text}
98
+
99
+ ่ฏทๅˆ†ๆžไปฅไธŠ่ฎบๆ–‡ๅ’Œไป“ๅบ“ๆ•ฐๆฎ๏ผŒๅฝ’็บณๆ–นๆณ•ๆ—่ฐฑ็ณปใ€‚"""
100
+
101
+ raw = call_llm_json(DIRECTION_SYSTEM_PROMPT, user_prompt, temperature=0.4, max_tokens=5000)
102
+ data = parse_json_safe(raw, "direction_analyzer")
103
+ return validate_direction_output(data)
104
+
105
+
106
+ # ============================================================
107
+ # ่‡ชๆต‹
108
+ # ============================================================
109
+ if __name__ == "__main__":
110
+ fix_windows_encoding()
111
+
112
+ # ๆž„้€ ๆจกๆ‹Ÿ GitHub ๆœ็ดข็ป“ๆžœ
113
+ mock_repos = [
114
+ {
115
+ "full_name": "openvinotoolkit/anomalib",
116
+ "description": "An anomaly detection library comprising state-of-the-art algorithms.",
117
+ "stars": 4000, "language": "Python",
118
+ "topics": ["anomaly-detection", "pytorch", "benchmarking", "industrial-inspection"],
119
+ "html_url": "https://github.com/openvinotoolkit/anomalib",
120
+ },
121
+ {
122
+ "full_name": "hcw-00/PatchCore",
123
+ "description": "Official implementation of PatchCore: Towards Total Recall in Industrial Anomaly Detection.",
124
+ "stars": 850, "language": "Python",
125
+ "topics": ["anomaly-detection", "memory-bank", "pytorch"],
126
+ "html_url": "https://github.com/hcw-00/PatchCore",
127
+ },
128
+ {
129
+ "full_name": "taikiinoue45/STFPM",
130
+ "description": "Student-Teacher Feature Pyramid Matching for Anomaly Detection (PyTorch).",
131
+ "stars": 320, "language": "Python",
132
+ "topics": ["teacher-student", "anomaly-detection", "pytorch"],
133
+ "html_url": "https://github.com/taikiinoue45/STFPM",
134
+ },
135
+ {
136
+ "full_name": "VitjanZ/DRAEM",
137
+ "description": "DRAEM: Discriminatively trained reconstruction embedding for surface anomaly detection.",
138
+ "stars": 180, "language": "Python",
139
+ "topics": ["synthetic-defect", "anomaly-detection", "reconstruction"],
140
+ "html_url": "https://github.com/VitjanZ/DRAEM",
141
+ },
142
+ {
143
+ "full_name": "marugoto/CFLow",
144
+ "description": "CFLow: Real-time unsupervised anomaly detection via conditional normalizing flows.",
145
+ "stars": 130, "language": "Python",
146
+ "topics": ["normalizing-flow", "anomaly-detection", "pytorch"],
147
+ "html_url": "https://github.com/marugoto/CFLow",
148
+ },
149
+ {
150
+ "full_name": "xiahaifeng1995/PaDiM-Anomaly-Detection",
151
+ "description": "Unofficial implementation of PaDiM: Patch Distribution Modeling for anomaly detection.",
152
+ "stars": 150, "language": "Python",
153
+ "topics": ["padim", "anomaly-detection", "mvtec-ad"],
154
+ "html_url": "https://github.com/xiahaifeng1995/PaDiM-Anomaly-Detection",
155
+ },
156
+ {
157
+ "full_name": "someone/anomaly-detection-tutorial",
158
+ "description": "A tutorial collection of anomaly detection papers and resources.",
159
+ "stars": 45, "language": "Markdown",
160
+ "topics": ["awesome-list", "anomaly-detection"],
161
+ "html_url": "https://github.com/someone/anomaly-detection-tutorial",
162
+ },
163
+ ]
164
+
165
+ print("=" * 60)
166
+ print("Agent 1 ๆต‹่ฏ•๏ผšๅŸบไบŽ GitHub ไป“ๅบ“็š„ๆ–นๆณ•ๆ—ๅฝ’็บณ")
167
+ print("=" * 60)
168
+
169
+ result = analyze_direction(
170
+ "PaDiM: a Patch Distribution Modeling Framework for Anomaly Detection and Localization",
171
+ "We present a new framework for anomaly detection and localization based on "
172
+ "patch distribution modeling. PaDiM uses pretrained convolutional neural networks "
173
+ "to extract patch features and models their distribution using multivariate "
174
+ "Gaussian distributions.",
175
+ mock_repos,
176
+ )
177
+
178
+ print(f"ๅญ้ข†ๅŸŸ: {result.get('subfield')}")
179
+ print(f"่ถ‹ๅŠฟ: {result.get('subfield_trend')}")
180
+ print(f"ๆ–นๆณ•ๆ—ๆ•ฐ้‡: {len(result.get('method_families', []))}")
181
+ for mf in result.get("method_families", []):
182
+ repos_in_family = mf.get("matched_repos", [])
183
+ print(f" - {mf.get('family_name')}: {mf.get('description')[:60]}...")
184
+ print(f" ๅฝ’ๅฑžไป“ๅบ“: {repos_in_family}")
185
+ print(f" ๆœ็ดข่ฏ: {mf.get('search_queries', [])}")
186
+ print(f"ๅฎฝๆณ›ๆœ็ดข่ฏ: {result.get('broad_queries', [])}")
187
+ total_queries = sum(len(mf.get('search_queries', [])) for mf in result.get('method_families', [])) + len(result.get('broad_queries', []))
188
+ print(f"ๆ€ปๆœ็ดข่ฏๆ•ฐ: {total_queries}")
189
+
190
+ print()
191
+ print("=" * 60)
192
+ print("Agent 1 ๆต‹่ฏ•๏ผš้™็บงๆจกๅผ๏ผˆๆ—  GitHub ๆ•ฐๆฎ๏ผ‰")
193
+ print("=" * 60)
194
+
195
+ result2 = analyze_direction(
196
+ "Attention Is All You Need",
197
+ "The dominant sequence transduction models are based on complex recurrent "
198
+ "or convolutional neural networks...",
199
+ None,
200
+ )
201
+ print(f"ๅญ้ข†ๅŸŸ: {result2.get('subfield')}")
202
+ print(f"ๆ–นๆณ•ๆ—: {[mf.get('family_name') for mf in result2.get('method_families', [])]}")
gradio_output.log ADDED
@@ -0,0 +1,8 @@
 
0
 
1
 
 
 
 
 
 
 
 
 
1
+ Downloading ngrok ...
2
 
3
 
4
+ ============================================================
5
+ ๏ฟฝ๏ฟฝ๏ฟฝ๏ฟฝ๏ฟฝ๏ฟฝ๏ฟฝ๏ฟฝ: https://anvil-wackiness-hazing.ngrok-free.dev
6
+ ๏ฟฝฮบ๏ฟฝ๏ฟฝหฟ๏ฟฝ๏ฟฝิด๒ฟชด๏ฟฝ๏ฟฝ๏ฟฝ๏ฟฝ๏ฟฝสน๏ฟฝ๏ฟฝ ResearchRadar
7
+ ============================================================
8
+
9
+ * Running on local URL: http://0.0.0.0:7860
10
+ * To create a public link, set `share=True` in `launch()`.
llm_utils.py ADDED
@@ -0,0 +1,263 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # llm_utils.py
2
+ # ============================================================
3
+ # ็ฑปๅž‹๏ผšๅ…ฌๅ…ฑๅทฅๅ…ทๆจกๅ—๏ผˆไน™่ดŸ่ดฃ๏ผ‰
4
+ # ๅŠŸ่ƒฝ๏ผšOpenAI ๅฎขๆˆท็ซฏ้…็ฝฎใ€JSON ๅฎ‰ๅ…จ่งฃๆžใ€LLM ่ฐƒ็”จ้‡่ฏ•ใ€่พ“ๅ‡บๆ ก้ชŒ
5
+ # ่ขซ direction_analyzer.py ๅ’Œ repo_evaluator.py ๅ…ฑ็”จ
6
+ # ============================================================
7
+
8
+ import json
9
+ import re
10
+ import time
11
+ import sys
12
+ import os
13
+ from openai import OpenAI
14
+
15
+ # ---- ้…็ฝฎ ----
16
+ DEEPSEEK_API_KEY = os.getenv("DEEPSEEK_API_KEY", "sk-a115c4388d444cf688d323855edb36da")
17
+ DEEPSEEK_BASE_URL = "https://api.deepseek.com"
18
+ MODEL = "deepseek-v4-pro"
19
+ MAX_RETRIES = 3 # API ่ฐƒ็”จๅคฑ่ดฅๆ—ถๆœ€ๅคš้‡่ฏ•ๆฌกๆ•ฐ
20
+ RETRY_DELAY = 2.0 # ้‡่ฏ•้—ด้š”๏ผˆ็ง’๏ผ‰๏ผŒๆŒ‡ๆ•ฐๅขž้•ฟ
21
+
22
+ _client = None
23
+
24
+
25
+ def get_client() -> OpenAI:
26
+ """่Žทๅ– OpenAI ๅฎขๆˆท็ซฏๅ•ไพ‹"""
27
+ global _client
28
+ if _client is None:
29
+ _client = OpenAI(api_key=DEEPSEEK_API_KEY, base_url=DEEPSEEK_BASE_URL)
30
+ return _client
31
+
32
+
33
+ # ---- LLM ่ฐƒ็”จ๏ผˆๅธฆ้‡่ฏ•๏ผ‰ ----
34
+
35
+ def call_llm(
36
+ system_prompt: str,
37
+ user_prompt: str,
38
+ temperature: float = 0.3,
39
+ max_tokens: int = 2000,
40
+ response_format: dict | None = None,
41
+ ) -> str:
42
+ """่ฐƒ็”จ DeepSeek API๏ผŒ่‡ชๅŠจ้‡่ฏ•ใ€‚
43
+
44
+ Args:
45
+ system_prompt: ็ณป็ปŸๆ็คบ่ฏ
46
+ user_prompt: ็”จๆˆทๆ็คบ่ฏ
47
+ temperature: ๆธฉๅบฆๅ‚ๆ•ฐ
48
+ max_tokens: ๆœ€ๅคง่พ“ๅ‡บ token ๆ•ฐ
49
+ response_format: OpenAI ๅ…ผๅฎน็š„ response_format๏ผŒๅฆ‚ {"type": "json_object"}
50
+
51
+ Returns:
52
+ LLM ่ฟ”ๅ›ž็š„ๅŽŸๅง‹ๆ–‡ๆœฌ
53
+
54
+ Raises:
55
+ RuntimeError: ้‡่ฏ•ๆฌกๆ•ฐ็”จๅฐฝๅŽไปๅคฑ่ดฅ
56
+ """
57
+ client = get_client()
58
+ last_error = None
59
+
60
+ for attempt in range(MAX_RETRIES):
61
+ try:
62
+ kwargs = dict(
63
+ model=MODEL,
64
+ messages=[
65
+ {"role": "system", "content": system_prompt},
66
+ {"role": "user", "content": user_prompt},
67
+ ],
68
+ temperature=temperature,
69
+ max_tokens=max_tokens,
70
+ timeout=120.0,
71
+ )
72
+ if response_format is not None:
73
+ kwargs["response_format"] = response_format
74
+
75
+ response = client.chat.completions.create(**kwargs)
76
+ content = response.choices[0].message.content
77
+ if content is None or not content.strip():
78
+ raise ValueError("API ่ฟ”ๅ›ž็ฉบๅ†…ๅฎน")
79
+ return content.strip()
80
+ except Exception as e:
81
+ last_error = e
82
+ if attempt < MAX_RETRIES - 1:
83
+ wait = RETRY_DELAY * (2 ** attempt)
84
+ print(f" [้‡่ฏ• {attempt+1}/{MAX_RETRIES}] API ่ฐƒ็”จๅคฑ่ดฅ: {e}๏ผŒ{wait:.0f}s ๅŽ้‡่ฏ•...")
85
+ time.sleep(wait)
86
+
87
+ raise RuntimeError(f"LLM API ่ฐƒ็”จๅคฑ่ดฅ๏ผˆๅทฒ้‡่ฏ• {MAX_RETRIES} ๆฌก๏ผ‰: {last_error}")
88
+
89
+
90
+ def call_llm_json(
91
+ system_prompt: str,
92
+ user_prompt: str,
93
+ temperature: float = 0.3,
94
+ max_tokens: int = 2000,
95
+ ) -> str:
96
+ """่ฐƒ็”จ DeepSeek API ๅนถๅผบๅˆถ JSON ่พ“ๅ‡บๆจกๅผใ€‚
97
+
98
+ ่ฟ™ๆ˜ฏ call_llm ็š„ไพฟๆทๅฐ่ฃ…๏ผŒ่‡ชๅŠจ่ฎพ็ฝฎ response_format={"type": "json_object"}ใ€‚
99
+ DeepSeek API ๅœจ JSON ๆจกๅผไธ‹ไผš็บฆๆŸ่พ“ๅ‡บไธบๅˆๆณ• JSON๏ผŒๅคงๅน…ๅ‡ๅฐ‘ๆ ผๅผๅๅทฎใ€‚
100
+ """
101
+ return call_llm(
102
+ system_prompt=system_prompt,
103
+ user_prompt=user_prompt,
104
+ temperature=temperature,
105
+ max_tokens=max_tokens,
106
+ response_format={"type": "json_object"},
107
+ )
108
+
109
+
110
+ # ---- JSON ๅฎ‰ๅ…จ่งฃๆž ----
111
+
112
+ def parse_json_safe(raw: str, module_name: str = "") -> dict:
113
+ """ๅฎ‰ๅ…จ่งฃๆž LLM ่ฟ”ๅ›ž็š„ JSON๏ผŒ7 ็ง็ญ–็•ฅไพๆฌกๅฐ่ฏ•ใ€‚
114
+
115
+ LLM ๅฏ่ƒฝ่ฟ”ๅ›ž๏ผš
116
+ 1. ็บฏ JSON
117
+ 2. ```json ... ``` ไปฃ็ ๅ—ๅŒ…่ฃน
118
+ 3. ``` ... ``` ๆ— ่ฏญ่จ€ๆ ‡่ฎฐๅŒ…่ฃน
119
+ 4. ๅธฆๅ‰็ผ€ๆ–‡ๅญ— + JSON
120
+ 5. ๅธฆๅŽ็ผ€ๆ–‡ๅญ— + JSON
121
+ 6. ๅคš่กŒไธญๅคนๆ‚ JSON ๅฏน่ฑก
122
+ 7. ๅซๆœช่ฝฌไน‰ๆŽงๅˆถๅญ—็ฌฆ็š„ JSON
123
+ """
124
+ # ๅ…จๅฑ€้ข„ๅค„็†๏ผšๆ›ฟๆขๆœช่ฝฌไน‰็š„ๆŽงๅˆถๅญ—็ฌฆ๏ผˆๆข่กŒใ€ๅˆถ่กจ็ฌฆ๏ผ‰
125
+ # JSON ๅญ—็ฌฆไธฒๅ†…ไธๅ…่ฎธ่ฟ™ไบ›ๅญ—็ฌฆ๏ผŒๆ›ฟๆขไธบ็ฉบๆ ผไธไผšๅฝฑๅ“ๅˆๆณ•็š„ JSON ็ป“ๆž„
126
+ raw = raw.replace('\r\n', ' ').replace('\n', ' ').replace('\r', ' ').replace('\t', ' ')
127
+
128
+ strategies_tried = []
129
+
130
+ # ็ญ–็•ฅ 1๏ผš็›ดๆŽฅ่งฃๆž
131
+ try:
132
+ return json.loads(raw)
133
+ except json.JSONDecodeError as e:
134
+ strategies_tried.append(f"็›ดๆŽฅ่งฃๆž: {e}")
135
+
136
+ # ็ญ–็•ฅ 2๏ผšๆๅ– ```json ... ``` ไปฃ็ ๅ—
137
+ match = re.search(r'```json\s*([\s\S]*?)```', raw)
138
+ if match:
139
+ try:
140
+ return json.loads(match.group(1).strip())
141
+ except json.JSONDecodeError:
142
+ pass
143
+
144
+ # ็ญ–็•ฅ 3๏ผšๆๅ– ``` ... ``` ไปปๆ„ไปฃ็ ๅ—
145
+ match = re.search(r'```\s*([\s\S]*?)```', raw)
146
+ if match:
147
+ try:
148
+ return json.loads(match.group(1).strip())
149
+ except json.JSONDecodeError:
150
+ pass
151
+
152
+ # ็ญ–็•ฅ 4๏ผšๆๅ–ๆœ€ๅค–ๅฑ‚ { ... }
153
+ match = re.search(r'\{[\s\S]*\}', raw)
154
+ if match:
155
+ try:
156
+ return json.loads(match.group(0))
157
+ except json.JSONDecodeError:
158
+ pass
159
+
160
+ # ็ญ–็•ฅ 5๏ผšไฟฎๅคๅธธ่ง JSON ้”™่ฏฏ๏ผˆๅฐพ้ƒจ้€—ๅทใ€ๅ•ๅผ•ๅท๏ผ‰
161
+ cleaned = raw.strip()
162
+ cleaned = re.sub(r',\s*}', '}', cleaned) # ็งป้™คๅฐพ้ƒจ้€—ๅท
163
+ cleaned = re.sub(r',\s*]', ']', cleaned) # ็งป้™คๆ•ฐ็ป„ๅฐพ้ƒจ้€—ๅท
164
+ try:
165
+ return json.loads(cleaned)
166
+ except json.JSONDecodeError:
167
+ pass
168
+
169
+ # ็ญ–็•ฅ 6๏ผšๅฐ่ฏ•ๆ‰พๅŒ…ๅซ "subfield" ๆˆ– "score" ็š„ { ... }
170
+ for keyword in ["subfield", "overall_score", "score"]:
171
+ pattern = r'\{[^{}]*"' + keyword + r'"[^{}]*\{[^{}]*\}[^{}]*\}|\{[^{}]*"' + keyword + r'"[^{}]*\}'
172
+ match = re.search(pattern, raw)
173
+ if match:
174
+ try:
175
+ return json.loads(match.group(0))
176
+ except json.JSONDecodeError:
177
+ pass
178
+
179
+ # ๅ…จ้ƒจๅคฑ่ดฅ
180
+ module_tag = f"[{module_name}] " if module_name else ""
181
+ raise ValueError(
182
+ f"{module_tag}ๆ— ๆณ•่งฃๆž LLM ่ฟ”ๅ›ž็š„ JSONใ€‚\n"
183
+ f"ๅŽŸๅง‹่ฟ”ๅ›ž๏ผˆๅ‰ 500 ๅญ—็ฌฆ๏ผ‰:\n{raw[:500]}"
184
+ )
185
+
186
+
187
+ # ---- ่พ“ๅ‡บๆ ก้ชŒ ----
188
+
189
+ def validate_direction_output(data: dict) -> dict:
190
+ """ๆ ก้ชŒ direction_analyzer ็š„่พ“ๅ‡บ๏ผŒๅกซๅ……็ผบๅคฑๅญ—ๆฎตไธบ้ป˜่ฎคๅ€ผใ€‚"""
191
+ if not isinstance(data, dict):
192
+ raise ValueError(f"direction_analyzer ่พ“ๅ‡บไธๆ˜ฏ dict๏ผŒ่€Œๆ˜ฏ {type(data).__name__}")
193
+
194
+ # ็กฎไฟ method_families ๆ˜ฏ list
195
+ families = data.get("method_families", [])
196
+ if not isinstance(families, list):
197
+ families = []
198
+
199
+ # ๆ ก้ชŒๆฏไธชๆ–นๆณ•ๆ—
200
+ validated_families = []
201
+ for mf in families:
202
+ if not isinstance(mf, dict):
203
+ continue
204
+ validated_families.append({
205
+ "family_name": mf.get("family_name", "ๆœชๅ‘ฝๅๆ–นๆณ•ๆ—"),
206
+ "description": mf.get("description", ""),
207
+ "representative_work": mf.get("representative_work", ""),
208
+ "search_queries": mf.get("search_queries", []) if isinstance(mf.get("search_queries"), list) else [],
209
+ "matched_repos": mf.get("matched_repos", []) if isinstance(mf.get("matched_repos"), list) else [],
210
+ })
211
+
212
+ # ็กฎไฟ broad_queries ๆ˜ฏ list
213
+ broad_queries = data.get("broad_queries", [])
214
+ if not isinstance(broad_queries, list):
215
+ broad_queries = []
216
+
217
+ return {
218
+ "subfield": data.get("subfield", "ๆœช็Ÿฅๅญ้ข†ๅŸŸ"),
219
+ "subfield_trend": data.get("subfield_trend", "ๆš‚ๆ— ่ถ‹ๅŠฟๅˆ†ๆž"),
220
+ "method_families": validated_families,
221
+ "broad_queries": broad_queries,
222
+ }
223
+
224
+
225
+ def validate_eval_output(data: dict) -> dict:
226
+ """ๆ ก้ชŒ repo_evaluator ็š„่พ“ๅ‡บ๏ผŒๅกซๅ……็ผบๅคฑๅญ—ๆฎตไธบ้ป˜่ฎคๅ€ผใ€‚"""
227
+ if not isinstance(data, dict):
228
+ raise ValueError(f"repo_evaluator ่พ“ๅ‡บไธๆ˜ฏ dict๏ผŒ่€Œๆ˜ฏ {type(data).__name__}")
229
+
230
+ # ๆ•ฐๅ€ผๅญ—ๆฎต้ป˜่ฎค 0
231
+ int_fields = [
232
+ "reproducibility_score", "benchmark_fitness_score", "overall_score",
233
+ "env_score", "doc_score", "code_score", "community_score", "dep_score",
234
+ "benchmark_score",
235
+ ]
236
+ for field in int_fields:
237
+ if field not in data or not isinstance(data[field], (int, float)):
238
+ data[field] = 0
239
+ else:
240
+ data[field] = int(data[field])
241
+
242
+ # ๅญ—็ฌฆไธฒๅญ—ๆฎต
243
+ data.setdefault("verdict", "error")
244
+ data.setdefault("reasoning", "่ฏ„ไผฐๆœช่ฟ”ๅ›žๅˆ†ๆž")
245
+ data.setdefault("benchmark_readiness", "not_ready")
246
+ data.setdefault("suggested_use", "่ฏทๆ‰‹ๅŠจ่ฏ„ไผฐ่ฏฅไป“ๅบ“")
247
+
248
+ # ๅˆ—่กจๅญ—ๆฎต
249
+ if "risks" not in data or not isinstance(data["risks"], list):
250
+ data["risks"] = []
251
+
252
+ return data
253
+
254
+
255
+ # ---- Windows ็ปˆ็ซฏ็ผ–็  ----
256
+
257
+ def fix_windows_encoding():
258
+ """ไฟฎๅค Windows ็ปˆ็ซฏไธญๆ–‡ๆ˜พ็คบไนฑ็ ้—ฎ้ข˜"""
259
+ if sys.platform == "win32":
260
+ try:
261
+ sys.stdout.reconfigure(encoding="utf-8", errors="replace")
262
+ except Exception:
263
+ pass
paper_fetcher.py ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import urllib.request
2
+ import urllib.error
3
+ import xml.etree.ElementTree as ET
4
+ import re
5
+ import time
6
+
7
+
8
+ def extract_arxiv_id(url: str) -> str:
9
+ """ไปŽ arxiv URL ๆๅ–่ฎบๆ–‡ ID
10
+
11
+ ๆ”ฏๆŒๆ ผๅผ:
12
+ - https://arxiv.org/abs/1706.03762
13
+ - https://arxiv.org/abs/1706.03762v2
14
+ - https://arxiv.org/pdf/1706.03762.pdf
15
+ - arxiv:1706.03762
16
+ - 1706.03762
17
+ """
18
+ match = re.search(r'arxiv\.org/(?:abs|pdf)/(\d{4}\.\d{4,5})(?:v\d+)?(?:\.pdf)?', url)
19
+ if match:
20
+ return match.group(1)
21
+ match = re.search(r'(?:arxiv:)?(\d{4}\.\d{4,5})', url)
22
+ if match:
23
+ return match.group(1)
24
+ raise ValueError(f"ๆ— ๆณ•ไปŽ URL ๆๅ– arxiv ID: {url}")
25
+
26
+
27
+ def fetch_paper_info(arxiv_url: str) -> dict:
28
+ """ไปŽ arxiv ่Žทๅ–่ฎบๆ–‡ๅ…ƒไฟกๆฏ
29
+
30
+ Returns:
31
+ dict: {
32
+ "arxiv_id": str, "title": str, "abstract": str,
33
+ "authors": [str, ...], "categories": [str, ...], "published": str
34
+ }
35
+ """
36
+ arxiv_id = extract_arxiv_id(arxiv_url)
37
+ api_url = f"http://export.arxiv.org/api/query?id_list={arxiv_id}&max_results=1"
38
+
39
+ req = urllib.request.Request(api_url, headers={"User-Agent": "ResearchRadar/1.0"})
40
+
41
+ last_error = None
42
+ for attempt in range(3):
43
+ try:
44
+ with urllib.request.urlopen(req, timeout=30) as resp:
45
+ xml_text = resp.read().decode("utf-8")
46
+ break
47
+ except urllib.error.HTTPError as e:
48
+ last_error = e
49
+ if e.code in (429, 503) and attempt < 2:
50
+ wait = 5 * (2 ** attempt)
51
+ print(f" arxiv API ้™้€Ÿ๏ผŒ{wait}s ๅŽ้‡่ฏ•...")
52
+ time.sleep(wait)
53
+ continue
54
+ raise RuntimeError(f"arxiv API ่ฏทๆฑ‚ๅคฑ่ดฅ (HTTP {e.code})๏ผŒ่ฏทๆฃ€ๆŸฅ่ฎบๆ–‡ ID ๆ˜ฏๅฆๆญฃ็กฎ")
55
+ except urllib.error.URLError as e:
56
+ last_error = e
57
+ if attempt < 2:
58
+ wait = 3 * (2 ** attempt)
59
+ print(f" ็ฝ‘็ปœ้”™่ฏฏ๏ผŒ{wait}s ๅŽ้‡่ฏ•: {e.reason}")
60
+ time.sleep(wait)
61
+ continue
62
+ raise RuntimeError(f"ๆ— ๆณ•่ฟžๆŽฅ arxiv API๏ผŒ่ฏทๆฃ€ๆŸฅ็ฝ‘็ปœ: {e.reason}")
63
+ else:
64
+ raise RuntimeError(f"arxiv API ่ฏทๆฑ‚ๅคฑ่ดฅ (HTTP {getattr(last_error, 'code', '?')})๏ผŒ้‡่ฏ• 3 ๆฌกไปๅคฑ่ดฅ")
65
+
66
+ root = ET.fromstring(xml_text)
67
+ ns = {
68
+ "atom": "http://www.w3.org/2005/Atom",
69
+ "arxiv": "http://arxiv.org/schemas/atom",
70
+ }
71
+
72
+ entry = root.find("atom:entry", ns)
73
+ if entry is None:
74
+ raise ValueError(f"arxiv ๆœชๆ‰พๅˆฐ่ฎบๆ–‡ {arxiv_id}๏ผŒ่ฏทๆฃ€ๆŸฅ ID ๆ˜ฏๅฆๆญฃ็กฎ")
75
+
76
+ title_el = entry.find("atom:title", ns)
77
+ summary_el = entry.find("atom:summary", ns)
78
+ title = title_el.text.strip().replace("\n", " ") if title_el is not None and title_el.text else ""
79
+ abstract = summary_el.text.strip().replace("\n", " ") if summary_el is not None and summary_el.text else ""
80
+
81
+ authors = [
82
+ a.find("atom:name", ns).text
83
+ for a in entry.findall("atom:author", ns)
84
+ if a.find("atom:name", ns) is not None
85
+ ]
86
+ categories = [
87
+ c.get("term", "") for c in entry.findall("atom:category", ns)
88
+ ]
89
+ published_el = entry.find("atom:published", ns)
90
+ published = published_el.text if published_el is not None and published_el.text else ""
91
+
92
+ return {
93
+ "arxiv_id": arxiv_id,
94
+ "title": title,
95
+ "abstract": abstract,
96
+ "authors": authors,
97
+ "categories": categories,
98
+ "published": published,
99
+ }
100
+
101
+
102
+ if __name__ == "__main__":
103
+ # ่‡ชๆต‹๏ผš็”จ PaDiM ่ฎบๆ–‡ๆต‹่ฏ•
104
+ test_url = "https://arxiv.org/abs/2011.08785"
105
+ paper = fetch_paper_info(test_url)
106
+ print("=== paper_fetcher ่‡ชๆต‹ ===")
107
+ for k, v in paper.items():
108
+ val = str(v)
109
+ if len(val) > 120:
110
+ val = val[:120] + "..."
111
+ print(f" {k}: {val}")
112
+ print(" ๆ‰€ๆœ‰ๅฟ…้œ€ๅญ—ๆฎต:",
113
+ all(k in paper for k in ["arxiv_id", "title", "abstract", "authors"]))
repo_evaluator.py ADDED
@@ -0,0 +1,268 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # repo_evaluator.py
2
+ # ============================================================
3
+ # ็ฑปๅž‹๏ผšAGENT๏ผˆไน™่ดŸ่ดฃ๏ผ‰
4
+ # ๅŠŸ่ƒฝ๏ผšLLM ๅ…ญ็ปดๅบฆ่ฏ„ๅˆ†๏ผˆๅฏๅค็Žฐๆ€ง 5 ็ปด + ๅฏนๆฏ”ๅฎž้ชŒ้€‚้…ๅบฆ 1 ็ปด๏ผ‰
5
+ # ็”จๆณ•๏ผšpython repo_evaluator.py๏ผˆ่‡ชๆต‹๏ผ‰
6
+ # ============================================================
7
+ from llm_utils import call_llm_json, parse_json_safe, validate_eval_output, fix_windows_encoding
8
+
9
+ EVAL_SYSTEM_PROMPT = """ไฝ ๆ˜ฏไธ€ไธช AI ่ฎบๆ–‡ไปฃ็ ่ฏ„ไผฐไธ“ๅฎถใ€‚ไฝ ้œ€่ฆๅŒๆ—ถ่ฏ„ไผฐไธ€ไธช GitHub ไป“ๅบ“็š„ไธคไธชๆ–น้ข๏ผš
10
+ - **ๅฏๅค็Žฐๆ€ง**๏ผš่ฟ™ไธชไป“ๅบ“็š„ไปฃ็ ่ƒฝไธ่ƒฝ่ขซไป–ไบบๆˆๅŠŸ่ท‘้€š๏ผŸ
11
+ - **ๅฏนๆฏ”ๅฎž้ชŒ้€‚้…ๅบฆ**๏ผš่ฟ™ไธชไป“ๅบ“้€‚ไธ้€‚ๅˆ็›ดๆŽฅๆ‹ฟๆฅๅ†™่ฎบๆ–‡ๅšๅฏนๆฏ”ๅฎž้ชŒ๏ผˆbaseline / comparison๏ผ‰๏ผŸ
12
+
13
+ ## ่ฏ„ไผฐๆก†ๆžถ๏ผˆๅ…ญ็ปดๅบฆ๏ผŒๆ€ปๅˆ† 100๏ผ‰
14
+
15
+ ### ๅฏๅค็Žฐๆ€ง๏ผˆไบ”็ปดๅบฆ๏ผŒๅˆ่ฎก 80 ๅˆ†๏ผ‰
16
+
17
+ 1. **็Žฏๅขƒ้…็ฝฎๅฎŒๆ•ดๆ€ง๏ผˆ0-15 ๅˆ†๏ผ‰**
18
+ - 15 ๅˆ†๏ผšๆไพ›ไบ† requirements.txt / environment.yml๏ผŒๆ‰€ๆœ‰ไพ่ต–็‰ˆๆœฌๅทๆ˜Ž็กฎ
19
+ - 8-14 ๅˆ†๏ผšๆœ‰ไพ่ต–ๆ–‡ไปถไฝ†้ƒจๅˆ†็‰ˆๆœฌๅทไฝฟ็”จ่Œƒๅ›ด๏ผˆ>=, ~=๏ผ‰ๆˆ–็ผบๅคฑ
20
+ - 0-7 ๅˆ†๏ผšๆฒกๆœ‰ไพ่ต–ๆ–‡ไปถๆˆ–ไธฅ้‡ไธๅฎŒๆ•ด
21
+
22
+ 2. **ๆ–‡ๆกฃ่ดจ้‡๏ผˆ0-20 ๅˆ†๏ผ‰**
23
+ - 16-20 ๅˆ†๏ผšๆœ‰ๆธ…ๆ™ฐ็š„ๅฎ‰่ฃ…ๆญฅ้ชคใ€่ฎญ็ปƒๅ‘ฝไปคใ€ๆŽจ็†ๅ‘ฝไปคใ€ๆ•ฐๆฎ้›†่Žทๅ–ๆ–นๅผ
24
+ - 8-15 ๅˆ†๏ผšๆœ‰ๅŸบๆœฌ่ฏดๆ˜Žไฝ†ๅ…ณ้”ฎๆญฅ้ชค็ผบๅคฑ๏ผˆๅฆ‚ๆฒก่ฏดๆ•ฐๆฎ้›†ๅœจๅ“ชไธ‹่ฝฝ๏ผ‰
25
+ - 0-7 ๅˆ†๏ผšREADME ๅชๆœ‰้กน็›ฎ็ฎ€ไป‹๏ผŒๆฒกๆœ‰ๅฏๆ‰ง่กŒๅ‘ฝไปค
26
+
27
+ 3. **ไปฃ็ ๅฏ็”จๆ€ง๏ผˆ0-20 ๅˆ†๏ผ‰**
28
+ - 16-20 ๅˆ†๏ผš่ฎญ็ปƒ่„šๆœฌ + ๆŽจ็†่„šๆœฌ + ้…็ฝฎๆ–‡ไปถ + ๆจกๅž‹ๅฎšไน‰ๅ…ฅๅฃๅฎŒๆ•ด
29
+ - 8-15 ๅˆ†๏ผšๆœ‰ๆ ธๅฟƒไปฃ็ ไฝ†็ผบๅฐ‘ๅ…ณ้”ฎ็ป„ไปถ๏ผˆๅฆ‚ๅชๆœ‰ๆŽจ็†ๆฒกๆœ‰่ฎญ็ปƒ๏ผ‰
30
+ - 0-7 ๅˆ†๏ผšไปฃ็ ๆฎ‹็ผบ๏ผŒๅชๆœ‰้ชจๆžถๆˆ– shell ่„šๆœฌ
31
+
32
+ 4. **็คพๅŒบๆดป่ทƒๅบฆ๏ผˆ0-10 ๅˆ†๏ผ‰**
33
+ - 8-10 ๅˆ†๏ผšStar > 500๏ผŒๆœ€่ฟ‘ๅŠๅนดๆœ‰ commit๏ผŒIssue ๆœ‰ไบบๅ›žๅค
34
+ - 4-7 ๅˆ†๏ผšๆœ‰ไธ€ๅฎšๅ…ณๆณจๅบฆไฝ†ๆ›ดๆ–ฐไธ้ข‘็น
35
+ - 0-3 ๅˆ†๏ผš้•ฟๆœŸไธๆ›ดๆ–ฐ๏ผŒStar < 10
36
+
37
+ 5. **ไพ่ต–ๅฅๅบทๅบฆ๏ผˆ0-15 ๅˆ†๏ผ‰**
38
+ - 12-15 ๅˆ†๏ผšไพ่ต–ๅ…จๆ˜ฏไธปๆตๆดป่ทƒๅŒ…๏ผŒ็‰ˆๆœฌ็›ธไบ’ๅ…ผๅฎน
39
+ - 6-11 ๅˆ†๏ผšๆœ‰ๅฐ‘้‡่ฟ‡ๆ—ถไพ่ต–ไฝ†ไธๅฝฑๅ“ๅฎ‰่ฃ…
40
+ - 0-5 ๅˆ†๏ผšไพ่ต–ๆถ‰ๅŠๅทฒๅœๆญข็ปดๆŠค็š„ๅŒ…๏ผˆๅฆ‚ TensorFlow 1.xใ€Python 2๏ผ‰๏ผŒๅทฒ็Ÿฅๆ— ๆณ•ๅœจ็Žฐไปฃ็Žฏๅขƒๅฎ‰่ฃ…
41
+
42
+ ### ๅฏนๆฏ”ๅฎž้ชŒ้€‚้…ๅบฆ๏ผˆไธ€็ปดๅบฆ๏ผŒ20 ๅˆ†๏ผ‰
43
+
44
+ 6. **ๅฏนๆฏ”ๅฎž้ชŒ้€‚้…ๅบฆ๏ผˆ0-20 ๅˆ†๏ผ‰**
45
+ - 16-20 ๅˆ†๏ผšๆไพ›ๆ ‡ๅ‡† benchmark ่ฏ„ไผฐ่„šๆœฌ + ้ข„่ฎญ็ปƒๆƒ้‡ไธ‹่ฝฝ + ๅ…ฌๅผ€ๆ•ฐๆฎ้›†ๆ”ฏๆŒ๏ผŒๅฏ็›ดๆŽฅ่ท‘ๅ‡บ่ฎบๆ–‡ๅธธ็”จๆŒ‡ๆ ‡๏ผŒๅฏไปฅๆ— ็ผๆ’ๅ…ฅ่ฎบๆ–‡็š„ Experiment section
46
+ - 10-15 ๅˆ†๏ผšๆœ‰่ฎญ็ปƒ่„šๆœฌๅ’Œ่ฏ„ไผฐไปฃ็ ๏ผŒไฝ†้œ€่ฆ่‡ชๅทฑๅ‡†ๅค‡ๆ•ฐๆฎๆˆ–้€‚้…ๆ•ฐๆฎ้›†ๆ ผๅผ
47
+ - 0-9 ๅˆ†๏ผšๅชๆœ‰ๆŽจ็† demoใ€ไฝฟ็”จ็งๆœ‰ๆ•ฐๆฎ้›†ใ€ๆฒกๆœ‰ๆ ‡ๅ‡†ๅŒ–่ฏ„ไผฐๆต็จ‹ใ€ๆˆ–ๆฒกๆœ‰้ข„่ฎญ็ปƒๆƒ้‡
48
+
49
+ ## Few-Shot ่ฏ„ๅˆ†้”šๅฎš็คบไพ‹
50
+
51
+ ### ็คบไพ‹ 1๏ผš้ซ˜่ดจ้‡ไป“ๅบ“๏ผˆๅฆ‚ anomalib๏ผ‰
52
+ - ไป“ๅบ“: openvinotoolkit/anomalib, 4000 Stars, 2026ๅนดๆ›ดๆ–ฐ
53
+ - README ๅซ่ฏฆ็ป†ๅฎ‰่ฃ…ๆญฅ้ชคใ€่ฎญ็ปƒๅ‘ฝไปคใ€ๆ•ฐๆฎ้›†ๅˆ—่กจ
54
+ - ไพ่ต–: requirements.txt ๅซ torch>=1.10, pytorch-lightning>=1.7, opencv-python
55
+ - ๆœ‰ๆ ‡ๅ‡†ๅŒ– benchmark ่„šๆœฌ tools/benchmark.py๏ผŒๆ”ฏๆŒ MVTec AD ็ญ‰ๆ ‡ๅ‡†ๆ•ฐๆฎ้›†
56
+
57
+ ๆญฃ็กฎ่ฏ„ๅˆ† JSON๏ผš
58
+ {
59
+ "reproducibility_score": 75,
60
+ "benchmark_fitness_score": 18,
61
+ "overall_score": 93,
62
+ "verdict": "reproducible",
63
+ "env_score": 14,
64
+ "doc_score": 18,
65
+ "code_score": 18,
66
+ "community_score": 10,
67
+ "dep_score": 15,
68
+ "benchmark_score": 18,
69
+ "reasoning": "่ฏฅไป“ๅบ“็ฑปไผผanomalib็บงๅˆซ๏ผšๆ–‡ๆกฃๅฎŒๅ–„ๆœ‰ๅฎ‰่ฃ…/่ฎญ็ปƒ/ๆ•ฐๆฎ่ฏดๆ˜Ž๏ผŒไพ่ต–ๆ–‡ไปถๅฎŒๆ•ดไธ”ไฝฟ็”จไธปๆตๆดป่ทƒๅŒ…๏ผŒ็คพๅŒบๆดป่ทƒ๏ผˆ4000+ Stars๏ผ‰๏ผŒๆไพ›ๆ ‡ๅ‡†ๅŒ–benchmark่„šๆœฌๅ’Œ้ข„่ฎญ็ปƒๆƒ้‡๏ผŒๅฏ็›ดๆŽฅ่ท‘ๅ‡บ่ฎบๆ–‡ๅธธ็”จๆŒ‡ๆ ‡ใ€‚ๅฏๅค็Žฐๆ€ง้ซ˜๏ผˆๅฎ‰่ฃ…ๅˆฐ่ฎญ็ปƒๆต็จ‹ๆธ…ๆ™ฐ๏ผ‰๏ผŒ้€‚้…ๅบฆ้ซ˜๏ผˆbenchmarkๅฏ็›ดๆŽฅ็”จไบŽๅฏนๆฏ”ๅฎž้ชŒ๏ผ‰ใ€‚",
70
+ "risks": ["้ƒจๅˆ†ไพ่ต–็‰ˆๆœฌๅทไฝฟ็”จ>=่Œƒๅ›ด๏ผŒๅฏ่ƒฝๅญ˜ๅœจbreaking change้ฃŽ้™ฉ"],
71
+ "benchmark_readiness": "ready",
72
+ "suggested_use": "ๅฏไปฅ็›ดๆŽฅไฝœไธบๅฏนๆฏ”ๅฎž้ชŒ baseline"
73
+ }
74
+
75
+ ### ็คบไพ‹ 2๏ผšไฝŽ่ดจ้‡ไป“ๅบ“๏ผˆไป…ๆŽจ็† demo๏ผ‰
76
+ - ไป“ๅบ“: someone/anomaly-demo, 15 Stars, 2024ๅนดๆ›ดๆ–ฐ
77
+ - README ๅชๆœ‰: "A simple demo. python demo.py --image path/to/image.jpg. Pretrained weights: Google Drive link"
78
+ - ๆ— ไพ่ต–ๆ–‡ไปถ
79
+ - ไป…ๆœ‰ๆŽจ็†่„šๆœฌ๏ผŒๆ— ่ฎญ็ปƒไปฃ็ ใ€ๆ— ่ฏ„ไผฐ่„šๆœฌ
80
+
81
+ ๆญฃ็กฎ่ฏ„ๅˆ† JSON๏ผš
82
+ {
83
+ "reproducibility_score": 15,
84
+ "benchmark_fitness_score": 3,
85
+ "overall_score": 18,
86
+ "verdict": "not_reproducible",
87
+ "env_score": 2,
88
+ "doc_score": 4,
89
+ "code_score": 4,
90
+ "community_score": 2,
91
+ "dep_score": 3,
92
+ "benchmark_score": 3,
93
+ "reasoning": "ๅฏๅค็Žฐๆ€งๆžไฝŽ๏ผšๆ— ไพ่ต–ๆ–‡ไปถๆ— ๆณ•ๅค็Žฐ็Žฏๅขƒ๏ผŒๆ–‡ๆกฃไป…ๆœ‰ๅ•่กŒๅ‘ฝไปค๏ผŒ้ข„่ฎญ็ปƒๆƒ้‡้“พๆŽฅๅฏ่ƒฝๅคฑๆ•ˆใ€‚้€‚้…ๅบฆๆžไฝŽ๏ผšไป…ๆœ‰ๆŽจ็†demo๏ผŒๆ— ่ฎญ็ปƒ่„šๆœฌ๏ผŒๆ— ๆ ‡ๅ‡†่ฏ„ไผฐๆต็จ‹๏ผŒๆ— ๆณ•็›ดๆŽฅ็”จไบŽๅฏนๆฏ”ๅฎž้ชŒใ€‚",
94
+ "risks": ["ๆ— ไพ่ต–ๆ–‡ไปถ", "้ข„่ฎญ็ปƒๆƒ้‡ๆฅ่‡ชGoogle Drive้“พๆŽฅๅฏ่ƒฝๅคฑๆ•ˆ", "ๆ— ่ฎญ็ปƒไปฃ็ "],
95
+ "benchmark_readiness": "not_ready",
96
+ "suggested_use": "ไป…้€‚ๅˆๅ‚่€ƒไปฃ็ ๅฎž็Žฐ๏ผŒไธ้€‚ๅˆ็›ดๆŽฅ่ท‘ๅฏนๆฏ”ๅฎž้ชŒ"
97
+ }
98
+
99
+ ## ้‡่ฆๆ้†’
100
+ - ๅฏๅค็Žฐๆ€งๅ’Œ้€‚้…ๅบฆๆ˜ฏไธคไธช็‹ฌ็ซ‹็š„็ปดๅบฆ๏ผŒๅฟ…้กปๅˆ†ๅผ€ๅˆคๆ–ญ
101
+ - ไพ‹ๅฆ‚๏ผšไธ€ไธชไป“ๅบ“ๅฏไปฅ README ๅพˆ๏ฟฝ๏ฟฝๅ–„๏ผˆๆ–‡ๆกฃ 20 ๅˆ†๏ผ‰ไฝ†ๅชๆไพ›ๆŽจ็† demo ๆฒกๆœ‰่ฎญ็ปƒ๏ผˆ้€‚้…ๅบฆ 5 ๅˆ†๏ผ‰
102
+ - ๅˆๅฆ‚๏ผšanomalib ๅœจๅฏๅค็Žฐๆ€ง๏ผˆๆ–‡ๆกฃๅฅฝใ€ๆœ‰ requirements.txt๏ผ‰ๅ’Œ้€‚้…ๅบฆ๏ผˆๆœ‰ benchmark ่„šๆœฌใ€ๆœ‰้ข„่ฎญ็ปƒๆƒ้‡๏ผ‰้ƒฝๅพˆ้ซ˜
103
+
104
+ ## ่พ“ๅ‡บๆ ผๅผ๏ผˆไธฅๆ ผ JSON๏ผŒ็ดงๅ‡‘ๆ ผๅผไธ่ฆ็ผฉ่ฟ›ๅ’Œๅคšไฝ™็ฉบๆ ผ๏ผ‰
105
+ {
106
+ "reproducibility_score": 80,
107
+ "benchmark_fitness_score": 20,
108
+ "overall_score": 100,
109
+ "verdict": "reproducible",
110
+ "env_score": 15,
111
+ "doc_score": 20,
112
+ "code_score": 20,
113
+ "community_score": 10,
114
+ "dep_score": 15,
115
+ "benchmark_score": 20,
116
+ "reasoning": "200ๅญ—ไปฅๅ†…็š„ไธญๆ–‡ๅˆ†ๆž๏ผŒๅฟ…้กปๅŒบๅˆ†'่ƒฝไธ่ƒฝ่ท‘'ๅ’Œ'้€‚ไธ้€‚ๅˆ่ท‘ๅฏนๆฏ”ๅฎž้ชŒ'ไธคไธชๅฑ‚้ข",
117
+ "risks": ["้ฃŽ้™ฉ1", "้ฃŽ้™ฉ2"],
118
+ "benchmark_readiness": "ready",
119
+ "suggested_use": "ๅฏไปฅ็›ดๆŽฅไฝœไธบๅฏนๆฏ”ๅฎž้ชŒ baseline"
120
+ }
121
+
122
+ ## ๅˆคๅฎš่ง„ๅˆ™
123
+ - overall_score >= 70 โ†’ verdict = "reproducible"
124
+ - 40 <= overall_score < 70 โ†’ verdict = "partially"
125
+ - overall_score < 40 โ†’ verdict = "not_reproducible"
126
+
127
+ - benchmark_score >= 16 โ†’ benchmark_readiness = "ready"
128
+ - 10 <= benchmark_score < 16 โ†’ benchmark_readiness = "partial"
129
+ - benchmark_score < 10 โ†’ benchmark_readiness = "not_ready"
130
+
131
+ ## benchmark_readiness ไธŽ suggested_use ็š„ๅฏนๅบ”ๅ…ณ็ณป
132
+ - "ready" โ†’ "ๅฏไปฅ็›ดๆŽฅไฝœไธบๅฏนๆฏ”ๅฎž้ชŒ baseline"
133
+ - "partial" โ†’ "้œ€่ฆๅฐ‘้‡ไฟฎๆ”นๅŽๅฏ็”จไบŽๅฏนๆฏ”ๅฎž้ชŒ"
134
+ - "not_ready" โ†’ "ไป…้€‚ๅˆๅ‚่€ƒไปฃ็ ๅฎž็Žฐ๏ผŒไธ้€‚ๅˆ็›ดๆŽฅ่ท‘ๅฏนๆฏ”ๅฎž้ชŒ"
135
+ """
136
+
137
+
138
+ def evaluate_repo(
139
+ repo: dict,
140
+ readme: str | None,
141
+ dependencies: dict[str, str],
142
+ method_family: str = "",
143
+ ) -> dict:
144
+ """่ฏ„ไผฐๅ•ไธชไป“ๅบ“็š„ๅฏๅค็Žฐๆ€งๅ’Œๅฏนๆฏ”ๅฎž้ชŒ้€‚้…ๅบฆใ€‚
145
+
146
+ Args:
147
+ repo: ไป“ๅบ“ๅ…ƒไฟกๆฏ dict๏ผŒๅŒ…ๅซ full_name, html_url, stars ็ญ‰
148
+ readme: README ๆ–‡ไปถๅ…จๆ–‡๏ผˆๆˆ– None๏ผ‰
149
+ dependencies: {ๆ–‡ไปถๅ: ๆ–‡ไปถๅ†…ๅฎน} ็š„ dict
150
+ method_family: ่ฏฅไป“ๅบ“ๆ‰€ๅฑž็š„ๆ–นๆณ•ๆ—ๅ็งฐ๏ผˆ็ฉบๅญ—็ฌฆไธฒ่กจ็คบๆœชๅฝ’็ฑป๏ผ‰
151
+
152
+ Returns:
153
+ dict: ๅ…ญ็ปดๅบฆ่ฏ„ๅˆ†็ป“ๆžœ
154
+ """
155
+ readme_text = (readme or "README ๆœชๆ‰พๅˆฐ")[:2500]
156
+
157
+ if dependencies:
158
+ deps_parts = []
159
+ for fname, content in dependencies.items():
160
+ truncated = content[:1000] if content else "(็ฉบๆ–‡ไปถ)"
161
+ deps_parts.append(f"### {fname}\n```\n{truncated}\n```")
162
+ deps_text = "\n\n".join(deps_parts)
163
+ else:
164
+ deps_text = "ๆœชๆ‰พๅˆฐไปปไฝ•ไพ่ต–ๆ–‡ไปถ๏ผˆrequirements.txt / environment.yml ็ญ‰๏ผ‰"
165
+
166
+ family_hint = f"\n- ๆ–นๆณ•ๆ—: {method_family}" if method_family else ""
167
+
168
+ user_prompt = f"""## ไป“ๅบ“ๅŸบๆœฌไฟกๆฏ
169
+ - ๅ็งฐ: {repo.get('full_name', 'ๆœช็Ÿฅ')}
170
+ - URL: {repo.get('html_url', '')}
171
+ - Stars: {repo.get('stars', 0)}
172
+ - ๆ่ฟฐ: {repo.get('description', 'ๆ— ๆ่ฟฐ')}
173
+ - ๆœ€ๅŽๆ›ดๆ–ฐ: {repo.get('updated_at', 'ๆœช็Ÿฅ')}
174
+ - ไธป่ฆ่ฏญ่จ€: {repo.get('language', 'ๆœช็Ÿฅ')}{family_hint}
175
+
176
+ ## README ๅ†…ๅฎน
177
+ {readme_text}
178
+
179
+ ## ไพ่ต–ๆ–‡ไปถ
180
+ {deps_text}
181
+
182
+ ่ฏท่ฏ„ไผฐ่ฏฅไป“ๅบ“็š„ๅฏๅค็Žฐๆ€งๅ’Œๅฏนๆฏ”ๅฎž้ชŒ้€‚้…ๅบฆใ€‚"""
183
+
184
+ raw = call_llm_json(EVAL_SYSTEM_PROMPT, user_prompt, temperature=0.3, max_tokens=4000)
185
+ data = parse_json_safe(raw, "repo_evaluator")
186
+ return validate_eval_output(data)
187
+
188
+
189
+ # ============================================================
190
+ # ่‡ชๆต‹
191
+ # ============================================================
192
+ if __name__ == "__main__":
193
+ fix_windows_encoding()
194
+
195
+ # ๆต‹่ฏ• 1๏ผšanomalib๏ผˆๅทฅไธš็ผบ้™ทๆฃ€ๆต‹ๆ˜Žๆ˜Ÿ้กน็›ฎ๏ผŒ้ข„ๆœŸ้ซ˜ๅˆ†๏ผ‰
196
+ print("=" * 60)
197
+ print("ๆต‹่ฏ• 1๏ผšOpenVINO anomalib๏ผˆ้ข„ๆœŸ๏ผšๅฏๅค็Žฐๆ€ง้ซ˜ + ้€‚้…ๅบฆ้ซ˜๏ผ‰")
198
+ print("=" * 60)
199
+ mock_repo = {
200
+ "full_name": "openvinotoolkit/anomalib",
201
+ "html_url": "https://github.com/openvinotoolkit/anomalib",
202
+ "stars": 4000,
203
+ "description": "An anomaly detection library comprising state-of-the-art algorithms and features such as experiment management, hyper-parameter optimization, and edge inference.",
204
+ "updated_at": "2026-01-15T00:00:00Z",
205
+ "language": "Python",
206
+ }
207
+ mock_readme = """# Anomalib
208
+ A library for benchmarking, developing and deploying deep learning based anomaly detection algorithms.
209
+
210
+ ## Installation
211
+ ```
212
+ pip install anomalib
213
+ ```
214
+
215
+ ## Training
216
+ ```python
217
+ from anomalib.engine import Engine
218
+ engine = Engine()
219
+ engine.train()
220
+ ```
221
+
222
+ ## Supported Datasets
223
+ - MVTec AD
224
+ - BTAD
225
+ - KSDD2
226
+
227
+ ## Benchmarking
228
+ Use `tools/benchmark.py` for standardized evaluation across all models.
229
+ """
230
+ mock_deps = {"requirements.txt": "torch>=1.10\npytorch-lightning>=1.7\nopencv-python\nnumpy\nscikit-learn"}
231
+ result = evaluate_repo(mock_repo, mock_readme, mock_deps, "Embedding-based")
232
+ print(f" ็ปผๅˆ: {result.get('overall_score')}/100")
233
+ print(f" ๅฏๅค็Žฐๆ€ง: {result.get('reproducibility_score')}/80")
234
+ print(f" ้€‚้…ๅบฆ: {result.get('benchmark_fitness_score')}/20")
235
+ print(f" ๅˆคๅฎš: {result.get('verdict')} | ๅฎž้ชŒๅฐฑ็ปช: {result.get('benchmark_readiness')}")
236
+ print(f" ๅˆ†ๆž: {result.get('reasoning')}")
237
+
238
+ print()
239
+
240
+ # ๆต‹่ฏ• 2๏ผšๅชๆœ‰ๆŽจ็† demo ็š„ไธชไบบ้กน็›ฎ๏ผˆ้ข„ๆœŸไฝŽ้€‚้…ๅบฆ๏ผ‰
241
+ print("=" * 60)
242
+ print("ๆต‹่ฏ• 2๏ผšไป…ๆŽจ็†็š„ไธชไบบ้กน็›ฎ๏ผˆ้ข„ๆœŸ๏ผšๅฏๅค็Žฐๆ€งไธญ็ญ‰ + ้€‚้…ๅบฆไฝŽ๏ผ‰")
243
+ print("=" * 60)
244
+ mock_repo2 = {
245
+ "full_name": "someone/anomaly-detection-demo",
246
+ "html_url": "https://github.com/someone/anomaly-detection-demo",
247
+ "stars": 15,
248
+ "description": "A simple demo of anomaly detection using pretrained models.",
249
+ "updated_at": "2024-03-01T00:00:00Z",
250
+ "language": "Python",
251
+ }
252
+ mock_readme2 = """# Anomaly Detection Demo
253
+ A simple inference demo.
254
+
255
+ ## Usage
256
+ ```
257
+ python demo.py --image path/to/image.jpg
258
+ ```
259
+
260
+ Pretrained weights: download from Google Drive (link may expire).
261
+ """
262
+ mock_deps2 = {}
263
+ result2 = evaluate_repo(mock_repo2, mock_readme2, mock_deps2, "Embedding-based")
264
+ print(f" ็ปผๅˆ: {result2.get('overall_score')}/100")
265
+ print(f" ๅฏๅค็Žฐๆ€ง: {result2.get('reproducibility_score')}/80")
266
+ print(f" ้€‚้…ๅบฆ: {result2.get('benchmark_fitness_score')}/20")
267
+ print(f" ๅˆคๅฎš: {result2.get('verdict')} | ๅฎž้ชŒๅฐฑ็ปช: {result2.get('benchmark_readiness')}")
268
+ print(f" ๅปบ่ฎฎ: {result2.get('suggested_use')}")
repo_fetcher.py ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import base64
3
+ import requests
4
+
5
+ API_BASE = "https://api.github.com"
6
+
7
+ DEPENDENCY_FILENAMES = [
8
+ "requirements.txt", "environment.yml", "environment.yaml",
9
+ "setup.py", "setup.cfg", "pyproject.toml",
10
+ "Pipfile", "Pipfile.lock", "Dockerfile", "Makefile", "CMakeLists.txt",
11
+ ]
12
+
13
+
14
+ def _get_headers():
15
+ token = os.getenv("GITHUB_TOKEN", "")
16
+ headers = {"Accept": "application/vnd.github.v3+json"}
17
+ if token:
18
+ headers["Authorization"] = f"Bearer {token}"
19
+ return headers
20
+
21
+
22
+ def _github_get(path: str) -> dict | None:
23
+ """็ปŸไธ€็š„ GitHub API GET ่ฏทๆฑ‚๏ผŒๅคฑ่ดฅ่ฟ”ๅ›ž None"""
24
+ headers = _get_headers()
25
+ url = f"{API_BASE}{path}"
26
+ try:
27
+ resp = requests.get(url, headers=headers, timeout=15)
28
+ if resp.status_code == 404:
29
+ return None
30
+ if resp.status_code in (403, 429):
31
+ print(f"[่ญฆๅ‘Š] GitHub API ้™้€Ÿ: {path}")
32
+ return None
33
+ resp.raise_for_status()
34
+ return resp.json()
35
+ except Exception as e:
36
+ print(f"[่ญฆๅ‘Š] GitHub API ่ฏทๆฑ‚ๅคฑ่ดฅ ({path}): {e}")
37
+ return None
38
+
39
+
40
+ def fetch_readme(owner: str, name: str) -> str | None:
41
+ """่Žทๅ–ไป“ๅบ“ README ๆ–‡ไปถๅ†…ๅฎน"""
42
+ try:
43
+ data = _github_get(f"/repos/{owner}/{name}/readme")
44
+ if not data:
45
+ return None
46
+ content = data.get("content", "")
47
+ return base64.b64decode(content).decode("utf-8", errors="replace")
48
+ except Exception:
49
+ return None
50
+
51
+
52
+ def fetch_dependencies(owner: str, name: str) -> dict[str, str]:
53
+ """่Žทๅ–ไป“ๅบ“็š„ไพ่ต–ๆ–‡ไปถ
54
+
55
+ Returns:
56
+ dict: {ๆ–‡ไปถๅ: ๆ–‡ไปถๅ†…ๅฎน}๏ผŒๆ‰พไธๅˆฐไปปไฝ•ไพ่ต–ๆ–‡ไปถๆ—ถ่ฟ”ๅ›ž {}
57
+ """
58
+ result = {}
59
+ try:
60
+ contents = _github_get(f"/repos/{owner}/{name}/contents") or []
61
+ for item in contents:
62
+ fname = item.get("name", "")
63
+ if fname in DEPENDENCY_FILENAMES:
64
+ file_data = _github_get(f"/repos/{owner}/{name}/contents/{fname}")
65
+ if file_data and file_data.get("content"):
66
+ try:
67
+ decoded = base64.b64decode(file_data["content"]).decode("utf-8", errors="replace")
68
+ result[fname] = decoded
69
+ except Exception:
70
+ pass
71
+ except Exception:
72
+ pass
73
+ return result
74
+
75
+
76
+ if __name__ == "__main__":
77
+ # ่‡ชๆต‹๏ผšๅฏน anomalib ไป“ๅบ“่ฐƒ็”จ
78
+ owner, name = "openvinotoolkit", "anomalib"
79
+ print("=== repo_fetcher ่‡ชๆต‹ ===")
80
+
81
+ readme = fetch_readme(owner, name)
82
+ print(f"README ้•ฟๅบฆ: {len(readme) if readme else 0} ๅญ—็ฌฆ")
83
+ if readme:
84
+ print(f" ๅ‰ 200 ๅญ—็ฌฆ: {readme[:200]}...")
85
+
86
+ deps = fetch_dependencies(owner, name)
87
+ print(f"ไพ่ต–ๆ–‡ไปถ: {len(deps)} ไธช โ€” {list(deps.keys())}")
88
+ for fname, content in deps.items():
89
+ print(f" {fname}: {len(content)} ๅญ—็ฌฆ")
repo_searcher.py ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import requests
2
+ import os
3
+
4
+
5
+ def search_repos(queries: list[str], max_per_keyword: int = 5) -> list[dict]:
6
+ """ๅœจ GitHub ๆœ็ดขไป“ๅบ“
7
+
8
+ Args:
9
+ queries: ๆœ็ดขๆŸฅ่ฏขๅญ—็ฌฆไธฒๅˆ—่กจ
10
+ max_per_keyword: ๆฏไธชๆŸฅ่ฏขๆœ€ๅคšไฟ็•™็š„ไป“ๅบ“ๆ•ฐ
11
+
12
+ Returns:
13
+ list[dict]: ๅทฒๅŽป้‡๏ผˆๆŒ‰ full_name๏ผ‰ใ€ๆŒ‰ stars ้™ๅบๆŽ’ๅˆ—็š„ๅ€™้€‰ไป“ๅบ“ๅˆ—่กจใ€‚
14
+ ๆฏไธช dict ๅŒ…ๅซ: full_name, html_url, description, stars,
15
+ language, updated_at, topics, match_keyword
16
+ """
17
+ token = os.getenv("GITHUB_TOKEN", "")
18
+ headers = {"Accept": "application/vnd.github.v3+json"}
19
+ if token:
20
+ headers["Authorization"] = f"Bearer {token}"
21
+
22
+ seen = set()
23
+ results = []
24
+
25
+ for kw in queries:
26
+ query = f"{kw} language:python stars:>=5"
27
+ url = "https://api.github.com/search/repositories"
28
+ params = {"q": query, "sort": "stars", "order": "desc", "per_page": max_per_keyword}
29
+
30
+ try:
31
+ resp = requests.get(url, headers=headers, params=params, timeout=15)
32
+ if resp.status_code == 403 and "rate limit" in resp.text.lower():
33
+ print(f"[่ญฆๅ‘Š] GitHub ๆœ็ดข้™้€Ÿ๏ผŒ่ทณ่ฟ‡ๅ…ณ้”ฎ่ฏ: {kw}")
34
+ continue
35
+ resp.raise_for_status()
36
+ data = resp.json()
37
+ except Exception as e:
38
+ print(f"[่ญฆๅ‘Š] ๆœ็ดขๅ…ณ้”ฎ่ฏ '{kw}' ๅคฑ่ดฅ: {e}")
39
+ continue
40
+
41
+ for item in data.get("items", []):
42
+ full_name = item["full_name"]
43
+ if full_name not in seen:
44
+ seen.add(full_name)
45
+ results.append({
46
+ "full_name": full_name,
47
+ "html_url": item["html_url"],
48
+ "description": item.get("description", ""),
49
+ "stars": item.get("stargazers_count", 0),
50
+ "language": item.get("language", ""),
51
+ "updated_at": item.get("updated_at", ""),
52
+ "topics": item.get("topics", []),
53
+ "match_keyword": kw,
54
+ })
55
+
56
+ results.sort(key=lambda r: r["stars"], reverse=True)
57
+ return results
58
+
59
+
60
+ if __name__ == "__main__":
61
+ # ่‡ชๆต‹๏ผš็”จๅ‡ ไธชๆœ็ดข่ฏ่ฐƒ็”จ
62
+ test_queries = [
63
+ "padim anomaly detection pytorch",
64
+ "patch distribution modeling anomaly detection",
65
+ ]
66
+ repos = search_repos(test_queries, max_per_keyword=3)
67
+ print(f"=== repo_searcher ่‡ชๆต‹ ===\nๆ‰พๅˆฐ {len(repos)} ไธชไป“ๅบ“:")
68
+ for r in repos:
69
+ print(f" {r['full_name']} โญ{r['stars']} โ€” {r.get('match_keyword', '?')}")
requirements.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ gradio>=6.0
2
+ openai>=2.0
3
+ requests>=2.30
run.py ADDED
@@ -0,0 +1,489 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # run.py
2
+ # ============================================================
3
+ # ็ฑปๅž‹๏ผš่ฐƒๅบฆๅ™จ๏ผˆไน™่ดŸ่ดฃ๏ผ‰
4
+ # ๅŠŸ่ƒฝ๏ผšๆŒ‰"ๅ…ˆๆœๅ†ๅˆ†ๆž"ๆต็จ‹ไธฒ่” Workflow๏ผˆ็”ฒ๏ผ‰ๅ’Œ Agent๏ผˆไน™๏ผ‰
5
+ # ็”จๆณ•๏ผšpython run.py <arxiv_url>
6
+ # ็คบไพ‹๏ผšpython run.py https://arxiv.org/abs/2011.08785
7
+ # ============================================================
8
+ import sys
9
+ import time
10
+ import re
11
+ import os
12
+ from concurrent.futures import ThreadPoolExecutor, as_completed
13
+
14
+ # ่‡ชๅŠจๅŠ ่ฝฝ .env ๆ–‡ไปถ
15
+ _ENV_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), ".env")
16
+ if os.path.isfile(_ENV_PATH):
17
+ with open(_ENV_PATH, "r", encoding="utf-8") as _f:
18
+ for _line in _f:
19
+ _line = _line.strip()
20
+ if _line and not _line.startswith("#") and "=" in _line:
21
+ _key, _val = _line.split("=", 1)
22
+ if _key not in os.environ:
23
+ os.environ[_key] = _val.strip().strip('"').strip("'")
24
+
25
+ # ---- ๅฏผๅ…ฅ็”ฒ็š„ Workflow ๆจกๅ— ----
26
+ try:
27
+ from paper_fetcher import fetch_paper_info
28
+ except ImportError as e:
29
+ raise ImportError(
30
+ f"็ผบๅฐ‘ๆจกๅ—: paper_fetcher.py๏ผˆ็”ฒ่ดŸ่ดฃ๏ผ‰๏ผŒ่ฏท็ญ‰ๅพ…็”ฒไบคไป˜ใ€‚\n"
31
+ f" ๅŽŸๅง‹้”™่ฏฏ: {e}"
32
+ ) from e
33
+
34
+ try:
35
+ from repo_searcher import search_repos
36
+ except ImportError as e:
37
+ raise ImportError(
38
+ f"็ผบๅฐ‘ๆจกๅ—: repo_searcher.py๏ผˆ็”ฒ่ดŸ่ดฃ๏ผ‰๏ผŒ่ฏท็ญ‰ๅพ…็”ฒไบคไป˜ใ€‚\n"
39
+ f" ๅŽŸๅง‹้”™่ฏฏ: {e}"
40
+ ) from e
41
+
42
+ try:
43
+ from repo_fetcher import fetch_readme, fetch_dependencies
44
+ except ImportError as e:
45
+ raise ImportError(
46
+ f"็ผบๅฐ‘ๆจกๅ—: repo_fetcher.py๏ผˆ็”ฒ่ดŸ่ดฃ๏ผ‰๏ผŒ่ฏท็ญ‰ๅพ…็”ฒไบคไป˜ใ€‚\n"
47
+ f" ๅŽŸๅง‹้”™่ฏฏ: {e}"
48
+ ) from e
49
+
50
+ from direction_analyzer import analyze_direction
51
+ from repo_evaluator import evaluate_repo
52
+ from llm_utils import call_llm_json, parse_json_safe
53
+
54
+
55
+ def run(arxiv_url: str, top_n: int = 5) -> dict:
56
+ """ไธปๅ…ฅๅฃ๏ผšๅ…ˆๆœ็ดข GitHub โ†’ ๅ†ๅŸบไบŽๅฎž้™…ไป“ๅบ“ๅฝ’็บณๆ–นๆณ•ๆ— โ†’ ๆœ€ๅŽ่ฏ„ไผฐใ€‚
57
+
58
+ ๆ–ฐๆต็จ‹๏ผˆๆ–นๆกˆ B๏ผ‰๏ผš
59
+ ็ฌฌไธ€ๅฑ‚๏ผš่ฎบๆ–‡ไฟกๆฏ่Žทๅ– (Workflow)
60
+ ็ฌฌไบŒๅฑ‚๏ผšๅฎฝๆณ› GitHub ๆœ็ดข (Workflow)
61
+ ็ฌฌไธ‰ๅฑ‚๏ผšๅŸบไบŽไป“ๅบ“ๆ•ฐๆฎๅฝ’็บณๆ–นๆณ•ๆ— (Agent 1) โ† ไธๅ†ๆ˜ฏ LLM ๅ‡ญ่ฎฐๅฟ†
62
+ ็ฌฌๅ››ๅฑ‚๏ผšไป“ๅบ“่ฏ„ไผฐ (Agent 2)
63
+
64
+ Args:
65
+ arxiv_url: arxiv ่ฎบๆ–‡ URL
66
+ top_n: ๆœ€็ปˆ่ฏ„ไผฐ็š„ไป“ๅบ“ๆ•ฐ้‡
67
+
68
+ Returns:
69
+ dict: {"paper": {...}, "direction": {...}, "repos": [{...}], "error": "..."}
70
+ """
71
+ t_start = time.time()
72
+
73
+ # ================================================================
74
+ # [1/5] ่ฎบๆ–‡ไฟกๆฏ่Žทๅ–๏ผˆ็”ฒ Workflow๏ผ‰
75
+ # ================================================================
76
+ print("=" * 60)
77
+ print("[1/5] ๆญฃๅœจ่Žทๅ–่ฎบๆ–‡ไฟกๆฏ...")
78
+ t0 = time.time()
79
+ try:
80
+ paper = fetch_paper_info(arxiv_url)
81
+ except Exception as e:
82
+ return {"error": f"่Žทๅ–่ฎบๆ–‡ไฟกๆฏๅคฑ่ดฅ: {e}"}
83
+
84
+ title = paper.get("title", "")
85
+ abstract = paper.get("abstract", "")
86
+ print(f" ๆ ‡้ข˜: {title[:100]}...")
87
+ print(f" ไฝœ่€…: {', '.join(paper.get('authors', [])[:3])}")
88
+ print(f" ๅˆ†็ฑป: {', '.join(paper.get('categories', []))}")
89
+ print(f" ่€—ๆ—ถ: {time.time() - t0:.1f}s")
90
+
91
+ # ================================================================
92
+ # [2/5] ๅฎฝๆณ› GitHub ๆœ็ดข๏ผˆ็”ฒ Workflow๏ผ‰
93
+ # ================================================================
94
+ print()
95
+ print("[2/5] ๆญฃๅœจๅฎฝๆณ›ๆœ็ดข GitHub๏ผˆๅ…ˆๆœไป“ๅบ“๏ผŒๅ†่ฎฉ Agent ๅˆ†ๆž๏ผ‰...")
96
+ t0 = time.time()
97
+
98
+ broad_queries = _extract_broad_queries(title, abstract)
99
+ print(f" ็”Ÿๆˆ {len(broad_queries)} ไธชๅฎฝๆณ›ๆœ็ดข่ฏ:")
100
+ for q in broad_queries:
101
+ print(f" - {q}")
102
+
103
+ try:
104
+ broad_results = search_repos(broad_queries, max_per_keyword=5)
105
+ except Exception as e:
106
+ return {
107
+ "paper": paper,
108
+ "error": f"GitHub ๆœ็ดขๅคฑ่ดฅ: {e}",
109
+ }
110
+
111
+ print(f" ๅŽป้‡ๅŽ่Žทๅพ— {len(broad_results)} ไธชๅ€™้€‰ไป“ๅบ“")
112
+ print(f" ่€—ๆ—ถ: {time.time() - t0:.1f}s")
113
+
114
+ if not broad_results:
115
+ return {
116
+ "paper": paper,
117
+ "direction": {},
118
+ "repos": [],
119
+ "error": "ๆœชๆ‰พๅˆฐ็›ธๅ…ณๅผ€ๆบไป“ๅบ“ใ€‚่ฏฅๆ–นๅ‘ๅฏ่ƒฝๅคชๆ–ฐๆˆ–ๅคชๅ†ท้—จ๏ผŒๆš‚ๆ— ้ซ˜่ดจ้‡ๅผ€ๆบๅฎž็Žฐใ€‚",
120
+ }
121
+
122
+ # ================================================================
123
+ # [2.5] LLM ่ฟ‡ๆปคไธ็›ธๅ…ณไป“ๅบ“
124
+ # ================================================================
125
+ print()
126
+ print("[2.5] ๆญฃๅœจ็”จ LLM ่ฟ‡ๆปคไธ็›ธๅ…ณไป“ๅบ“...")
127
+ t0 = time.time()
128
+ filtered_results = _filter_repos(title, abstract, broad_results)
129
+ print(f" ่€—ๆ—ถ: {time.time() - t0:.1f}s")
130
+
131
+ # ================================================================
132
+ # [3/5] ๅŸบไบŽไป“ๅบ“ๆ•ฐๆฎๅฝ’็บณๆ–นๆณ•ๆ—๏ผˆAgent 1๏ผ‰ โ† ๆ ธๅฟƒๆ”นๅŠจ
133
+ # ================================================================
134
+ print()
135
+ print(f"[3/5] ๆญฃๅœจๅˆ†ๆž {len(filtered_results)} ไธชไป“ๅบ“๏ผŒๅฝ’็บณๆ–นๆณ•ๆ—๏ผˆAgent 1๏ผ‰...")
136
+ t0 = time.time()
137
+
138
+ try:
139
+ direction = analyze_direction(title, abstract, filtered_results)
140
+ except Exception as e:
141
+ return {
142
+ "paper": paper,
143
+ "error": f"็ ”็ฉถๆ–นๅ‘่งฃๆžๅคฑ่ดฅ: {e}",
144
+ }
145
+
146
+ subfield = direction.get("subfield", "ๆœช็Ÿฅ")
147
+ families = direction.get("method_families", [])
148
+ print(f" ๅญ้ข†ๅŸŸ: {subfield}")
149
+ print(f" ่ถ‹ๅŠฟ: {direction.get('subfield_trend', '')[:80]}...")
150
+ print(f" ๆ–นๆณ•ๆ— ({len(families)} ไธช):")
151
+ for mf in families:
152
+ matched = mf.get("matched_repos", [])
153
+ print(f" - {mf.get('family_name', '?')}: {len(matched)} ไธชไป“ๅบ“ {matched}")
154
+ print(f" ่€—ๆ—ถ: {time.time() - t0:.1f}s")
155
+
156
+ # ================================================================
157
+ # [4/5] ็ญ›้€‰ไป“ๅบ“ + ๆž„ๅปบๆ–นๆณ•ๆ—ๅฝ’ๅฑžๆ˜ ๅฐ„
158
+ # ================================================================
159
+ print()
160
+ print(f"[4/5] ๆญฃๅœจ็ญ›้€‰ๅนถ่Žทๅ–ไป“ๅบ“่ฏฆๆƒ…...")
161
+ t0 = time.time()
162
+
163
+ # ไปŽ Agent 1 ็š„ matched_repos ไธญๅปบ็ซ‹ full_name โ†’ family_name ๆ˜ ๅฐ„
164
+ repo_family_map = {}
165
+ for mf in families:
166
+ family_name = mf.get("family_name", "")
167
+ for repo_name in mf.get("matched_repos", []):
168
+ repo_family_map[repo_name] = family_name
169
+
170
+ # ไปŽ filtered_results ไธญ็ญ›้€‰๏ผšไผ˜ๅ…ˆ้€‰ๆœ‰ๆ–นๆณ•ๆ—ๅฝ’ๅฑž็š„๏ผŒๅ†ๆŒ‰ stars ๆŽ’ๅบ
171
+ classified = [r for r in filtered_results if r["full_name"] in repo_family_map]
172
+ unclassified = [r for r in filtered_results if r["full_name"] not in repo_family_map]
173
+ classified.sort(key=lambda r: r.get("stars", 0), reverse=True)
174
+ unclassified.sort(key=lambda r: r.get("stars", 0), reverse=True)
175
+
176
+ candidates = (classified + unclassified)[:top_n]
177
+
178
+ print(f" ๆœ‰ๆ–นๆณ•ๆ—ๅฝ’ๅฑž: {len(classified)} ไธช๏ผŒๆœชๅฝ’็ฑป: {len(unclassified)} ไธช")
179
+ print(f" ๆœ€็ปˆ้€‰ๅ– {len(candidates)} ไธชไป“ๅบ“:")
180
+ for i, c in enumerate(candidates):
181
+ family = repo_family_map.get(c["full_name"], "ๆœชๅฝ’็ฑป")
182
+ print(f" {i+1}. {c['full_name']} [{family}] Stars:{c.get('stars', 0)}")
183
+ print(f" ่€—ๆ—ถ: {time.time() - t0:.1f}s")
184
+
185
+ # ================================================================
186
+ # [5/5] ไป“ๅบ“่ฏฆๆƒ…่Žทๅ– + ่ฏ„ไผฐ๏ผˆAgent 2๏ผŒๅนถ่กŒ๏ผ‰
187
+ # ================================================================
188
+ print()
189
+ print(f"[5/5] ๆญฃๅœจ่Žทๅ–ไป“ๅบ“่ฏฆๆƒ…ๅนถ่ฏ„ไผฐ๏ผˆAgent 2๏ผŒ{len(candidates)} ไธชไป“ๅบ“ๅนถ่กŒ๏ผ‰...")
190
+ t0 = time.time()
191
+
192
+ def _eval_single(repo, idx, total):
193
+ """ๅ•ไธชไป“ๅบ“่ฏ„ไผฐ๏ผˆๅœจ็บฟ็จ‹ไธญๆ‰ง่กŒ๏ผ‰"""
194
+ full_name = repo["full_name"]
195
+ try:
196
+ owner, name = full_name.split("/", 1)
197
+ except ValueError:
198
+ return idx, None
199
+
200
+ matched_family = repo_family_map.get(full_name, "")
201
+ family_tag = f"[{matched_family}]" if matched_family else "[ๆœชๅฝ’็ฑป]"
202
+ print(f" ({idx+1}/{total}) {full_name} {family_tag}...")
203
+
204
+ try:
205
+ readme = fetch_readme(owner, name)
206
+ deps = fetch_dependencies(owner, name)
207
+ evaluation = evaluate_repo(repo, readme, deps, matched_family)
208
+ except Exception as e:
209
+ print(f" [WARN] {full_name} ่ฏ„ไผฐๅคฑ่ดฅ: {e}")
210
+ evaluation = _make_error_evaluation(str(e))
211
+
212
+ return idx, {
213
+ "full_name": repo.get("full_name", ""),
214
+ "html_url": repo.get("html_url", ""),
215
+ "description": repo.get("description", ""),
216
+ "stars": repo.get("stars", 0),
217
+ "language": repo.get("language", ""),
218
+ "updated_at": repo.get("updated_at", ""),
219
+ "topics": repo.get("topics", []),
220
+ "match_keyword": repo.get("match_keyword", ""),
221
+ "method_family": matched_family,
222
+ "evaluation": evaluation,
223
+ }
224
+
225
+ n = len(candidates)
226
+ evaluated = [None] * n
227
+ with ThreadPoolExecutor(max_workers=min(n, 5)) as executor:
228
+ futures = {
229
+ executor.submit(_eval_single, repo, i, n): i
230
+ for i, repo in enumerate(candidates)
231
+ }
232
+ for future in as_completed(futures):
233
+ idx, result = future.result()
234
+ if result is not None:
235
+ evaluated[idx] = result
236
+
237
+ evaluated = [r for r in evaluated if r is not None]
238
+
239
+ # ๆŒ‰็ปผๅˆ่ฏ„ๅˆ†้™ๅบๆŽ’ๅˆ—
240
+ evaluated.sort(key=lambda r: r["evaluation"].get("overall_score", 0), reverse=True)
241
+ print(f" ่€—ๆ—ถ: {time.time() - t0:.1f}s")
242
+
243
+ # ================================================================
244
+ # ๆฑ‡ๆ€ป
245
+ # ================================================================
246
+ print()
247
+ print("=" * 60)
248
+ print("ๅฎŒๆˆ๏ผ")
249
+ best = evaluated[0] if evaluated else None
250
+ if best:
251
+ print(f" ๆœ€้ซ˜ๅˆ†: {best['full_name']} ({best['evaluation'].get('overall_score', 0)}/100)")
252
+ print(f" ๆ€ป่€—ๆ—ถ: {time.time() - t_start:.1f}s")
253
+ print("=" * 60)
254
+
255
+ return {
256
+ "paper": paper,
257
+ "direction": direction,
258
+ "repos": evaluated,
259
+ }
260
+
261
+
262
+ def _extract_broad_queries(title: str, abstract: str) -> list[str]:
263
+ """็”จ LLM ไปŽ่ฎบๆ–‡ๆ ‡้ข˜ๅ’Œๆ‘˜่ฆไธญ็”Ÿๆˆ GitHub ๆœ็ดขๅ…ณ้”ฎ่ฏใ€‚
264
+
265
+ ็›ธๆฏ”่ง„ๅˆ™ๆๅ–๏ผŒLLM ็†่งฃ่ฎบๆ–‡ๅŽ่ƒฝ็”Ÿๆˆๆ›ด็ฒพๅ‡†็š„้ข†ๅŸŸๆœ็ดข่ฏใ€‚
266
+ ๅฆ‚ๆžœ LLM ่ฐƒ็”จๅคฑ่ดฅ๏ผŒ้™็บงไธบ่ง„ๅˆ™ๆๅ–ใ€‚
267
+ """
268
+ system_prompt = """ไฝ ๆ˜ฏๅญฆๆœฏ่ฎบๆ–‡ๆœ็ดขไธ“ๅฎถใ€‚ๆ นๆฎ่ฎบๆ–‡ๆ ‡้ข˜ๅ’Œๆ‘˜่ฆ๏ผŒ็”Ÿๆˆ 6-8 ไธช GitHub ๆœ็ดขๆŸฅ่ฏข๏ผŒ็”จไบŽๆ‰พๅˆฐ่ฏฅ็ ”็ฉถๆ–นๅ‘็š„ๅผ€ๆบๅฎž็Žฐใ€‚
269
+
270
+ ่ฆๆฑ‚๏ผš
271
+ - ๆŸฅ่ฏขๅฟ…้กปๆ˜ฏ่‹ฑๆ–‡๏ผŒ็”จ็ฉบๆ ผๅˆ†้š”ๅ…ณ้”ฎ่ฏ
272
+ - ๅŒ…ๅซๆ–นๆณ•ๅ/ๆŠ€ๆœฏๅ…ณ้”ฎ่ฏ + ้™ๅฎš่ฏ๏ผˆimplementation / pytorch / code๏ผ‰
273
+ - ๅŒ…ๅซ 1-2 ไธชๅฎฝๆณ›็š„้ข†ๅŸŸๆŸฅ่ฏข๏ผˆๅฆ‚ "anomaly detection pytorch library"๏ผ‰
274
+ - ไธ่ฆ็”จๅผ•ๅทๆˆ–็‰นๆฎŠ็ฌฆๅท
275
+ - ้ฟๅ…ๅคชๆณ›็š„่ฏ๏ผˆๅฆ‚ๅ•ไธ€ไธช "anomaly"๏ผ‰
276
+
277
+ ่พ“ๅ‡บไธฅๆ ผ JSON๏ผš
278
+ {"queries": ["query1", "query2", ...]}"""
279
+
280
+ user_prompt = f"""่ฎบๆ–‡ๆ ‡้ข˜: {title[:300]}
281
+ ๆ‘˜่ฆ: {abstract[:800]}
282
+
283
+ ่ฏทไธบ่ฏฅ่ฎบๆ–‡็”Ÿๆˆ GitHub ๆœ็ดขๆŸฅ่ฏขใ€‚"""
284
+
285
+ try:
286
+ raw = call_llm_json(system_prompt, user_prompt, temperature=0.3, max_tokens=500)
287
+ data = parse_json_safe(raw, "broad_queries")
288
+ queries = data.get("queries", [])
289
+ if isinstance(queries, list) and len(queries) >= 3:
290
+ return queries[:10]
291
+ except Exception as e:
292
+ print(f" [WARN] LLM ๅ…ณ้”ฎ่ฏ็”Ÿๆˆๅคฑ่ดฅ๏ผŒ้™็บงไธบ่ง„ๅˆ™ๆๅ–: {e}")
293
+
294
+ return _extract_broad_queries_fallback(title, abstract)
295
+
296
+
297
+ def _extract_broad_queries_fallback(title: str, abstract: str) -> list[str]:
298
+ """ไปŽ่ฎบๆ–‡ๆ ‡้ข˜ๅ’Œๆ‘˜่ฆไธญๆๅ–ๅฎฝๆณ›ๆœ็ดขๅ…ณ้”ฎ่ฏ๏ผˆ่ง„ๅˆ™้™็บง็‰ˆ๏ผ‰ใ€‚
299
+
300
+ ็ญ–็•ฅ๏ผšๆๅ–ๆœ‰ๆ„ไน‰็š„่ฏ็ป„ + ๆœ็ดข้™ๅฎš่ฏ๏ผŒๆŒ‰ๅคšๆ ทๆ€ง้‡‡ๆ ท๏ผˆไธๅๅ‘ๆ ‡้ข˜ๅ‰็ผ€๏ผ‰ใ€‚
301
+ ไธไพ่ต– LLM๏ผŒ็บฏ่ง„ๅˆ™ๆๅ–ใ€‚
302
+ """
303
+ abstract_first = abstract.split(".")[0] if abstract else ""
304
+ text = f"{title} {abstract_first}".lower()
305
+ text = re.sub(r'[^\w\s-]', ' ', text)
306
+ words = text.split()
307
+
308
+ stop_words = {
309
+ 'a', 'an', 'the', 'of', 'for', 'in', 'on', 'to', 'and', 'or', 'is', 'are',
310
+ 'we', 'our', 'that', 'this', 'with', 'from', 'by', 'as', 'at', 'be', 'it',
311
+ 'its', 'not', 'can', 'has', 'have', 'been', 'was', 'were', 'will', 'would',
312
+ 'could', 'should', 'may', 'do', 'does', 'did', 'so', 'if', 'no', 'new',
313
+ 'based', 'using', 'which', 'into', 'such', 'than', 'then', 'these', 'those',
314
+ 'propose', 'present', 'method', 'approach', 'framework', 'novel', 'model',
315
+ 'learning', 'deep', 'via', 'et', 'al', 'all', 'we', 'you', 'they',
316
+ }
317
+
318
+ meaningful = [w for w in words if w not in stop_words and len(w) > 1]
319
+
320
+ bigrams = [f"{meaningful[i]} {meaningful[i+1]}" for i in range(len(meaningful) - 1)]
321
+ trigrams = [f"{meaningful[i]} {meaningful[i+1]} {meaningful[i+2]}" for i in range(len(meaningful) - 2)]
322
+
323
+ def _sample_diverse(items: list[str], n: int) -> list[str]:
324
+ if len(items) <= n:
325
+ return items
326
+ step = max(1, len(items) // n)
327
+ return [items[i] for i in range(0, len(items), step)][:n]
328
+
329
+ phrases = _sample_diverse(trigrams, 3) + _sample_diverse(bigrams, 4)
330
+
331
+ seen = set()
332
+ unique_phrases = []
333
+ for p in sorted(phrases, key=len, reverse=True):
334
+ if p not in seen:
335
+ seen.add(p)
336
+ unique_phrases.append(p)
337
+
338
+ qualifiers = [
339
+ "implementation pytorch",
340
+ "official code",
341
+ "pytorch github",
342
+ ]
343
+
344
+ queries = []
345
+ for phrase in unique_phrases[:5]:
346
+ if len(phrase) > 5:
347
+ queries.append(f"{phrase} {qualifiers[0]}")
348
+
349
+ mid = len(meaningful) // 2
350
+ broad = " ".join(meaningful[max(0, mid-2):mid+2])
351
+ if len(broad) > 10:
352
+ queries.append(f"{broad} {qualifiers[2]}")
353
+
354
+ seen = set()
355
+ unique = []
356
+ for q in queries:
357
+ if q not in seen:
358
+ seen.add(q)
359
+ unique.append(q)
360
+
361
+ return unique[:10]
362
+
363
+
364
+ def _filter_repos(title: str, abstract: str, repos: list[dict]) -> list[dict]:
365
+ """็”จ LLM ๅฟซ้€Ÿ่ฟ‡ๆปคไธ็›ธๅ…ณ็š„ไป“ๅบ“๏ผŒๅชไฟ็•™ไธŽ่ฎบๆ–‡ๆ–นๅ‘็›ธๅ…ณ็š„ใ€‚
366
+
367
+ ๅœจ GitHub ๅ…ณ้”ฎ่ฏๆœ็ดขไน‹ๅŽใ€Agent 1 ๅˆ†ๆžไน‹ๅ‰่ฟ่กŒใ€‚
368
+ ๅฆ‚ๆžœ่ฟ‡ๆปคๅŽไธบ็ฉบๆˆ– LLM ่ฐƒ็”จๅคฑ่ดฅ๏ผŒ่ฟ”ๅ›žๅŽŸๅง‹ๅˆ—่กจไฝœไธบ้™็บงๆ–นๆกˆใ€‚
369
+ """
370
+ if len(repos) <= 3:
371
+ return repos
372
+
373
+ # ่ถ…่ฟ‡ 20 ไธชไป“ๅบ“ๆ—ถ๏ผŒๅชๅ– top 20๏ผˆๆŒ‰ stars๏ผ‰๏ผŒ้ฟๅ… JSON ่พ“ๅ‡บ่ฟ‡้•ฟ
374
+ if len(repos) > 20:
375
+ repos_for_filter = sorted(repos, key=lambda r: r.get("stars", 0), reverse=True)[:20]
376
+ else:
377
+ repos_for_filter = repos
378
+
379
+ # ๆž„้€ ็ผฉ็•ฅ็š„ไป“ๅบ“ๆธ…ๅ•ไพ› LLM ๅˆคๆ–ญ
380
+ repo_list_parts = []
381
+ for i, r in enumerate(repos_for_filter):
382
+ desc = (r.get("description") or "")[:120]
383
+ topics = ", ".join(r.get("topics", [])[:8])
384
+ repo_list_parts.append(
385
+ f"{i+1}. {r['full_name']} (Stars:{r.get('stars',0)})\n"
386
+ f" Description: {desc}\n"
387
+ f" Topics: {topics}"
388
+ )
389
+ repo_list_text = "\n".join(repo_list_parts)
390
+
391
+ system_prompt = """ไฝ ๆ˜ฏไธ€ไธชๅญฆๆœฏ่ฎบๆ–‡ไธŽๅผ€ๆบไปฃ็ ๅŒน้…๏ฟฝ๏ฟฝๅฎถใ€‚ไปปๅŠกๆ˜ฏๅˆคๆ–ญ GitHub ไป“ๅบ“ๆ˜ฏๅฆไธŽ็ป™ๅฎš่ฎบๆ–‡ๅฑžไบŽๅŒไธ€็ ”็ฉถๆ–นๅ‘ใ€‚
392
+
393
+ ๆŽ’้™คๆ ‡ๅ‡†๏ผˆๆปก่ถณไปปไธ€ๅณๆŽ’้™ค๏ผ‰๏ผš
394
+ - ไป“ๅบ“่งฃๅ†ณ็š„ไธšๅŠก้—ฎ้ข˜ไธŽ่ฎบๆ–‡ๅฎŒๅ…จไธๅŒ๏ผˆๅฆ‚่ฎบๆ–‡ๅšๅทฅไธš็ผบ้™ทๆฃ€ๆต‹๏ผŒไป“ๅบ“ๅšๅŒปๅญฆๅ›พๅƒ/่‡ชๅŠจ้ฉพ้ฉถ/ไบบ่„ธ่ฏ†ๅˆซ๏ผ‰
395
+ - ไป“ๅบ“ไฝฟ็”จ็š„ๆ ธๅฟƒๆŠ€ๆœฏๆ–นๆณ•ไธŽ่ฎบๆ–‡ๅฎŒๅ…จๆ— ๅ…ณ
396
+ - ไป“ๅบ“ๆ˜ฏ้€š็”จๆ•™็จ‹/่ฏพ็จ‹ไฝœไธš/้ข่ฏ•้ข˜้›†ๅˆ
397
+
398
+ ไฟ็•™ๆ ‡ๅ‡†๏ผˆๆปก่ถณไปปไธ€ๅณไฟ็•™๏ผ‰๏ผš
399
+ - ไป“ๅบ“ๅฎž็Žฐ็š„ๆ–นๆณ•ไธŽ่ฎบๆ–‡ๆ–นๆณ•ๅŒๅฑžไธ€ไธชๆŠ€ๆœฏ่Œƒๅผ
400
+ - ไป“ๅบ“ๅฏ็”จไบŽ่ฏฅ่ฎบๆ–‡็š„ๅฏนๆฏ”ๅฎž้ชŒ๏ผˆbaseline/comparison๏ผ‰
401
+ - ไป“ๅบ“ๆ˜ฏ่ฏฅ้ข†ๅŸŸ็š„็Ÿฅๅ benchmarking ๅบ“
402
+
403
+ ่พ“ๅ‡บไธฅๆ ผ JSON๏ผˆไธ่ฆ reasoning ๅญ—ๆฎตไปฅ่Š‚็œ token๏ผ‰๏ผš
404
+ {"relevant": ["owner/repo1", "owner/repo2"], "irrelevant": ["owner/repo3"]}"""
405
+
406
+ user_prompt = f"""## ่ฎบๆ–‡ไฟกๆฏ
407
+ ๆ ‡้ข˜: {title[:200]}
408
+ ๆ‘˜่ฆ: {abstract[:500]}
409
+
410
+ ## GitHub ไป“ๅบ“ๆธ…ๅ•๏ผˆๅ…ฑ {len(repos_for_filter)} ไธช๏ผ‰
411
+ {repo_list_text}
412
+
413
+ ่ฏทๅˆคๆ–ญๆฏไธชไป“ๅบ“ๆ˜ฏๅฆไธŽ่ฏฅ่ฎบๆ–‡ๆ–นๅ‘็›ธๅ…ณใ€‚"""
414
+
415
+ try:
416
+ raw = call_llm_json(system_prompt, user_prompt, temperature=0.1, max_tokens=4000)
417
+ data = parse_json_safe(raw, "filter_repos")
418
+ except Exception as e:
419
+ print(f" [WARN] ไป“ๅบ“่ฟ‡ๆปคๅคฑ่ดฅ๏ผŒไฟ็•™ๅ…จ้ƒจ: {e}")
420
+ return repos
421
+
422
+ relevant_set = set(data.get("relevant", []))
423
+ irrelevant = data.get("irrelevant", [])
424
+
425
+ if irrelevant:
426
+ print(f" [่ฟ‡ๆปค] ๅ‰”้™ค {len(irrelevant)} ไธชไธ็›ธๅ…ณไป“ๅบ“: {irrelevant}")
427
+
428
+ filtered = [r for r in repos if r["full_name"] in relevant_set]
429
+
430
+ if len(filtered) < 2:
431
+ print(f" [WARN] ่ฟ‡ๆปคๅŽไป…ๅ‰ฉ {len(filtered)} ไธชไป“ๅบ“๏ผŒๅ›ž้€€ๅˆฐๅŽŸๅง‹็ป“ๆžœ")
432
+ return repos
433
+
434
+ print(f" ่ฟ‡ๆปคๅŽไฟ็•™ {len(filtered)}/{len(repos)} ไธชไป“ๅบ“")
435
+ return filtered
436
+
437
+
438
+ def _make_error_evaluation(error_msg: str) -> dict:
439
+ """ๆž„้€ ไธ€ไธช่กจ็คบ่ฏ„ไผฐๅคฑ่ดฅ็š„ evaluation dict"""
440
+ return {
441
+ "reproducibility_score": 0,
442
+ "benchmark_fitness_score": 0,
443
+ "overall_score": 0,
444
+ "verdict": "error",
445
+ "env_score": 0,
446
+ "doc_score": 0,
447
+ "code_score": 0,
448
+ "community_score": 0,
449
+ "dep_score": 0,
450
+ "benchmark_score": 0,
451
+ "reasoning": f"่ฏ„ไผฐๅคฑ่ดฅ: {error_msg[:100]}",
452
+ "risks": ["่ฏ„ไผฐ่ฟ‡็จ‹ๅ‡บ้”™๏ผŒ่ฏทๆ‰‹ๅŠจๆฃ€ๆŸฅ่ฏฅไป“ๅบ“"],
453
+ "benchmark_readiness": "not_ready",
454
+ "suggested_use": "่ฏ„ไผฐๅคฑ่ดฅ๏ผŒ่ฏทๆ‰‹ๅŠจๆฃ€ๆŸฅ",
455
+ }
456
+
457
+
458
+ # ============================================================
459
+ # ๅ‘ฝไปค่กŒๅ…ฅๅฃ
460
+ # ============================================================
461
+ if __name__ == "__main__":
462
+ # ไฟฎๅค Windows ็ปˆ็ซฏ emoji ็ผ–็ ้—ฎ้ข˜
463
+ from llm_utils import fix_windows_encoding
464
+ fix_windows_encoding()
465
+
466
+ if len(sys.argv) < 2:
467
+ print("็”จๆณ•: python run.py <arxiv_url>")
468
+ print("็คบไพ‹: python run.py https://arxiv.org/abs/1706.03762")
469
+ print("็คบไพ‹: python run.py https://arxiv.org/abs/2011.08785")
470
+ sys.exit(1)
471
+
472
+ url = sys.argv[1]
473
+ result = run(url)
474
+
475
+ if result.get("error"):
476
+ print(f"\n{'='*60}")
477
+ print(f"่ฟ่กŒๆœชๅฎŒๅ…จๆˆๅŠŸ")
478
+ print(f"{'='*60}")
479
+ print(f" {result['error']}")
480
+
481
+ if result.get("paper"):
482
+ print(f"\n ่ฎบๆ–‡ไฟกๆฏๅทฒ่Žทๅ–: {result['paper'].get('title', '')[:80]}")
483
+ if result.get("direction"):
484
+ print(f" ๆ–นๅ‘่งฃๆžๅทฒๅฎŒๆˆ: {result['direction'].get('subfield', '')}")
485
+ else:
486
+ from app import format_report
487
+ # ็”จ utf-8 ็ผ–็ ่พ“ๅ‡บ้ฟๅ… emoji ไนฑ็ 
488
+ report = format_report(result)
489
+ print(report.encode(sys.stdout.encoding or 'utf-8', errors='replace').decode(sys.stdout.encoding or 'utf-8', errors='replace'))
state.py ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # state.py
2
+ # ============================================================
3
+ # ็ฑปๅž‹๏ผšๆ•ฐๆฎๆจกๅž‹ๅฎšไน‰๏ผˆไน™่ดŸ่ดฃ๏ผŒPPT ๅฑ•็คบ็”จ๏ผ‰
4
+ # ๅŠŸ่ƒฝ๏ผšๅฎšไน‰ๆ•ดไธช็ณป็ปŸ็š„ๆ ธๅฟƒๆ•ฐๆฎ็ป“ๆž„
5
+ # ็”จ้€”๏ผšPPT ไธญๅฑ•็คบๆžถๆž„่ฎพ่ฎก่ƒฝๅŠ›๏ผ›ไปฃ็ ไธญไธ้œ€่ฆ import๏ผŒไป…ไฝœไธบๅ‚่€ƒ
6
+ # ============================================================
7
+
8
+ from dataclasses import dataclass, field
9
+ from typing import Optional
10
+
11
+
12
+ # ---- ็ฌฌไธ€ๅฑ‚๏ผš็ ”็ฉถๆ–นๅ‘ ----
13
+
14
+ @dataclass
15
+ class MethodFamily:
16
+ """ๆ–นๆณ•ๆ—"""
17
+ family_name: str # ๅฆ‚ "Embedding-based Anomaly Detection"
18
+ description: str # ไธ€ๅฅ่ฏๆ ธๅฟƒ็‰น็‚น
19
+ representative_work: str # ไปฃ่กจ่ฎบๆ–‡๏ผŒๅฆ‚ "PaDiM (2021)"
20
+ search_queries: list[str] # GitHub ๆœ็ดข่ฏ
21
+
22
+
23
+ @dataclass
24
+ class DirectionAnalysis:
25
+ """็ ”็ฉถๆ–นๅ‘่งฃๆž็ป“ๆžœ๏ผˆAgent 1 ็š„่พ“ๅ‡บ๏ผ‰"""
26
+ subfield: str # ๅญ้ข†ๅŸŸๅ
27
+ subfield_trend: str # ๅฝ“ๅ‰่ถ‹ๅŠฟ
28
+ method_families: list[MethodFamily]
29
+ broad_queries: list[str] # ๅฎฝๆณ›ๆœ็ดข่ฏ
30
+
31
+
32
+ # ---- ็ฌฌไบŒๅฑ‚๏ผšๅ€™้€‰ไป“ๅบ“ ----
33
+
34
+ @dataclass
35
+ class CandidateRepo:
36
+ """ๅ€™้€‰ไป“ๅบ“๏ผˆ็”ฒ็š„ Workflow ่พ“ๅ‡บ๏ผ‰"""
37
+ full_name: str # "openvinotoolkit/anomalib"
38
+ html_url: str
39
+ description: str
40
+ stars: int
41
+ language: str
42
+ updated_at: str
43
+ topics: list[str]
44
+ match_keyword: str # ๅ‘ฝไธญๅ“ชไธชๆœ็ดข่ฏ
45
+ method_family: str # ๅฝ’็ฑปๅˆฐๅ“ชไธชๆ–นๆณ•ๆ—๏ผˆๅฏไธบ็ฉบ๏ผ‰
46
+ readme: Optional[str] = None
47
+ dependencies: dict[str, str] = field(default_factory=dict)
48
+
49
+
50
+ # ---- ็ฌฌไธ‰ๅฑ‚๏ผš่ฏ„ไผฐ็ป“ๆžœ ----
51
+
52
+ @dataclass
53
+ class EvalResult:
54
+ """ๅ•ไธชไป“ๅบ“็š„่ฏ„ไผฐ็ป“ๆžœ๏ผˆAgent 2 ็š„่พ“ๅ‡บ๏ผ‰"""
55
+ reproducibility_score: int # ๅฏๅค็Žฐๆ€งๆ€ปๅˆ† (0-80)
56
+ benchmark_fitness_score: int # ๅฏนๆฏ”ๅฎž้ชŒ้€‚้…ๅบฆๆ€ปๅˆ† (0-20)
57
+ overall_score: int # ็ปผๅˆๆ€ปๅˆ† (0-100)
58
+ verdict: str # reproducible / partially / not_reproducible
59
+ env_score: int # 0-15
60
+ doc_score: int # 0-20
61
+ code_score: int # 0-20
62
+ community_score: int # 0-10
63
+ dep_score: int # 0-15
64
+ benchmark_score: int # 0-20
65
+ reasoning: str # ๅˆ†ๆžๆ–‡ๆœฌ
66
+ risks: list[str] # ้ฃŽ้™ฉๅˆ—่กจ
67
+ benchmark_readiness: str # ready / partial / not_ready
68
+ suggested_use: str # ๅปบ่ฎฎ็”จ้€”
69
+
70
+
71
+ # ---- ้กถๅฑ‚๏ผšๅฎŒๆ•ด็ ”ๆŠฅ ----
72
+
73
+ @dataclass
74
+ class ResearchReport:
75
+ """ๅฎŒๆ•ด็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ็ ”ๆŠฅ๏ผˆrun.py ็š„ๆœ€็ปˆ่พ“ๅ‡บ๏ผ‰"""
76
+ paper_info: dict
77
+ direction: DirectionAnalysis
78
+ repos: list[tuple[CandidateRepo, EvalResult]]
test_agents.py ADDED
@@ -0,0 +1,294 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # test_agents.py
2
+ # ============================================================
3
+ # ็ฑปๅž‹๏ผšๆต‹่ฏ•ๅทฅๅ…ท๏ผˆไน™่ดŸ่ดฃ๏ผ‰
4
+ # ๅŠŸ่ƒฝ๏ผšไธ€้”ฎ่ฟ่กŒไธคไธช Agent ็š„่‡ชๆต‹๏ผŒ้ชŒ่ฏๅŸบ็ก€ๅŠŸ่ƒฝๆญฃๅธธ
5
+ # ็”จๆณ•๏ผšpython test_agents.py
6
+ # ============================================================
7
+ import sys
8
+ from llm_utils import fix_windows_encoding
9
+
10
+
11
+ # ๆจกๆ‹Ÿ GitHub ๆœ็ดข็ป“ๆžœ๏ผˆไพ› Agent 1 ๆต‹่ฏ•ไฝฟ็”จ๏ผ‰
12
+ MOCK_REPOS = [
13
+ {
14
+ "full_name": "openvinotoolkit/anomalib",
15
+ "html_url": "https://github.com/openvinotoolkit/anomalib",
16
+ "description": "An anomaly detection library comprising state-of-the-art algorithms.",
17
+ "stars": 4000, "language": "Python",
18
+ "topics": ["anomaly-detection", "pytorch", "benchmarking"],
19
+ },
20
+ {
21
+ "full_name": "hcw-00/PatchCore",
22
+ "html_url": "https://github.com/hcw-00/PatchCore",
23
+ "description": "Official implementation of PatchCore: Towards Total Recall in Industrial Anomaly Detection.",
24
+ "stars": 850, "language": "Python",
25
+ "topics": ["memory-bank", "anomaly-detection", "pytorch"],
26
+ },
27
+ {
28
+ "full_name": "taikiinoue45/STFPM",
29
+ "html_url": "https://github.com/taikiinoue45/STFPM",
30
+ "description": "Student-Teacher Feature Pyramid Matching for Anomaly Detection (PyTorch).",
31
+ "stars": 320, "language": "Python",
32
+ "topics": ["teacher-student", "anomaly-detection"],
33
+ },
34
+ {
35
+ "full_name": "VitjanZ/DRAEM",
36
+ "html_url": "https://github.com/VitjanZ/DRAEM",
37
+ "description": "DRAEM: Discriminatively trained reconstruction embedding for surface anomaly detection.",
38
+ "stars": 180, "language": "Python",
39
+ "topics": ["synthetic-defect", "anomaly-detection", "reconstruction"],
40
+ },
41
+ {
42
+ "full_name": "marugoto/CFLow",
43
+ "html_url": "https://github.com/marugoto/CFLow",
44
+ "description": "CFLow: Real-time unsupervised anomaly detection via conditional normalizing flows.",
45
+ "stars": 130, "language": "Python",
46
+ "topics": ["normalizing-flow", "anomaly-detection"],
47
+ },
48
+ {
49
+ "full_name": "someone/anomaly-tutorial",
50
+ "html_url": "https://github.com/someone/anomaly-tutorial",
51
+ "description": "A curated list of anomaly detection papers and resources.",
52
+ "stars": 45, "language": "Markdown",
53
+ "topics": ["awesome-list", "anomaly-detection"],
54
+ },
55
+ ]
56
+
57
+
58
+ def test_agent1():
59
+ """ๆต‹่ฏ• Agent 1๏ผšdirection_analyzer๏ผˆๆ–ฐ็‰ˆโ€”โ€”ๅŸบไบŽไป“ๅบ“ๆ•ฐๆฎๅฝ’็บณ๏ผ‰"""
60
+ from direction_analyzer import analyze_direction
61
+
62
+ print("=" * 60)
63
+ print("Agent 1 ๆต‹่ฏ•๏ผšๅŸบไบŽ GitHub ไป“ๅบ“็š„ๆ–นๆณ•ๆ—ๅฝ’็บณ")
64
+ print("=" * 60)
65
+
66
+ test_cases = [
67
+ {
68
+ "name": "PaDiM + ็œŸๅฎžไป“ๅบ“ๆ•ฐๆฎ",
69
+ "title": "PaDiM: a Patch Distribution Modeling Framework for Anomaly Detection and Localization",
70
+ "abstract": (
71
+ "We present a new framework for anomaly detection and localization based on "
72
+ "patch distribution modeling. PaDiM uses pretrained convolutional neural networks "
73
+ "to extract patch features and models their distribution using multivariate "
74
+ "Gaussian distributions. It achieves state-of-the-art results on MVTec AD and "
75
+ "STC datasets for industrial defect detection."
76
+ ),
77
+ "repos": MOCK_REPOS,
78
+ "checks": [
79
+ ("subfield ้ž็ฉบ", lambda r: bool(r.get("subfield"))),
80
+ ("ๆœ‰ >= 2 ไธชๆ–นๆณ•ๆ—", lambda r: len(r.get("method_families", [])) >= 2),
81
+ ("่‡ณๅฐ‘ 1 ไธชๆ–นๆณ•ๆ—ๆœ‰ matched_repos", lambda r: any(
82
+ len(mf.get("matched_repos", [])) >= 1
83
+ for mf in r.get("method_families", [])
84
+ )),
85
+ ("ๆœ‰ broad_queries", lambda r: len(r.get("broad_queries", [])) >= 1),
86
+ ],
87
+ },
88
+ {
89
+ "name": "Transformer (้™็บงๆจกๅผ๏ผšๆ— ไป“ๅบ“ๆ•ฐๆฎ)",
90
+ "title": "Attention Is All You Need",
91
+ "abstract": (
92
+ "The dominant sequence transduction models are based on complex recurrent "
93
+ "or convolutional neural networks... We propose a new simple network "
94
+ "architecture, the Transformer, based solely on attention mechanisms."
95
+ ),
96
+ "repos": None, # ้™็บงๆจกๅผ
97
+ "checks": [
98
+ ("subfield ้ž็ฉบ", lambda r: bool(r.get("subfield"))),
99
+ ("ๆœ‰ >= 3 ไธชๆ–นๆณ•ๆ—", lambda r: len(r.get("method_families", [])) >= 3),
100
+ ("ๆœ‰ broad_queries", lambda r: len(r.get("broad_queries", [])) >= 1),
101
+ ],
102
+ },
103
+ {
104
+ "name": "EfficientAD (่ฝป้‡ไป“ๅบ“ๆ•ฐๆฎ)",
105
+ "title": "EfficientAD: Accurate Visual Anomaly Detection at Millisecond-Level Latencies",
106
+ "abstract": (
107
+ "We propose EfficientAD, a lightweight anomaly detection method that achieves "
108
+ "millisecond-level latency. It uses a teacher-student architecture with a simple "
109
+ "student network that learns to mimic the teacher's features on normal images only."
110
+ ),
111
+ "repos": MOCK_REPOS[:4], # ๅช็ป™ 4 ไธชไป“ๅบ“
112
+ "checks": [
113
+ ("subfield ้ž็ฉบ", lambda r: bool(r.get("subfield"))),
114
+ ("ๆœ‰ subfield_trend", lambda r: len(r.get("subfield_trend", "")) >= 10),
115
+ ("ๆ–นๆณ•ๆ—ๆœ‰ search_queries", lambda r: any(
116
+ len(mf.get("search_queries", [])) >= 1
117
+ for mf in r.get("method_families", [])
118
+ )),
119
+ ],
120
+ },
121
+ ]
122
+
123
+ passed = 0
124
+ failed = 0
125
+ for tc in test_cases:
126
+ print(f"\n [{tc['name']}]")
127
+ try:
128
+ result = analyze_direction(tc["title"], tc["abstract"], tc["repos"])
129
+ subfield = result.get("subfield", "?")
130
+ families = result.get("method_families", [])
131
+ print(f" ๅญ้ข†ๅŸŸ: {subfield}")
132
+ print(f" ๆ–นๆณ•ๆ—:")
133
+ for mf in families:
134
+ print(f" - {mf.get('family_name', '?')}: matched_repos={mf.get('matched_repos', [])}")
135
+
136
+ tc_failed = False
137
+ for check_name, check_fn in tc["checks"]:
138
+ if check_fn(result):
139
+ print(f" โœ… {check_name}")
140
+ else:
141
+ print(f" โŒ {check_name}")
142
+ tc_failed = True
143
+
144
+ if tc_failed:
145
+ failed += 1
146
+ else:
147
+ passed += 1
148
+ except Exception as e:
149
+ print(f" โŒ ๅผ‚ๅธธ: {e}")
150
+ import traceback
151
+ traceback.print_exc()
152
+ failed += 1
153
+
154
+ return passed, failed
155
+
156
+
157
+ def test_agent2():
158
+ """ๆต‹่ฏ• Agent 2๏ผšrepo_evaluator"""
159
+ from repo_evaluator import evaluate_repo
160
+
161
+ print("\n" + "=" * 60)
162
+ print("Agent 2 ๆต‹่ฏ•๏ผšไป“ๅบ“่ฏ„ไผฐ")
163
+ print("=" * 60)
164
+
165
+ test_cases = [
166
+ {
167
+ "name": "anomalib (้ข„ๆœŸ้ซ˜ๅˆ†)",
168
+ "repo": {
169
+ "full_name": "openvinotoolkit/anomalib",
170
+ "html_url": "https://github.com/openvinotoolkit/anomalib",
171
+ "stars": 4000,
172
+ "description": "An anomaly detection library comprising state-of-the-art algorithms.",
173
+ "updated_at": "2026-01-15T00:00:00Z",
174
+ "language": "Python",
175
+ },
176
+ "readme": (
177
+ "# Anomalib\nA library for benchmarking anomaly detection.\n\n"
178
+ "## Installation\n```\npip install anomalib\n```\n\n"
179
+ "## Training\n```python\nfrom anomalib.engine import Engine\nengine = Engine()\nengine.train()\n```\n\n"
180
+ "## Supported Datasets\n- MVTec AD\n- BTAD\n\n"
181
+ "## Benchmarking\nUse `tools/benchmark.py` for standardized evaluation."
182
+ ),
183
+ "deps": {"requirements.txt": "torch>=1.10\npytorch-lightning>=1.7\nopencv-python\nnumpy"},
184
+ "family": "Patch Distribution Modeling",
185
+ "checks": [
186
+ ("overall_score >= 55", lambda r: r.get("overall_score", 0) >= 55),
187
+ ("verdict ้ž error", lambda r: r.get("verdict") in ("reproducible", "partially")),
188
+ ("benchmark_readiness ้ž not_ready", lambda r: r.get("benchmark_readiness") in ("ready", "partial")),
189
+ ("reasoning ้ž็ฉบ", lambda r: len(r.get("reasoning", "")) >= 10),
190
+ ],
191
+ },
192
+ {
193
+ "name": "ๆŽจ็† Demo ้กน็›ฎ (้ข„ๆœŸไฝŽๅˆ†)",
194
+ "repo": {
195
+ "full_name": "someone/anomaly-demo",
196
+ "html_url": "https://github.com/someone/anomaly-demo",
197
+ "stars": 15,
198
+ "description": "A simple demo of anomaly detection.",
199
+ "updated_at": "2024-03-01T00:00:00Z",
200
+ "language": "Python",
201
+ },
202
+ "readme": "# Anomaly Detection Demo\nJust a demo.\n\n```\npython demo.py --image path/to/image.jpg\n```\nPretrained weights: Google Drive link.",
203
+ "deps": {},
204
+ "family": "",
205
+ "checks": [
206
+ ("overall_score < 50", lambda r: r.get("overall_score", 0) < 50),
207
+ ("benchmark_readiness ไธบ not_ready", lambda r: r.get("benchmark_readiness") == "not_ready"),
208
+ ("ๆœ‰ risks ๅˆ—่กจ", lambda r: isinstance(r.get("risks"), list)),
209
+ ],
210
+ },
211
+ {
212
+ "name": "ไธญ็ญ‰้กน็›ฎ (้ƒจๅˆ†ๅฏๅค็Žฐ)",
213
+ "repo": {
214
+ "full_name": "research-lab/anomaly-pytorch",
215
+ "html_url": "https://github.com/research-lab/anomaly-pytorch",
216
+ "stars": 200,
217
+ "description": "PyTorch implementation of anomaly detection methods.",
218
+ "updated_at": "2025-08-01T00:00:00Z",
219
+ "language": "Python",
220
+ },
221
+ "readme": (
222
+ "# Anomaly Detection in PyTorch\n\n"
223
+ "## Install\n```\npip install -r requirements.txt\n```\n\n"
224
+ "## Train\n```\npython train.py --config config.yaml\n```"
225
+ ),
226
+ "deps": {"requirements.txt": "torch>=1.8\nnumpy\nopencv-python\nmatplotlib"},
227
+ "family": "Teacher-Student",
228
+ "checks": [
229
+ ("overall_score ๅœจ 40-85 ไน‹้—ด", lambda r: 40 <= r.get("overall_score", 0) <= 85),
230
+ ("verdict ้ž error", lambda r: r.get("verdict") != "error"),
231
+ ],
232
+ },
233
+ ]
234
+
235
+ passed = 0
236
+ failed = 0
237
+ for tc in test_cases:
238
+ print(f"\n [{tc['name']}]")
239
+ try:
240
+ result = evaluate_repo(tc["repo"], tc["readme"], tc["deps"], tc["family"])
241
+ print(f" ็ปผๅˆ: {result.get('overall_score')}/100")
242
+ print(f" ๅฏๅค็Žฐๆ€ง: {result.get('reproducibility_score')}/80")
243
+ print(f" ้€‚้…ๅบฆ: {result.get('benchmark_fitness_score')}/20")
244
+ print(f" ๅˆคๅฎš: {result.get('verdict')} | ๅฐฑ็ปช: {result.get('benchmark_readiness')}")
245
+
246
+ tc_failed = False
247
+ for check_name, check_fn in tc["checks"]:
248
+ if check_fn(result):
249
+ print(f" โœ… {check_name}")
250
+ else:
251
+ print(f" โŒ {check_name}")
252
+ tc_failed = True
253
+
254
+ if tc_failed:
255
+ failed += 1
256
+ else:
257
+ passed += 1
258
+ except Exception as e:
259
+ print(f" โŒ ๅผ‚ๅธธ: {e}")
260
+ import traceback
261
+ traceback.print_exc()
262
+ failed += 1
263
+
264
+ return passed, failed
265
+
266
+
267
+ # ============================================================
268
+ # ไธปๅ…ฅๅฃ
269
+ # ============================================================
270
+ if __name__ == "__main__":
271
+ fix_windows_encoding()
272
+
273
+ print("ResearchRadar ยท Agent ่‡ชๆฃ€ๅฅ—ไปถ")
274
+ print("=" * 60)
275
+
276
+ a1_pass, a1_fail = test_agent1()
277
+ a2_pass, a2_fail = test_agent2()
278
+
279
+ print("\n" + "=" * 60)
280
+ print("ๆต‹่ฏ•ๆฑ‡ๆ€ป")
281
+ print("=" * 60)
282
+ print(f" Agent 1 (ๆ–นๆณ•ๆ—ๅฝ’็บณ): {a1_pass} ้€š่ฟ‡, {a1_fail} ๅคฑ่ดฅ")
283
+ print(f" Agent 2 (ไป“ๅบ“่ฏ„ไผฐ): {a2_pass} ้€š่ฟ‡, {a2_fail} ๅคฑ่ดฅ")
284
+
285
+ total_pass = a1_pass + a2_pass
286
+ total_fail = a1_fail + a2_fail
287
+ print(f" ๆ€ป่ฎก: {total_pass} ้€š่ฟ‡, {total_fail} ๅคฑ่ดฅ")
288
+
289
+ if total_fail > 0:
290
+ print("\nโš  ้ƒจๅˆ†ๆต‹่ฏ•ๆœช้€š่ฟ‡๏ผŒ่ฏทๆฃ€ๆŸฅๅฏนๅบ” Agent ็š„่พ“ๅ‡บใ€‚")
291
+ sys.exit(1)
292
+ else:
293
+ print("\nโœ… ๆ‰€ๆœ‰ๆต‹่ฏ•้€š่ฟ‡๏ผ")
294
+ sys.exit(0)
ไน™_ๅฎž็Žฐๆ€ป็ป“.md ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ResearchRadar ยท ไน™ๅฎž็Žฐๆ€ป็ป“
2
+
3
+ > ็‰ˆๆœฌ๏ผšv2.0 | ๆ—ฅๆœŸ๏ผš2026-05-04
4
+
5
+ ## ไน™็š„่ง’่‰ฒ
6
+
7
+ ๅœจ ResearchRadar ไธ‰ไบบๅˆ†ๅทฅไธญ๏ผŒไน™่ดŸ่ดฃ**"ๆ™บ่ƒฝๅˆคๆ–ญๅฑ‚ + ่ฐƒๅบฆๅฑ‚ + ็•Œ้ขๅฑ‚"**โ€”โ€”ๅณๆ‰€ๆœ‰้œ€่ฆ่ฐƒ็”จ LLM ๅšๅ†ณ็ญ–็š„ๆจกๅ—ใ€ไธฒ่”ๅ…จๆต็จ‹็š„่ฐƒๅบฆๅ™จใ€ไปฅๅŠ็”จๆˆทไบคไบ’็•Œ้ขใ€‚
8
+
9
+ ---
10
+
11
+ ## ๅฎž็Žฐไบ†ไป€ไนˆ
12
+
13
+ ๅ…ฑ **7 ไธชๆ–‡ไปถ**๏ผŒ่ฆ†็›–ไธ‰ไธชๅฑ‚ๆฌก๏ผš
14
+
15
+ ### 1. ๅ…ฌๅ…ฑๅŸบ็ก€่ฎพๆ–ฝ โ€” `llm_utils.py`
16
+
17
+ ๆ‰€ๆœ‰ Agent ็š„ๅ…ฑไบซๅบ•ๅบง๏ผŒ่งฃๅ†ณ LLM ่ฐƒ็”จ็š„ไธ‰ไธชๆ ธๅฟƒ็—›็‚น๏ผš
18
+
19
+ | ่ƒฝๅŠ› | ๅฎž็Žฐ |
20
+ |------|------|
21
+ | **API ้Ÿงๆ€ง** | `call_llm()` โ€” 3 ๆฌกๆŒ‡ๆ•ฐ้€€้ฟ้‡่ฏ•๏ผˆ2s โ†’ 4s โ†’ 8s๏ผ‰๏ผŒๆ”ฏๆŒ `response_format` ๅผบๅˆถ JSON ๆจกๅผ |
22
+ | **JSON ๅฎน้”™** | `parse_json_safe()` โ€” 6 ็ง็ญ–็•ฅไพๆฌกๅฐ่ฏ•๏ผˆ็›ดๆŽฅ่งฃๆž โ†’ ```json``` ไปฃ็ ๅ— โ†’ ้€š็”จไปฃ็ ๅ— โ†’ ๆญฃๅˆ™ๆๅ– โ†’ ๅฐพ้ƒจ้€—ๅทไฟฎๅค โ†’ ๅ…ณ้”ฎ่ฏๅฎšไฝ๏ผ‰ |
23
+ | **่พ“ๅ‡บๅ…œๅบ•** | `validate_direction_output()` / `validate_eval_output()` โ€” ็ผบๅคฑๅญ—ๆฎต่‡ชๅŠจๅกซๅ……้ป˜่ฎคๅ€ผ๏ผŒ็ฑปๅž‹ๆ ก้ชŒ |
24
+
25
+ ### 2. Agent 1๏ผšๆ–นๆณ•ๆ—ๅฝ’็บณ โ€” `direction_analyzer.py`
26
+
27
+ **่พ“ๅ…ฅ**๏ผš่ฎบๆ–‡ๆ ‡้ข˜ + ๆ‘˜่ฆ + GitHub ๆœ็ดข็ป“ๆžœ๏ผˆไป“ๅบ“ๅˆ—่กจ๏ผ‰
28
+ **่พ“ๅ‡บ**๏ผš็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ๏ผˆๅญ้ข†ๅŸŸใ€่ถ‹ๅŠฟใ€ๆ–นๆณ•ๆ—่ฐฑ็ณป + ๆฏไธชๆ—็š„ๅฝ’ๅฑžไป“ๅบ“๏ผ‰
29
+
30
+ - **ๆ ธๅฟƒ่ฎพ่ฎก**๏ผšไธๅ†่ฎฉ LLM ๅ‡ญ่ฎฐๅฟ†ๅˆ’ๅˆ†ๆ–นๆณ•ๆ—๏ผŒ่€Œๆ˜ฏๅŸบไบŽๅฎž้™… GitHub ไป“ๅบ“ๆ•ฐๆฎๅšๅˆ†ๆžๅฝ’็บณ
31
+ - ๆฏไธชๆ–นๆณ•ๆ—้™„ๅธฆ `matched_repos`โ€”โ€”ๆ˜Ž็กฎๆŒ‡ๅ‡บๅ“ชไบ›ไป“ๅบ“ๅฑžไบŽ่ฏฅๆ—
32
+ - ๆ”ฏๆŒ้™็บงๆจกๅผ๏ผˆๆ— ไป“ๅบ“ๆ•ฐๆฎๆ—ถๅŸบไบŽ LLM ่‡ช่บซ็Ÿฅ่ฏ†๏ผ‰
33
+ - ไฝฟ็”จ `response_format: {"type": "json_object"}` + Few-Shot ็คบไพ‹
34
+
35
+ ### 3. Agent 2๏ผšไป“ๅบ“่ฏ„ไผฐ โ€” `repo_evaluator.py`
36
+
37
+ **่พ“ๅ…ฅ**๏ผšไป“ๅบ“ๅ…ƒไฟกๆฏ + README + ไพ่ต–ๆ–‡ไปถ + ๆ–นๆณ•ๆ—
38
+ **่พ“ๅ‡บ**๏ผšๅ…ญ็ปดๅบฆ่ฏ„ๅˆ†๏ผˆๆปกๅˆ† 100๏ผ‰
39
+
40
+ ่ฏ„ๅˆ†ๆก†ๆžถ๏ผš
41
+ - **ๅฏๅค็Žฐๆ€ง**๏ผˆ5 ็ปดๅบฆ๏ผŒ80 ๅˆ†๏ผ‰๏ผš็Žฏๅขƒ้…็ฝฎ(15) + ๆ–‡ๆกฃ่ดจ้‡(20) + ไปฃ็ ๅฏ็”จๆ€ง(20) + ็คพๅŒบๆดป่ทƒๅบฆ(10) + ไพ่ต–ๅฅๅบทๅบฆ(15)
42
+ - **ๅฏนๆฏ”ๅฎž้ชŒ้€‚้…ๅบฆ**๏ผˆ1 ็ปดๅบฆ๏ผŒ20 ๅˆ†๏ผ‰๏ผšbenchmark ่„šๆœฌ + ้ข„่ฎญ็ปƒๆƒ้‡ + ๆ ‡ๅ‡†ๅŒ–่ฏ„ไผฐ
43
+ - Few-Shot ่ฏ„ๅˆ†้”šๅฎš็คบไพ‹๏ผˆ้ซ˜ๅˆ† 93 / ไฝŽๅˆ† 18๏ผ‰
44
+
45
+ ### 4. ่ฐƒๅบฆๅ™จ โ€” `run.py`
46
+
47
+ **ๆ–ฐๆต็จ‹๏ผˆๅ…ˆๆœๅ†ๅˆ†ๆž๏ผ‰**๏ผš
48
+
49
+ ```
50
+ arxiv URL
51
+ โ†’ [1] paper_fetcher๏ผˆ่Žทๅ–่ฎบๆ–‡ไฟกๆฏ๏ผ‰
52
+ โ†’ [2] ๅ…ณ้”ฎ่ฏๆๅ– โ†’ repo_searcher๏ผˆๅฎฝๆณ›ๆœ็ดข GitHub๏ผ‰
53
+ โ†’ [3] direction_analyzer๏ผˆๅŸบไบŽไป“ๅบ“ๆ•ฐๆฎๅฝ’็บณๆ–นๆณ•ๆ—๏ผ‰ โ† Agent 1
54
+ โ†’ [4] repo_fetcher + repo_evaluator๏ผˆ่Žทๅ–่ฏฆๆƒ… + ๅ…ญ็ปดๅบฆ่ฏ„ๅˆ†๏ผ‰ โ† Agent 2
55
+ โ†’ [5] ๆฑ‡ๆ€ป่พ“ๅ‡บ
56
+ ```
57
+
58
+ - ไป“ๅบ“โ†’ๆ–นๆณ•ๆ—ๅฝ’ๅฑž็”ฑ Agent 1 ็š„ `matched_repos` ็›ดๆŽฅ็ป™ๅ‡บ๏ผŒไธๅ†้œ€่ฆๅ…ณ้”ฎ่ฏๆ˜ ๅฐ„
59
+ - ็บฏ่ง„ๅˆ™็š„ๅ…ณ้”ฎ่ฏๆๅ–๏ผˆ`_extract_broad_queries`๏ผ‰๏ผŒไธ้ขๅค–ๆถˆ่€— LLM ่ฐƒ็”จ
60
+
61
+ ### 5. ็•Œ้ขๅฑ‚ โ€” `app.py`
62
+
63
+ Gradio Web ็•Œ้ข๏ผš
64
+ - ่พ“ๅ…ฅๆก† + 3 ไธช็คบไพ‹ไธ€้”ฎๅกซๅ……
65
+ - `format_report()` ็”Ÿๆˆๅ››ๆฎตๅผ Markdown ็ ”ๆŠฅ๏ผš
66
+ 1. ่ฎบๆ–‡ไฟกๆฏ
67
+ 2. ็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ๏ผˆๅญ้ข†ๅŸŸ + ่ถ‹ๅŠฟ + ๆ–นๆณ•ๆ—่กจๆ ผ๏ผ‰
68
+ 3. ๅผ€ๆบๅฎž็Žฐ่ฏ„ไผฐๆŽ’ๅ๏ผˆๆฏไธชไป“ๅบ“ๅ…ญ็ปดๅบฆๆ˜Ž็ป† + ๅˆ†ๆž + ้ฃŽ้™ฉ็‚น๏ผ‰
69
+ 4. ๅฏนๆฏ”ๅฎž้ชŒๆŽจ่ๆ€ป็ป“๏ผˆๆŒ‰ ready/partial/not_ready ๅˆ†็ป„๏ผ‰
70
+
71
+ ### 6. ๆ•ฐๆฎๆจกๅž‹ โ€” `state.py`
72
+
73
+ 5 ไธช dataclass ๅฎšไน‰็ณป็ปŸๆ ธๅฟƒๆ•ฐๆฎ็ป“ๆž„๏ผš`MethodFamily` โ†’ `DirectionAnalysis` โ†’ `CandidateRepo` โ†’ `EvalResult` โ†’ `ResearchReport`
74
+
75
+ ### 7. ่‡ชๆฃ€ๆต‹่ฏ• โ€” `test_agents.py`
76
+
77
+ ไธ€้”ฎ่ฟ่กŒไธคไธช Agent ็š„่‡ชๆต‹๏ผŒ6 ไธชๆต‹่ฏ•็”จไพ‹๏ผŒๆ–ญ่จ€ + ๆฑ‡ๆ€ป๏ผŒ้ž้›ถ้€€ๅ‡บ็ ใ€‚
78
+
79
+ ---
80
+
81
+ ## ่ฎพ่ฎกๅŽŸๅˆ™
82
+
83
+ - **ๆ— ๆก†ๆžถไพ่ต–**๏ผšAgent = Python ๅ‡ฝๆ•ฐ + LLM ่ฐƒ็”จ๏ผŒไธ็”จ LangChain/LangGraph
84
+ - **ๆ•ฐๆฎๆต็ฎ€ๅ•**๏ผšdict ไผ ๅ‚๏ผŒif/else ๅˆ†ๆ”ฏ๏ผŒๆ˜พๅผไผ˜ไบŽ้šๅผ
85
+ - **้”™่ฏฏไธๅดฉๆบƒ**๏ผšLLM ่พ“ๅ‡บๅผ‚ๅธธ โ†’ ๅกซๅ……้ป˜่ฎคๅ€ผ๏ผ›API ๅคฑ่ดฅ โ†’ 3 ๆฌก้‡่ฏ•๏ผ›่ฏ„ไผฐๅคฑ่ดฅ โ†’ ๆ ‡่ฎฐ error ็ปง็ปญ
86
+ - **ๆŽฅๅฃๅฅ‘็บฆๆธ…ๆ™ฐ**๏ผšไธŽ็”ฒ็š„ๅฏนๆŽฅ้€š่ฟ‡ๅ‡ฝๆ•ฐ็ญพๅ + dict ๅญ—ๆฎตๅ็บฆๅฎš๏ผŒ่ฏฆ่ง `ไน™โ†’็”ฒ_ๅฏนๆŽฅๆ–‡ๆกฃ.md`
87
+
88
+ ---
89
+
90
+ ## ๅทฒ็กฎ่ฎคๅฏ่ฟ่กŒ
91
+
92
+ ```bash
93
+ python test_agents.py # 6/6 ้€š่ฟ‡
94
+ python direction_analyzer.py # 3 ็ฏ‡่ฎบๆ–‡็‹ฌ็ซ‹่‡ชๆต‹
95
+ python repo_evaluator.py # 3 ไธชไป“ๅบ“ๅœบๆ™ฏ็‹ฌ็ซ‹่‡ชๆต‹
96
+ ```
97
+
98
+ ---
99
+
100
+ ## ็”ฒๆจกๅ—ๅทฒ้›†ๆˆ โœ“
101
+
102
+ ็”ฒ็š„ 3 ไธช Workflow ๆจกๅ—ๅทฒไบคไป˜ๅนถ้‡ๅ‘ฝๅไธบๆญฃๅผๆ–‡ไปถๅ๏ผš
103
+ - `paper_fetcher.py` โ€” arxiv API ่ฎบๆ–‡ไฟกๆฏ่Žทๅ–
104
+ - `repo_searcher.py` โ€” GitHub ไป“ๅบ“ๆœ็ดข๏ผˆๆ”ฏๆŒๅŽป้‡ + Stars ๆŽ’ๅบ + Token ่ฎค่ฏ๏ผ‰
105
+ - `repo_fetcher.py` โ€” GitHub ไป“ๅบ“ README + ไพ่ต–ๆ–‡ไปถ่Žทๅ–
106
+
107
+ `python -c "import run"` ๅฏผๅ…ฅๆˆๅŠŸ๏ผŒๆŽฅไธ‹ๆฅๅฏ่ฟ›่กŒ็ซฏๅˆฐ็ซฏ่”่ฐƒๆต‹่ฏ•ใ€‚
ไน™โ†’็”ฒ_ๅฏนๆŽฅๆ–‡ๆกฃ.md ADDED
@@ -0,0 +1,772 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ResearchRadar ยท ไน™โ†’็”ฒ ๅฏนๆŽฅๆ–‡ๆกฃ
2
+
3
+ > ็‰ˆๆœฌ๏ผšv1.0 | ๆ—ฅๆœŸ๏ผš2026-05-04 | ๆ’ฐๅ†™๏ผšไน™
4
+
5
+ ---
6
+
7
+ ## ็›ฎๅฝ•
8
+
9
+ 1. [ไน™ๅฎŒๆˆๆƒ…ๅ†ตๆ€ป่งˆ](#1-ไน™ๅฎŒๆˆๆƒ…ๅ†ตๆ€ป่งˆ)
10
+ 2. [็”ฒ้œ€ไบคไป˜็š„3ไธชๆจกๅ—๏ผˆAPIๅฅ‘็บฆ๏ผ‰](#2-็”ฒ้œ€ไบคไป˜็š„3ไธชๆจกๅ—apiๅฅ‘็บฆ)
11
+ - [2.1 paper_fetcher.py](#21-paper_fetcherpy)
12
+ - [2.2 repo_searcher.py](#22-repo_searcherpy)
13
+ - [2.3 repo_fetcher.py](#23-repo_fetcherpy)
14
+ 3. [ๆ•ฐๆฎๆตๅ…จๆ™ฏๅ›พ](#3-ๆ•ฐๆฎๆตๅ…จๆ™ฏๅ›พ)
15
+ 4. [ๅฏนๆŽฅๆญฅ้ชค๏ผˆๆŠ€ๆœฏ + ๆฒŸ้€š๏ผ‰](#4-ๅฏนๆŽฅๆญฅ้ชคๆŠ€ๆœฏ--ๆฒŸ้€š)
16
+ 5. [ๅฏนๆŽฅๅŽๆ•ˆๆžœ](#5-ๅฏนๆŽฅๅŽๆ•ˆๆžœ)
17
+ 6. [ๆ•…้šœๆŽ’ๆŸฅๆŒ‡ๅ—](#6-ๆ•…้šœๆŽ’ๆŸฅๆŒ‡ๅ—)
18
+ 7. [ๅทฒ็Ÿฅๅฑ€้™ไธŽๅŽ็ปญ่ฟญไปฃ](#7-ๅทฒ็Ÿฅๅฑ€้™ไธŽๅŽ็ปญ่ฟญไปฃ)
19
+
20
+ ---
21
+
22
+ ## 1. ไน™ๅฎŒๆˆๆƒ…ๅ†ตๆ€ป่งˆ
23
+
24
+ ### 1.1 ๅทฒๅฎŒๆˆ็š„7ไธชๆ–‡ไปถ
25
+
26
+ | # | ๆ–‡ไปถๅ | ็ฑปๅž‹ | ๅŠŸ่ƒฝ | ็Šถๆ€ |
27
+ |---|--------|------|------|------|
28
+ | 1 | `llm_utils.py` | ๅ…ฌๅ…ฑๅทฅๅ…ท | OpenAI ๅฎขๆˆท็ซฏใ€LLM่ฐƒ็”จ๏ผˆ3ๆฌก้‡่ฏ•๏ผ‰ใ€JSONๅฎ‰ๅ…จ่งฃๆž๏ผˆ6็ญ–็•ฅ๏ผ‰ใ€่พ“ๅ‡บๆ ก้ชŒ | โœ… ๅฎŒๆˆ |
29
+ | 2 | `direction_analyzer.py` | Agent 1 | LLM่งฃๆž็ ”็ฉถๆ–นๅ‘โ†’ๅญ้ข†ๅŸŸ+ๆ–นๆณ•ๆ—+ๆœ็ดข่ฏ | โœ… ๅฎŒๆˆ |
30
+ | 3 | `repo_evaluator.py` | Agent 2 | LLMๅ…ญ็ปดๅบฆ่ฏ„ๅˆ†๏ผˆๅฏๅค็Žฐๆ€ง5็ปด80ๅˆ† + ๅฏนๆฏ”ๅฎž้ชŒ้€‚้…ๅบฆ1็ปด20ๅˆ†๏ผ‰ | โœ… ๅฎŒๆˆ |
31
+ | 4 | `run.py` | ่ฐƒๅบฆๅ™จ | ไธฒ่”3ๅฑ‚ๆžถๆž„๏ผŒๆ•ฐๆฎไผ ้€’๏ผŒๆ–นๆณ•ๆ—ๅŒน้…๏ผŒ่€—ๆ—ถ็ปŸ่ฎก | โœ… ๅฎŒๆˆ |
32
+ | 5 | `app.py` | UIๅฑ‚ | Gradio็•Œ้ข + Markdown็ ”ๆŠฅๆ ผๅผๅŒ–๏ผˆๅ››ๆฎตๅผ๏ผ‰ | โœ… ๅฎŒๆˆ |
33
+ | 6 | `state.py` | ๆ•ฐๆฎๆจกๅž‹ | dataclassๅฎšไน‰๏ผˆMethodFamily, DirectionAnalysis, CandidateRepo, EvalResult, ResearchReport๏ผ‰ | โœ… ๅฎŒๆˆ |
34
+ | 7 | `test_agents.py` | ๆต‹่ฏ•ๅทฅๅ…ท | ไธ€้”ฎๆต‹่ฏ•ไธคไธชAgent๏ผŒ6ไธชๆต‹่ฏ•็”จไพ‹๏ผŒๆ–ญ่จ€+ๆฑ‡ๆ€ป | โœ… ๅฎŒๆˆ |
35
+
36
+ ### 1.2 ่ฏšๅฎž่ฏ„ไผฐ๏ผšๅ“ชไบ›ๆ˜ฏ็œŸๆญฃๅฎŒๆˆ็š„๏ผŒๅ“ชไบ›ๆœ‰ๅ‰ๆๆกไปถ
37
+
38
+ **ๅฏไปฅ100%็กฎ่ฎคๅฎŒๆˆ็š„๏ผš**
39
+ - Agent 1๏ผˆๆ–นๅ‘่งฃๆž๏ผ‰๏ผš็‹ฌ็ซ‹ๅฏ่ฟ่กŒ๏ผŒ3็ฏ‡่ฎบๆ–‡ๆต‹่ฏ•้€š่ฟ‡ใ€‚่พ“ๅ…ฅๆ ‡้ข˜+ๆ‘˜่ฆโ†’่พ“ๅ‡บ็ป“ๆž„ๅŒ–็š„็ ”็ฉถๆ–นๅ‘JSONใ€‚
40
+ - Agent 2๏ผˆไป“ๅบ“่ฏ„ไผฐ๏ผ‰๏ผš็‹ฌ็ซ‹ๅฏ่ฟ่กŒ๏ผŒ3ไธชๅœบๆ™ฏๆต‹่ฏ•้€š่ฟ‡๏ผˆ้ซ˜ๅˆ†/ไฝŽๅˆ†/ไธญ็ญ‰๏ผ‰ใ€‚่พ“ๅ…ฅไป“ๅบ“ๅ…ƒไฟกๆฏ+README+ไพ่ต–โ†’่พ“ๅ‡บๅ…ญ็ปดๅบฆ่ฏ„ๅˆ†JSONใ€‚
41
+ - ๅ…ฌๅ…ฑๅทฅๅ…ทๅฑ‚๏ผˆllm_utils.py๏ผ‰๏ผšAPI้‡่ฏ•ใ€JSON่งฃๆžๅฎน้”™ใ€่พ“ๅ‡บๆ ก้ชŒๅ…จ้ƒจๅฐฑ็ปชใ€‚
42
+ - ๆต‹่ฏ•ๅฅ—ไปถ๏ผˆtest_agents.py๏ผ‰๏ผšไธ€้”ฎ่ฟ่กŒ๏ผŒ6/6้€š่ฟ‡ใ€‚
43
+
44
+ **ไปฃ็ ๅทฒๅฎŒๆˆไฝ†้œ€็”ฒไบคไป˜ๅŽๆ‰่ƒฝ้ชŒ่ฏ็š„๏ผš**
45
+ - `run.py`๏ผš่ฐƒๅบฆ้€ป่พ‘ๅฎŒๆ•ด๏ผŒๅซ่€—ๆ—ถ็ปŸ่ฎกใ€ๆ–นๆณ•ๆ—ๅŒน้…๏ผˆไธญ่‹ฑๅŒ่ฏญๅ…ณ้”ฎ่ฏ๏ผ‰ใ€้”™่ฏฏๅค„็†้“พ่ทฏใ€‚ไฝ†**ๆ— ๆณ•่ฟ่กŒ**โ€”โ€”ๅฎƒไพ่ต–็”ฒ็š„3ไธชๆจกๅ—๏ผŒๅฝ“ๅ‰ๅฏผๅ…ฅๅณๆŠฅ้”™ใ€‚
46
+ - `app.py`๏ผšGradio็•Œ้ข + Markdownๆ ผๅผๅŒ–ๅฎŒๆ•ดใ€‚ๅŒๆ ท**ๆ— ๆณ•ๅฏๅŠจ**โ€”โ€”ๅฎƒๅฏผๅ…ฅ `run`๏ผŒ่€Œ `run` ๅฏผๅ…ฅ็”ฒ็š„ๆจกๅ—ใ€‚
47
+
48
+ **ๅทฒ็Ÿฅๅฑ€้™๏ผˆ้žbug๏ผŒๆ˜ฏMVP่Œƒๅ›ดๅ†…็š„ๅ–่ˆ๏ผ‰๏ผš**
49
+ - `_match_family` ไป“ๅบ“โ†’ๆ–นๆณ•ๆ—ๅŒน้…ๆ˜ฏๅ…ณ้”ฎ่ฏ็บงๅˆซ็š„๏ผŒ้žLLM่ฏญไน‰ๅŒน้…ใ€‚ๅฝ“LLM่ฟ”ๅ›žไธญๆ–‡ๆ–นๆณ•ๆ—ๅ๏ผˆๅฆ‚"ๅŸบไบŽ้‡ๅปบ็š„ๆ–นๆณ•"๏ผ‰ๆ—ถ๏ผŒไพ่ต–ไธญ่‹ฑๅŒ่ฏญๅ…ณ้”ฎ่ฏๆ˜ ๅฐ„ๆฅๅ…œๅบ•๏ผŒไฝ†ไธ่ƒฝไฟ่ฏ100%ๅ‡†็กฎใ€‚ๆญฃ็กฎ็އไผฐ่ฎก70-80%ใ€‚ไปปๅŠกไนฆ่ฎกๅˆ’ไธญ Week 3 ็”จ LLM ๆ›ฟๆขใ€‚
50
+ - LLM่พ“ๅ‡บไพ่ต–ๆ็คบ่ฏ็บฆๆŸ๏ผŒๅถๆœ‰ๆ ผๅผๅๅทฎใ€‚ๅทฒ้€š่ฟ‡ `parse_json_safe`๏ผˆ6็ง่งฃๆž็ญ–็•ฅ๏ผ‰+ `validate_*_output`๏ผˆๅญ—ๆฎตๅกซๅ……้ป˜่ฎคๅ€ผ๏ผ‰ๅŒ้‡ๅ…œๅบ•๏ผŒ็กฎไฟไธ‹ๆธธไธๅดฉๆบƒใ€‚
51
+
52
+ ### 1.3 ่‡ชๆฃ€ๅ‘ฝไปค
53
+
54
+ ```bash
55
+ # ็”ฒๆจกๅ—ๅฐšๆœชไบคไป˜ๆ—ถ๏ผŒๅช่ƒฝ่ฟ่กŒ่ฟ™ไธคไธช๏ผš
56
+ python direction_analyzer.py # ๆต‹่ฏ• Agent 1๏ผˆ3็ฏ‡่ฎบๆ–‡๏ผ‰
57
+ python repo_evaluator.py # ๆต‹่ฏ• Agent 2๏ผˆ3ไธชไป“ๅบ“ๅœบๆ™ฏ๏ผ‰
58
+ python test_agents.py # ไธ€้”ฎๆต‹่ฏ•ไธคไธชAgent๏ผˆๆŽจ่๏ผ‰
59
+
60
+ # ็”ฒๆจกๅ—ไบคไป˜ๅŽ๏ผš
61
+ python run.py https://arxiv.org/abs/2011.08785 # ๅฎŒๆ•ด้“พ่ทฏ
62
+ python app.py # ๅฏๅŠจ Gradio ็•Œ้ข
63
+ ```
64
+
65
+ ---
66
+
67
+ ## 2. ็”ฒ้œ€ไบคไป˜็š„3ไธชๆจกๅ—๏ผˆAPIๅฅ‘็บฆ๏ผ‰
68
+
69
+ > **้‡่ฆ**๏ผšไปฅไธ‹ๅฅ‘็บฆๆ˜ฏๅŒๆ–นไปฃ็ ่ƒฝๅฏนๆŽฅ็š„**ๅ”ฏไธ€ๆŽฅๅฃๆ ‡ๅ‡†**ใ€‚ๅ‡ฝๆ•ฐๅใ€ๅ‚ๆ•ฐๅใ€่ฟ”ๅ›žๅ€ผ็ฑปๅž‹ใ€dict็š„keyๅ้ƒฝๅฟ…้กปไธฅๆ ผไธ€่‡ดใ€‚ๅฆ‚ๆœ‰ๅ˜ๅŠจ๏ผŒๅฟ…้กปๅŒๆ–นๅŒๆญฅ็กฎ่ฎคใ€‚
70
+
71
+ ### 2.1 paper_fetcher.py
72
+
73
+ **่Œ่ดฃ**๏ผšๆ นๆฎ arxiv URL ่Žทๅ–่ฎบๆ–‡ๅ…ƒไฟกๆฏใ€‚
74
+
75
+ #### ๅ‡ฝๆ•ฐ็ญพๅ
76
+
77
+ ```python
78
+ def fetch_paper_info(arxiv_url: str) -> dict:
79
+ """ไปŽ arxiv URL ่Žทๅ–่ฎบๆ–‡ไฟกๆฏใ€‚
80
+
81
+ Args:
82
+ arxiv_url: arxiv ่ฎบๆ–‡ URL๏ผŒๅฆ‚ "https://arxiv.org/abs/2011.08785"
83
+
84
+ Returns:
85
+ dict๏ผŒๅฟ…้กปๅŒ…ๅซไปฅไธ‹ๅญ—ๆฎตใ€‚ๅฆ‚่Žทๅ–ๅคฑ่ดฅ๏ผŒๆŠ›ๅ‡บ Exceptionใ€‚
86
+ """
87
+ ```
88
+
89
+ #### ่ฟ”ๅ›žๅ€ผ dict ๅญ—ๆฎตๅฎšไน‰
90
+
91
+ | ๅญ—ๆฎตๅ | ็ฑปๅž‹ | ๅฟ…้œ€ | ่ฏดๆ˜Ž | ็คบไพ‹ |
92
+ |--------|------|------|------|------|
93
+ | `arxiv_id` | `str` | โœ… ๅฟ…้œ€ | arxiv ID | `"2011.08785"` |
94
+ | `title` | `str` | โœ… ๅฟ…้œ€ | ่ฎบๆ–‡ๆ ‡้ข˜๏ผˆๅฎŒๆ•ด๏ผŒไธ่ฆๆˆชๆ–ญ๏ผ‰ | `"PaDiM: a Patch Distribution Modeling..."` |
95
+ | `abstract` | `str` | โœ… ๅฟ…้œ€ | ่ฎบๆ–‡ๆ‘˜่ฆ๏ผˆๅฎŒๆ•ด๏ผ‰ | `"We present a new framework..."` |
96
+ | `authors` | `list[str]` | โœ… ๅฟ…้œ€ | ไฝœ่€…ๅˆ—่กจ | `["Thomas Defard", ...]` |
97
+ | `published` | `str` | ๆŽจ่ | ๅ‘่กจๆ—ฅๆœŸ๏ผˆISOๆ ผๅผๅญ—็ฌฆไธฒๅณๅฏ๏ผ‰ | `"2021-03-15"` ๆˆ– `"2021-03-15T00:00:00Z"` |
98
+ | `categories` | `list[str]` | ๆŽจ่ | arxiv ๅˆ†็ฑปๆ ‡็ญพ | `["cs.CV", "cs.LG"]` |
99
+
100
+ #### ไน™็š„่ฐƒ็”จไธŠไธ‹ๆ–‡
101
+
102
+ ```python
103
+ # run.py ็ฌฌ74่กŒ๏ผš
104
+ paper = fetch_paper_info(arxiv_url)
105
+
106
+ # ็ดงๆŽฅ็€ไฝฟ็”จ็š„ๅญ—ๆฎต๏ผˆ็ฌฌ78-82่กŒ๏ผ‰๏ผš
107
+ title = paper.get("title", "")
108
+ abstract = paper.get("abstract", "")
109
+ # authors ๅ’Œ categories ไป…็”จไบŽๆ‰“ๅฐๆ—ฅๅฟ—
110
+ # app.py ้ขๅค–ไฝฟ็”จ arxiv_id ๅ’Œ published ๆ˜พ็คบๅœจ็ ”ๆŠฅไธญ
111
+ ```
112
+
113
+ #### ๅฎž็Žฐๆ็คบ๏ผˆ็ป™็”ฒ๏ผ‰
114
+
115
+ - arxiv ๆœ‰ๅ…ฌๅผ€ API๏ผš`https://export.arxiv.org/api/query?id_list={arxiv_id}`
116
+ - ่ฟ”ๅ›ž XML ๆ ผๅผ๏ผŒ้œ€่ฆ่งฃๆž `<title>`, `<summary>`, `<author>`, `<published>`, `<category>` ็ญ‰ๆ ‡็ญพ
117
+ - ไนŸๅฏไปฅ็”จ `arxiv` Python ๅŒ…๏ผˆ`pip install arxiv`๏ผ‰
118
+ - ๅฆ‚ๆžœ็ฝ‘็ปœไธ้€š๏ผŒๅฏไปฅ็›ดๆŽฅๆŠ›ๅ‡บๅผ‚ๅธธ๏ผŒrun.py ไผšๆ•่Žทๅนถ่ฟ”ๅ›ž้”™่ฏฏไฟกๆฏ
119
+ - ๅปบ่ฎฎๅœจ `__main__` ้‡Œๅ†™ไธช่‡ชๆต‹๏ผš`python paper_fetcher.py` ๆ‰“ๅฐไธ€็ฏ‡่ฎบๆ–‡็š„ไฟกๆฏ
120
+
121
+ ---
122
+
123
+ ### 2.2 repo_searcher.py
124
+
125
+ **่Œ่ดฃ**๏ผšๆ นๆฎๆœ็ดขๆŸฅ่ฏขๅˆ—่กจๅœจ GitHub ไธŠๆœ็ดขไป“ๅบ“๏ผŒๅŽป้‡ๅŽ่ฟ”ๅ›žใ€‚
126
+
127
+ #### ๅ‡ฝๆ•ฐ็ญพๅ
128
+
129
+ ```python
130
+ def search_repos(queries: list[str], max_per_keyword: int = 5) -> list[dict]:
131
+ """ๅœจ GitHub ๆœ็ดขไป“ๅบ“ใ€‚
132
+
133
+ Args:
134
+ queries: ๆœ็ดขๆŸฅ่ฏขๅญ—็ฌฆไธฒๅˆ—่กจ๏ผŒๅฆ‚ ["padim anomaly detection pytorch", ...]
135
+ max_per_keyword: ๆฏไธชๆŸฅ่ฏขๆœ€ๅคšไฟ็•™็š„ไป“ๅบ“ๆ•ฐ
136
+
137
+ Returns:
138
+ list[dict]: ๅ€™้€‰ไป“ๅบ“ๅˆ—่กจ๏ผŒๆฏไธช dict ๅญ—ๆฎต่งไธ‹่กจใ€‚ๅทฒๅŽป้‡๏ผˆๆŒ‰ full_name๏ผ‰ใ€‚
139
+ """
140
+ ```
141
+
142
+ #### ่ฟ”ๅ›žๅ€ผ list[dict] ๅญ—ๆฎตๅฎšไน‰
143
+
144
+ | ๅญ—ๆฎตๅ | ็ฑปๅž‹ | ๅฟ…้œ€ | ่ฏดๆ˜Ž | ็คบไพ‹ |
145
+ |--------|------|------|------|------|
146
+ | `full_name` | `str` | โœ… ๅฟ…้œ€ | ไป“ๅบ“ๅ…จๅ๏ผˆowner/repo๏ผ‰ | `"openvinotoolkit/anomalib"` |
147
+ | `html_url` | `str` | โœ… ๅฟ…้œ€ | ไป“ๅบ“ URL | `"https://github.com/openvinotoolkit/anomalib"` |
148
+ | `description` | `str` | โœ… ๅฟ…้œ€ | ไป“ๅบ“ๆ่ฟฐ๏ผˆๅฏไธบ็ฉบๅญ—็ฌฆไธฒ๏ผ‰ | `"An anomaly detection library..."` |
149
+ | `stars` | `int` | โœ… ๅฟ…้œ€ | Star ๆ•ฐ้‡ | `4000` |
150
+ | `language` | `str` | โœ… ๅฟ…้œ€ | ไธป่ฆ่ฏญ่จ€ | `"Python"` |
151
+ | `updated_at` | `str` | โœ… ๅฟ…้œ€ | ๆœ€ๅŽๆ›ดๆ–ฐๆ—ถ้—ด๏ผˆISOๆ ผๅผ๏ผ‰ | `"2026-01-15T00:00:00Z"` |
152
+ | `topics` | `list[str]` | โœ… ๅฟ…้œ€ | GitHub topics ๆ ‡็ญพ๏ผˆๅฏไธบ็ฉบๅˆ—่กจ๏ผ‰ | `["anomaly-detection", "pytorch"]` |
153
+ | `match_keyword` | `str` | ๆŽจ่ | ๅ‘ฝไธญไบ†ๅ“ชไธชๆœ็ดขๆŸฅ่ฏข๏ผˆ็”จไบŽ็ ”ๆŠฅๅฑ•็คบ๏ผ‰ | `"padim anomaly detection pytorch"` |
154
+
155
+ #### ไน™็š„่ฐƒ็”จไธŠไธ‹ๆ–‡
156
+
157
+ ```python
158
+ # run.py ็ฌฌ119่กŒ๏ผš
159
+ candidates = search_repos(all_queries, max_per_keyword=5)
160
+ # all_queries ็คบไพ‹๏ผˆ18ไธชๆœ็ดข่ฏ๏ผ‰๏ผš
161
+ # ["patch distribution modeling anomaly detection pytorch",
162
+ # "padim implementation pytorch",
163
+ # "memory bank anomaly detection code",
164
+ # ...,
165
+ # "industrial anomaly detection pytorch", โ† broad_queries
166
+ # "unsupervised anomaly localization github",
167
+ # "MVTec AD anomaly detection code"]
168
+
169
+ # ็ดงๆŽฅ็€๏ผˆ็ฌฌ127่กŒ๏ผ‰ๅ–ๅ‰ top_n=5 ไธช๏ผš
170
+ candidates = candidates[:top_n]
171
+
172
+ # ็„ถๅŽๅฏนๆฏไธช candidate ไฝฟ็”จๅญ—ๆฎต๏ผˆ็ฌฌ152-166่กŒ๏ผ‰๏ผš
173
+ full_name = repo["full_name"] # ็”จไบŽๆ‹†ๅˆ† owner/name ๅ’Œๆ˜พ็คบ
174
+ repo.get("html_url", "") # ็”จไบŽ็ ”ๆŠฅ้“พๆŽฅ
175
+ repo.get("description", "") # ็”จไบŽๆ–นๆณ•ๆ—ๅŒน้…
176
+ repo.get("stars", 0) # ็”จไบŽ่ฏ„ๅˆ†่พ“ๅ…ฅๅ’Œๆ˜พ็คบ
177
+ repo.get("match_keyword", "") # ็”จไบŽ็ ”ๆŠฅๅฑ•็คบ
178
+ ```
179
+
180
+ #### ้‡่ฆ็บฆๆŸ
181
+
182
+ - **ๅŽป้‡**๏ผšๅŒไธ€ไธช `full_name` ๅช่ƒฝๅ‡บ็Žฐไธ€ๆฌกใ€‚ๅคšไธชๆœ็ดขๆŸฅ่ฏขๅฏ่ƒฝๅ‘ฝไธญๅŒไธ€ไธชไป“ๅบ“ใ€‚
183
+ - **ๆŽ’ๅบ**๏ผšๅปบ่ฎฎๆŒ‰ `stars` ้™ๅบๆŽ’ๅˆ—ๅŽๅ†ๅŽป้‡๏ผŒ่ฟ™ๆ ทไฟ็•™็š„ๆ˜ฏ star ๆœ€้ซ˜็š„็‰ˆๆœฌใ€‚
184
+ - **max_per_keyword**๏ผšไน™ไผ 5๏ผŒๆ„ๅ‘ณ็€ๆฏไธชๆœ็ดขๆŸฅ่ฏขๆœ€ๅคš่ฟ”ๅ›ž5ไธชไป“ๅบ“ใ€‚ๅฆ‚ๆžœ `all_queries` ๆœ‰18ไธช๏ผŒ็†่ฎบๆœ€ๅคš90ไธช๏ผŒๅŽป้‡ๅŽ้€šๅธธๅ‰ฉ10-30ไธชใ€‚ไน™ๅ†ๆˆชๆ–ญๅˆฐ5ไธชใ€‚
185
+ - **GitHub API**๏ผšๅฏไปฅ็”จ `GET /search/repositories?q=...`๏ผŒๆœช่ฎค่ฏ้™ๅˆถ 10 req/min๏ผŒ่ฎค่ฏๅŽ 30 req/minใ€‚ๅฆ‚ๆžœ18ไธชๆŸฅ่ฏขๅ…จ่ตฐAPIๅฏ่ƒฝ่งฆๅ‘้™ๆตใ€‚ๅปบ่ฎฎ๏ผšๅฆ‚ๆžœๆœ‰ GitHub token๏ผŒๆ”พๅˆฐ header ้‡Œใ€‚
186
+
187
+ #### ๅฎž็Žฐๆ็คบ๏ผˆ็ป™็”ฒ๏ผ‰
188
+
189
+ ```python
190
+ # GitHub Search API ็คบไพ‹
191
+ import requests
192
+ url = "https://api.github.com/search/repositories"
193
+ params = {"q": query, "sort": "stars", "order": "desc", "per_page": max_per_keyword}
194
+ headers = {"Accept": "application/vnd.github.v3+json"}
195
+ # ๅฆ‚ๆžœๆœ‰ token: headers["Authorization"] = "token ghp_xxx"
196
+ response = requests.get(url, params=params, headers=headers)
197
+ data = response.json()
198
+ for item in data.get("items", []):
199
+ # ๆๅ–ๅญ—ๆฎตๆž„ๅปบ dict
200
+ ...
201
+ ```
202
+
203
+ ---
204
+
205
+ ### 2.3 repo_fetcher.py
206
+
207
+ **่Œ่ดฃ**๏ผš่Žทๅ–ๅ•ไธชไป“ๅบ“็š„ README ๅ’Œไพ่ต–ๆ–‡ไปถๅ†…ๅฎนใ€‚
208
+
209
+ #### ๅ‡ฝๆ•ฐ็ญพๅ
210
+
211
+ ```python
212
+ def fetch_readme(owner: str, name: str) -> str | None:
213
+ """่Žทๅ–ไป“ๅบ“ README ๆ–‡ไปถๅ†…ๅฎนใ€‚
214
+
215
+ Args:
216
+ owner: ไป“ๅบ“ๆ‰€ๆœ‰่€…๏ผŒๅฆ‚ "openvinotoolkit"
217
+ name: ไป“ๅบ“ๅ๏ผŒๅฆ‚ "anomalib"
218
+
219
+ Returns:
220
+ README ๆ–‡ไปถๅ…จๆ–‡๏ผˆๅญ—็ฌฆไธฒ๏ผ‰๏ผŒ่Žทๅ–ๅคฑ่ดฅ่ฟ”ๅ›ž Noneใ€‚
221
+ """
222
+
223
+ def fetch_dependencies(owner: str, name: str) -> dict[str, str]:
224
+ """่Žทๅ–ไป“ๅบ“็š„ไพ่ต–ๆ–‡ไปถใ€‚
225
+
226
+ Args:
227
+ owner: ไป“ๅบ“ๆ‰€ๆœ‰่€…
228
+ name: ไป“ๅบ“ๅ
229
+
230
+ Returns:
231
+ dict: {ๆ–‡ไปถๅ: ๆ–‡ไปถๅ†…ๅฎน}๏ผŒๅฆ‚ {"requirements.txt": "torch>=1.10\n...", ...}
232
+ ๆ‰พไธๅˆฐไปปไฝ•ไพ่ต–ๆ–‡ไปถๆ—ถ่ฟ”ๅ›ž็ฉบ dict {}ใ€‚
233
+ ๅธธ่ง็›ฎๆ ‡ๆ–‡ไปถ๏ผšrequirements.txt, environment.yml, setup.py, setup.cfg, Pipfile, pyproject.toml
234
+ """
235
+ ```
236
+
237
+ #### ไน™็š„่ฐƒ็”จไธŠไธ‹ๆ–‡
238
+
239
+ ```python
240
+ # run.py ็ฌฌ165-167่กŒ๏ผˆๅœจ for ๅพช็Žฏๅ†…ๅฏนๆฏไธชไป“ๅบ“่ฐƒ็”จ๏ผ‰๏ผš
241
+ readme = fetch_readme(owner, name) # โ†’ str | None
242
+ deps = fetch_dependencies(owner, name) # โ†’ dict[str, str]
243
+ evaluation = evaluate_repo(repo, readme, deps, matched_family)
244
+
245
+ # repo_evaluator.py ๅ†…้ƒจ๏ผˆ็ฌฌ105่กŒ๏ผ‰๏ผš
246
+ readme_text = (readme or "README ๆœชๆ‰พๅˆฐ")[:2500] # ๆˆชๆ–ญๅˆฐ 2500 ๅญ—็ฌฆ
247
+ # deps ่ขซๆ ผๅผๅŒ–ไธบๆ–‡ๆœฌๅŽๅŒๆ ทๆˆชๆ–ญๆฏไธชๆ–‡ไปถๅˆฐ 1000 ๅญ—็ฌฆ
248
+ ```
249
+
250
+ #### ้‡่ฆ็บฆๆŸ
251
+
252
+ - `fetch_readme`๏ผšGitHub API ่ทฏๅพ„ `GET /repos/{owner}/{name}/readme`๏ผŒ่ฟ”ๅ›ž base64 ็ผ–็ ๅ†…ๅฎน๏ผŒ้œ€่งฃ็ ใ€‚
253
+ - `fetch_dependencies`๏ผš้œ€่ฆ้€ไธชๆฃ€ๆŸฅไพ่ต–ๆ–‡ไปถๆ˜ฏๅฆๅญ˜ๅœจใ€‚GitHub API `GET /repos/{owner}/{name}/contents/{path}`ใ€‚**ไธ่ฆ**ๆŠŠๆ•ดไธชไป“ๅบ“ clone ไธ‹ๆฅโ€”โ€”MVP ้˜ถๆฎตไธ€ไธช API ่ฐƒ็”จๅณๅฏใ€‚
254
+ - ไธคไธชๅ‡ฝๆ•ฐ้ƒฝ**ไธ่ƒฝๆŠ›ๅ‡บๅผ‚ๅธธ**ใ€‚ๅคฑ่ดฅๆ—ถ่ฟ”ๅ›ž `None` ๆˆ– `{}`ใ€‚ไน™็š„่ฐƒๅบฆๅ™จๅทฒ็ปๅฏนๆฏไธชไป“ๅบ“ๅšไบ† try/except๏ผŒไฝ†ๅฆ‚ๆžœๅ‡ฝๆ•ฐๅ†…้ƒจๆŠ›ๅ‡บๅผ‚ๅธธ๏ผŒ่ฏ„ไผฐๅพช็Žฏๅฏ่ƒฝไผšไธญๆ–ญ๏ผˆๅฝ“ๅ‰ๅชๆ•่Žทไบ†่ฏ„ไผฐๅคฑ่ดฅ๏ผŒๆœชๆ•่Žท fetch ๅคฑ่ดฅโ€”โ€”่ฟ™ๆ˜ฏไธ€ไธชไน™ๅทฒ็Ÿฅ็š„้˜ฒๅพก็ผบๅฃ๏ผŒ่ง7.1่Š‚๏ผ‰ใ€‚
255
+
256
+ #### ๅฎž็Žฐๆ็คบ๏ผˆ็ป™็”ฒ๏ผ‰
257
+
258
+ ```python
259
+ import requests
260
+ import base64
261
+
262
+ def fetch_readme(owner, name):
263
+ url = f"https://api.github.com/repos/{owner}/{name}/readme"
264
+ headers = {"Accept": "application/vnd.github.v3+json"}
265
+ resp = requests.get(url, headers=headers)
266
+ if resp.status_code != 200:
267
+ return None
268
+ content = resp.json().get("content", "")
269
+ return base64.b64decode(content).decode("utf-8", errors="replace")
270
+
271
+ def fetch_dependencies(owner, name):
272
+ target_files = ["requirements.txt", "environment.yml", "setup.py",
273
+ "setup.cfg", "Pipfile", "pyproject.toml"]
274
+ result = {}
275
+ for fname in target_files:
276
+ url = f"https://api.github.com/repos/{owner}/{name}/contents/{fname}"
277
+ resp = requests.get(url, headers={"Accept": "application/vnd.github.v3+json"})
278
+ if resp.status_code == 200:
279
+ content = resp.json().get("content", "")
280
+ result[fname] = base64.b64decode(content).decode("utf-8", errors="replace")
281
+ return result
282
+ ```
283
+
284
+ ---
285
+
286
+ ## 3. ๆ•ฐๆฎๆตๅ…จๆ™ฏๅ›พ
287
+
288
+ ```
289
+ ็”จๆˆท่พ“ๅ…ฅ arxiv URL
290
+ โ”‚
291
+ โ–ผ
292
+ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
293
+ โ”‚ [1/5] paper_fetcher.py โ† ็”ฒ๏ผˆWorkflow๏ผ‰ โ”‚
294
+ โ”‚ fetch_paper_info(url) โ†’ dict โ”‚
295
+ โ”‚ {arxiv_id, title, abstract, authors, published, categories} โ”‚
296
+ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
297
+ โ”‚ title, abstract
298
+ โ–ผ
299
+ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
300
+ โ”‚ [2/5] direction_analyzer.py โ† ไน™๏ผˆAgent 1๏ผ‰ โ”‚
301
+ โ”‚ analyze_direction(title, abstract) โ†’ dict โ”‚
302
+ โ”‚ {subfield, subfield_trend, method_families, broad_queries} โ”‚
303
+ โ”‚ โ”‚
304
+ โ”‚ method_families: [ โ”‚
305
+ โ”‚ {family_name, description, representative_work, โ”‚
306
+ โ”‚ search_queries: ["...", "..."]} โ”‚
307
+ โ”‚ ] โ”‚
308
+ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
309
+ โ”‚ all_queries = flatten(search_queries) + broad_queries
310
+ โ”‚ ็บฆ 12-18 ไธชๆœ็ดขๅญ—็ฌฆไธฒ
311
+ โ–ผ
312
+ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
313
+ โ”‚ [3/5] repo_searcher.py โ† ็”ฒ๏ผˆWorkflow๏ผ‰ โ”‚
314
+ โ”‚ search_repos(queries, max_per_keyword=5) โ†’ list[dict] โ”‚
315
+ โ”‚ [{full_name, html_url, description, stars, language, โ”‚
316
+ โ”‚ updated_at, topics, match_keyword}] โ”‚
317
+ โ”‚ โ”‚
318
+ โ”‚ ๅŽป้‡ โ†’ ๆˆชๆ–ญ top_n=5 โ†’ ไผ ็ป™ไธ‹ไธ€ๆญฅ โ”‚
319
+ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€๏ฟฝ๏ฟฝโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
320
+ โ”‚ ๆฏไธชๅ€™้€‰ไป“ๅบ“: full_name โ†’ owner/name
321
+ โ–ผ
322
+ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
323
+ โ”‚ [4/5] ๅฏนๆฏไธชไป“ๅบ“ๅนถ่กŒ๏ผš โ”‚
324
+ โ”‚ โ”‚
325
+ โ”‚ repo_fetcher.py โ† ็”ฒ๏ผˆWorkflow๏ผ‰ โ”‚
326
+ โ”‚ fetch_readme(owner, name) โ†’ str | None โ”‚
327
+ โ”‚ fetch_dependencies(owner, name) โ†’ dict[str, str] โ”‚
328
+ โ”‚ โ”‚
329
+ โ”‚ run.py โ† ไน™๏ผˆ่ฐƒๅบฆๅ™จ๏ผ‰ โ”‚
330
+ โ”‚ _match_family(repo, families) โ†’ str (ๆ–นๆณ•ๆ—ๅ or "") โ”‚
331
+ โ”‚ โ”‚
332
+ โ”‚ repo_evaluator.py โ† ไน™๏ผˆAgent 2๏ผ‰ โ”‚
333
+ โ”‚ evaluate_repo(repo, readme, deps, method_family) โ†’ dict โ”‚
334
+ โ”‚ {reproducibility_score, benchmark_fitness_score, โ”‚
335
+ โ”‚ overall_score, verdict, env_score, doc_score, โ”‚
336
+ โ”‚ code_score, community_score, dep_score, โ”‚
337
+ โ”‚ benchmark_score, reasoning, risks, โ”‚
338
+ โ”‚ benchmark_readiness, suggested_use} โ”‚
339
+ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
340
+ โ”‚ ๆŒ‰ overall_score ้™ๅบๆŽ’ๅˆ—
341
+ โ–ผ
342
+ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
343
+ โ”‚ [5/5] app.py โ† ไน™๏ผˆUI๏ผ‰ โ”‚
344
+ โ”‚ format_report(result) โ†’ Markdown ็ ”ๆŠฅ โ”‚
345
+ โ”‚ โ”‚
346
+ โ”‚ ็ ”ๆŠฅ็ป“ๆž„๏ผš โ”‚
347
+ โ”‚ ไธ€ใ€่ฎบๆ–‡ไฟกๆฏ๏ผˆarxiv_id, title, authors, categories๏ผ‰ โ”‚
348
+ โ”‚ ไบŒใ€็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ๏ผˆๅญ้ข†ๅŸŸ + ่ถ‹ๅŠฟ + ๆ–นๆณ•ๆ—่ฐฑ็ณป่กจๆ ผ๏ผ‰ โ”‚
349
+ โ”‚ ไธ‰ใ€ไป“ๅบ“่ฏ„ไผฐๆŽ’ๅ๏ผˆๆฏไธชไป“ๅบ“ๅ…ญ็ปดๅบฆๆ˜Ž็ป† + ๅˆ†ๆž + ้ฃŽ้™ฉ็‚น๏ผ‰ โ”‚
350
+ โ”‚ ๅ››ใ€ๅฏนๆฏ”ๅฎž้ชŒๆŽจ่ๆ€ป็ป“๏ผˆๆŒ‰ ready/partial/not_ready ๅˆ†็ป„๏ผ‰ โ”‚
351
+ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
352
+ ```
353
+
354
+ ### ๆ•ฐๆฎ้‡็บงๅ‚่€ƒ
355
+
356
+ | ้˜ถๆฎต | ่พ“ๅ…ฅ | ่พ“ๅ‡บ | ้ข„่ฎก่€—ๆ—ถ |
357
+ |------|------|------|----------|
358
+ | 1. ่ฎบๆ–‡ไฟกๆฏ | 1ไธชURL | 1ไธชdict | ~1s๏ผˆ็ฝ‘็ปœ๏ผ‰ |
359
+ | 2. ๆ–นๅ‘่งฃๆž | ๆ ‡้ข˜+ๆ‘˜่ฆ | 1ไธชdict๏ผˆ3-6ๆ–นๆณ•ๆ—+3ๅฎฝๆณ›๏ผ‰ | ~5-15s๏ผˆLLM๏ผ‰ |
360
+ | 3. ไป“ๅบ“ๆœ็ดข | ~15ไธชๆŸฅ่ฏข | ~5ไธชๅ€™้€‰ไป“ๅบ“ | ~10-30s๏ผˆGitHub API ร— 15๏ผ‰ |
361
+ | 4. ไป“ๅบ“่ฏ„ไผฐ | 5ไธชไป“ๅบ“ ร— (README+ไพ่ต–) | 5ไธชevaluation dict | ~25-75s๏ผˆLLM ร— 5๏ผ‰ |
362
+ | **ๅˆ่ฎก** | | | **็บฆ40-120็ง’** |
363
+
364
+ ---
365
+
366
+ ## 4. ๅฏนๆŽฅๆญฅ้ชค๏ผˆๆŠ€ๆœฏ + ๆฒŸ้€š๏ผ‰
367
+
368
+ ### 4.1 ็”ฒไบคไป˜ๆ–‡ไปถๆ—ถ
369
+
370
+ ็”ฒๆฏๆฌกไบคไป˜ไธ€ไธชๆจกๅ—๏ผŒไน™ๆ–นๆŒ‰ไปฅไธ‹ๆต็จ‹้ชŒ่ฏใ€‚
371
+
372
+ #### ้˜ถๆฎตA๏ผš็”ฒไบคไป˜ `paper_fetcher.py`
373
+
374
+ **็”ฒ้œ€็กฎไฟ๏ผš**
375
+ - [ ] ๆ–‡ไปถๅ๏ผš`paper_fetcher.py`
376
+ - [ ] ๆ”พๅ…ฅ่ทฏๅพ„๏ผš`D:\Desktop\AI_opencode_judging\paper_fetcher.py`
377
+ - [ ] ๆ–‡ไปถๅ†…ๅŒ…ๅซๅ‡ฝๆ•ฐ `fetch_paper_info(arxiv_url: str) -> dict`
378
+ - [ ] ่‡ชๆต‹ๅฏ่ฟ่กŒ๏ผš`python paper_fetcher.py` ่ƒฝๆ‰“ๅฐไธ€็ฏ‡่ฎบๆ–‡็š„ไฟกๆฏ
379
+ - [ ] ่ฟ”ๅ›žๅ€ผๅŒ…ๅซๆ‰€ๆœ‰ๅฟ…้œ€ๅญ—ๆฎต๏ผˆ่ง2.1่Š‚๏ผ‰
380
+
381
+ **ไน™้ชŒ่ฏๆญฅ้ชค๏ผš**
382
+ ```bash
383
+ # 1. ็กฎ่ฎคๆ–‡ไปถๅญ˜ๅœจ
384
+ ls paper_fetcher.py
385
+
386
+ # 2. ๆฃ€ๆŸฅๅ‡ฝๆ•ฐ็ญพๅ
387
+ python -c "from paper_fetcher import fetch_paper_info; help(fetch_paper_info)"
388
+
389
+ # 3. ๆต‹่ฏ•ๅฎž้™…่ฐƒ็”จ
390
+ python -c "
391
+ from paper_fetcher import fetch_paper_info
392
+ paper = fetch_paper_info('https://arxiv.org/abs/2011.08785')
393
+ for k in ['arxiv_id', 'title', 'abstract', 'authors']:
394
+ print(f'{k}: {str(paper.get(k))[:80]}...')
395
+ "
396
+
397
+ # 4. ็กฎ่ฎค run.py ไธๅ†ๆŠฅ paper_fetcher ็š„ ImportError
398
+ python -c "import run" 2>&1 | head -5
399
+ # ้ข„ๆœŸ๏ผšไธๅ†ๅ‡บ็Žฐ "็ผบๅฐ‘ๆจกๅ—: paper_fetcher.py"๏ผŒ่€Œๆ˜ฏๆŠฅไธ‹ไธ€ไธช็ผบๅคฑๆจกๅ—
400
+ ```
401
+
402
+ #### ้˜ถๆฎตB๏ผš็”ฒไบคไป˜ `repo_searcher.py`
403
+
404
+ **็”ฒ้œ€็กฎไฟ๏ผš**
405
+ - [ ] ๆ–‡ไปถๅ๏ผš`repo_searcher.py`
406
+ - [ ] ๅ‡ฝๆ•ฐ `search_repos(queries: list[str], max_per_keyword: int = 5) -> list[dict]`
407
+ - [ ] ่‡ชๆต‹ๅฏ่ฟ่กŒ๏ผš็”จๅ‡ ไธชๆž„้€ ็š„ๆœ็ดข่ฏ่ฐƒ็”จ๏ผŒๆ‰“ๅฐ่ฟ”ๅ›ž็š„ไป“ๅบ“ๆ•ฐ้‡ๅ’Œๅ็งฐ
408
+ - [ ] ๅŽป้‡ๅทฒๅฎž็Žฐ๏ผˆๅŒไธ€ full_name ๅชๅ‡บ็Žฐไธ€ๆฌก๏ผ‰
409
+ - [ ] ๆฏไธช dict ๅŒ…ๅซๆ‰€ๆœ‰ๅฟ…้œ€ๅญ—ๆฎต๏ผˆ่ง2.2่Š‚๏ผ‰
410
+
411
+ **ไน™้ชŒ่ฏๆญฅ้ชค๏ผš**
412
+ ```bash
413
+ # 1. ็กฎ่ฎคๆœ็ดขๅฏ็”จ
414
+ python -c "
415
+ from repo_searcher import search_repos
416
+ repos = search_repos(['padim anomaly detection pytorch'], max_per_keyword=3)
417
+ print(f'ๆ‰พๅˆฐ {len(repos)} ไธชไป“ๅบ“')
418
+ for r in repos:
419
+ print(f' {r[\"full_name\"]} โญ{r[\"stars\"]} โ€” {r.get(\"match_keyword\", \"?\")}')
420
+ "
421
+
422
+ # 2. ็กฎ่ฎค run.py ไธๅ†ๆŠฅ repo_searcher ็š„ ImportError
423
+ python -c "import run" 2>&1 | head -5
424
+ # ้ข„ๆœŸ๏ผšไธๅ†ๅ‡บ็Žฐ "็ผบๅฐ‘ๆจกๅ—: repo_searcher.py"๏ผŒ่€Œๆ˜ฏๆŠฅไธ‹ไธ€ไธช็ผบๅคฑๆจกๅ—
425
+ ```
426
+
427
+ #### ้˜ถๆฎตC๏ผš็”ฒไบคไป˜ `repo_fetcher.py`
428
+
429
+ **็”ฒ้œ€็กฎไฟ๏ผš**
430
+ - [ ] ๆ–‡ไปถๅ๏ผš`repo_fetcher.py`
431
+ - [ ] ๅ‡ฝๆ•ฐ `fetch_readme(owner: str, name: str) -> str | None`
432
+ - [ ] ๅ‡ฝๆ•ฐ `fetch_dependencies(owner: str, name: str) -> dict[str, str]`
433
+ - [ ] ่‡ชๆต‹ๅฏ่ฟ่กŒ๏ผšๅฏน anomalib ไป“ๅบ“่ฐƒ็”จไธคไธชๅ‡ฝๆ•ฐ๏ผŒๆ‰“ๅฐ็ป“ๆžœ
434
+
435
+ **ไน™้ชŒ่ฏๆญฅ้ชค๏ผš**
436
+ ```bash
437
+ # 1. ็กฎ่ฎค่ƒฝ่Žทๅ– README
438
+ python -c "
439
+ from repo_fetcher import fetch_readme
440
+ readme = fetch_readme('openvinotoolkit', 'anomalib')
441
+ print(f'README ้•ฟๅบฆ: {len(readme) if readme else 0} ๅญ—็ฌฆ')
442
+ print((readme or '')[:200])
443
+ "
444
+
445
+ # 2. ็กฎ่ฎค่ƒฝ่Žทๅ–ไพ่ต–
446
+ python -c "
447
+ from repo_fetcher import fetch_dependencies
448
+ deps = fetch_dependencies('openvinotoolkit', 'anomalib')
449
+ print(f'ๆ‰พๅˆฐ {len(deps)} ไธชไพ่ต–ๆ–‡ไปถ: {list(deps.keys())}')
450
+ for fname, content in deps.items():
451
+ print(f' {fname}: {len(content)} ๅญ—็ฌฆ')
452
+ "
453
+
454
+ # 3. ็กฎ่ฎค run.py ไธๅ†ๆŠฅไปปไฝ• ImportError
455
+ python -c "import run; print('run.py ๅฏผๅ…ฅๆˆๅŠŸ๏ผ')"
456
+ # ้ข„ๆœŸ๏ผšrun.py ๅฏผๅ…ฅๆˆๅŠŸ๏ผ
457
+ ```
458
+
459
+ ### 4.2 ๅ…จ้ƒจไบคไป˜ๅŽ็š„่”่ฐƒ
460
+
461
+ ```bash
462
+ # ็ฌฌไธ€ๆญฅ๏ผš็กฎไฟไน™็š„ไธคไธช Agent ๆญฃๅธธ
463
+ python test_agents.py
464
+ # ้ข„ๆœŸ๏ผš6/6 ้€š่ฟ‡
465
+
466
+ # ็ฌฌไบŒๆญฅ๏ผš็”จไธ€็ฏ‡ๅทฅไธš็ผบ้™ทๆฃ€ๆต‹่ฎบๆ–‡่ท‘ๅฎŒๆ•ด้“พ่ทฏ
467
+ python run.py https://arxiv.org/abs/2011.08785
468
+ # ้ข„ๆœŸ๏ผš
469
+ # [1/5] ่Žทๅ–่ฎบๆ–‡ไฟกๆฏ โ†’ ๆ˜พ็คบๆ ‡้ข˜ใ€ไฝœ่€…ใ€ๅˆ†็ฑปใ€่€—ๆ—ถ
470
+ # [2/5] ่งฃๆž็ ”็ฉถๆ–นๅ‘ โ†’ ๆ˜พ็คบๅญ้ข†ๅŸŸใ€่ถ‹ๅŠฟใ€ๆ–นๆณ•ๆ—ใ€็”Ÿๆˆ็š„ๆœ็ดข่ฏๆ•ฐ
471
+ # [3/5] ๆœ็ดข GitHub โ†’ ๆ˜พ็คบๅ€™้€‰ไป“ๅบ“ๅˆ—่กจ๏ผˆๆœ€ๅคš5ไธช๏ผ‰
472
+ # [4/5] ่ฏ„ไผฐไป“ๅบ“ โ†’ ้€ไธชๆ˜พ็คบ่ฏ„ๅˆ†
473
+ # [5/5] ๅฎŒๆˆ โ†’ ๆ‰“ๅฐๅฎŒๆ•ด Markdown ็ ”ๆŠฅ
474
+
475
+ # ็ฌฌไธ‰ๆญฅ๏ผšๅฏๅŠจ Gradio ็•Œ้ข
476
+ python app.py
477
+ # ๆต่งˆๅ™จๆ‰“ๅผ€ http://127.0.0.1:7860
478
+ # ็ฒ˜่ดด arxiv URL๏ผŒ็‚นๅ‡ป Submit๏ผŒๆŸฅ็œ‹็ ”ๆŠฅ
479
+ ```
480
+
481
+ ### 4.3 ๅฏนๆŽฅๆ—ถ็š„ๆฒŸ้€š่ฆ็‚น
482
+
483
+ 1. **็ปŸไธ€ๅผ€ๅ‘็Žฏๅขƒ**
484
+ - Python ็‰ˆๆœฌ๏ผš3.10+ ๏ผˆไน™ๅฝ“ๅ‰ไฝฟ็”จ 3.11+๏ผŒ`str | None` ็ฑปๅž‹ๆณจ่งฃ้œ€่ฆ 3.10+๏ผ‰
485
+ - ไพ่ต–ๅฎ‰่ฃ…๏ผš`pip install -r requirements.txt`๏ผˆopenai, gradio, requests ๅทฒๅˆ—ๅฅฝ๏ผ‰
486
+ - ็”ฒๅฆ‚้œ€ๆ–ฐๅขžไพ่ต–๏ผŒ้œ€ๅŒๆญฅๆ›ดๆ–ฐ `requirements.txt`
487
+
488
+ 2. **GitHub API ้™ๆต**
489
+ - ๆœช่ฎค่ฏ๏ผš10 ๆฌก/ๅˆ†้’Ÿใ€‚18ไธชๆœ็ดขๆŸฅ่ฏขไผš่ถ…้™ใ€‚
490
+ - ่ฎค่ฏ๏ผš30 ๆฌก/ๅˆ†้’Ÿใ€‚ๅปบ่ฎฎ็”ฒ็”ณ่ฏทไธ€ไธช GitHub Personal Access Token๏ผˆๅ…่ดน๏ผŒSettings โ†’ Developer settings โ†’ Tokens๏ผ‰๏ผŒไธ้œ€่ฆไปปไฝ•ๆƒ้™๏ผˆpublic repo ๅช่ฏปไธ้œ€่ฆ scope๏ผ‰ใ€‚
491
+ - Token ้…็ฝฎๆ–นๅผ๏ผš็”ฒ่‡ช็”ฑ้€‰ๆ‹ฉ๏ผˆ็Žฏๅขƒๅ˜้‡ `GITHUB_TOKEN`ใ€้…็ฝฎๆ–‡ไปถใ€ๆˆ–็กฌ็ผ–็ โ€”โ€”ไฝ†ๅˆซๆไบคๅˆฐ git๏ผ‰ใ€‚
492
+
493
+ 3. **้”™่ฏฏๅค„็†็บฆๅฎš**
494
+ - Workflow ๅ‡ฝๆ•ฐ๏ผˆ็”ฒ็š„ๆจกๅ—๏ผ‰๏ผšๅฐฝ้‡ไธๆŠ›ๅผ‚ๅธธใ€‚่Žทๅ–ๅคฑ่ดฅ่ฟ”ๅ›ž `None`ใ€`[]`ใ€`{}` ็ญ‰็ฉบๅ€ผใ€‚
495
+ - Agent ๅ‡ฝๆ•ฐ๏ผˆไน™็š„ๆจกๅ—๏ผ‰๏ผšLLM ่งฃๆžๅคฑ่ดฅๆŠ› `ValueError`๏ผŒAPI ่ฐƒ็”จๅคฑ่ดฅๆŠ› `RuntimeError`ใ€‚
496
+ - ่ฐƒๅบฆๅ™จ๏ผˆrun.py๏ผ‰๏ผšๆ•่Žทๆ‰€ๆœ‰ๅผ‚ๅธธ๏ผŒ่ฝฌไธบ `{"error": "..."}` ๆˆ– fallback ๅ€ผ๏ผŒไฟ่ฏๆต็จ‹ไธๅดฉๆบƒใ€‚
497
+
498
+ 4. **Git ๅไฝœๅปบ่ฎฎ**
499
+ ```
500
+ D:\Desktop\AI_opencode_judging\
501
+ โ”œโ”€โ”€ .gitignore โ† ๅปบ่ฎฎๆทปๅŠ ๏ผš__pycache__/, .env, *.pyc
502
+ โ”œโ”€โ”€ requirements.txt
503
+ โ”œโ”€โ”€ state.py โ† ๅŒๆ–นๅ…ฑไบซ็š„ๆ•ฐๆฎๆจกๅž‹ๅ‚่€ƒ
504
+ โ”œโ”€โ”€ paper_fetcher.py โ† ็”ฒ
505
+ โ”œโ”€โ”€ repo_searcher.py โ† ็”ฒ
506
+ โ”œโ”€โ”€ repo_fetcher.py โ† ็”ฒ
507
+ โ”œโ”€โ”€ llm_utils.py โ† ไน™
508
+ โ”œโ”€โ”€ direction_analyzer.py โ† ไน™
509
+ โ”œโ”€โ”€ repo_evaluator.py โ† ไน™
510
+ โ”œโ”€โ”€ run.py โ† ไน™
511
+ โ”œโ”€โ”€ app.py โ† ไน™
512
+ โ”œโ”€โ”€ test_agents.py โ† ไน™
513
+ โ””โ”€โ”€ test_set/ โ† ็”ฒๆ”พ็ฝฎๆต‹่ฏ•ๆ•ฐๆฎ
514
+ ```
515
+
516
+ ---
517
+
518
+ ## 5. ๅฏนๆŽฅๅŽๆ•ˆๆžœ
519
+
520
+ ### 5.1 ๅฎŒๆ•ด็ ”ๆŠฅ็คบไพ‹๏ผˆ็ป“ๆž„ๅฑ•็คบ๏ผ‰
521
+
522
+ ๅฏนๆŽฅๅฎŒๆˆๅŽ๏ผŒ่พ“ๅ…ฅ `https://arxiv.org/abs/2011.08785`๏ผˆPaDiM๏ผ‰๏ผŒ่พ“ๅ‡บ็ ”ๆŠฅ็ป“ๆž„ๅฆ‚ไธ‹๏ผš
523
+
524
+ ```markdown
525
+ # ResearchRadar ็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ็ ”ๆŠฅ
526
+
527
+ ---
528
+
529
+ ## ไธ€ใ€่ฎบๆ–‡ไฟกๆฏ
530
+
531
+ | ้กน็›ฎ | ๅ†…ๅฎน |
532
+ |------|------|
533
+ | **ๆ ‡้ข˜** | PaDiM: a Patch Distribution Modeling Framework for Anomaly Detection and Localization |
534
+ | **arXiv** | [2011.08785](https://arxiv.org/abs/2011.08785) |
535
+ | **ไฝœ่€…** | Thomas Defard, Aleksandr Setkov, Angelique Loesch, Romaric Audigier |
536
+ | **ๅ‘่กจๆ—ถ้—ด** | 2021-03-15 |
537
+ | **ๅˆ†็ฑป** | cs.CV, cs.LG |
538
+
539
+ ---
540
+
541
+ ## ไบŒใ€็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ
542
+
543
+ ### ๅญ้ข†ๅŸŸๅฎšไฝ
544
+
545
+ **ๅทฅไธšๅ›พๅƒๅผ‚ๅธธๆฃ€ๆต‹ไธŽๅฎšไฝ**
546
+
547
+ ### ๅฝ“ๅ‰่ถ‹ๅŠฟ
548
+
549
+ 2024-2025ๅนดไธปๆต่ถ‹ๅŠฟๅŒ…ๆ‹ฌๅˆฉ็”จ้ข„่ฎญ็ปƒ่ง†่ง‰ๆจกๅž‹ๆๅ–ๅฑ€้ƒจ็‰นๅพ๏ผŒ็ป“ๅˆๆฆ‚็އๅปบๆจก่ฟ›่กŒๆ— ็›‘็ฃๅผ‚ๅธธๆฃ€ๆต‹๏ผŒๅผบ่ฐƒ้ซ˜ๆ•ˆๆŽจ็†ๅ’Œ็ป†็ฒ’ๅบฆๅฎšไฝใ€‚
550
+
551
+ ### ๆ–นๆณ•ๆ—๏ฟฝ๏ฟฝ๏ฟฝ็ณป
552
+
553
+ | # | ๆ–นๆณ•ๆ— | ๆ ธๅฟƒ็‰น็‚น | ไปฃ่กจๅทฅไฝœ |
554
+ |---|--------|----------|----------|
555
+ | 1 | **Patch Distribution Modeling** | ้ข„่ฎญ็ปƒCNNๆๅ–patch็‰นๅพ+้ซ˜ๆ–ฏๅˆ†ๅธƒๅปบๆจก | PaDiM (2021) |
556
+ | 2 | **Memory Bank-based Methods** | ๅญ˜ๅ‚จๆญฃๅธธๆ ทๆœฌ็‰นๅพๅˆฐ่ฎฐๅฟ†ๅบ“่ฟ›่กŒๆฃ€็ดข | PatchCore (2022) |
557
+ | 3 | **Knowledge Distillation Methods** | ๅธˆ็”Ÿ็ฝ‘็ปœ๏ผŒๅญฆ็”Ÿๅญฆไน ๆจกไปฟๆ•™ๅธˆ่พ“ๅ‡บ | STFPM (2021) |
558
+ | 4 | **Reconstruction-based Methods** | ่‡ช็ผ–็ ๅ™จ้‡ๅปบๆญฃๅธธๆ ทๆœฌ๏ผŒๅผ‚ๅธธ้‡ๅปบ่ฏฏๅทฎๅคง | AutoEncoder |
559
+ | 5 | **Synthetic Defect Methods** | ็”Ÿๆˆๅˆๆˆ็ผบ้™ท็”จไบŽ่ฎญ็ปƒ | DRAEM (2021) |
560
+
561
+ ---
562
+
563
+ ## ไธ‰ใ€ๅผ€ๆบๅฎž็Žฐ่ฏ„ไผฐๆŽ’ๅ
564
+
565
+ ### 1. [openvinotoolkit/anomalib](https://github.com/openvinotoolkit/anomalib) `[Embedding-based]`
566
+
567
+ โœ… **ๅฏๅค็Žฐๆ€ง: 80/80** | ๐ŸŸข **ๅฏนๆฏ”ๅฎž้ชŒ้€‚้…ๅบฆ: 20/20** | **็ปผๅˆ: 100/100**
568
+
569
+ | ็ปดๅบฆ | ๅพ—ๅˆ† | ๆปกๅˆ† | ่ฏดๆ˜Ž |
570
+ |------|:----:|:----:|------|
571
+ | ็Žฏๅขƒ้…็ฝฎ | 15 | 15 | ไพ่ต–ๆ–‡ไปถๅฎŒๆ•ดๆ€ง |
572
+ | ๆ–‡ๆกฃ่ดจ้‡ | 20 | 20 | ๅฎ‰่ฃ…/่ฎญ็ปƒ/ๆ•ฐๆฎ่ฏดๆ˜Ž |
573
+ | ไปฃ็ ๅฏ็”จๆ€ง | 20 | 20 | ่ฎญ็ปƒ+ๆŽจ็†่„šๆœฌ |
574
+ | ็คพๅŒบๆดป่ทƒๅบฆ | 10 | 10 | Star + ๆ›ดๆ–ฐ้ข‘็އ |
575
+ | ไพ่ต–ๅฅๅบทๅบฆ | 15 | 15 | ไพ่ต–ๅ…ผๅฎนๆ€ง |
576
+ | **ๅฏนๆฏ”ๅฎž้ชŒ** | **20** | **20** | **benchmark + ้ข„่ฎญ็ปƒๆƒ้‡** |
577
+
578
+ โญ Star: 4000 | ๅŒน้…่ฏ: `anomaly detection library pytorch`
579
+
580
+ **ๅˆ†ๆž**: anomalibๆไพ›ไบ†ๅฎŒๆ•ด็š„benchmarkๆก†ๆžถ๏ผŒๆ”ฏๆŒMVTec AD็ญ‰ๆ ‡ๅ‡†ๆ•ฐๆฎ้›†๏ผŒๆœ‰้ข„่ฎญ็ปƒๆƒ้‡ๅ’Œๆ ‡ๅ‡†ๅŒ–่ฏ„ไผฐ่„šๆœฌ๏ผŒๅฏไปฅ็›ดๆŽฅ็”จไบŽๅฏนๆฏ”ๅฎž้ชŒใ€‚
581
+
582
+ **ๅฏนๆฏ”ๅฎž้ชŒๅปบ่ฎฎ**: ๅฏไปฅ็›ดๆŽฅไฝœไธบๅฏนๆฏ”ๅฎž้ชŒ baseline
583
+
584
+ ---
585
+
586
+ ### 2. [ๅ…ถไป–ไป“ๅบ“...]
587
+
588
+ ๏ผˆๆฏไธชไป“ๅบ“ๅŒๆ ทๆ ผๅผ็š„ๅ…ญ็ปดๅบฆๆ˜Ž็ป†่กจ๏ผ‰
589
+
590
+ ---
591
+
592
+ ## ๅ››ใ€ๅฏนๆฏ”ๅฎž้ชŒๆŽจ่ๆ€ป็ป“
593
+
594
+ ### ๐ŸŸข ๅฏ็›ดๆŽฅ็”จไบŽๅฏนๆฏ”ๅฎž้ชŒ
595
+
596
+ - **[openvinotoolkit/anomalib](https://github.com/openvinotoolkit/anomalib)** โ€” ๅฏไปฅ็›ดๆŽฅไฝœไธบๅฏนๆฏ”ๅฎž้ชŒ baseline
597
+
598
+ ### ๐ŸŸก ้œ€่ฆๅฐ‘้‡ไฟฎๆ”นๅŽๅฏ็”จ
599
+
600
+ - **[ๅ…ถไป–ไป“ๅบ“...]** โ€” ้œ€่ฆ้€‚้…ๆ•ฐๆฎ้›†ๆ ผๅผ
601
+
602
+ ### ๐Ÿ”ด ไป…้€‚ๅˆๅ‚่€ƒไปฃ็ ๅฎž็Žฐ
603
+
604
+ - **[ๅ…ถไป–ไป“ๅบ“...]** โ€” ไป…ๆไพ›ๆŽจ็†demo๏ผŒๆ— ่ฎญ็ปƒ่„šๆœฌ...
605
+
606
+ ---
607
+
608
+ > ResearchRadar ยท ่ฎบๆ–‡็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ + ๅผ€ๆบไปฃ็ ่ฏ„ไผฐ + ๅฏนๆฏ”ๅฎž้ชŒๆŽจ่
609
+ ```
610
+
611
+ ### 5.2 Gradio ็•Œ้ขๆ•ˆๆžœ
612
+
613
+ ๆต่งˆๅ™จๆ‰“ๅผ€ `http://127.0.0.1:7860`๏ผŒไผš็œ‹ๅˆฐ๏ผš
614
+ - ๆ ‡้ข˜ๆ ๏ผšResearchRadar โ€” ็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ + ๅผ€ๆบไปฃ็ ่ฏ„ไผฐ
615
+ - ่พ“ๅ…ฅๆก†๏ผš่ฎบๆ–‡ arxiv ้“พๆŽฅ๏ผˆๅธฆ3ไธช็คบไพ‹ไธ€้”ฎๅกซๅ……๏ผ‰
616
+ - ่พ“ๅ‡บๅŒบ๏ผšMarkdown ๆธฒๆŸ“็š„ๅฎŒๆ•ด็ ”ๆŠฅ๏ผˆๅฆ‚5.1่Š‚ๆ‰€็คบ๏ผ‰
617
+ - ๅบ•้ƒจ็คบไพ‹๏ผšPaDiM / Transformer / DRAEM ไธ‰็ฏ‡่ฎบๆ–‡ๅฟซๆทๅ…ฅๅฃ
618
+
619
+ ### 5.3 ๅ‘ฝไปค่กŒ่พ“ๅ‡บๆ•ˆๆžœ
620
+
621
+ ```
622
+ ============================================================
623
+ [1/5] ๆญฃๅœจ่Žทๅ–่ฎบๆ–‡ไฟกๆฏ...
624
+ ๆ ‡้ข˜: PaDiM: a Patch Distribution Modeling Framework for Anomaly Detection and Localization
625
+ ไฝœ่€…: Thomas Defard, Aleksandr Setkov, Angelique Loesch
626
+ ๅˆ†็ฑป: cs.CV, cs.LG
627
+ ่€—ๆ—ถ: 0.8s
628
+
629
+ [2/5] ๆญฃๅœจ่งฃๆž็ ”็ฉถๆ–นๅ‘๏ผˆAgent 1๏ผšLLM ่ฐƒ็”จ๏ผ‰...
630
+ ๅญ้ข†ๅŸŸ: ๅทฅไธšๅ›พๅƒๅผ‚ๅธธๆฃ€ๆต‹ไธŽๅฎšไฝ
631
+ ่ถ‹ๅŠฟ: 2024-2025ๅนดไธปๆต่ถ‹ๅŠฟๅŒ…ๆ‹ฌๅˆฉ็”จ้ข„่ฎญ็ปƒ่ง†่ง‰ๆจกๅž‹ๆๅ–ๅฑ€้ƒจ็‰นๅพ...
632
+ ๆ–นๆณ•ๆ— (5 ไธช):
633
+ - Patch Distribution Modeling: ้ข„่ฎญ็ปƒCNNๆๅ–patch็‰นๅพ+้ซ˜ๆ–ฏๅˆ†ๅธƒๅปบๆจก...
634
+ - Memory Bank-based Methods: ๅญ˜ๅ‚จๆญฃๅธธๆ ทๆœฌ็‰นๅพๅˆฐ่ฎฐๅฟ†ๅบ“...
635
+ - Knowledge Distillation Methods: ๅธˆ็”Ÿ็ฝ‘็ปœๅญฆไน ๆจกไปฟ...
636
+ - Reconstruction-based Methods: ่‡ช็ผ–็ ๅ™จ้‡ๅปบๆญฃๅธธๆ ทๆœฌ...
637
+ - Synthetic Defect Methods: ็”Ÿๆˆๅˆๆˆ็ผบ้™ท็”จไบŽ่ฎญ็ปƒ...
638
+ ็”Ÿๆˆ 18 ไธชๆœ็ดขๆŸฅ่ฏข
639
+ ่€—ๆ—ถ: 8.3s
640
+
641
+ [3/5] ๆญฃๅœจๆœ็ดข GitHub ไป“ๅบ“๏ผˆ18 ไธชๆŸฅ่ฏข๏ผ‰...
642
+ ๅŽป้‡ๅŽ่Žทๅพ— 5 ไธชๅ€™้€‰ไป“ๅบ“
643
+ ่€—ๆ—ถ: 22.1s
644
+ ๅ€™้€‰ๅˆ—่กจ:
645
+ 1. openvinotoolkit/anomalib โญ4000
646
+ 2. hcw-00/PatchCore โญ850
647
+ 3. taikiinoue45/STFPM โญ320
648
+ 4. VitjanZ/DRAEM โญ180
649
+ 5. xiahaifeng1995/PaDiM-Anomaly-Detection โญ150
650
+
651
+ [4/5] ๆญฃๅœจ่ฏ„ไผฐ 5 ไธชไป“ๅบ“...
652
+ (1/5) ่ฏ„ไผฐ openvinotoolkit/anomalib [Embedding-based]...
653
+ (2/5) ่ฏ„ไผฐ hcw-00/PatchCore [Memory Bank]...
654
+ (3/5) ่ฏ„ไผฐ taikiinoue45/STFPM [Teacher-Student]...
655
+ (4/5) ่ฏ„ไผฐ VitjanZ/DRAEM [Synthetic Defect]...
656
+ (5/5) ่ฏ„ไผฐ xiahaifeng1995/PaDiM-Anomaly-Detection [Embedding-based]...
657
+ ่ฏ„ไผฐ่€—ๆ—ถ: 42.5s
658
+
659
+ [5/5] ๅฎŒๆˆ๏ผ
660
+ ๆœ€้ซ˜ๅˆ†: openvinotoolkit/anomalib (100/100)
661
+ ๆ€ป่€—ๆ—ถ: 73.7s
662
+ ============================================================
663
+
664
+ [ๆญคๅค„่พ“ๅ‡บๅฎŒๆ•ด Markdown ็ ”ๆŠฅ๏ผŒๅŒ5.1่Š‚]
665
+ ```
666
+
667
+ ---
668
+
669
+ ## 6. ๆ•…้šœๆŽ’ๆŸฅๆŒ‡ๅ—
670
+
671
+ ### 6.1 ็”ฒๆจกๅ—ๅฐšๆœชไบคไป˜
672
+
673
+ | ็—‡็Šถ | ๅŽŸๅ›  | ๅค„็† |
674
+ |------|------|------|
675
+ | `python run.py ...` ๆŠฅ้”™ `็ผบๅฐ‘ๆจกๅ—: paper_fetcher.py` | ็”ฒๆœชไบคไป˜ | ็ญ‰ๅพ…็”ฒ๏ผŒๆˆ–ๅ…ˆ็”จ `python test_agents.py` ้ชŒ่ฏไน™็š„้ƒจๅˆ† |
676
+ | `python app.py` ๆŠฅ้”™ `ๆ— ๆณ•ๅฏผๅ…ฅ run.py` | ๅŒไธŠ | ๅŒไธŠ |
677
+
678
+ ### 6.2 API ็›ธๅ…ณ
679
+
680
+ | ็—‡็Šถ | ๅŽŸๅ›  | ๅค„็† |
681
+ |------|------|------|
682
+ | `LLM API ่ฐƒ็”จๅคฑ่ดฅ๏ผˆๅทฒ้‡่ฏ• 3 ๆฌก๏ผ‰` | DeepSeek API ไธๅฏ่พพๆˆ–ๆฌ ่ดน | ๆฃ€ๆŸฅ `llm_utils.py` ไธญ็š„ API Key ๆ˜ฏๅฆๆœ‰ๆ•ˆ๏ผ›ๆฃ€ๆŸฅ็ฝ‘็ปœ |
683
+ | GitHub API `403 rate limit exceeded` | GitHub ้™ๆต | ็”ฒ้œ€ๆทปๅŠ  GitHub Token๏ผ›ๆˆ–ๅ‡ๅฐ‘ๆœ็ดขๆŸฅ่ฏขๆ•ฐ |
684
+ | arxiv API ่ถ…ๆ—ถ | arxiv ๆœๅŠกๅ™จๆ…ข | ้‡่ฏ•๏ผ›็”ฒๅฏๅŠ  timeout ๅ‚ๆ•ฐ |
685
+
686
+ ### 6.3 ๆ•ฐๆฎๆ ผๅผไธๅŒน้…
687
+
688
+ | ็—‡็Šถ | ๅŽŸๅ›  | ๅค„็† |
689
+ |------|------|------|
690
+ | `KeyError: 'full_name'` | ็”ฒ็š„ `search_repos` ่ฟ”ๅ›ž็š„ dict ็ผบๅฐ‘ๅญ—ๆฎต | ๅฏน็…งๆœฌๆ–‡ๆกฃ็ฌฌ2.2่Š‚็š„ๅญ—ๆฎต่กจ่กฅ้ฝ |
691
+ | `ValueError: ๆ— ๆณ•่งฃๆž LLM ่ฟ”ๅ›ž็š„ JSON` | LLM ่ฟ”ๅ›žๆ ผๅผๅผ‚ๅธธ | ๆŸฅ็œ‹็ปˆ็ซฏ่พ“ๅ‡บไธญ็š„"ๅŽŸๅง‹่ฟ”ๅ›ž๏ผˆๅ‰500ๅญ—็ฌฆ๏ผ‰"๏ผ›่”็ณปไน™ไผ˜ๅŒ–่งฃๆž้€ป่พ‘ |
692
+ | ็ ”ๆŠฅๆ˜พ็คบ "N/A" ่ฟ‡ๅคš | ็”ฒ่ฟ”ๅ›ž็š„ dict ไธญๅฏ้€‰ๅญ—ๆฎต็ผบๅคฑ | ๆฃ€ๆŸฅ paper dict ๆ˜ฏๅฆๅซ `published`, `categories` ็ญ‰ๅญ—ๆฎต |
693
+
694
+ ### 6.4 ่ฏ„ๅˆ†ๅผ‚ๅธธ
695
+
696
+ | ็—‡็Šถ | ๅŽŸๅ›  | ๅค„็† |
697
+ |------|------|------|
698
+ | ๆ‰€ๆœ‰ไป“ๅบ“ๅพ—ๅˆ†็›ธ่ฟ‘ | LLM ่ฏ„ๅˆ†ไธๅคŸๆœ‰ๅŒบๅˆ†ๅบฆ | ๆฃ€ๆŸฅไผ ็ป™ LLM ็š„ README/deps ไฟกๆฏๆ˜ฏๅฆ่ถณๅคŸไธฐๅฏŒ |
699
+ | ๆ˜Žๆ˜พไผ˜่ดจไป“ๅบ“ๅพ—ๅˆ†ๅพˆไฝŽ | `fetch_readme` ๆˆ– `fetch_dependencies` ่ฟ”ๅ›žไบ†็ฉบๅ€ผ | ๆฃ€ๆŸฅ API ่ฐƒ็”จๆ˜ฏๅฆๆˆๅŠŸ๏ผŒ็ฝ‘็ปœๆ˜ฏๅฆๅฏ่พพ |
700
+ | ๆ‰€ๆœ‰ไป“ๅบ“ `[ๆœชๅฝ’็ฑป]` | `_match_family` ไธญ่‹ฑๅŒ่ฏญๅŒน้…้ƒฝๆœชๅ‘ฝไธญ | ๆฃ€ๆŸฅ LLM ่ฟ”ๅ›ž็š„ๆ–นๆณ•ๆ—ๅๆ˜ฏๅฆ้žๅธธ่ง่กจ่ฟฐ๏ผ›ๅฏๅœจ `_DOMAIN_KEYWORDS` ่กฅๅ……ๅ…ณ้”ฎ่ฏ |
701
+
702
+ ---
703
+
704
+ ## 7. ๅทฒ็Ÿฅๅฑ€้™ไธŽๅŽ็ปญ่ฟญไปฃ
705
+
706
+ ### 7.1 ไน™ๅทฒ่ฏ†ๅˆซไฝ†ๆœชไฟฎๅค็š„้˜ฒๅพก็ผบๅฃ
707
+
708
+ 1. **ไป“ๅบ“่ฏ„ไผฐๅพช็Žฏไธญ็š„ๅผ‚ๅธธ็ฒ’ๅบฆไธๅคŸ็ป†**๏ผˆ`run.py` ็ฌฌ164-170่กŒ๏ผ‰๏ผšๅฝ“ๅ‰ `try/except` ๅŒ…่ฃนไบ† `fetch_readme` + `fetch_dependencies` + `evaluate_repo` ็š„ๆ•ดๆฎตใ€‚ๅฆ‚ๆžœ `fetch_readme` ๆŠ›ๅ‡บๅผ‚ๅธธ๏ผˆ่™ฝ็„ถๅฎƒไธๅบ”่ฏฅ๏ผ‰๏ผŒๆ•ดไธชไป“ๅบ“็š„่ฏ„ไผฐ้ƒฝไผšๆ ‡่ฎฐไธบ error๏ผŒไฝ†ๆ— ๆณ•็Ÿฅ้“ๆ˜ฏ่Žทๅ–ๅคฑ่ดฅ่ฟ˜ๆ˜ฏ่ฏ„ไผฐๅคฑ่ดฅใ€‚ๆ”น่ฟ›ๆ–นๅ‘๏ผšๅˆ†ๆญฅ try/exceptใ€‚
709
+
710
+ 2. **`_match_family` ไฝฟ็”จๅ…ณ้”ฎ่ฏๅŒน้…่€Œ้ž่ฏญไน‰ๅŒน้…**๏ผšๅทฒ้€š่ฟ‡ไธญ่‹ฑๅŒ่ฏญๅ…ณ้”ฎ่ฏๆ˜ ๅฐ„ๅฐฝๅŠ›ๆๅ‡ๅŒน้…็އ๏ผŒไฝ†ๆญฃ็กฎ็އไป้ž100%ใ€‚ไปปๅŠกไนฆ่ฎกๅˆ’ Week 3 ็”จ LLM ๅš็ฒพๅ‡†ๅฝ’็ฑปใ€‚
711
+
712
+ 3. **`parse_json_safe` ็ญ–็•ฅ4๏ผˆๆœ€ๅค–ๅฑ‚ `{...}`็š„่ดชๅฉชๅŒน้…๏ผ‰**๏ผšๅฆ‚ๆžœ LLM ไธ€ๆฌก่ฟ”ๅ›žไบ†ๅคšไธช JSON ๅฏน่ฑก๏ผˆๅฆ‚ๅ…ˆ่งฃ้‡Šๅ†็ป™ JSON๏ผ‰๏ผŒ็ญ–็•ฅ4็š„ `\{[\s\S]*\}` ไผšไปŽ็ฌฌไธ€ไธช `{` ๅŒน้…ๅˆฐๆœ€ๅŽไธ€ไธช `}`๏ผŒไบง็”Ÿไธ€ไธช้žๆณ• JSON ๅญ—็ฌฆไธฒใ€‚ๆญคๆƒ…ๅ†ตๆžๅฐ‘ๅ‡บ็Žฐ๏ผŒ็ญ–็•ฅ5๏ผˆๆธ…็†ๅฐพ้ƒจ้€—ๅท๏ผ‰ๅฏ่ƒฝไนŸไฟฎไธๅฅฝ๏ผŒๆœ€็ปˆไผš่งฆๅ‘็ญ–็•ฅ6๏ผˆๅ…ณ้”ฎ่ฏๅฎšไฝ๏ผ‰ๆˆ–ๅคฑ่ดฅใ€‚ๅฝ“ๅ‰้€š่ฟ‡็บฆๆŸ Prompt๏ผˆ"ไธ่ฆไปปไฝ•้ขๅค–ๆ–‡ๅญ—"๏ผ‰ๆฅ้ฟๅ…ใ€‚
713
+
714
+ 4. **ๆ— ้‡่ฏ•ๆœบๅˆถๅœจ GitHub API ๅฑ‚้ข**๏ผš`run.py` ๆฒกๆœ‰ๅฏน็”ฒ็š„ Workflow ๅ‡ฝๆ•ฐๅš้‡่ฏ•ใ€‚ๅฆ‚ๆžœ GitHub API ไธดๆ—ถ่ฟ”ๅ›ž 503๏ผŒไป“ๅบ“ๆœ็ดขๆˆ– README ่Žทๅ–ไผš็›ดๆŽฅๅคฑ่ดฅใ€‚ๅปบ่ฎฎ็”ฒๅœจ่‡ชๅทฑ็š„ๅ‡ฝๆ•ฐๅ†…้ƒจๅŠ ้‡่ฏ•๏ผˆ1-2ๆฌกๅณๅฏ๏ผ‰ใ€‚
715
+
716
+ ### 7.2 MVP ๅŽๅปบ่ฎฎ่ฟญไปฃๆ–นๅ‘
717
+
718
+ | ไผ˜ๅ…ˆ็บง | ๅ†…ๅฎน | ่ดŸ่ดฃ | ้ข„่ฎกๅทฅไฝœ้‡ |
719
+ |--------|------|------|------------|
720
+ | P0 | ็”ฒไบคไป˜3ไธชWorkflowๆจกๅ— | ็”ฒ | 2-3ๅคฉ |
721
+ | P0 | ่”่ฐƒ + ็ซฏๅˆฐ็ซฏๆต‹่ฏ• | ็”ฒไน™ไธ€่ตท | 1ๅคฉ |
722
+ | P1 | `_match_family` ๅ‡็บงไธบ LLM ่ฏญไน‰ๅŒน้… | ไน™ | ๅŠๅคฉ |
723
+ | P1 | fetch_readme ๅคฑ่ดฅๆ—ถไผ˜้›…้™็บง๏ผˆไธ้˜ปๆ–ญ่ฏ„ไผฐ๏ผ‰ | ไน™ | 1ๅฐๆ—ถ |
724
+ | P2 | ๆ”ฏๆŒๆ›ดๅคš่ฎบๆ–‡ๆบ๏ผˆไธไป…ๆ˜ฏ arxiv๏ผ‰ | ็”ฒ | 1ๅคฉ |
725
+ | P2 | ่ฏ„ไผฐ็ปดๅบฆๆƒ้‡ๅฏ้…็ฝฎ | ไน™ | 2ๅฐๆ—ถ |
726
+ | P3 | ็ผ“ๅญ˜ LLM ่ฏ„ไผฐ็ป“ๆžœ๏ผˆ้ฟๅ…้‡ๅค่ฐƒ็”จ่Šฑ้’ฑ๏ผ‰ | ไน™ | ๅŠๅคฉ |
727
+ | P3 | ๆ”ฏๆŒๆ‰น้‡่ฎบๆ–‡่พ“ๅ…ฅ | ็”ฒ+ไน™ | 1ๅคฉ |
728
+
729
+ ---
730
+
731
+ ## ้™„ๅฝ•A๏ผš่‡ชๆฃ€ Checklist๏ผˆไน™็š„ไปฃ็ ๏ผ‰
732
+
733
+ ๅœจ็”ฒไบคไป˜ๅ‰๏ผŒไน™ๅฏ็”จๆญคๆธ…ๅ•่‡ชๆŸฅ๏ผš
734
+
735
+ - [x] `python test_agents.py` โ†’ 6/6 ้€š่ฟ‡
736
+ - [x] `python direction_analyzer.py` โ†’ 3็ฏ‡่ฎบๆ–‡ๆญฃๅธธ่พ“ๅ‡บ
737
+ - [x] `python repo_evaluator.py` โ†’ 3ไธชๅœบๆ™ฏ่ฏ„ๅˆ†ๅˆ็†
738
+ - [x] `llm_utils.py` โ†’ call_llm ๆœ‰3ๆฌก้‡่ฏ•ใ€parse_json_safe ๆœ‰6็ง็ญ–็•ฅ
739
+ - [x] `run.py` โ†’ ๅฏผๅ…ฅๅคฑ่ดฅๆ—ถๆŠ›ๅ‡บ ImportError ่€Œ้ž sys.exit
740
+ - [x] `_DOMAIN_KEYWORDS` โ†’ ไธญ่‹ฑๅŒ่ฏญๅ…ณ้”ฎ่ฏๅทฒ่ฆ†็›–6ๅคงๆ–นๆณ•ๆ—็ฑปๅˆซ
741
+ - [x] `app.py` โ†’ ๅฏผๅ…ฅๅคฑ่ดฅๆœ‰ๆ˜Ž็กฎ้”™่ฏฏๆ็คบ
742
+ - [x] `state.py` โ†’ ๆ•ฐๆฎๆจกๅž‹ๅฎšไน‰ๅฎŒๆ•ด
743
+ - [x] `requirements.txt` โ†’ openai, gradio, requests ๅทฒๅˆ—ๅ‡บ
744
+
745
+ ## ้™„ๅฝ•B๏ผš่‡ชๆฃ€ Checklist๏ผˆ็”ฒ็š„3ไธชๆจกๅ—๏ผ‰
746
+
747
+ ็”ฒไบคไป˜ๆ—ถ่ฏทๅฏน็…งๆญคๆธ…ๅ•็กฎ่ฎค๏ผš
748
+
749
+ **paper_fetcher.py**
750
+ - [ ] `fetch_paper_info(arxiv_url)` ๅ‡ฝๆ•ฐๅญ˜ๅœจ
751
+ - [ ] ่ฟ”ๅ›žๅ€ผๅŒ…ๅซ `arxiv_id`, `title`, `abstract`, `authors`๏ผˆๅฟ…้œ€๏ผ‰
752
+ - [ ] ่ฟ”ๅ›žๅ€ผๅŒ…ๅซ `published`, `categories`๏ผˆๆŽจ่๏ผ‰
753
+ - [ ] `python paper_fetcher.py` ่‡ชๆต‹ๅฏ่ฟ่กŒ
754
+
755
+ **repo_searcher.py**
756
+ - [ ] `search_repos(queries, max_per_keyword=5)` ๅ‡ฝๆ•ฐๅญ˜ๅœจ
757
+ - [ ] ่ฟ”ๅ›žๅ€ผๆฏไธช dict ๅŒ…ๅซๆ‰€ๆœ‰ๅฟ…้œ€ๅญ—ๆฎต๏ผˆ่ง2.2่Š‚๏ผ‰
758
+ - [ ] ๅทฒๆŒ‰ `full_name` ๅŽป้‡
759
+ - [ ] ๅทฒๆŒ‰ `stars` ้™ๅบๆŽ’ๅˆ—
760
+ - [ ] ๅค„็†ไบ† GitHub API ้™ๆต๏ผˆๅŠ  token ๆˆ–ๅ‡ๅฐ‘่ฏทๆฑ‚้ข‘็އ๏ผ‰
761
+ - [ ] `python repo_searcher.py` ่‡ชๆต‹ๅฏ่ฟ่กŒ
762
+
763
+ **repo_fetcher.py**
764
+ - [ ] `fetch_readme(owner, name)` ๅ‡ฝๆ•ฐๅญ˜ๅœจ๏ผŒๅคฑ่ดฅ่ฟ”ๅ›ž `None`
765
+ - [ ] `fetch_dependencies(owner, name)` ๅ‡ฝๆ•ฐๅญ˜ๅœจ๏ผŒๅคฑ่ดฅ่ฟ”ๅ›ž `{}`
766
+ - [ ] ่ฆ†็›–ไบ† `requirements.txt`, `environment.yml`, `setup.py`, `setup.cfg`, `Pipfile`, `pyproject.toml`
767
+ - [ ] ไธๆŠ›ๅ‡บๅผ‚ๅธธ
768
+ - [ ] `python repo_fetcher.py` ่‡ชๆต‹ๅฏ่ฟ่กŒ
769
+
770
+ ---
771
+
772
+ > ๆœฌๆ–‡ๆกฃ้šไปฃ็ ๅŒๆญฅ็ปดๆŠคใ€‚ๅฆ‚ๆœ‰็–‘้—ฎๆˆ–้œ€่ฆไฟฎๆ”นๅฅ‘็บฆ๏ผŒ่ฏท็”ฒไน™ๅŒๆ–นๅฝ“้ข็กฎ่ฎคๅŽๅ†ๆ”นไปฃ็ ๏ผŒ้ฟๅ…ไฟกๆฏไธๅฏน็งฐใ€‚
ๆต็จ‹ๆžถๆž„ๅ›พ.md ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ResearchRadar ๆต็จ‹ๆžถๆž„ๅ›พ
2
+
3
+ ```
4
+ ็”จๆˆท่พ“ๅ…ฅ arxiv URL
5
+ โ”‚
6
+ โ–ผ
7
+ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
8
+ โ”‚ [1/5] ่ฎบๆ–‡ไฟกๆฏ่Žทๅ– (Workflow ยท ็”ฒ) โ”‚
9
+ โ”‚ paper_fetcher.fetch_paper_info() โ”‚
10
+ โ”‚ โ†’ arxiv API XML ่งฃๆž โ”‚
11
+ โ”‚ โ†’ {title, abstract, authors, categories, published} โ”‚
12
+ โ”‚ ๅซ้‡่ฏ•: 429/503 ่‡ชๅŠจ้€€้ฟ้‡่ฏ• 3 ๆฌก โ”‚
13
+ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
14
+ โ”‚
15
+ โ–ผ
16
+ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
17
+ โ”‚ [2/5] ๅฎฝๆณ› GitHub ๆœ็ดข (Workflow ยท ็”ฒ) โ”‚
18
+ โ”‚ โ”‚
19
+ โ”‚ โ‘  _extract_broad_queries(title, abstract) โ”‚
20
+ โ”‚ โ””โ”€ LLM ็”Ÿๆˆ 6-8 ไธช็ฒพๅ‡†่‹ฑๆ–‡ๆœ็ดข่ฏ โ”‚
21
+ โ”‚ (ๅคฑ่ดฅๅˆ™้™็บงไธบ่ง„ๅˆ™ๆๅ– fallback) โ”‚
22
+ โ”‚ โ”‚
23
+ โ”‚ โ‘ก repo_searcher.search_repos(queries) โ”‚
24
+ โ”‚ โ””โ”€ GitHub API ๆœ็ดข๏ผŒๆฏ่ฏๅ– top 5 โ”‚
25
+ โ”‚ โ””โ”€ ๆŒ‰ full_name ๅŽป้‡๏ผŒๆŒ‰ stars ้™ๅบ โ”‚
26
+ โ”‚ โ†’ 10~36 ไธชๅ€™้€‰ไป“ๅบ“ โ”‚
27
+ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
28
+ โ”‚
29
+ โ–ผ
30
+ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
31
+ โ”‚ [2.5] LLM ่ฟ‡ๆปคไธ็›ธๅ…ณไป“ๅบ“ (ๆ–ฐๅขž ยท ไน™) โ”‚
32
+ โ”‚ _filter_repos(title, abstract, repos) โ”‚
33
+ โ”‚ โ”œโ”€ >20 ไธชไป“ๅบ“ โ†’ ๆŒ‰ stars ๅ– top 20 ้€็ป™ LLM โ”‚
34
+ โ”‚ โ”œโ”€ LLM ๅˆคๆ–ญๆฏไธชไป“ๅบ“ๆ˜ฏๅฆไธŽ่ฎบๆ–‡ๆ–นๅ‘็›ธๅ…ณ โ”‚
35
+ โ”‚ โ”œโ”€ ๅ‰”้™คไธ็›ธๅ…ณไป“ๅบ“ โ”‚
36
+ โ”‚ โ””โ”€ ่ฟ‡ๆปคๅŽ <2 ไธช โ†’ ๅ›ž้€€ๅˆฐๅŽŸๅง‹็ป“ๆžœ โ”‚
37
+ โ”‚ โ†’ 15~17 ไธช็›ธๅ…ณไป“ๅบ“ โ”‚
38
+ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
39
+ โ”‚
40
+ โ–ผ
41
+ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
42
+ โ”‚ [3/5] ๆ–นๆณ•ๆ—ๅฝ’็บณ (Agent 1 ยท ไน™) โ”‚
43
+ โ”‚ direction_analyzer.analyze_direction() โ”‚
44
+ โ”‚ ่พ“ๅ…ฅ: ่ฎบๆ–‡ title + abstract + ่ฟ‡ๆปคๅŽ็š„ไป“ๅบ“ๅˆ—่กจ โ”‚
45
+ โ”‚ LLM ๅˆ†ๆžไป“ๅบ“ description/topics/stars โ†’ โ”‚
46
+ โ”‚ ่พ“ๅ‡บ: โ”‚
47
+ โ”‚ โ”œโ”€ subfield (ๅญ้ข†ๅŸŸๅฎšไฝ) โ”‚
48
+ โ”‚ โ”œโ”€ subfield_trend (่ถ‹ๅŠฟๅˆคๆ–ญ) โ”‚
49
+ โ”‚ โ”œโ”€ method_families[] โ”‚
50
+ โ”‚ โ”‚ โ”œโ”€ family_name, description โ”‚
51
+ โ”‚ โ”‚ โ”œโ”€ matched_repos[] โ† ไป“ๅบ“ๅฝ’ๅฑžๅ“ชไธชๆ–นๆณ•ๆ— โ”‚
52
+ โ”‚ โ”‚ โ””โ”€ search_queries[] โ† ่ฏฅๆ—็ฒพๅ‡†ๆœ็ดข่ฏ โ”‚
53
+ โ”‚ โ””โ”€ broad_queries[] โ† ๆœช่ฆ†็›–ๆ–นๅ‘็š„ๅฎฝๆณ›ๆœ็ดข่ฏ โ”‚
54
+ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
55
+ โ”‚
56
+ โ–ผ
57
+ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
58
+ โ”‚ [4/5] ไป“ๅบ“็ญ›้€‰ + ๆ–นๆณ•ๆ—ๆ˜ ๅฐ„ (่ฐƒๅบฆๅ™จ ยท ไน™) โ”‚
59
+ โ”‚ โ”œโ”€ ไปŽ Agent 1 ็š„ matched_repos ๅปบ็ซ‹ โ”‚
60
+ โ”‚ โ”‚ full_name โ†’ family_name ๆ˜ ๅฐ„ โ”‚
61
+ โ”‚ โ”œโ”€ classified: ๆœ‰ๆ–นๆณ•ๆ—ๅฝ’ๅฑž็š„ไป“ๅบ“ (ๆŒ‰ stars ๆŽ’ๅบ) โ”‚
62
+ โ”‚ โ”œโ”€ unclassified: ๆœชๅฝ’็ฑป็š„ไป“ๅบ“ (ๆŒ‰ stars ๆŽ’ๅบ) โ”‚
63
+ โ”‚ โ””โ”€ ๅ– top 5 ไฝœไธบๆœ€็ปˆ่ฏ„ไผฐๅ€™้€‰ โ”‚
64
+ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
65
+ โ”‚
66
+ โ–ผ
67
+ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
68
+ โ”‚ [5/5] ไป“ๅบ“่ฏฆๆƒ…่Žทๅ– + ่ฏ„ไผฐ (Agent 2 ยท ไน™) [ๅนถ่กŒ] โ”‚
69
+ โ”‚ โ”‚
70
+ โ”‚ ๅฏน 5 ไธชๅ€™้€‰ไป“ๅบ“๏ผŒThreadPoolExecutor ๅนถ่กŒๆ‰ง่กŒ: โ”‚
71
+ โ”‚ โ”‚
72
+ โ”‚ โ‘  repo_fetcher.fetch_readme(owner, name) โ”‚
73
+ โ”‚ โ””โ”€ GitHub Contents API๏ผŒbase64 ่งฃ็  โ”‚
74
+ โ”‚ โ”‚
75
+ โ”‚ โ‘ก repo_fetcher.fetch_dependencies(owner, name) โ”‚
76
+ โ”‚ โ””โ”€ ๆฃ€ๆŸฅ 11 ็งไพ่ต–ๆ–‡ไปถ็ฑปๅž‹ โ”‚
77
+ โ”‚ โ”‚
78
+ โ”‚ โ‘ข repo_evaluator.evaluate_repo() โ”‚
79
+ โ”‚ โ””โ”€ LLM ๅ…ญ็ปดๅบฆ่ฏ„ๅˆ† (ๅฏๅค็Žฐๆ€ง 80 + ้€‚้…ๅบฆ 20) โ”‚
80
+ โ”‚ โ”œโ”€ ็Žฏๅขƒ้…็ฝฎ 0-15 โ”‚
81
+ โ”‚ โ”œโ”€ ๆ–‡ๆกฃ่ดจ้‡ 0-20 โ”‚
82
+ โ”‚ โ”œโ”€ ไปฃ็ ๅฏ็”จๆ€ง 0-20 โ”‚
83
+ โ”‚ โ”œโ”€ ็คพๅŒบๆดป่ทƒๅบฆ 0-10 โ”‚
84
+ โ”‚ โ”œโ”€ ไพ่ต–ๅฅๅบทๅบฆ 0-15 โ”‚
85
+ โ”‚ โ””โ”€ ๅฏนๆฏ”ๅฎž้ชŒ้€‚้…ๅบฆ 0-20 โ”‚
86
+ โ”‚ โ”‚
87
+ โ”‚ ๆŒ‰ overall_score ้™ๅบๆŽ’ๅˆ— โ”‚
88
+ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
89
+ โ”‚
90
+ โ–ผ
91
+ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
92
+ โ”‚ ๆฑ‡ๆ€ป โ†’ format_report() (app.py) โ”‚
93
+ โ”‚ โ”‚
94
+ โ”‚ ็”Ÿๆˆ Markdown ็ ”ๆŠฅ๏ผŒๅ…ฑ 5 ้ƒจๅˆ†: โ”‚
95
+ โ”‚ ไธ€ใ€่ฎบๆ–‡ไฟกๆฏ (arXiv ้“พๆŽฅใ€ไฝœ่€…ใ€ๅˆ†็ฑป) โ”‚
96
+ โ”‚ ไบŒใ€็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ โ”‚
97
+ โ”‚ โ”œโ”€ ๅญ้ข†ๅŸŸๅฎšไฝ + ่ถ‹ๅŠฟ โ”‚
98
+ โ”‚ โ”œโ”€ ๆ–นๆณ•ๆ—่ฐฑ็ณป่กจ โ”‚
99
+ โ”‚ โ””โ”€ ๆ–นๆณ•ๆ—ๅผ€ๆบ่ฆ†็›–ๅบฆ (ๅ“ชไบ›ๅทฒๆœ‰ๅฎž็Žฐ/ๅ“ชไบ›ๆ˜ฏ็ฉบ็™ฝ) โ”‚
100
+ โ”‚ ๅ››ใ€ๅผ€ๆบๅฎž็Žฐ่ฏ„ไผฐๆŽ’ๅ (ๅ…ญ็ปดๅบฆ่ฏ„ๅˆ†ๆ˜Ž็ป†) โ”‚
101
+ โ”‚ ไบ”ใ€ๅฏนๆฏ”ๅฎž้ชŒๆŽจ่ๆ€ป็ป“ (๐ŸŸขๅฐฑ็ปช / ๐ŸŸก้œ€ไฟฎๆ”น / ๐Ÿ”ดไป…ๅ‚่€ƒ) โ”‚
102
+ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
103
+ ```
104
+
105
+ ## ๆ ธๅฟƒ่ฎพ่ฎกๅ†ณ็ญ–
106
+
107
+ > **ๅ…ˆๆœไป“ๅบ“ โ†’ ๅ†ๅŸบไบŽๅฎž้™…ๆ•ฐๆฎ่ฎฉ Agent ๅˆ†ๆž**๏ผŒ่€Œ้ž่ฎฉ LLM ๅ‡ญ่ฎฐๅฟ†็ผ–้€ ๆ–นๆณ•ๆ—ใ€‚
108
+ > Agent ๆ˜ฏ"ๅˆ†ๆžๆ•ด็†ๅทฅๅ…ท"๏ผŒไธๆ˜ฏ"็Ÿฅ่ฏ†ๅบ“"ใ€‚
109
+
110
+ ## ๆจกๅ—ๅˆ†ๅทฅ
111
+
112
+ | ๆจกๅ— | ่ดŸ่ดฃ | ็ฑปๅž‹ | ่ฏดๆ˜Ž |
113
+ |------|:----:|:----:|------|
114
+ | `paper_fetcher.py` | ็”ฒ | Workflow | arxiv API ่Žทๅ–่ฎบๆ–‡ๅ…ƒไฟกๆฏ๏ผŒๅซ้‡่ฏ•้€ป่พ‘ |
115
+ | `repo_searcher.py` | ็”ฒ | Workflow | GitHub API ๅ…ณ้”ฎ่ฏๆœ็ดข๏ผŒๅŽป้‡ๆŽ’ๅบ |
116
+ | `repo_fetcher.py` | ็”ฒ | Workflow | ่Žทๅ–ไป“ๅบ“ README + 11 ็งไพ่ต–ๆ–‡ไปถ |
117
+ | `direction_analyzer.py` | ไน™ | Agent 1 | LLM ๅŸบไบŽไป“ๅบ“ๆ•ฐๆฎๅฝ’็บณๆ–นๆณ•ๆ—่ฐฑ็ณป |
118
+ | `repo_evaluator.py` | ไน™ | Agent 2 | LLM ๅ…ญ็ปดๅบฆ่ฏ„ๅˆ† (ๅฏๅค็Žฐๆ€ง + ้€‚้…ๅบฆ) |
119
+ | `run.py` | ไน™ | ่ฐƒๅบฆๅ™จ | ไธฒ่”ๅ…จๆต็จ‹ [1/5]โ†’[5/5] + ๅ…ณ้”ฎ่ฏ็”Ÿๆˆ + ่ฟ‡ๆปค |
120
+ | `app.py` | ไน™ | ็•Œ้ข | Gradio Web UI + Markdown ็ ”ๆŠฅๆ ผๅผๅŒ– |
121
+ | `llm_utils.py` | ไน™ | ๅ…ฌๅ…ฑ | DeepSeek API ๅฎขๆˆท็ซฏ + JSON ๅฎ‰ๅ…จ่งฃๆž + ่พ“ๅ‡บๆ ก้ชŒ |
122
+
123
+ ## ็ ”ๆŠฅ่พ“ๅ‡บ็ป“ๆž„
124
+
125
+ 1. **่ฎบๆ–‡ไฟกๆฏ** โ€” ๆ ‡้ข˜ใ€arXiv ้“พๆŽฅใ€ไฝœ่€…ใ€ๅ‘่กจๆ—ถ้—ดใ€ๅˆ†็ฑป
126
+ 2. **็ ”็ฉถๆ–นๅ‘ๅ…จๆ™ฏ** โ€” ๅญ้ข†ๅŸŸๅฎšไฝใ€ๅฝ“ๅ‰่ถ‹ๅŠฟใ€ๆ–นๆณ•ๆ—่ฐฑ็ณป่กจใ€ๅผ€ๆบ่ฆ†็›–ๅบฆ
127
+ 3. **ๆ–นๆณ•ๆ—ๅผ€ๆบ่ฆ†็›–ๅบฆ** โ€” ๅ“ชไบ›ๆ–นๆณ•ๆ—ๅทฒๆœ‰ๅฎž็Žฐใ€ๅ“ชไบ›ๆ˜ฏ็ ”็ฉถ็ฉบ็™ฝ
128
+ 4. **ๅผ€ๆบๅฎž็Žฐ่ฏ„ไผฐๆŽ’ๅ** โ€” ๆฏไธชไป“ๅบ“ๅ…ญ็ปดๅบฆ่ฏ„ๅˆ†ๆ˜Ž็ป† + ้ฃŽ้™ฉ็‚น + ๅฏนๆฏ”ๅฎž้ชŒๅปบ่ฎฎ
129
+ 5. **ๅฏนๆฏ”ๅฎž้ชŒๆŽจ่ๆ€ป็ป“** โ€” ๐ŸŸขๅฏ็›ดๆŽฅไฝฟ็”จ / ๐ŸŸก้œ€ๅฐ‘้‡ไฟฎๆ”น / ๐Ÿ”ดไป…้€‚ๅˆๅ‚่€ƒ