Major Changes: - Add StreamingService with OpenAI Compatible format - Upgrade Chat component V2 with Ant Design X integration - Implement AIA module with 12 intelligent agents - Update API routes to unified /api/v1 prefix - Update system documentation Backend (~1300 lines): - common/streaming: OpenAI Compatible adapter - modules/aia: 12 agents, conversation service, streaming integration - Update route versions (RVW, PKB to v1) Frontend (~3500 lines): - modules/aia: AgentHub + ChatWorkspace (100% prototype restoration) - shared/Chat: AIStreamChat, ThinkingBlock, useAIStream Hook - Update API endpoints to v1 Documentation: - AIA module status guide - Universal capabilities catalog - System overview updates - All module documentation sync Tested: Stream response verified, authentication working Status: AIA V2.0 core completed (85%)
71 KiB
AI<EFBFBD>箄<EFBFBD><EFBFBD><EFBFBD>讃 - <20>冽<EFBFBD>憭滨<E686AD>撘<EFBFBD><E69298>𤏸恣<F0A48FB8>?
*<EFBFBD><EFBFBD>﹝<EFBFBD><EFBFBD>𧋦嚗? V1.2
*<EFBFBD>𥕦遣<EFBFBD>交<EFBFBD>嚗? 2025-11-22
<EFBFBD><EFBFBD><EFBFBD>擧凒<EFBFBD>堆<EFBFBD> 2025-11-23
*<EFBFBD><EFBFBD>鍂<EFBFBD>嗆挾嚗? MVP<56>嗆挾
*憸<EFBFBD>恣撌交<EFBFBD>嚗? 2<>? 蝏湔擪<EFBFBD><EFBFBD><EFBFBD> ASL撘<4C><E69298>穃𣪧<E7A983>?
<EFBFBD><EFBFBD> 撘<><E69298>𤏸<EFBFBD>摨行<E691A8>閫?
**敶枏<E695B6><E69E8F>嗆<EFBFBD>?*嚗𡄻<E59A97>?Day 1-5 撌脣<E6928C><E884A3>琜<EFBFBD><E7909C>𡒊垢<F0A1928A>券<EFBFBD>摰峕<E691B0>嚗㚁<E59A97>敺<EFBFBD><E695BA>蝡臬<E89DA1><E887AC>?
| <EFBFBD>嗆挾 | <EFBFBD>園𡢿 | <EFBFBD>嗆<EFBFBD>? | 摰峕<EFBFBD>摨? |
|---|---|---|---|
| Week 1 | 2025-11-22 ~ 2025-11-23 | <EFBFBD>?撌脣<E6928C><E884A3>? | 100% |
| - Day 1: PDF摮睃<E691AE><E79D83>滚𦛚 | 2025-11-22 | <EFBFBD>?撌脣<E6928C><E884A3>? | 100% |
| - Day 2: LLM 12摮埈挾<E59F88>滚𦛚 | 2025-11-22 | <EFBFBD>?撌脣<E6928C><E884A3>? | 100% |
| - Day 3: 撉諹<E69289><E8ABB9>滚𦛚 | 2025-11-22 | <EFBFBD>?撌脣<E6928C><E884A3>? | 100% |
| - Day 4銝𠰴<E98A9D>: <20>唳旿摨栞挽霈∩<E99C88>餈<EFBFBD>宏 | 2025-11-23 | <EFBFBD>?撌脣<E6928C><E884A3>? | 100% |
| - Day 4銝见<E98A9D>: <20>孵<EFBFBD><E5ADB5><EFBFBD><EFBFBD><EFBFBD>? | 2025-11-23 | <EFBFBD>?撌脣<E6928C><E884A3>? | 100% |
| - Day 5: API撘<49><E69298>? | 2025-11-23 | <EFBFBD>?撌脣<E6928C><E884A3>? | 100% |
| Week 2 | 2025-11-24 ~ 2025-11-27 | <EFBFBD>?敺<><E695BA>憪? | 0% |
| - Day 6-7: <20>滨垢撘<E59EA2><E69298>? | 敺<EFBFBD><EFBFBD>憪? | <EFBFBD>?敺<><E695BA>憪? | 0% |
| - Day 8: <20>滚<EFBFBD>蝡航<E89DA1>靚<EFBFBD><E99D9A>霂? | 敺<EFBFBD><EFBFBD>憪? | <EFBFBD>?敺<><E695BA>憪? | 0% |
**撌脣<E6928C><E884A3>鞉瓲敹<E793B2><E695B9><EFBFBD>?*嚗?- <20>?PDF摮睃<E691AE>銝擧<E98A9D><E693A7>𡝗<EFBFBD><F0A19D97>∴<EFBFBD><E288B4><EFBFBD><EFBFBD>撅<EFBFBD><E69285>
- <EFBFBD>?Prompt撌亦<E6928C>雿梶頂嚗𠄎ystem/User/Schema嚗?- <20>?LLM 12摮埈挾<E59F88>滚𦛚嚗𠃊ougat隡睃<E99AA1> + <20>峕芋<E5B395>?+ 3撅<33>SON閫<4E><E996AB>嚗?- <20>?<3F>餃郎<E9A483>餉<EFBFBD>撉諹<E69289><E8ABB9>剁<EFBFBD>5<EFBFBD>∟<EFBFBD><E2889F>辷<EFBFBD>
- <EFBFBD>?霂<>旿<EFBFBD>暸<EFBFBD>霂<EFBFBD>膥
- <EFBFBD>?<3F>脩<EFBFBD>璉<EFBFBD>瘚𧢲<E7989A><F0A7A2B2>?- <20>?<3F><><EFBFBD>瘚贝<E7989A>獢<EFBFBD>沲
- <EFBFBD>?<3F>唳旿摨廍chema霈曇恣嚗?撘㰘”嚗?- <20>?<3F>唳旿摨𤘪<E691A8><F0A498AA>刻<EFBFBD>蝘餃<E89D98><E9A483>?- <20>?FulltextScreeningService嚗<65>鸌憭<E9B88C><E686AD><EFBFBD>滚𦛚嚗?- <20>?5銝芣瓲敹<E793B2>PI<50>亙藁
- <EFBFBD>?Excel撖澆枂<E6BE86>滚𦛚嚗?銝杵heet嚗?- <20>?Zod<6F><64>㺭撉諹<E69289>
- <EFBFBD>?REST Client瘚贝<E7989A><E8B49D>其<EFBFBD>嚗?1銝迎<E98A9D>
**銝衤<E98A9D>甇?*嚗鋽ay 6 <20>滨垢UI撘<49><E69298>?
<EFBFBD><EFBFBD> <20>桀<EFBFBD>
- 1. 憿寧𤌍璁<F0A48C8D>膩
- 2. <20>嗆<EFBFBD>霈曇恣
- 3. <20>𡁶鍂<F0A181B6>賢<EFBFBD>撅<EFBFBD>挽霈∴<E99C88><E288B4>臬<EFBFBD><E887AC>剁<EFBFBD>
- [4. <20>唳旿摨栞挽霈((#4-<2D>唳旿摨栞挽霈?
- 5. API霈曇恣
- [6. <20>冽<EFBFBD>憭滨<E686AD>銝𡁜𦛚撅<F0A69B9A>挽霈((#6-<2D>冽<EFBFBD>憭滨<E686AD>銝𡁜𦛚撅<F0A69B9A>挽霈?
- 7. <20>滨垢霈曇恣
- [8. 撘<><E69298>烐<EFBFBD><E78390>篏(#8-撘<><E69298>烐<EFBFBD><E78390>?
- [9. <20><><EFBFBD>航<EFBFBD><E888AA>鉛(#9-<2D><><EFBFBD>航<EFBFBD><E888AA>?
- [10. 憌𡡞埯銝擧釣<E693A7>譍<EFBFBD>憿鉛(#10-憌𡡞埯銝擧釣<E693A7>譍<EFBFBD>憿?
1. 憿寧𤌍璁<F0A48C8D>膩
1.1 <20>蠘<EFBFBD>摰帋<E691B0>
<EFBFBD>冽<EFBFBD>憭滨<EFBFBD><EFBFBD>舐頂蝏蠘<EFBFBD>隞?Meta<74><61><EFBFBD><EFBFBD><EFBFBD>洵鈭屸𧫴畾萇<E795BE><E89087>㚁<EFBFBD>撖寞<E69296>憸䀹<E686B8>閬<EFBFBD><E996AC>蝑𥕦<E89D91>"<22>脲郊蝥喳<E89DA5>"<22><><EFBFBD><EFBFBD>殷<EFBFBD><E6AEB7>箔<EFBFBD><EFBFBD>冽<EFBFBD><EFBFBD><EFBFBD>捆餈𥡝<EFBFBD>銝交聢<EFBFBD><EFBFBD><EFBFBD>甈∠<EFBFBD><EFBFBD>剹<EFBFBD>?
1.2 <20>詨<EFBFBD>隞瑕<E99A9E>?
| 蝏游漲 | 霂湔<EFBFBD> |
|---|---|
| <EFBFBD>桃<EFBFBD> | <EFBFBD>斗鱏<EFBFBD><EFBFBD>讃<EFBFBD>唳旿<EFBFBD>?*摰峕㟲<E5B395>批<EFBFBD><E689B9>舐鍂<E88890>?*嚗𣬚&摰𡁏<E691B0>蝏<EFBFBD>熙<EFBFBD>? |
| 靘脲旿 | 12摮埈挾璅⊥踎嚗<EFBFBD>抅鈭焾ochrane RoB 2.0<EFBFBD><EFBFBD><EFBFBD>嚗? |
| 颲枏<EFBFBD> | <EFBFBD><EFBFBD><EFBFBD><EFBFBD>䁅<EFBFBD><EFBFBD>萘<EFBFBD><EFBFBD>?<3F>脲郊蝥喳<E89DA5>"<22><>讃嚗<E8AE83>歇<EFBFBD>瑕<EFBFBD><E79195>冽<EFBFBD>PDF嚗? |
| 颲枏枂 | <EFBFBD><EFBFBD>蝏<EFBFBD>熙<EFBFBD>亙<EFBFBD>銵?+ <20>㘾膄<E398BE>𡑒” + PRISMA蝏蠘恣 + 摰峕㟲霂<E39FB2>旿<EFBFBD>? |
| <EFBFBD>𡒊賒 | <EFBFBD><EFBFBD>蝏<EFBFBD>熙<EFBFBD>亦<EFBFBD><EFBFBD><EFBFBD>讃餈𥕦<EFBFBD>"<22>冽<EFBFBD><E586BD>唳旿<E594B3>𣂼<EFBFBD>"<22>嗆挾 |
| 韐券<EFBFBD><EFBFBD><EFBFBD><EFBFBD> | <EFBFBD><EFBFBD>&<EFBFBD><EFBFBD>竉85%嚗㇈VP嚗㚁<E59A97><E39A81>?2%嚗Ā1.0嚗㚁<EFBFBD><EFBFBD>?6%嚗Ā2.0嚗? |
1.3 12摮埈挾璅⊥踎嚗<E8B88E>抅鈭焾ochrane RoB 2.0<EFBFBD><EFBFBD><EFBFBD>嚗?
1. <20><>讃<EFBFBD>交<EFBFBD>嚗<EFBFBD>洵銝<E6B4B5>雿𡏭<E99BBF><F0A18FAD><EFBFBD><EFBFBD>僑隞賬<E99A9E><E8B3AC><EFBFBD><EFBFBD>𨳍<EFBFBD><F0A8B38D>OI嚗?2. <20>𠉛弦蝐餃<E89D90>嚗㇌CT<43><54><EFBFBD><EFBFBD>㛖<EFBFBD>蝛嗥<E89D9B>嚗?3. <20>𠉛弦霈曇恣蝏<E681A3><E89D8F>嚗<EFBFBD><E59A97>霈踵𧒄<E8B8B5>氬<EFBFBD><E6B0AC>㺭<EFBFBD>格䔉皞琜<E79A9E>
4. <20>曄<EFBFBD>霂𦠜鱏<F0A6A09C><E9B18F><EFBFBD>
5. 鈭箇黎<E7AE87>孵<EFBFBD>嚗<EFBFBD>甅<EFBFBD>祇<EFBFBD><E7A587><EFBFBD>犖<EFBFBD><E78A96><EFBFBD>霈∪郎嚗争<E59A97>
6. <20>箇瑪<E7AE87>唳旿嚗<E697BF><E59A97><EFBFBD>賣<EFBFBD><E8B3A3><EFBFBD><EFBFBD><EFBFBD><EFBFBD>撟嗥<E6929F>嚗争<E59A97>
7. 撟脤<E6929F><E884A4>芣鴌嚗<E9B48C>晓<EFBFBD>押<EFBFBD><E68ABC><EFBFBD><EFBFBD>譌<EFBFBD><E8AD8C><EFBFBD>蝔页<E89D94>潃?8. 撖寧<E69296><E5AFA7>芣鴌
9. 蝏枏<E89D8F><E69E8F><EFBFBD><EFBFBD>嚗<EFBFBD>蜓閬?甈∟<E79488>蝏枏<E89D8F>嚗争<E59A97>潃鐥<E6BD83> <20><><EFBFBD>喲睸
10. 蝏蠘恣<E8A098>寞<EFBFBD>
11. 韐券<E99F90>霂<EFBFBD>遠嚗<E981A0><E59A97><EFBFBD>箏<EFBFBD><E7AE8F><EFBFBD>𤩅瘜𨰻<E7989C><F0A8B0BB>TT<54><54><EFBFBD>蝑㚁<E89D91>潃鐥<E6BD83> <20>喲睸<E596B2>寞<EFBFBD>摮?12. <20>嗡<EFBFBD>靽⊥<E99DBD>嚗<EFBFBD>釣<EFBFBD><E987A3>噡<EFBFBD><E599A1>⏚<EFBFBD>𠰴<EFBFBD>蝒<EFBFBD><E89D92>
**<2A>冽<EFBFBD>憭滨<E686AD><E6BBA8><EFBFBD>遙<EFBFBD>?*嚗朞<E59A97>隡啗<E99AA1>12銝芸<E98A9D>畾萇<E795BE>**摮睃銁<E79D83>扼<EFBFBD><E689BC><EFBFBD><EFBFBD>湔<EFBFBD>扼<EFBFBD><E689BC>虾<EFBFBD>𣂼<EFBFBD><F0A382BC>?*嚗<>抅鈭?Cochrane<EFBFBD>誩<EFBFBD>𡁻<EFBFBD><EFBFBD>抵<EFBFBD>隡唳<EFBFBD><EFBFBD>?<EFBFBD>斗鱏<EFBFBD>臬炏蝥喳<EFBFBD><EFBFBD>? <EFBFBD>喲睸摮埈挾<EFBFBD>滨<EFBFBD>霂<EFBFBD>摯嚗<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ochrane RoB 2.0嚗㚁<EFBFBD>
- <EFBFBD>𤩺㦤<EFBFBD>𡝗䲮瘜𤏪<EFBFBD>摨誩<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>漤<EFBFBD><EFBFBD>𧶏<EFBFBD>
- <EFBFBD>脫<EFBFBD>嚗<EFBFBD>鴌<EFBFBD>脣笆鞊~<EFBFBD><EFBFBD>鴌<EFBFBD>脫䲮瘜𤏪<EFBFBD>
- 蝏𤘪<EFBFBD>摰峕㟲<EFBFBD>改<EFBFBD>憭梯挪<EFBFBD><EFBFBD><EFBFBD><EFBFBD>TT<EFBFBD><EFBFBD><EFBFBD>嚗?- <20>㗇𥋘<E39787>扳𥁒<E689B3>𠺪<EFBFBD>瘜典<E7989C><E585B8>寞<EFBFBD>撖寞<E69296>嚗?
1.4 銝𤾸<E98A9D><F0A4BEB8><EFBFBD><EFBFBD><EFBFBD>𣇉<EFBFBD><F0A38789>喟頂
| 蝏游漲 | <EFBFBD>冽<EFBFBD>憭滨<EFBFBD> | <EFBFBD>冽<EFBFBD><EFBFBD>𣂼<EFBFBD> |
|---|---|---|
| <EFBFBD>桃<EFBFBD> | <EFBFBD>斗鱏<EFBFBD>臬炏<EFBFBD>舐鍂嚗<EFBFBD>熙<EFBFBD>?<3F>㘾膄嚗? | <EFBFBD>𣂼<EFBFBD><EFBFBD>瑚<EFBFBD><EFBFBD>唳旿嚗<EFBFBD>鍂鈭𤾸<EFBFBD><EFBFBD>琜<EFBFBD> |
| 12摮埈挾<EFBFBD>冽<EFBFBD> | 霂<EFBFBD>摯**<2A>臬炏摰峕㟲** | <EFBFBD>𣂼<EFBFBD>**<2A>瑚<EFBFBD><E7919A>啣<EFBFBD>?* |
| 颲枏枂 | 鈭<EFBFBD><EFBFBD>蝐鳴<EFBFBD>蝥喳<EFBFBD>/<2F>㘾膄嚗? <20><>眏 | 霂衣<EFBFBD><EFBFBD><EFBFBD><EFBFBD>畾萄<EFBFBD>潘<EFBFBD><EFBFBD>唳旿銵剁<EFBFBD> |
| *摨訫<EFBFBD><EFBFBD><EFBFBD><EFBFBD>? | **摰<><E691B0><EFBFBD>詨<EFBFBD>**嚗㇊DF<44><46>LM<4C><4D><EFBFBD>蝒<EFBFBD><E89D92>瘚讠<E7989A>嚗? | 摰<EFBFBD><EFBFBD><EFBFBD>詨<EFBFBD> |
| <EFBFBD>唳旿摨?API | <EFBFBD>祉<EFBFBD>霈曇恣嚗<EFBFBD><EFBFBD><EFBFBD>芣<EFBFBD>餈𨥈<EFBFBD> | <EFBFBD>祉<EFBFBD>霈曇恣 |
**<2A>詨<EFBFBD>霈曇恣<E69B87>脲<EFBFBD>**嚗?- <20>?**摨訫<E691A8><E8A8AB><EFBFBD><EFBFBD>舫<EFBFBD>摨血<E691A8><E8A180>?*嚗䥪DF摮睃<E691AE><E79D83><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>硔<EFBFBD><E7A194>LM靚<4D>鍂<EFBFBD><E98D82><EFBFBD>蝒<EFBFBD><E89D92>瘚讠<E7989A><E8AEA0>質情銝粹<E98A9D>𡁶鍂<F0A181B6>賢<EFBFBD>撅?- <20>?**摨𠉛鍂撅<E98D82>𡠺蝡贝挽霈?*嚗𡁜<E59A97><F0A1819C><EFBFBD><EFBFBD>蝑𥕦<E89D91><F0A595A6>冽<EFBFBD><E586BD>𣂼<EFBFBD><F0A382BC><EFBFBD>䌊<EFBFBD><E48C8A>㺭<EFBFBD>桀<EFBFBD>銵具<E98AB5><E585B7>PI<50><49><EFBFBD><EFBFBD>⊿<EFBFBD>餉<EFBFBD><E9A489>祉<EFBFBD>瞍磰<E79E8D>
- <EFBFBD>?**<2A>芣䔉銝漤<E98A9D><E6BCA4><EFBFBD><EFBFBD>**嚗帋<E59A97>銝<EFBFBD>撘<EFBFBD>憪见停<E8A781>舐𡠺蝡讠<E89DA1>璅∪<E79285>
2. <20>嗆<EFBFBD>霈曇恣
2.1 銝匧<E98A9D><E58CA7>嗆<EFBFBD>
<EFBFBD>𢞖<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>?<3F>? 摨𠉛鍂撅<E98D82><E69285><EFBFBD>祆活撘<E6B4BB><E69298>煾<EFBFBD><E785BE>對<EFBFBD> <20>?<3F>? <20>?<3F>? <20>冽<EFBFBD>憭滨<E686AD>璅∪<E79285> <20>?<3F>? <20>鎿<EFBFBD> AslFulltextScreeningTask嚗<6B>㺭<EFBFBD>株”嚗? <20>?<3F>? <20>鎿<EFBFBD> AslFulltextScreeningResult嚗<74>㺭<EFBFBD>株”嚗? <20>?<3F>? <20>鎿<EFBFBD> FulltextScreeningService嚗<65><E59A97><EFBFBD>⊿<EFBFBD>餉<EFBFBD>嚗? <20>?<3F>? <20>鎿<EFBFBD> FulltextScreeningController嚗<72>綉<EFBFBD>嗅膥嚗? <20>?<3F>? <20>鎿<EFBFBD> fulltext-screening routes嚗㇁PI嚗? <20>?<3F>? <20>婙<EFBFBD> <20>滨垢摰⊥瓲撌乩<E6928C><E4B9A9>? <20>?<3F>婙<EFBFBD><E5A999><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>砂<EFBFBD><E7A082><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>? <20>?靚<>鍂
<20>?<3F>𢞖<EFBFBD><F0A29E96><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>?<3F>? <20>𡁶鍂<F0A181B6>賢<EFBFBD>撅<EFBFBD><E69285><EFBFBD>祆活撘<E6B4BB><E69298>𡢅<EFBFBD><F0A1A285>芣䔉憭滨鍂嚗争<E59A97> <20>?<3F>? <20>?<3F>? <20>鎿<EFBFBD> PDFStorageService嚗㇊DF銝𠹺<E98A9D>/摮睃<E691AE>/銝贝蝸嚗? <20>?<3F>? <20>鎿<EFBFBD> FulltextExtractionClient嚗<74><E59A97><EFBFBD>沌ython敺格<E695BA><E6A0BC>∴<EFBFBD><E288B4>?<3F>? <20>鎿<EFBFBD> LLM12FieldsService嚗𡿨LM憭<4D><E686AD>12摮埈挾嚗? <20>?<3F>? <20>鎿<EFBFBD> ConflictDetectionService嚗<65><E59A97>蝒<EFBFBD><E89D92>瘚页<E7989A> <20>?<3F>? <20>婙<EFBFBD> AsyncTaskService嚗<65><E59A97>甇乩遙<E4B9A9>?餈𥕦漲餈質葵嚗? <20>?<3F>婙<EFBFBD><E5A999><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>? <20>?靚<>鍂
<0A>𢞖<EFBFBD><F0A29E96><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>?<3F>? 撟喳蝱<E596B3>箇<EFBFBD>撅<EFBFBD><E69285>撌脣<E6928C><E884A3>堆<EFBFBD> <20>?<3F>? storage | logger | cache | jobQueue | prisma <20>?<3F>婙<EFBFBD><E5A999><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>?```
### 2.2 <20>桀<EFBFBD>蝏𤘪<E89D8F>
```bash
backend/src/modules/asl/
<0A>鎿<EFBFBD><E98EBF><EFBFBD> common/ # <20>𡁶鍂<F0A181B6>賢<EFBFBD>撅?潃?<3F>航◤extraction憭滨鍂
<0A>? <20>鎿<EFBFBD><E98EBF><EFBFBD> pdf/ # <20>?PDF摮睃<E691AE>嚗<EFBFBD>歇摰峕<E691B0>Day 1嚗争<E59A97>
<0A>? <20>? <20>鎿<EFBFBD><E98EBF><EFBFBD> PDFStorageService.ts
<0A>? <20>? <20>鎿<EFBFBD><E98EBF><EFBFBD> PDFStorageFactory.ts
<0A>? <20>? <20>鎿<EFBFBD><E98EBF><EFBFBD> adapters/
<0A>? <20>? <20>? <20>鎿<EFBFBD><E98EBF><EFBFBD> DifyPDFStorageAdapter.ts
<0A>? <20>? <20>? <20>婙<EFBFBD><E5A999><EFBFBD> OSSPDFStorageAdapter.ts
<0A>? <20>? <20>婙<EFBFBD><E5A999><EFBFBD> types.ts
<0A>? <20>鎿<EFBFBD><E98EBF><EFBFBD> llm/ # <20>?LLM<4C>滚𦛚
<0A>? <20>? <20>鎿<EFBFBD><E98EBF><EFBFBD> LLM12FieldsService.ts # LLM憭<4D><E686AD>12摮埈挾
<0A>? <20>? <20>鎿<EFBFBD><E98EBF><EFBFBD> PromptBuilder.ts # Prompt<70><74>遣嚗𠄎ection-Aware嚗争<E59A97>
<0A>? <20>? <20>婙<EFBFBD><E5A999><EFBFBD> types.ts
<0A>? <20>鎿<EFBFBD><E98EBF><EFBFBD> validation/ # <20>?撉諹<E69289><E8ABB9>滚𦛚嚗<F0A69B9A>鰵憓痹<E68693>潃?<3F>? <20>? <20>鎿<EFBFBD><E98EBF><EFBFBD> MedicalLogicValidator.ts # <20>餃郎<E9A483>餉<EFBFBD>撉諹<E69289>
<0A>? <20>? <20>鎿<EFBFBD><E98EBF><EFBFBD> EvidenceChainValidator.ts # 霂<>旿<EFBFBD>暸<EFBFBD>霂?<3F>? <20>? <20>婙<EFBFBD><E5A999><EFBFBD> ConflictDetectionService.ts # <20>脩<EFBFBD>璉<EFBFBD>瘚?<3F>? <20>鎿<EFBFBD><E98EBF><EFBFBD> tasks/ # <20>?撘<>郊隞餃𦛚
<0A>? <20>? <20>婙<EFBFBD><E5A999><EFBFBD> AsyncTaskService.ts
<0A>? <20>婙<EFBFBD><E5A999><EFBFBD> utils/
<0A>? <20>鎿<EFBFBD><E98EBF><EFBFBD> tokenCalculator.ts
<0A>? <20>婙<EFBFBD><E5A999><EFBFBD> jsonParser.ts
<0A>?<3F>鎿<EFBFBD><E98EBF><EFBFBD> fulltext-screening/ # <20>冽<EFBFBD>憭滨<E686AD>璅∪<E79285> 潃?<3F>祆活撘<E6B4BB><E69298>煾<EFBFBD><E785BE>?<3F>? <20>鎿<EFBFBD><E98EBF><EFBFBD> routes/
<0A>? <20>? <20>婙<EFBFBD><E5A999><EFBFBD> fulltext-screening.ts # 5銝服PI<50>亙藁
<0A>? <20>鎿<EFBFBD><E98EBF><EFBFBD> controllers/
<0A>? <20>? <20>婙<EFBFBD><E5A999><EFBFBD> FulltextScreeningController.ts
<0A>? <20>鎿<EFBFBD><E98EBF><EFBFBD> services/
<0A>? <20>? <20>婙<EFBFBD><E5A999><EFBFBD> FulltextScreeningService.ts # 銝𡁜𦛚<F0A1819C>餉<EFBFBD>
<0A>? <20>鎿<EFBFBD><E98EBF><EFBFBD> types/
<0A>? <20>? <20>婙<EFBFBD><E5A999><EFBFBD> screening.types.ts # TypeScript蝐餃<E89D90>
<0A>? <20>婙<EFBFBD><E5A999><EFBFBD> prompts/
<0A>? <20>鎿<EFBFBD><E98EBF><EFBFBD> system_prompt.md # System Prompt嚗𠄎ection-Aware嚗争<E59A97>
<0A>? <20>鎿<EFBFBD><E98EBF><EFBFBD> user_prompt_template.md # User Prompt璅⊥踎
<0A>? <20>鎿<EFBFBD><E98EBF><EFBFBD> cochrane_standards/ # Cochrane<6E><65><EFBFBD><EFBFBD>讛膩嚗<E886A9><E59A97>摮埈挾嚗争<E59A97>
<0A>? <20>? <20>鎿<EFBFBD><E98EBF><EFBFBD> <20>𤩺㦤<F0A4A9BA>𡝗䲮瘜?md
<0A>? <20>? <20>鎿<EFBFBD><E98EBF><EFBFBD> <20>脫<EFBFBD>.md
<0A>? <20>? <20>鎿<EFBFBD><E98EBF><EFBFBD> 蝏𤘪<E89D8F>摰峕㟲<E5B395>?md
<0A>? <20>? <20>婙<EFBFBD><E5A999><EFBFBD> ...
<0A>? <20>婙<EFBFBD><E5A999><EFBFBD> few_shot_examples/ # Few-shot<6F>餃郎獢<E9838E><E78DA2>摨𣏾<E691A8>
<0A>? <20>鎿<EFBFBD><E98EBF><EFBFBD> 擃䁅捶<E48185>嗬CT.md
<0A>? <20>鎿<EFBFBD><E98EBF><EFBFBD> 韐券<E99F90>銝滩雲獢<E99BB2><E78DA2>.md
<0A>? <20>婙<EFBFBD><E5A999><EFBFBD> 靽⊥<E99DBD><E28AA5>其葉<E585B6>港<EFBFBD>蝵格<E89DB5>靘?md # <20>?<3F>孵<EFBFBD><E5ADB5>滩<EFBFBD>
<0A>?<3F>婙<EFBFBD><E5A999><EFBFBD> title-screening/ # <20><><EFBFBD><EFBFBD>䁅<EFBFBD><E48185>萘<EFBFBD>嚗<EFBFBD>歇摰峕<E691B0>嚗? <20>婙<EFBFBD><E5A999><EFBFBD> ...
frontend-v2/src/modules/asl/
<0A>鎿<EFBFBD><E98EBF><EFBFBD> pages/
<0A>? <20>鎿<EFBFBD><E98EBF><EFBFBD> FulltextScreeningSettings.tsx # 霈曄蔭銝𤾸鍳<F0A4BEB8>?<3F>? <20>鎿<EFBFBD><E98EBF><EFBFBD> FulltextScreeningWorkbench.tsx # 摰⊥瓲撌乩<E6928C><E4B9A9>?潃?<3F>詨<EFBFBD>
<0A>? <20>婙<EFBFBD><E5A999><EFBFBD> FulltextScreeningResults.tsx # 憭滨<E686AD>蝏𤘪<E89D8F>
<0A>?<3F>鎿<EFBFBD><E98EBF><EFBFBD> components/
<0A>? <20>鎿<EFBFBD><E98EBF><EFBFBD> screening/
<0A>? <20>? <20>鎿<EFBFBD><E98EBF><EFBFBD> PICOSPanel.tsx # PICOS<4F><53><EFBFBD>撅閧內嚗<E585A7>虾憭滨鍂嚗?<3F>? <20>? <20>鎿<EFBFBD><E98EBF><EFBFBD> ScreeningTable.tsx # 銵冽聢<E586BD>硋恣<E7A18B>賂<EFBFBD><E8B382>臬<EFBFBD><E887AC>剁<EFBFBD>
<0A>? <20>? <20>鎿<EFBFBD><E98EBF><EFBFBD> ReviewModal.tsx # <20>諹<EFBFBD><E8ABB9>曉<EFBFBD><E69B89><EFBFBD>恣<EFBFBD>?<3F>? <20>? <20>婙<EFBFBD><E5A999><EFBFBD> PDFViewer.tsx # PDF<44><46>粉<EFBFBD>?<3F>? <20>婙<EFBFBD><E5A999><EFBFBD> shared/
<0A>? <20>鎿<EFBFBD><E98EBF><EFBFBD> LiteratureAcquisitionTable.tsx # <20>冽<EFBFBD><E586BD>瑕<EFBFBD>銵冽聢
<0A>? <20>婙<EFBFBD><E5A999><EFBFBD> ExcelExporter.tsx # Excel撖澆枂
3. <20>𡁶鍂<F0A181B6>賢<EFBFBD>撅<EFBFBD>挽霈∴<E99C88><E288B4>臬<EFBFBD><E887AC>剁<EFBFBD>
**潃?<3F>滩<EFBFBD>霂湔<E99C82>嚗𡁻<EFBFBD>𡁶鍂<EFBFBD>賢<EFBFBD>撅<EFBFBD>糓<EFBFBD>祆活撘<EFBFBD><EFBFBD>𤑳<EFBFBD><EFBFBD>滩<EFBFBD><EFBFBD>典<EFBFBD>嚗諹<EFBFBD>鈭𥟇<EFBFBD><EFBFBD>?*<2A>芣䔉撠<E49489>◤<EFBFBD>冽<EFBFBD><E586BD>𣂼<EFBFBD>璅∪<E79285>憭滨鍂嚗<EFBFBD><EFBFBD>甇日<EFBFBD>閬<EFBFBD><EFBFBD>韐券<EFBFBD>摰䂿緵<EFBFBD>?
3.1 PDFStorageService - PDF摮睃<E691AE><E79D83>滚𦛚
3.1.1 <20>蠘<EFBFBD>摰帋<E691B0>
*<EFBFBD>𩤃<EFBFBD> 瘜冽<E7989C>嚗朞<E59A97><E69C9E>臬<EFBFBD>鋆<EFBFBD>緵<EFBFBD>㕑<EFBFBD><E39591>𨥈<EFBFBD>銝齿糓<E9BDBF>齿鰵撘<E9B0B5><E69298>?
蝏煺<EFBFBD><EFBFBD><EFBFBD>DF<EFBFBD><EFBFBD>辣蝞∠<EFBFBD><EFBFBD>滚𦛚嚗諹<EFBFBD>韐<EFBFBD><EFBFBD>
- PDF銝𠹺<EFBFBD>嚗㇄ify or OSS嚗? 憭滨鍂<EFBFBD>唳<EFBFBD>storage<EFBFBD>滚𦛚 <20>?- <20>冽<EFBFBD><E586BD>𣂼<EFBFBD>嚗<EFBFBD><E59A97><EFBFBD>沌ython敺格<E695BA><E6A0BC>∴<EFBFBD>- 憭滨鍂ExtractionClient <20>?- PDF銝贝蝸/<2F>𣳇膄
- <EFBFBD>舀<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>冽芋撘𧶏<EFBFBD><EFBFBD>嗡誨<EFBFBD><EFBFBD><EFBFBD><EFBFBD>g<EFBFBD>
**<2A>唳<EFBFBD>蝟餌<E89D9F>撌脣<E6928C><E884A3>?*嚗?- <20>?Python敺格<E695BA><E6A0BC>∴<EFBFBD>PyMuPDF + Nougat嚗?- <20>?ExtractionClient.ts嚗<73><E59A97>蝡舫<E89DA1><E888AB>琜<EFBFBD>
- <EFBFBD>?TokenService.ts嚗㇍oken霈∠<E99C88>嚗?- <20>?storage<67>滚𦛚嚗<F0A69B9A>像<EFBFBD>啣抅蝖<E68A85>撅<EFBFBD><E69285>
<EFBFBD>祆活撘<EFBFBD><EFBFBD>?*嚗𡁜蘨<F0A1819C><E898A8><EFBFBD><EFBFBD><EFBFBD><EFBFBD>鞟<EFBFBD>銝<EFBFBD><EFBFBD>亙藁**嚗<>漲200銵䔶誨<E494B6><E8AAA8><EFBFBD>
3.1.2 <20>詨<EFBFBD><E8A9A8>亙藁
// backend/src/modules/asl/common/services/PDFStorageService.ts
export interface PDFUploadResult {
storageRef: string // 摮睃<E691AE>撘閧鍂嚗㇄ify: documentId, OSS: key嚗? storageType: string // 'dify' or 'oss'
url: string | null // Dify: null, OSS: <20>砍<EFBFBD>URL
fullText: string // <20>𣂼<EFBFBD><F0A382BC><EFBFBD><EFBFBD><EFBFBD>? tokenCount: number // Token<65>圈<EFBFBD>
charCount: number // 摮㛖泵<E39B96>?
extractionMethod: string // 'pymupdf' or 'nougat'
extractionQuality: number // 0.0-1.0
detectedLanguage: string // 'chinese' or 'english'
}
export class PDFStorageService {
/**
* 銝𠹺<E98A9D>PDF撟嗆<E6929F><E59786>硋<EFBFBD><E7A18B>? */
async uploadAndExtract(
literatureId: string,
pdfBuffer: Buffer,
filename: string
): Promise<PDFUploadResult>
/**
* 銝贝蝸PDF
*/
async download(storageRef: string): Promise<Buffer>
/**
* <20>𣳇膄PDF
*/
async delete(storageRef: string): Promise<void>
/**
* 璉<><E79289>仙DF<44>臬炏摮睃銁
*/
async exists(storageRef: string): Promise<boolean>
}
3.1.3 MVP<56>嗆挾摮睃<E691AE><E79D83>寞<EFBFBD>
**雿輻鍂Dify摮睃<E691AE>嚗<EFBFBD><E59A97><EFBFBD>沌KB璅∪<E79285>嚗?*嚗?
// backend/src/modules/asl/common/adapters/DifyPDFStorageAdapter.ts
export class DifyPDFStorageAdapter implements PDFStorageAdapter {
private difyClient: DifyClient // <20>?憭滨鍂<E6BBA8>唳<EFBFBD>DifyClient <20>?
constructor() {
// 憭滨鍂common/rag/DifyClient.ts嚗<73>歇摰䂿緵嚗争<E59A97>
this.difyClient = new DifyClient(
process.env.DIFY_API_KEY!,
process.env.DIFY_BASE_URL!
)
}
async upload(literatureId: string, pdfBuffer: Buffer, filename: string) {
// 1. 銝𠹺<E98A9D><F0A0B9BA>蚤ify嚗<79><E59A97><EFBFBD>函緵<E587BD>侨KB<4B>蠘<EFBFBD>嚗争<E59A97>
const datasetId = process.env.DIFY_ASL_DATASET_ID!
const difyDocId = await this.difyClient.uploadDocument(
datasetId,
pdfBuffer,
filename
)
logger.info('PDF uploaded to Dify', { literatureId, difyDocId })
return {
ref: difyDocId,
type: 'dify',
url: null // Dify瘝⊥<E7989D><E28AA5>砍<EFBFBD>URL
}
}
async download(difyDocId: string): Promise<Buffer> {
return await this.difyClient.downloadDocument(difyDocId)
}
}
<EFBFBD>臬<EFBFBD><EFBFBD>滨蔭嚗?
# .env
PDF_STORAGE_TYPE=dify # MVP<56>嗆挾雿輻鍂Dify
DIFY_API_KEY=app-xxx
DIFY_BASE_URL=http://localhost:5001/v1
DIFY_ASL_DATASET_ID=dataset-xxx # ASL銝梶鍂<E6A2B6>亥<EFBFBD>摨?
# <20>芣䔉餈<E49489>宏OSS嚗<53>蘨<EFBFBD><E898A8><EFBFBD>寥<EFBFBD>蝵殷<E89DB5>
# PDF_STORAGE_TYPE=oss
# OSS_REGION=cn-hangzhou
# OSS_BUCKET=asl-literatures
3.1.4 <20>冽<EFBFBD><E586BD>𣂼<EFBFBD><F0A382BC><EFBFBD><EFBFBD>
*<EFBFBD>𩤃<EFBFBD> 瘜冽<E7989C>嚗𡁶凒<F0A181B6>亙<EFBFBD><E4BA99>函緵<E587BD><E7B7B5>xtractionClient嚗䔶<E59A97><E494B6><EFBFBD>閬<EFBFBD><E996AC><EFBFBD>啣<EFBFBD><E595A3>?
// 憭滨鍂<E6BBA8>唳<EFBFBD>ExtractionClient嚗<74>歇摰䂿緵嚗争<E59A97>
import { ExtractionClient } from '@/common/document/ExtractionClient'
private async extractFulltext(pdfBuffer: Buffer): Promise<ExtractionResult> {
// ExtractionClient撌脣<E6928C><E884A3>堆<EFBFBD><E5A086>湔𦻖雿輻鍂 <20>? const extractionClient = new ExtractionClient(
process.env.EXTRACTION_SERVICE_URL || 'http://localhost:8000'
)
// Python敺格<E695BA><E6A0BC>∴<EFBFBD>PyMuPDF + Nougat嚗匧歇<E58CA7>函蔡餈鞱<E9A488> <20>? const result = await extractionClient.extractPDF(pdfBuffer)
return {
text: result.text,
method: result.extraction_method, // 'pymupdf' or 'nougat'
quality: result.extraction_quality, // 0.0-1.0
language: result.detected_language // 'chinese' or 'english'
}
}
**<2A>唳<EFBFBD>蝟餌<E89D9F>嚗<EFBFBD>虾<EFBFBD>湔𦻖雿輻鍂嚗?*嚗?- <20>?backend/src/common/document/ExtractionClient.ts嚗<EFBFBD>歇摰䂿緵嚗?- <20>?extraction_service/嚗㇊ython敺格<EFBFBD><EFBFBD>∴<EFBFBD>撌脤<EFBFBD>蝵莎<EFBFBD>
- <EFBFBD>?PyMuPDF<44><46>ougat<61><74>祗閮<E7A597>璉<EFBFBD>瘚页<E7989A>撌脣<E6928C><E884A3>琜<EFBFBD>
3.2 LLM12FieldsService - LLM憭<4D><E686AD>12摮埈挾<E59F88>滚𦛚
3.2.1 <20>蠘<EFBFBD>摰帋<E691B0>
蝏煺<EFBFBD><EFBFBD><EFBFBD>LM靚<EFBFBD>鍂<EFBFBD>滚𦛚嚗峕𣈲<EFBFBD><EFBFBD><EFBFBD>
- screening璅∪<EFBFBD>嚗朞<EFBFBD>隡?2摮埈挾摰峕㟲<E5B395>改<EFBFBD><E694B9>箔<EFBFBD>Cochrane<6E><65><EFBFBD>嚗?- extraction璅∪<E79285>嚗𡁏<E59A97><F0A1818F>?2摮埈挾霂衣<E99C82><E8A1A3>唳旿嚗<E697BF>𧊋<EFBFBD>伐<EFBFBD>
- <EFBFBD>峕芋<EFBFBD>见僎銵䕘<EFBFBD>DeepSeek-V3 + Qwen3-Max嚗?- 蝏𤘪<E89D8F>蝻枏<E89DBB>嚗<EFBFBD><E59A97>雿擧<E99BBF><E693A7>穿<EFBFBD>
- **Nougat蝏𤘪<E89D8F><F0A498AA>碶<EFBFBD><E7A2B6>?*嚗<>㘚<EFBFBD><E3989A>捏<EFBFBD><E68D8F><EFBFBD>
3.2.2 <20>詨<EFBFBD><E8A9A8>亙藁
// backend/src/modules/asl/common/services/LLM12FieldsService.ts
export enum LLM12FieldsMode {
SCREENING = '12fields-screening', // 霂<>摯璅∪<E79285>嚗㇃ochrane<6E><65><EFBFBD>嚗? EXTRACTION = '12fields-extraction' // <20>𣂼<EFBFBD>璅∪<E79285>嚗<EFBFBD>𧊋<EFBFBD>伐<EFBFBD>
}
export interface LLMResult {
result: any // 閫<><E996AB><EFBFBD>𡒊<EFBFBD>JSON蝏𤘪<E89D8F>
processingTime: number // 憭<><E686AD><EFBFBD>園𡢿嚗<F0A1A2BF>神蝘𡜐<E89D98>
tokenUsage: number // Token雿輻鍂<E8BCBB>? cost: number // <20>鞉𧋦嚗<F0A78BA6>犖瘞穃<E7989E>嚗? extractionMethod: string // 'nougat' | 'pymupdf'
structuredFormat: boolean // <20>臬炏銝箇<E98A9D><E7AE87><EFBFBD><EFBFBD><EFBFBD>澆<EFBFBD>嚗㇈arkdown嚗?}
export class LLM12FieldsService {
/**
* 憭<><E686AD>12摮埈挾嚗ìcreening or extraction嚗? *
* <20>𩤃<EFBFBD> 蝑𣇉裦嚗𡁜<E59A97><F0A1819C><EFBFBD><EFBFBD>甈⊥<E79488>扯<EFBFBD><E689AF>伐<EFBFBD>雿<EFBFBD><E99BBF>朞<EFBFBD>Prompt撌亦<E6928C>隡睃<E99AA1>
*/
async process12Fields(
mode: LLM12FieldsMode,
model: string, // 'deepseek-v3' | 'qwen-max'
fullTextMarkdown: string, // 潃?Nougat<61>𣂼<EFBFBD><F0A382BC><EFBFBD>arkdown<77>澆<EFBFBD><E6BE86>冽<EFBFBD>
context: any // <20>𠉛弦<F0A0899B>寞<EFBFBD>銝𠹺<E98A9D><F0A0B9BA>?+ PICOS<4F><53><EFBFBD>
): Promise<LLMResult>
/**
* <20>峕芋<E5B395>见僎銵諹<E98AB5><E8ABB9>? */
async processDualModels(
mode: LLM12FieldsMode,
modelA: string, // 暺䁅恕 'deepseek-v3'
modelB: string, // 暺䁅恕 'qwen-max'
fullTextMarkdown: string,
context: any
): Promise<{ resultA: LLMResult, resultB: LLMResult }>
}
3.2.3 Nougat隡睃<E99AA1>蝑𣇉裦
/**
* PDF<44>冽<EFBFBD><E586BD>𣂼<EFBFBD>蝑𣇉裦
*
* 隡睃<E99AA1>雿輻鍂Nougat嚗<74><E59A97><EFBFBD><EFBFBD><EFBFBD>Markdown嚗㚁<E59A97><E39A81>滨漣雿輻鍂PyMuPDF
*/
async extractFullTextStructured(
pdfBuffer: Buffer,
filename: string
): Promise<{ text: string; method: string; structured: boolean }> {
// Step 1: 璉<>瘚贝祗閮<E7A597>
const language = await detectLanguage(pdfBuffer);
// Step 2: <20>望<EFBFBD>霈箸<E99C88>隡睃<E99AA1><E79D83>沐ougat
if (language === 'english') {
try {
const nougatResult = await extractionClient.extractPdf(
pdfBuffer, filename, 'nougat'
);
if (nougatResult.quality > 0.8) {
logger.info('<27>?雿輻鍂Nougat<61>𣂼<EFBFBD>嚗<EFBFBD><E59A97><EFBFBD><EFBFBD><EFBFBD>Markdown嚗?);
return {
text: nougatResult.text,
method: 'nougat',
structured: true // 潃?Markdown<77>澆<EFBFBD>
};
}
} catch (error) {
logger.warn('<EFBFBD>𩤃<EFBFBD> Nougat憭梯揖嚗屸<EFBFBD>蝥批<EFBFBD>PyMuPDF');
}
}
// Step 3: <20>滨漣雿輻鍂PyMuPDF
const pymupdfResult = await extractionClient.extractPdf(
pdfBuffer, filename, 'pymupdf'
);
return {
text: pymupdfResult.text,
method: 'pymupdf',
structured: false // 蝥舀<E89DA5><E88880>? };
}
**Nougat<61><74><EFBFBD><EFBFBD>?*嚗?- <20>?颲枏枂Markdown<77>澆<EFBFBD>嚗䔶<E59A97><E494B6>嗵<EFBFBD><E597B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD># Methods<64>?# Randomization嚗?- <20>?銵冽聢頧祆揢銝撤arkdown銵冽聢嚗𥴰LM<4C>舐凒<E88890>亦<EFBFBD>閫?- <20>?<3F>砍<EFBFBD>霂<EFBFBD><E99C82>銝摔aTeX
- <EFBFBD>?憭𡁏<E686AD>撣<EFBFBD><E692A3><EFBFBD>箄<EFBFBD>憭<EFBFBD><E686AD>
3.2.4 蝻枏<E89DBB>蝑𣇉裦
// LLM<4C>滚<EFBFBD>蝻枏<E89DBB>嚗<EFBFBD><E59A97>雿擧<E99BBF><E693A7>穿<EFBFBD>
async process12Fields(mode, model, fullText, context): Promise<LLMResult> {
// 1. <20><><EFBFBD>蝻枏<E89DBB>key
const cacheKey = `llm:${mode}:${model}:${hash(fullText + JSON.stringify(context))}`
// 2. 璉<><E79289>亦<EFBFBD>摮? const cached = await cache.get(cacheKey)
if (cached) {
logger.info('LLM cache hit', { mode, model })
return cached
}
// 3. 靚<>鍂LLM嚗<4D><E59A97><EFBFBD><EFBFBD><EFBFBD>甈⊥<E79488>扯<EFBFBD><E689AF>伐<EFBFBD>
const result = await this.callLLM(mode, model, fullText, context)
// 4. 蝻枏<E89DBB>1撠𤩺𧒄
await cache.set(cacheKey, result, 3600)
return result
}
3.3 MedicalLogicValidator - <20>餃郎<E9A483>餉<EFBFBD>撉諹<E69289><E8ABB9>滚𦛚嚗<F0A69B9A>鰵憓痹<E68693>潃?
3.3.1 <20>蠘<EFBFBD>摰帋<E691B0>
<EFBFBD>箔<EFBFBD>敺芾<EFBFBD><EFBFBD>餃郎<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>䌊<EFBFBD>券<EFBFBD>餉<EFBFBD>撉諹<EFBFBD>嚗峕<EFBFBD><EFBFBD>伐<EFBFBD>
- RCT<EFBFBD>𠉛弦敹<EFBFBD>◆<EFBFBD>厰<EFBFBD><EFBFBD>箏<EFBFBD><EFBFBD>讛膩
- <EFBFBD>𣬚𤩅<EFBFBD>𠉛弦敹<EFBFBD>◆霂湔<EFBFBD><EFBFBD>脫<EFBFBD>
- <EFBFBD>瑟𧋦<EFBFBD>譍<EFBFBD><EFBFBD>箇瑪<EFBFBD>唳旿銝<EFBFBD><EFBFBD>湔<EFBFBD>?- <20>箇瑪銝滚像銵⊿<E98AB5>閬<EFBFBD><E996AC><EFBFBD>游<EFBFBD><E6B8B8>?- ITT<54><54><EFBFBD>摰峕㟲<E5B395>?
3.3.2 <20>詨<EFBFBD><E8A9A8>亙藁
// backend/src/modules/asl/common/services/MedicalLogicValidator.ts
export interface LogicViolation {
ruleId: string;
ruleName: string;
severity: 'error' | 'warning';
message: string;
field: string;
suggestedAction: string;
}
export interface LogicReport {
totalRules: number;
passedRules: number;
violations: LogicViolation[];
overallValidity: boolean;
}
export class MedicalLogicValidator {
/**
* 撉諹<E69289><E8ABB9>餃郎<E9A483>餉<EFBFBD>銝<EFBFBD><E98A9D>湔<EFBFBD>? */
validate(extractedData: ExtractionResult): LogicReport {
const violations = [];
// 閫<><E996AB>1嚗鑹CT敹<54>◆<EFBFBD>厰<EFBFBD><E58EB0>箏<EFBFBD>
if (this.isRCT(extractedData) && !this.hasRandomization(extractedData)) {
violations.push({
ruleId: 'rule_001',
ruleName: 'RCT敹<54>◆<EFBFBD>厰<EFBFBD><E58EB0>箏<EFBFBD>',
severity: 'error',
message: '<27>𠉛弦憯啁妍<E59581>爹CT雿<54>𧊋<EFBFBD>曉<EFBFBD><E69B89>𤩺㦤<F0A4A9BA>𡝗䲮瘜閙<E7989C>餈?,
field: '<EFBFBD>𤩺㦤<EFBFBD>𡝗䲮瘜?,
suggestedAction: 'flag_for_urgent_review'
});
}
// 閫<><E996AB>2-5嚗𡁜<E59A97>隞㚚<E99A9E>霂<EFBFBD><E99C82><EFBFBD>?..
return {
totalRules: MEDICAL_LOGIC_RULES.length,
passedRules: MEDICAL_LOGIC_RULES.length - violations.length,
violations,
overallValidity: violations.filter(v => v.severity === 'error').length === 0
};
}
}
3.4 ConflictDetectionService - <20>脩<EFBFBD>璉<EFBFBD>瘚𧢲<E7989A><F0A7A2B2>?
3.4.1 <20>蠘<EFBFBD>摰帋<E691B0>
<EFBFBD>峕芋<EFBFBD>讠<EFBFBD><EFBFBD>𨅯<EFBFBD>蝒<EFBFBD><EFBFBD>瘚页<EFBFBD><EFBFBD>舀<EFBFBD>嚗?- screening<6E>脩<EFBFBD>嚗?2摮埈挾摰峕㟲<E5B395>扯<EFBFBD>隡啣<E99AA1>蝒?- extraction<6F>脩<EFBFBD>嚗𡁏㺭<F0A1818F>澆榆撘<E6A686><E69298>蝒<EFBFBD><E89D92><EFBFBD>芣䔉嚗?- <20>脩<EFBFBD>銝仿<E98A9D>蝔见漲<E8A781><E6BCB2>漣嚗<E6BCA3>抅鈭𤾸<E988AD>畾菟<E795BE>閬<EFBFBD><E996AC>改<EFBFBD>
3.4.2 <20>詨<EFBFBD><E8A9A8>亙藁
// backend/src/modules/asl/common/services/ConflictDetectionService.ts
export interface ConflictAnalysis {
hasConflict: boolean // <20>臬炏摮睃銁<E79D83>脩<EFBFBD>
conflictFields: string[] // <20>脩<EFBFBD><E884A9><EFBFBD><EFBFBD>畾萄<E795BE>銵? overallConflict: boolean // <20>颱<EFBFBD><E9A2B1>喟<EFBFBD><E5969F>臬炏<E887AC>脩<EFBFBD>
severity: string // 'low' | 'medium' | 'high'
criticalFieldConflicts: string[] // <20>喲睸摮埈挾<E59F88>脩<EFBFBD>嚗<EFBFBD><E59A97><EFBFBD>箏<EFBFBD><E7AE8F><EFBFBD>𤩅瘜𨰻<E7989C><F0A8B0BB><EFBFBD><EFBFBD>𨅯<EFBFBD><F0A885AF>湔<EFBFBD>改<EFBFBD>
needUrgentReview: boolean // <20>臬炏<E887AC><E7828F>閬<EFBFBD>揮<EFBFBD>乩犖撌亙<E6928C><E4BA99>?}
export class ConflictDetectionService {
/**
* 璉<>瘚𨈇creening<6E>脩<EFBFBD>嚗?2摮埈挾摰峕㟲<E5B395>扯<EFBFBD>隡堆<E99AA1>
*/
detectScreeningConflict(
modelAResult: ScreeningResult,
modelBResult: ScreeningResult
): ConflictAnalysis
/**
* 璉<>瘚𠸏xtraction<6F>脩<EFBFBD>嚗<EFBFBD>㺭<EFBFBD>澆榆撘<E6A686><E69298><EFBFBD>芣䔉嚗? */
detectExtractionConflict(
modelAResult: ExtractionResult,
modelBResult: ExtractionResult
): ConflictAnalysis
/**
* <20>箄<EFBFBD><E7AE84><EFBFBD><EFBFBD>嚗<EFBFBD>抅鈭𤾸<E988AD>蝒<EFBFBD>艇<EFBFBD>滨<EFBFBD>摨佗<E691A8>
*/
prioritizeReview(conflict: ConflictAnalysis): {
priority: number; // 0-100
reviewDeadline: Date;
reasons: string[];
}
}
3.4.3 <20>脩<EFBFBD>銝仿<E98A9D>蝔见漲閫<E6BCB2><E996AB>嚗<EFBFBD>抅鈭焾ochrane<6E><65><EFBFBD>嚗?
/**
* Screening<6E>脩<EFBFBD>銝仿<E98A9D>蝔见漲嚗<E6BCB2><E59A97><EFBFBD><EFBFBD>ochrane RoB 2.0<EFBFBD>喲睸摮埈挾嚗? */
private calculateSeverity(
conflictFields: string[],
overallConflict: boolean
): string {
// <20>喲睸<E596B2>寞<EFBFBD>摮血<E691AE>畾蛛<E795BE>Cochrane RoB 2.0<EFBFBD>詨<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
const criticalFields = ['<27>𤩺㦤<F0A4A9BA>𡝗䲮瘜?, '<EFBFBD>脫<EFBFBD>', '蝏𤘪<EFBFBD>摰峕㟲<EFBFBD>?];
const hasCriticalConflict = conflictFields.some(f => criticalFields.includes(f));
if (hasCriticalConflict) {
return 'high'; // <20>喲睸摮埈挾<E59F88>脩<EFBFBD> <20>?擃䀝<E69383><E4809D><EFBFBD>漣
}
// 1. field9嚗<39><E59A97>撅<EFBFBD><E69285><EFBFBD><EFBFBD>嚗匧<E59A97>蝒?<3F>?high
if (conflictFields.includes('field9')) {
return 'high'
}
// 2. <20>颱<EFBFBD><E9A2B1>喟<EFBFBD><E5969F>脩<EFBFBD> <20>?high
if (overallConflict) {
return 'high'
}
// 3. <20>喲睸摮埈挾嚗Êield5/6/7嚗匧<E59A97>蝒?<3F>?medium
const criticalFields = ['field5', 'field6', 'field7']
const hasCriticalConflict = conflictFields.some(f => criticalFields.includes(f))
if (hasCriticalConflict) {
return 'medium'
}
// 4. <20>脩<EFBFBD>摮埈挾>3銝?<3F>?medium
if (conflictFields.length > 3) {
return 'medium'
}
// 5. <20>嗡<EFBFBD> <20>?low
return conflictFields.length > 0 ? 'low' : 'none'
}
3.5 AsyncTaskService - 撘<>郊隞餃𦛚<E9A483>滚𦛚
3.5.1 <20>蠘<EFBFBD>摰帋<E691B0>
<EFBFBD>寥<EFBFBD>憭<EFBFBD><EFBFBD><EFBFBD>滚𦛚嚗峕𣈲<EFBFBD><EFBFBD><EFBFBD>
- <EFBFBD>箏<EFBFBD>撟嗅<EFBFBD>嚗?撟嗅<E6929F>嚗?- 餈𥕦漲餈質葵
- 憭梯揖<EFBFBD>滩<EFBFBD>
- 隞餃𦛚<EFBFBD>𡝗<EFBFBD>
3.4.2 <20>詨<EFBFBD><E8A9A8>亙藁
// backend/src/modules/asl/common/services/AsyncTaskService.ts
export interface BatchResult<T> {
totalCount: number
successCount: number
failedCount: number
results: T[]
}
export class AsyncTaskService {
/**
* <20>寥<EFBFBD>憭<EFBFBD><E686AD><EFBFBD><EFBFBD>讃
*/
async processBatch<T>(
taskId: string,
literatureIds: string[],
processor: (litId: string) => Promise<T>,
onProgress?: (completed: number, total: number) => void
): Promise<BatchResult<T>>
}
4. <20>唳旿摨栞挽霈?
4.1 <20>詨<EFBFBD>銵函<E98AB5><E587BD>?
4.1.1 AslFulltextScreeningTask - <20>冽<EFBFBD>憭滨<E686AD>隞餃𦛚銵?
model AslFulltextScreeningTask {
@@schema("asl_schema")
id String @id @default(uuid())
projectId String
userId String
name String @default("<22>冽<EFBFBD>憭滨<E686AD>")
// <20>唳旿<E594B3>交<EFBFBD>
sourceTitleTaskId String? // <20>交<EFBFBD><E4BAA4><EFBFBD><EFBFBD><EFBFBD>萘<EFBFBD>隞餃𦛚ID
sourceType String @default("title_screening") // title_screening/manual_import
// 隞餃𦛚<E9A483>滨蔭嚗㇈VP<56>嗆挾嚗𡁏<E59A97><F0A1818F>砍<EFBFBD>憟賣芋<E8B3A3>页<EFBFBD>潃? modelA String @default("deepseek-v3") // 瞼0.001/1K tokens
modelB String @default("qwen-max") // 瞼0.004/1K tokens
// 蝏蠘恣
totalCount Int @default(0)
pdfReadyCount Int @default(0)
processedCount Int @default(0)
includedCount Int @default(0)
excludedCount Int @default(0)
conflictCount Int @default(0)
pendingCount Int @default(0)
// 隞餃𦛚<E9A483>嗆<EFBFBD>? status String @default("pending")
// pending <20>?acquiring_pdfs <20>?processing <20>?completed/failed
errorMessage String? @db.Text
// <20>園𡢿蝏蠘恣
startedAt DateTime?
completedAt DateTime?
processingTime Int? // 蝘?
// <20>鞉𧋦蝏蠘恣
totalCost Float? // 蝢𤾸<E89DA2>
totalTokens Int?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// <20>唾<EFBFBD><E594BE>喟頂
project AslScreeningProject @relation(fields: [projectId], references: [id], onDelete: Cascade)
results AslFulltextScreeningResult[]
@@index([userId, status])
@@index([projectId])
@@index([status, createdAt])
}
4.1.2 AslFulltextScreeningResult - <20>冽<EFBFBD>憭滨<E686AD>蝏𤘪<E89D8F>銵?
model AslFulltextScreeningResult {
@@schema("asl_schema")
id String @id @default(uuid())
taskId String
literatureId String @unique // 銝<>銝芣<E98A9D><E88AA3>桀蘨<E6A180>劐<EFBFBD><E58A90>︿creening蝏𤘪<E89D8F>
// 璅∪<E79285>A<EFBFBD>斗鱏嚗?2摮埈挾霂<E68CBE>摯嚗? modelAResult Json
// 蝏𤘪<E89D8F>嚗㝯
// field1_source: { present: true, completeness: "摰峕㟲", note: "..." },
// field2_studyType: { present: true, completeness: "摰峕㟲", note: "RCT" },
// ... field3-12 ...
// field9_outcomes: { present: true, completeness: "摰峕㟲", extractable: true, note: "..." },
// overallAssessment: {
// fieldsComplete: "10/12",
// criticalFieldsMissing: [],
// dataQuality: "擃?,
// extractability: "<22>舀<EFBFBD><E88880>?,
// decision: "蝥喳<E89DA5>",
// reason: "<22><><EFBFBD>匧<EFBFBD><E58CA7>桀<EFBFBD>畾萄<E795BE><E89084>湛<EFBFBD><E6B99B>唳旿<E594B3>舀<EFBFBD><E88880>?,
// confidence: 0.95
// }
// }
modelAProcessTime Int? // 瘥怎<E798A5>
modelATokens Int?
modelACost Float? // 蝢𤾸<E89DA2>
// 璅∪<E79285>B<EFBFBD>斗鱏嚗<E9B18F><E59A97>銝羓<E98A9D><E7BE93><EFBFBD><EFBFBD>
modelBResult Json
modelBProcessTime Int?
modelBTokens Int?
modelBCost Float?
// <20>脩<EFBFBD><E884A9><EFBFBD><EFBFBD>
isConflict Boolean @default(false)
conflictFields Json? // ["field5", "field9"]
conflictSeverity String? // low/medium/high
overallConflict Boolean @default(false) // <20>颱<EFBFBD><E9A2B1>喟<EFBFBD><E5969F>臬炏<E887AC>脩<EFBFBD>
// <20><>蝏<EFBFBD><E89D8F>蝑? finalDecision String @default("pending")
// pending <20>?included/excluded
decisionMethod String? // ai_consensus/manual
exclusionReason String? @db.Text
exclusionCategory String?
// missing_outcomes/incomplete_data/poor_quality/
// protocol_violation/duplicate/other
// 韐券<E99F90><E588B8><EFBFBD>扇
dataQuality String? // high/medium/low
extractability String? // extractable/partial/non_extractable
// 鈭箏極摰⊥瓲
reviewedBy String?
reviewedAt DateTime?
reviewNote String? @db.Text
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// <20>唾<EFBFBD><E594BE>喟頂
task AslFulltextScreeningTask @relation(fields: [taskId], references: [id], onDelete: Cascade)
literature AslLiterature @relation(fields: [literatureId], references: [id], onDelete: Cascade)
@@index([taskId, finalDecision])
@@index([taskId, isConflict])
@@index([exclusionCategory])
@@index([dataQuality])
}
4.1.3 AslLiterature - <20><>讃銵剁<E98AB5><E58981>湔鰵摮埈挾嚗?
model AslLiterature {
@@schema("asl_schema")
id String @id @default(uuid())
projectId String
// <20>箸𧋦靽⊥<E99DBD>
pmid String?
doi String?
title String @db.Text
authors String? @db.Text
journal String?
year Int?
abstract String? @db.Text
// PDF摮睃<E691AE>嚗<EFBFBD>眏PDFStorageService蝞∠<E89D9E>嚗? hasPDF Boolean @default(false)
pdfStorageType String? // 'dify' or 'oss'
pdfStorageRef String? // Dify: documentId, OSS: key
pdfUrl String? // Dify: null, OSS: <20>砍<EFBFBD>URL
pdfStatus String? // acquiring/ready/failed
pdfAcquireMethod String? // auto/manual/knowledge_base
// <20>冽<EFBFBD>嚗<EFBFBD>眏PDFStorageService<63>𣂼<EFBFBD>撟嗅<E6929F><E59785>剁<EFBFBD>
fullText String? @db.Text
fullTextTokenCount Int?
fullTextCharCount Int?
extractionMethod String? // pymupdf/nougat
extractionQuality Float? // 0.0-1.0
detectedLanguage String? // chinese/english
// <20>嗆挾<E59786><E68CBE>扇
stage String @default("imported")
// imported <20>?title_screened <20>?pdf_acquired <20>?fulltext_screened <20>?extracted
// <20>園𡢿<E59C92>? importedAt DateTime @default(now())
updatedAt DateTime @updatedAt
// <20>唾<EFBFBD><E594BE>喟頂嚗<E9A082>𡠺蝡页<E89DA1>
project AslScreeningProject @relation(fields: [projectId], references: [id])
titleScreening AslTitleScreeningResult?
fulltextScreening AslFulltextScreeningResult? // <20>冽<EFBFBD>憭滨<E686AD>嚗<EFBFBD>𡠺蝡页<E89DA1>
dataExtraction AslDataExtractionResult? // <20>冽<EFBFBD><E586BD>𣂼<EFBFBD>嚗<EFBFBD>𧊋<EFBFBD>伐<EFBFBD><E4BC90>祉<EFBFBD>嚗?
@@index([projectId, stage])
@@index([pmid])
@@index([pdfStatus])
}
4.2 <20>唳旿摨栞<E691A8>蝘?
*<EFBFBD>𩤃<EFBFBD> 瞉<><E79E89>嚗?餈<>宏"撠望糓"<22>𥕦遣<F0A595A6>啗”"<22><><EFBFBD><EFBFBD>?
<EFBFBD>牐蛹AI<EFBFBD>箄<EFBFBD><EFBFBD><EFBFBD>讃璅∪<EFBFBD><EFBFBD>?*<2A>冽鰵<E586BD>?*嚗諹<E59A97>鈭𥡝”銋见<E98A8B>銝滚<EFBFBD><EFBFBD>?嚗峕<EFBFBD>隞仙risma<EFBFBD>?migrate"<22>滢<EFBFBD>摰鮋<E691B0>銝𠰴停<F0A0B0B4>?<EFBFBD>𥕦遣<EFBFBD>啗”<EFBFBD>?
# <20>𥕦遣<F0A595A6>啗”嚗<E2809D>洵銝<E6B4B5>甈⊥<E79488>銵?餈<>宏"嚗?cd backend
npx prisma migrate dev --name create_asl_fulltext_screening_tables
# 餈嗘葵<E59798>賭誘隡𡄯<E99AA1>
# 1. <20>?asl_schema 銝剖<E98A9D>撱?銝芣鰵銵剁<E98AB5>
# - AslFulltextScreeningTask嚗<6B><E59A97><EFBFBD>啣<EFBFBD>撱綽<E692B1>
# - AslFulltextScreeningResult嚗<74><E59A97><EFBFBD>啣<EFBFBD>撱綽<E692B1>
# - AslLiterature嚗<65><E59A97><EFBFBD>𨅯歇摮睃銁<E79D83>躰歲餈<E6ADB2><E9A488>銝滚<E98A9D><E6BB9A>典<EFBFBD><E585B8>𥕦遣嚗?# 2. <20><><EFBFBD>SQL<51>𡁏𧋦嚗īrisma/migrations/xxx/migration.sql嚗?# 3. <20>扯<EFBFBD>SQL<51>𥕦遣銵?# 4. 霈啣<E99C88>餈<EFBFBD>宏<EFBFBD><E5AE8F>蟮
# SQL蝷箔<E89DB7>嚗<EFBFBD><E59A97><EFBFBD><EFBFBD><EFBFBD>銵𣬚<E98AB5>嚗?CREATE TABLE "asl_schema"."AslFulltextScreeningTask" (
"id" TEXT NOT NULL,
"projectId" TEXT NOT NULL,
"userId" TEXT NOT NULL,
-- ... <20>嗡<EFBFBD>摮埈挾
PRIMARY KEY ("id")
);
CREATE TABLE "asl_schema"."AslFulltextScreeningResult" (
-- ...
);
**瘜冽<E7989C>鈭钅★**嚗?- <20>?銝滢<E98A9D>靽格㺿PKB/AIA蝑匧<E89D91>隞𡝗芋<F0A19D97>㛖<EFBFBD>銵?- <20>?摰<><E691B0><EFBFBD>祉<EFBFBD><E7A589><EFBFBD>鰵銵剁<E98AB5>asl_schema嚗?- <20>?Schema<6D>𠉛氖嚗峕㺭<E5B395>桅<EFBFBD>蝳?
5. API霈曇恣
5.1 API蝡舐<E89DA1><E88890>𡑒”
// <20>滨<EFBFBD>嚗?api/v1/asl/fulltext-screening
// 1. <20>𥕦遣<F0A595A6>冽<EFBFBD>憭滨<E686AD>隞餃𦛚
POST /tasks
// 2. <20>瑕<EFBFBD>隞餃𦛚<E9A483>𡑒”
GET /tasks
// 3. <20>瑕<EFBFBD>隞餃𦛚霂行<E99C82>嚗<EFBFBD>鉄餈𥕦漲嚗?GET /tasks/:taskId
// 4. <20>瑕<EFBFBD>隞餃𦛚蝏𤘪<E89D8F>
GET /tasks/:taskId/results
// 5. 鈭箏極摰⊥瓲<E28AA5>喟<EFBFBD>
PUT /results/:resultId/decision
// 6. 撖澆枂Excel
GET /tasks/:taskId/export-excel
// 7. <20>𡝗<EFBFBD>隞餃𦛚
POST /tasks/:taskId/cancel
// 8. <20>滩<EFBFBD>憭梯揖憿?POST /tasks/:taskId/retry-failed
5.2 霂衣<E99C82>API霈曇恣
5.2.1 <20>𥕦遣<F0A595A6>冽<EFBFBD>憭滨<E686AD>隞餃𦛚
POST /api/v1/asl/fulltext-screening/tasks
Request:
{
"projectId": "proj-123",
"name": "<22>冽<EFBFBD>憭滨<E686AD>-蝚砌<E89D9A><E7A08C>?,
"sourceTitleTaskId": "title-task-456", // <20>交<EFBFBD><E4BAA4><EFBFBD><EFBFBD><EFBFBD>萘<EFBFBD>隞餃𦛚
"literatureIds": ["lit-001", "lit-002", ...], // <20>脲郊蝥喳<E89DA5><E596B3><EFBFBD><EFBFBD><EFBFBD>? "modelA": "deepseek-v3",
"modelB": "qwen-max"
}
Response:
{
"success": true,
"data": {
"taskId": "fs-task-789",
"status": "pending",
"totalCount": 30,
"pdfReadyCount": 28, // PDF撌脣停蝏? "message": "隞餃𦛚<EFBFBD>𥕦遣<EFBFBD>𣂼<EFBFBD>嚗峕迤<EFBFBD>典<EFBFBD>憪见<EFBFBD><EFBFBD>?
}
}
// <20>𡒊垢<F0A1928A>餉<EFBFBD>
async createTask(req, reply) {
// 1. 撉諹<E69289><E8ABB9><EFBFBD>讃<EFBFBD>臬炏<E887AC>侨DF
const literatures = await prisma.aslLiterature.findMany({
where: { id: { in: req.body.literatureIds } }
})
const pdfReady = literatures.filter(lit => lit.pdfStatus === 'ready')
// 2. <20>𥕦遣隞餃𦛚
const task = await prisma.aslFulltextScreeningTask.create({
data: {
projectId: req.body.projectId,
userId: req.userId,
name: req.body.name,
sourceTitleTaskId: req.body.sourceTitleTaskId,
modelA: req.body.modelA,
modelB: req.body.modelB,
totalCount: literatures.length,
pdfReadyCount: pdfReady.length,
status: 'pending'
}
})
// 3. 撘<>郊憭<E9838A><E686AD>嚗<EFBFBD><E59A97><EFBFBD>餃<EFBFBD><E9A483>滚<EFBFBD>嚗? this.screeningService.processTask(task.id).catch(err => {
logger.error('Screening task failed', { taskId: task.id, error: err })
})
return { taskId: task.id, status: 'pending', totalCount: literatures.length }
}
5.2.2 <20>瑕<EFBFBD>隞餃𦛚餈𥕦漲
GET /api/v1/asl/fulltext-screening/tasks/:taskId
Response:
{
"success": true,
"data": {
"taskId": "fs-task-789",
"name": "<22>冽<EFBFBD>憭滨<E686AD>-蝚砌<E89D9A><E7A08C>?,
"status": "processing", // pending/acquiring_pdfs/processing/completed/failed
"totalCount": 30,
"pdfReadyCount": 28,
"processedCount": 15,
"includedCount": 10,
"excludedCount": 5,
"conflictCount": 3,
"pendingCount": 12,
"progress": 50, // <20>曉<EFBFBD>瘥? "estimatedTimeRemaining": 300, // 蝘?
"totalCost": 0.15, // 蝢𤾸<E89DA2>
"totalTokens": 150000,
"startedAt": "2025-11-22T10:00:00Z",
"updatedAt": "2025-11-22T10:05:00Z"
}
}
5.2.3 <20>瑕<EFBFBD>隞餃𦛚蝏𤘪<E89D8F>
GET /api/v1/asl/fulltext-screening/tasks/:taskId/results
Query:
- filter: 'all' | 'conflict' | 'included' | 'excluded' | 'pending'
- page: 1
- pageSize: 20
Response:
{
"success": true,
"data": {
"taskId": "fs-task-789",
"totalCount": 30,
"filteredCount": 3, // 蝚血<E89D9A>filter<65><72>㺭<EFBFBD>?
"results": [
{
"resultId": "result-001",
"literatureId": "lit-001",
"literature": {
"pmid": "PMID12345",
"title": "SGLT2<54>穃<EFBFBD><E7A983><EFBFBD>祥<EFBFBD>㛖<EFBFBD>撠輻<E692A0><E8BCBB>曄<EFBFBD><E69B84><EFBFBD>CT<43>𠉛弦",
"authors": "Smith JA, et al.",
"journal": "Lancet",
"year": 2023
},
// 璅∪<E79285>A<EFBFBD>斗鱏
"modelAResult": {
"field1_source": { "present": true, "completeness": "摰峕㟲", "note": "蝚砌<E89D9A>雿𡏭<E99BBF><F0A18FAD>mith, Lancet 2023" },
"field2_studyType": { "present": true, "completeness": "摰峕㟲", "note": "RCT" },
"field5_population": { "present": true, "completeness": "摰峕㟲", "note": "<22>瑟𧋦<E7919F>?00靘页<E99D98><E9A1B5>箇瑪<E7AE87>孵<EFBFBD>霂衣<E99C82>" },
"field9_outcomes": {
"present": true,
"completeness": "摰峕㟲",
"extractable": true,
"note": "銝餉<E98A9D>蝏枏<E89D8F>eGFR<46>匧<EFBFBD><E58CA7>湔㺭<E6B994>潭㺭<E6BDAD>殷<EFBFBD><E6AEB7><EFBFBD><EFBFBD>撢望<E692A2><E69C9B><EFBFBD>榆嚗?
},
// ... field3-4, 6-8, 10-12
"overallAssessment": {
"fieldsComplete": "12/12",
"criticalFieldsMissing": [],
"dataQuality": "擃?,
"extractability": "<22>舀<EFBFBD><E88880>?,
"decision": "蝥喳<EFBFBD>",
"reason": "<EFBFBD><EFBFBD><EFBFBD>?2銝芸<EFBFBD>畾萄<EFBFBD><EFBFBD>湛<EFBFBD><EFBFBD>喲睸<EFBFBD>唳旿嚗<EFBFBD>甅<EFBFBD>祇<EFBFBD><EFBFBD><EFBFBD>抅蝥踴<EFBFBD><EFBFBD>僕憸<EFBFBD><EFBFBD><EFBFBD><EFBFBD>撅<EFBFBD>嚗匧<EFBFBD><EFBFBD>舀<EFBFBD><EFBFBD>吔<EFBFBD><EFBFBD>唳旿韐券<EFBFBD>擃?,
"confidence": 0.95
}
},
// 璅∪<E79285>B<EFBFBD>斗鱏
"modelBResult": {
// <20>䔶<EFBFBD>蝏𤘪<E89D8F>
"overallAssessment": {
"decision": "蝥喳<E89DA5>",
"confidence": 0.92
}
},
// <20>脩<EFBFBD><E884A9><EFBFBD><EFBFBD>
"isConflict": false,
"conflictFields": [],
"overallConflict": false,
"conflictSeverity": "none",
// <20><>蝏<EFBFBD><E89D8F>蝑? "finalDecision": "pending", // 敺<>犖撌亙恣<E4BA99>貊&霈? "dataQuality": "high",
"extractability": "extractable",
"processingTime": 25000, // 25蝘? "totalCost": 0.008 // 蝢𤾸<E89DA2>
},
// <20>脩<EFBFBD>獢<EFBFBD><E78DA2>
{
"resultId": "result-002",
"literatureId": "lit-005",
"literature": { ... },
"modelAResult": {
"field9_outcomes": {
"present": false, // <20>?A霈支蛹蝻箏仃
"completeness": "蝻箏仃",
"extractable": false,
"note": "Results<74>典<EFBFBD><E585B8>芣𥁒<E88AA3>𠹺蜓閬<E89C93><E996AC>撅<EFBFBD><E69285>唳旿"
},
"overallAssessment": {
"decision": "<22>㘾膄", // <20>?A撱箄悅<E7AE84>㘾膄
"reason": "<22>喲睸摮埈挾field9蝻箏仃嚗峕<E59A97>瘜閧鍂鈭穽eta<74><61><EFBFBD>"
}
},
"modelBResult": {
"field9_outcomes": {
"present": true, // <20>?B霈支蛹摮睃銁
"completeness": "<22>典<EFBFBD>摰峕㟲",
"extractable": true,
"note": "銝餉<E98A9D>蝏枏<E89D8F><E69E8F>唳旿<E594B3>求iscussion<6F>典<EFBFBD><E585B8>亙<EFBFBD>"
},
"overallAssessment": {
"decision": "蝥喳<E89DA5>", // <20>?B撱箄悅蝥喳<E89DA5>
"reason": "<22>賜<EFBFBD>蝏枏<E89D8F><E69E8F><EFBFBD><EFBFBD><EFBFBD>求iscussion<6F>亙<EFBFBD>嚗䔶<E59A97><E494B6>唳旿摰峕㟲<E5B395>舀<EFBFBD><E88880>?
}
},
"isConflict": true, // <20>?<3F><>扇<EFBFBD>脩<EFBFBD>
"conflictFields": ["field9"],
"overallConflict": true,
"conflictSeverity": "high", // field9<64>脩<EFBFBD> <20>?擃䀝艇<E4809D>滨<EFBFBD>摨?
"finalDecision": "pending" // <20><>閬<EFBFBD>犖撌乩輔鋆? }
],
"pagination": {
"page": 1,
"pageSize": 20,
"totalPages": 2
}
}
}
5.2.4 鈭箏極摰⊥瓲<E28AA5>喟<EFBFBD>
PUT /api/v1/asl/fulltext-screening/results/:resultId/decision
Request:
{
"finalDecision": "excluded", // included/excluded
"exclusionReason": "<22>喲睸摮埈挾field9嚗<39><E59A97>撅<EFBFBD><E69285><EFBFBD><EFBFBD>嚗㗇㺭<E39787>桐<EFBFBD>摰峕㟲嚗䔶<E59A97><E494B6>侨<EFBFBD>潭<EFBFBD><E6BDAD>瑚<EFBFBD><E7919A>啣<EFBFBD>?,
"exclusionCategory": "missing_outcomes",
"reviewNote": "<EFBFBD>賜<EFBFBD><EFBFBD>亙<EFBFBD>鈭<EFBFBD>遬<EFBFBD>埈<EFBFBD>判<0.05嚗䔶<EFBFBD>蝻箏<EFBFBD><EFBFBD><EFBFBD><EFBFBD>撢望<EFBFBD><EFBFBD><EFBFBD>榆嚗峕<EFBFBD>瘜閧鍂鈭穽eta<EFBFBD><EFBFBD><EFBFBD>"
}
Response:
{
"success": true,
"message": "<EFBFBD>喟<EFBFBD>撌脖<EFBFBD>摮?
}
5.2.5 撖澆枂Excel
GET /api/v1/asl/fulltext-screening/tasks/:taskId/export-excel
Response:
// <20>湔𦻖銝贝蝸Excel<65><6C>辣
// Excel蝏𤘪<E89D8F>嚗<EFBFBD><E59A97>Sheet嚗?Sheet 1: <20><>蝏<EFBFBD>熙<EFBFBD>交<EFBFBD><E4BAA4>桀<EFBFBD>銵?- <20><>讃ID
- PMID
- <EFBFBD>𠉛弦ID嚗<EFBFBD><EFBFBD><EFBFBD>?撟港遢嚗?- <EFBFBD><EFBFBD>讃<EFBFBD>交<EFBFBD>
- <EFBFBD><EFBFBD><EFBFBD>
- <EFBFBD><EFBFBD>蝏<EFBFBD><EFBFBD>蝑?- <EFBFBD>喟<EFBFBD><EFBFBD>孵<EFBFBD>
- <EFBFBD>唳旿韐券<EFBFBD>
- <EFBFBD>舀<EFBFBD><EFBFBD>𡝗<EFBFBD>?
Sheet 2: <EFBFBD>㘾膄<EFBFBD><EFBFBD>讃<EFBFBD>𡑒”
- <EFBFBD><EFBFBD>讃ID
- PMID
- <EFBFBD>𠉛弦ID
- <EFBFBD><EFBFBD><EFBFBD>
- <EFBFBD>㘾膄<EFBFBD>笔<EFBFBD>
- <EFBFBD>㘾膄<EFBFBD><EFBFBD>掩
Sheet 3: PRISMA蝏蠘恣
- <EFBFBD>餉恣憭滨<EFBFBD>: n=30
- <EFBFBD><EFBFBD>蝏<EFBFBD>熙<EFBFBD>? n=18
- <EFBFBD>㘾膄: n=12
- 蝏枏<EFBFBD><EFBFBD><EFBFBD><EFBFBD>蝻箏仃: n=5
- <EFBFBD>唳旿銝滚<EFBFBD><EFBFBD>? n=3
- 韐券<EFBFBD><EFBFBD>桅<EFBFBD>: n=2
- <EFBFBD>寞<EFBFBD>餈肽<EFBFBD>: n=1
- <EFBFBD>嗡<EFBFBD>: n=1
6. <20>冽<EFBFBD>憭滨<E686AD>銝𡁜𦛚撅<F0A69B9A>挽霈?
6.1 FulltextScreeningService
// backend/src/modules/asl/fulltext-screening/services/FulltextScreeningService.ts
export class FulltextScreeningService {
private pdfStorage: PDFStorageService
private llmService: LLM12FieldsService
private conflictDetection: ConflictDetectionService
private asyncTask: AsyncTaskService
/**
* 憭<><E686AD><EFBFBD>冽<EFBFBD>憭滨<E686AD>隞餃𦛚
*/
async processTask(taskId: string): Promise<void> {
const task = await prisma.aslFulltextScreeningTask.findUnique({
where: { id: taskId },
include: { project: true }
})
// 1. <20>瑕<EFBFBD>敺<EFBFBD><E695BA><EFBFBD>㗇<EFBFBD><E39787>殷<EFBFBD>隞<EFBFBD>DF撌脣停蝏迎<E89D8F>
const literatures = await this.getLiteratures(task)
// 2. <20>湔鰵隞餃𦛚<E9A483>嗆<EFBFBD>? await prisma.aslFulltextScreeningTask.update({
where: { id: taskId },
data: {
status: 'processing',
startedAt: new Date()
}
})
// 3. <20>寥<EFBFBD>憭<EFBFBD><E686AD>嚗?撟嗅<E6929F>嚗? try {
await this.asyncTask.processBatch(
taskId,
literatures.map(lit => lit.id),
async (litId) => await this.screenLiterature(taskId, litId, task),
async (completed, total) => {
// 餈𥕦漲<F0A595A6>噼<EFBFBD>嚗𡁏凒<F0A1818F>唬遙<E594AC>∠<EFBFBD>霈? await this.updateTaskProgress(taskId, completed, total)
}
)
// 4. 隞餃𦛚摰峕<E691B0>
await this.completeTask(taskId)
} catch (error) {
// 5. 隞餃𦛚憭梯揖
await prisma.aslFulltextScreeningTask.update({
where: { id: taskId },
data: {
status: 'failed',
errorMessage: error.message
}
})
throw error
}
}
/**
* 蝑偦<E89D91>匧<EFBFBD>蝭<EFBFBD><E89DAD><EFBFBD>? */
private async screenLiterature(
taskId: string,
literatureId: string,
task: AslFulltextScreeningTask
): Promise<AslFulltextScreeningResult> {
// 1. <20>瑕<EFBFBD><E79195><EFBFBD>讃<EFBFBD>冽<EFBFBD>
const literature = await prisma.aslLiterature.findUnique({
where: { id: literatureId }
})
if (!literature.fullText) {
throw new Error('<27><>讃<EFBFBD>冽<EFBFBD><E586BD>芸停蝏?)
}
// 2. <20>峕芋<E5B395>见僎銵諹<E98AB5><E8ABB9>? const { resultA, resultB } = await this.llmService.processDualModels(
LLM12FieldsMode.SCREENING,
task.modelA,
task.modelB,
literature.fullText,
task.project // PICOS銝𠹺<E98A9D><F0A0B9BA>? )
// 3. <20>脩<EFBFBD>璉<EFBFBD>瘚? const conflict = this.conflictDetection.detectScreeningConflict(
resultA.result,
resultB.result
)
// 4. <20>芸𢆡<E88AB8>喟<EFBFBD>嚗<EFBFBD><E59A97><EFBFBD>脩<EFBFBD>銝𥪜<E98A9D>璅∪<E79285>銝<EFBFBD><E98A9D>湛<EFBFBD>
let finalDecision = 'pending'
let decisionMethod = null
if (!conflict.hasConflict) {
finalDecision = resultA.result.overallAssessment.decision
decisionMethod = 'ai_consensus'
}
// 5. 靽嘥<E99DBD>蝏𤘪<E89D8F>
const result = await prisma.aslFulltextScreeningResult.create({
data: {
taskId,
literatureId,
modelAResult: resultA.result,
modelAProcessTime: resultA.processingTime,
modelATokens: resultA.tokenUsage,
modelACost: resultA.cost,
modelBResult: resultB.result,
modelBProcessTime: resultB.processingTime,
modelBTokens: resultB.tokenUsage,
modelBCost: resultB.cost,
isConflict: conflict.hasConflict,
conflictFields: conflict.conflictFields,
overallConflict: conflict.overallConflict,
conflictSeverity: conflict.severity,
finalDecision,
decisionMethod,
dataQuality: resultA.result.overallAssessment.dataQuality,
extractability: resultA.result.overallAssessment.extractability
}
})
// 6. <20>湔鰵<E6B994><E9B0B5>讃<EFBFBD>嗆挾
await prisma.aslLiterature.update({
where: { id: literatureId },
data: { stage: 'fulltext_screened' }
})
logger.info('Literature screened', {
literatureId,
decision: finalDecision,
conflict: conflict.hasConflict
})
return result
}
}
6.2 Prompt璅⊥踎
6.2.1 System Prompt
// backend/src/modules/asl/fulltext-screening/prompts/12fields_screening_system.txt
<0A>冽糓敺芾<E695BA><E88ABE>餃郎銝枏振嚗諹<E59A97>韐<EFBFBD><E99F90>隡唳<E99AA1><E594B3>桃<EFBFBD><E6A183>唳旿摰峕㟲<E5B395>批<EFBFBD><E689B9>舐鍂<E88890>扼<EFBFBD>?
## 隞餃𦛚
<0A>箔<EFBFBD>12摮埈挾霂<E68CBE>摯獢<E691AF>沲嚗<E6B2B2>ế<EFBFBD>剜<EFBFBD><E5899C>格糓<E6A0BC>血虾<E8A180>其<EFBFBD>Meta<74><61><EFBFBD><EFBFBD>?
## <20>𠉛弦<F0A0899B>寞<EFBFBD>
- **鈭箇黎(P):** {{population}}
- **撟脤<E6929F>(I):** {{intervention}}
- **撖寧<E69296>(C):** {{comparison}}
- **蝏枏<E89D8F>(O):** {{outcome}}
- **<2A>𠉛弦霈曇恣(S):** {{studyDesign}}
## 12摮埈挾霂<E68CBE>摯<EFBFBD><E691AF><EFBFBD>
### 1. <20><>讃<EFBFBD>交<EFBFBD>
- 璉<><E79289>伐<EFBFBD>蝚砌<E89D9A>雿𡏭<E99BBF><F0A18FAD><EFBFBD><EFBFBD>僑隞賬<E99A9E><E8B3AC><EFBFBD><EFBFBD>𨳍<EFBFBD><F0A8B38D>OI<4F>臬炏朣𣂼<E69CA3>
- <20><><EFBFBD>嚗?憿寥<E686BF><E5AFA5>其蛹"摰峕㟲"
### 2. <20>𠉛弦蝐餃<E89D90>
- 璉<><E79289>伐<EFBFBD><E4BC90>臬炏<E887AC>𡒊&霂湔<E99C82><E6B994>𠉛弦霈曇恣嚗㇌CT<43><54><EFBFBD><EFBFBD>㛖<EFBFBD>蝛嗥<E89D9B>嚗?- <20><><EFBFBD>嚗𡁶掩<F0A181B6>𧢲<EFBFBD><F0A7A2B2>唳<EFBFBD>蝖?
### 3. <20>𠉛弦霈曇恣蝏<E681A3><E89D8F>
- 璉<><E79289>伐<EFBFBD>(1) <20>讛挪<E8AE9B>園𡢿 (2) <20>唳旿<E594B3>交<EFBFBD>嚗<EFBFBD><E59A97>/憭帋葉敹<E89189><E695B9>
- <20><><EFBFBD>嚗帋舅憿寥<E686BF><E5AFA5>劐蛹"摰峕㟲"
### 4. <20>曄<EFBFBD>霂𦠜鱏<F0A6A09C><E9B18F><EFBFBD>
- 璉<><E79289>伐<EFBFBD><E4BC90>臬炏<E887AC>㗇<EFBFBD>蝖株<E89D96><E6A0AA>剜<EFBFBD><E5899C><EFBFBD><EFBFBD><EFBFBD>?- <20><><EFBFBD>嚗𡁏<E59A97><F0A1818F><EFBFBD><EFBFBD>撘閧鍂
### 5. 鈭箇黎<E7AE87>孵<EFBFBD> 潃?<3F>喲睸
- 璉<><E79289>伐<EFBFBD>(1) <20>瑟𧋦<E7919F>𧶏<EFBFBD><F0A7B68F>餅㺭+<2B><><EFBFBD>嚗?(2) 鈭箏藁蝏蠘恣摮佗<E691AE>撟湧<E6929F><E6B9A7><EFBFBD><EFBFBD>批<EFBFBD>嚗?- <20><><EFBFBD>嚗𡁏甅<F0A1818F>祇<EFBFBD>摰峕㟲嚗<E39FB2>抅蝥輻鸌敺<E9B88C><E695BA><EFBFBD><EFBFBD><EFBFBD>撢望<E692A2><E69C9B><EFBFBD>榆
### 6. <20>箇瑪<E7AE87>唳旿 潃?<3F>喲睸
- 璉<><E79289>伐<EFBFBD>(1) 銝餉<E98A9D><E9A489>蠘<EFBFBD><E8A098><EFBFBD><EFBFBD> (2) <20><>僎<EFBFBD>?- <20><><EFBFBD>嚗𡁜<E59A97><F0A1819C>賣<EFBFBD><E8B3A3><EFBFBD><EFBFBD><EFBFBD>箇瑪<E7AE87>潘<EFBFBD><E6BD98><EFBFBD><EFBFBD>撢望<E692A2><E69C9B><EFBFBD>榆嚗?
### 7. 撟脤<E6929F><E884A4>芣鴌 潃?<3F>喲睸
- 璉<><E79289>伐<EFBFBD>(1) <20>舐<EFBFBD>蝐餃<E89D90> (2) <20><><EFBFBD>銝𡒊<E98A9D>蝔?- <20><><EFBFBD>嚗朞晓<E69C9E>押<EFBFBD><E68ABC><EFBFBD><EFBFBD>譌<EFBFBD><E8AD8C><EFBFBD>甈~<E79488><EFBD9E><EFBFBD>蝔钅<E89D94><E99285>𡒊&
### 8. 撖寧<E69296><E5AFA7>芣鴌
- 璉<><E79289>伐<EFBFBD>撖寧<E69296>蝏<EFBFBD>僕憸<E58395>糓<EFBFBD>行<EFBFBD>蝖?- <20><><EFBFBD>嚗𡁜笆<F0A1819C>批<EFBFBD>摰寞<E691B0><E5AF9E>?
### 9. 蝏枏<E89D8F><E69E8F><EFBFBD><EFBFBD> 潃鐥<E6BD83>潃?<3F><><EFBFBD>喲睸
- 璉<><E79289>伐<EFBFBD>(1) 銝餉<E98A9D>蝏枏<E89D8F><E69E8F>臬炏<E887AC>亙<EFBFBD><E4BA99>唳旿 (2) <20>臬炏<E887AC>匧<EFBFBD><E58CA7>撢望<E692A2><E69C9B><EFBFBD>榆
- <20><><EFBFBD>嚗?*敹<>◆<EFBFBD>匧<EFBFBD><E58CA7>湔㺭<E6B994>潭㺭<E6BDAD>桀虾靘𥟇<E99D98><F0A59F87>?*
- **<2A>㘾膄<E398BE><E88684><EFBFBD>**嚗𡁜蘨<F0A1819C>侨<EFBFBD>潭<EFBFBD><E6BDAD>瑚<EFBFBD><E7919A>唳旿<E594B3><E697BF>蘨<EFBFBD>匧<EFBFBD><E58CA7>扳<EFBFBD>餈?
### 10. 蝏蠘恣<E8A098>寞<EFBFBD>
- 璉<><E79289>伐<EFBFBD>蝏蠘恣頧臭辣<E887AD><E8BEA3><EFBFBD>撉峕䲮瘜?- <20><><EFBFBD>嚗𡁏䲮瘜閙<E7989C>敶?
### 11. 韐券<E99F90>霂<EFBFBD>遠
- 璉<><E79289>伐<EFBFBD><E4BC90>誩<EFBFBD>𡁻<EFBFBD><F0A181BB>抵<EFBFBD>隡?- <20><><EFBFBD>嚗帋<E59A97>憌𡡞埯隡睃<E99AA1>
### 12. <20>嗡<EFBFBD>靽⊥<E99DBD>
- 璉<><E79289>伐<EFBFBD>瘜典<E7989C><E585B8>瑯<EFBFBD><E791AF>⏚<EFBFBD>𠰴<EFBFBD>蝒?- <20><><EFBFBD>嚗帋縑<E5B88B>舫<EFBFBD>𤩺<EFBFBD>
## 颲枏枂<E69E8F>澆<EFBFBD>嚗<EFBFBD>艇<EFBFBD>嘲SON嚗?
{
"field1_source": {
"present": true,
"completeness": "摰峕㟲/銝滚<E98A9D><E6BB9A>?蝻箏仃",
"note": "蝚砌<E89D9A>雿𡏭<E99BBF><F0A18FAD>mith, Lancet 2023, DOI: 10.1016/..."
},
"field2_studyType": {
"present": true,
"completeness": "摰峕㟲",
"note": "RCT嚗峕<E59A97>蝖株秩<E6A0AA>𤾸<EFBFBD><F0A4BEB8>脤<EFBFBD><E884A4>箏笆<E7AE8F>扯<EFBFBD>撉?
},
// ... field3-8 <20>峕甅蝏𤘪<E89D8F>
"field9_outcomes": {
"present": true/false,
"completeness": "摰峕㟲/銝滚<E98A9D><E6BB9A>?蝻箏仃",
"extractable": true/false, // 潃?<3F>喲睸嚗𡁏㺭<F0A1818F>格糓<E6A0BC>血虾<E8A180>𣂼<EFBFBD>
"note": "銝餉<E98A9D>蝏枏<E89D8F>eGFR銝钅<E98A9D><E99285>毺<EFBFBD>嚗峕<E59A97>摰峕㟲<E5B395>啣<EFBFBD>潭㺭<E6BDAD>殷<EFBFBD>撟脤<E6929F>蝏?2.4簣5.2, 撖寧<E69296>蝏?5.5簣6.8嚗?
},
// ... field10-12
"overallAssessment": {
"fieldsComplete": "10/12",
"criticalFieldsMissing": ["field9"], // 蝻箏仃<E7AE8F><E4BB83><EFBFBD><EFBFBD>桀<EFBFBD>畾? "dataQuality": "擃?銝?雿?,
"extractability": "<22>舀<EFBFBD><E88880>?<3F>典<EFBFBD><E585B8>舀<EFBFBD><E88880>?<3F>䭾<EFBFBD><E4ADBE>𣂼<EFBFBD>",
"decision": "蝥喳<E89DA5>/<2F>㘾膄", // 潃?<3F><>蝏<EFBFBD>ế<EFBFBD>? "reason": "霂衣<E99C82>霂湔<E99C82><E6B994><EFBFBD>眏嚗?00摮堒<E691AE>嚗?,
"confidence": 0.0-1.0
}
}
## <20>孵<EFBFBD>瘜冽<E7989C>
1. **field9<64><39><EFBFBD>喲睸**嚗𡁏<E59A97><F0A1818F>啣<EFBFBD>潭㺭<E6BDAD>桀<EFBFBD>憿餅<E686BF><E9A485>?2. **field5/6/7銋罸<E98A8B>閬?*嚗𡁏甅<F0A1818F>祇<EFBFBD><E7A587><EFBFBD>抅蝥踴<E89DA5><E8B8B4>僕憸<E58395><E686B8>憿餃<E686BF><E9A483>?3. **<2A>㘾膄隡睃<E99AA1>**嚗𡁏<E59A97><F0A1818F>喲睸<E596B2>唳旿蝻箏仃<E7AE8F>𨀣鱏<F0A880A3>㘾膄
4. **<2A>唳旿<E594B3>舀<EFBFBD><E88880>𡝗<EFBFBD>?<3F>𤏸”韐券<E99F90>**嚗𡁏㺭<F0A1818F>桀<EFBFBD><E6A180>湔<EFBFBD><E6B994>笔<EFBFBD>敶勗<E695B6><E58B97>惩<EFBFBD><E683A9>滩<EFBFBD>
6.2.2 User Prompt
// backend/src/modules/asl/fulltext-screening/prompts/12fields_screening_user.txt
霂瑚<E99C82>蝏<EFBFBD><E89D8F>霂颱誑銝𧢲<E98A9D><F0A7A2B2>桃<EFBFBD>**<2A>冽<EFBFBD><E586BD><EFBFBD>捆**嚗屸<E59A97>鞾★霂<E29885>摯12銝芸<E98A9D>畾萸<E795BE>?
## <20>冽<EFBFBD><E586BD><EFBFBD>捆
{{fullText}}
7. <20>滨垢霈曇恣
7.1 憿菟𢒰蝏𤘪<E89D8F>嚗<EFBFBD><E59A97>摮鞱<E691AE><E99EB1>橘<EFBFBD>
<EFBFBD>冽<EFBFBD>憭滨<EFBFBD>璅∪<EFBFBD>
<0A>鎿<EFBFBD><E98EBF><EFBFBD> 摮鞱<E691AE><E99EB1>?嚗朞挽蝵桐<E89DB5><E6A190>臬𢆡
<0A>? <20>鎿<EFBFBD><E98EBF><EFBFBD> PICOS<4F><53><EFBFBD>撅閧內嚗<E585A7><E59A97><EFBFBD>𠉛弦<F0A0899B>寞<EFBFBD>蝏扳㗁嚗?<3F>? <20>鎿<EFBFBD><E98EBF><EFBFBD> <20>冽<EFBFBD><E586BD>瑕<EFBFBD>蝞∠<E89D9E>銵冽聢
<0A>? <20>婙<EFBFBD><E5A999><EFBFBD> 撘<>憪见<E686AA>蝑𥟇<E89D91><F0A59F87>?<3F>?<3F>鎿<EFBFBD><E98EBF><EFBFBD> 摮鞱<E691AE><E99EB1>?嚗𡁜恣<F0A1819C>詨極雿𨅯蝱 潃?<3F>詨<EFBFBD>
<0A>? <20>鎿<EFBFBD><E98EBF><EFBFBD> PICOS<4F><53><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>舀<EFBFBD><E88880>𩤃<EFBFBD>
<0A>? <20>鎿<EFBFBD><E98EBF><EFBFBD> 銵冽聢<E586BD>硋恣<E7A18B>貊<EFBFBD><E8B28A>?<3F>? <20>? <20>鎿<EFBFBD><E98EBF><EFBFBD> <20>諹<EFBFBD>銵典仍嚗<E4BB8D>芋<EFBFBD>?+ 12摮埈挾嚗?<3F>? <20>? <20>鎿<EFBFBD><E98EBF><EFBFBD> 銝餉<E98A9D>嚗𡁏<E59A97><F0A1818F>桐縑<E6A190>胯<EFBFBD><E883AF>ế<EFBFBD>剔<EFBFBD><E58994>栶<EFBFBD><E6A0B6><EFBFBD>蝒<EFBFBD>𠶖<EFBFBD><F0A0B696><EFBFBD><EFBFBD><EFBFBD>蝏<EFBFBD><E89D8F>蝑?<3F>? <20>? <20>婙<EFBFBD><E5A999><EFBFBD> 撅訫<E69285>銵䕘<E98AB5><E49598>峕芋<E5B395>?2摮埈挾霂衣<E99C82>霂<EFBFBD>摯
<0A>? <20>婙<EFBFBD><E5A999><EFBFBD> <20>孵稬<E5ADB5>斗鱏 <20>?撘孵枂<E5ADB5>諹<EFBFBD><E8ABB9>曉<EFBFBD><E69B89><EFBFBD>恣<EFBFBD>?<3F>?<3F>婙<EFBFBD><E5A999><EFBFBD> 摮鞱<E691AE><E99EB1>?嚗𡁜<E59A97>蝑𤤿<E89D91><F0A4A4BF>? <20>鎿<EFBFBD><E98EBF><EFBFBD> 蝏蠘恣<E8A098>∠<EFBFBD>嚗<EFBFBD><E59A97>餉恣/蝥喳<E89DA5>/<2F>㘾膄嚗? <20>鎿<EFBFBD><E98EBF><EFBFBD> PRISMA<4D>㘾膄<E398BE>笔<EFBFBD>蝏蠘恣
<20>鎿<EFBFBD><E98EBF><EFBFBD> Tab<61><62>揢嚗<E68FA2>熙<EFBFBD>?<3F>㘾膄<E398BE>𡑒”嚗? <20>婙<EFBFBD><E5A999><EFBFBD> Excel撖澆枂
7.2 <20>詨<EFBFBD>蝏<EFBFBD>辣
7.2.1 FulltextScreeningWorkbench - 摰⊥瓲撌乩<E6928C><E4B9A9>?
// frontend-v2/src/modules/asl/pages/FulltextScreeningWorkbench.tsx
export const FulltextScreeningWorkbench: React.FC = () => {
const { taskId } = useParams()
const [task, setTask] = useState<ScreeningTask | null>(null)
const [results, setResults] = useState<ScreeningResult[]>([])
const [expandedRows, setExpandedRows] = useState<Set<string>>(new Set())
const [reviewModalVisible, setReviewModalVisible] = useState(false)
const [currentReview, setCurrentReview] = useState<any>(null)
// 頧株砭隞餃𦛚餈𥕦漲
useEffect(() => {
const interval = setInterval(async () => {
const taskData = await fetchTaskProgress(taskId)
setTask(taskData)
if (taskData.status === 'completed' || taskData.status === 'failed') {
clearInterval(interval)
}
}, 2000)
return () => clearInterval(interval)
}, [taskId])
// <20>㰘蝸蝏𤘪<E89D8F>
useEffect(() => {
if (task?.status === 'processing' || task?.status === 'completed') {
fetchTaskResults(taskId).then(setResults)
}
}, [task?.status])
return (
<div className="fulltext-screening-workbench">
{/* 餈𥕦漲<F0A595A6>?*/}
{task?.status === 'processing' && (
<Progress
percent={task.progress}
status="active"
format={() => `${task.processedCount} / ${task.totalCount}`}
/>
)}
{/* PICOS<4F><53><EFBFBD><EFBFBD>X踎嚗<E8B88E>虾<EFBFBD>睃<EFBFBD>嚗?*/}
<PICOSPanel project={task?.project} collapsible />
{/* 銵冽聢<E586BD>硋恣<E7A18B>貊<EFBFBD><E8B28A>?*/}
<ScreeningTable
taskType="fulltext"
results={results}
expandedRows={expandedRows}
onToggleExpand={(id) => toggleExpanded(id)}
onClickJudge={(result, field) => {
setCurrentReview({ result, field })
setReviewModalVisible(true)
}}
onDecisionChange={(resultId, decision) =>
updateDecision(resultId, decision)
}
/>
{/* <20>諹<EFBFBD><E8ABB9>曉<EFBFBD><E69B89><EFBFBD>恣<EFBFBD>交芋<E4BAA4><E88A8B><EFBFBD> */}
<ReviewModal
visible={reviewModalVisible}
result={currentReview?.result}
field={currentReview?.field}
onClose={() => setReviewModalVisible(false)}
/>
</div>
)
}
7.2.2 ScreeningTable - 銵冽聢<E586BD>硋恣<E7A18B>?
// frontend-v2/src/modules/asl/components/screening/ScreeningTable.tsx
interface ScreeningTableProps {
taskType: 'title' | 'fulltext'
results: ScreeningResult[]
expandedRows: Set<string>
onToggleExpand: (id: string) => void
onClickJudge: (result: ScreeningResult, field: string) => void
onDecisionChange: (resultId: string, decision: string) => void
}
export const ScreeningTable: React.FC<ScreeningTableProps> = ({
taskType,
results,
expandedRows,
onToggleExpand,
onClickJudge,
onDecisionChange
}) => {
return (
<div className="screening-table">
<table>
{/* <20>諹<EFBFBD>銵典仍 */}
<thead>
<tr>
<th rowSpan={2}>撅訫<EFBFBD></th>
<th rowSpan={2}><EFBFBD><EFBFBD>讃ID</th>
<th rowSpan={2}><EFBFBD>𠉛弦ID</th>
<th rowSpan={2}><EFBFBD><EFBFBD>讃<EFBFBD>交<EFBFBD></th>
{/* DeepSeek<65>斗鱏 */}
<th colSpan={taskType === 'title' ? 5 : 13}>
DeepSeek <EFBFBD>斗鱏
</th>
{/* Qwen<65>斗鱏 */}
<th colSpan={taskType === 'title' ? 5 : 13}>
Qwen <EFBFBD>斗鱏
</th>
<th rowSpan={2}><EFBFBD>脩<EFBFBD><EFBFBD>嗆<EFBFBD>?/th>
<th rowSpan={2}><EFBFBD><EFBFBD>蝏<EFBFBD><EFBFBD>蝑?/th>
</tr>
<tr>
{/* DeepSeek蝏<6B><E89D8F>嚗<EFBFBD><E59A97><EFBFBD><EFBFBD><EFBFBD>蝑𨥈<E89D91>12摮埈挾嚗?*/}
{taskType === 'fulltext' ? (
<>
<th>F1</th><th>F2</th><th>F3</th><th>F4</th>
<th>F5</th><th>F6</th><th>F7</th><th>F8</th>
<th>F9</th><th>F10</th><th>F11</th><th>F12</th>
<th>蝏栞捏</th>
</>
) : (
// <20><><EFBFBD><EFBFBD>萘<EFBFBD>嚗䥪ICOS
<>
<th>P</th><th>I</th><th>C</th><th>S</th><th>蝏栞捏</th>
</>
)}
{/* Qwen蝏<6E><E89D8F>嚗<EFBFBD><E59A97>銝𠺪<E98A9D> */}
{/* ... */}
</tr>
</thead>
<tbody>
{results.map(result => (
<React.Fragment key={result.id}>
{/* 銝餉<E98A9D> */}
<tr className={result.isConflict ? 'conflict-row' : ''}>
<td onClick={() => onToggleExpand(result.id)}>
{expandedRows.has(result.id) ? '-' : '+'}
</td>
<td>{result.literature.pmid}</td>
<td>{result.literature.studyId}</td>
<td>{result.literature.source}</td>
{/* DeepSeek<65>斗鱏嚗?2摮埈挾嚗?*/}
{taskType === 'fulltext' && (
<>
{[1,2,3,4,5,6,7,8,9,10,11,12].map(i => (
<td
key={`a-f${i}`}
className="judge-cell"
onClick={() => onClickJudge(result, `field${i}`)}
>
{getFieldIcon(result.modelAResult[`field${i}`])}
</td>
))}
<td className={result.modelAResult.overallAssessment.decision === '蝥喳<E89DA5>' ? 'text-green' : 'text-red'}>
{result.modelAResult.overallAssessment.decision}
</td>
</>
)}
{/* Qwen<65>斗鱏嚗<E9B18F><E59A97>銝𠺪<E98A9D> */}
{/* ... */}
{/* <20>脩<EFBFBD><E884A9>嗆<EFBFBD>?*/}
<td>
{result.isConflict ? (
<Badge status="error" text="<22>脩<EFBFBD>" />
) : (
<Badge status="success" text="銝<><E98A9D>? />
)}
</td>
{/* <20><>蝏<EFBFBD><E89D8F>蝑?*/}
<td>
<Select
value={result.finalDecision}
onChange={(value) => onDecisionChange(result.id, value)}
>
<Option value="pending">敺<><E695BA></Option>
<Option value="included">蝥喳<E89DA5></Option>
<Option value="excluded"><3E>㘾膄</Option>
</Select>
</td>
</tr>
{/* 撅訫<E69285>銵䕘<E98AB5>12摮埈挾霂衣<E99C82>霂<EFBFBD>摯嚗?*/}
{expandedRows.has(result.id) && (
<tr className="expanded-row">
<td colSpan={30}>
<div className="grid grid-cols-2 gap-4">
{/* DeepSeek霂衣<E99C82>霂<EFBFBD>摯 */}
<div>
<h4>DeepSeek 霂<>摯</h4>
{Object.entries(result.modelAResult).map(([field, assessment]) => {
if (field.startsWith('field')) {
return (
<div key={field} className="field-assessment">
<strong>{field}:</strong>
<span>{assessment.completeness}</span>
<span className="note">{assessment.note}</span>
</div>
)
}
})}
</div>
{/* Qwen霂衣<E99C82>霂<EFBFBD>摯 */}
<div>
{/* <20>䔶<EFBFBD> */}
</div>
</div>
</td>
</tr>
)}
</React.Fragment>
))}
</tbody>
</table>
</div>
)
}
// 颲<>𨭌<EFBFBD>賣㺭嚗𡁏覔<F0A1818F>宮ompleteness餈𥪜<E9A488><F0A5AA9C>暹<EFBFBD>
function getFieldIcon(assessment: any): ReactNode {
const { completeness } = assessment
switch (completeness) {
case '摰峕㟲':
return <CheckCircle className="text-green-600" />
case '銝滚<E98A9D><E6BB9A>?:
return <ExclamationCircle className="text-yellow-600" />
case '蝻箏仃':
return <CloseCircle className="text-red-600" />
default:
return <QuestionCircle className="text-gray-400" />
}
}
7.2.3 ReviewModal - <20>諹<EFBFBD><E8ABB9>曉<EFBFBD><E69B89><EFBFBD>恣<EFBFBD>?
// frontend-v2/src/modules/asl/components/screening/ReviewModal.tsx
interface ReviewModalProps {
visible: boolean
result: ScreeningResult
field: string // 'field1'-'field12'
onClose: () => void
}
export const ReviewModal: React.FC<ReviewModalProps> = ({
visible,
result,
field,
onClose
}) => {
return (
<Modal
open={visible}
onCancel={onClose}
width="90vw"
footer={null}
title={`<60><><EFBFBD>摰⊥䰻 - ${result?.literature.title}`}
>
<div className="flex h-[80vh]">
{/* 撌虫儒嚗䥪DF<44>冽<EFBFBD> */}
<div className="w-1/2 border-r overflow-y-auto p-4">
<h3 className="font-bold mb-2"><EFBFBD>冽<EFBFBD></h3>
<PDFViewer
pdfUrl={result?.literature.pdfUrl}
highlightEvidence={getFieldEvidence(result, field)}
/>
</div>
{/* <20>喃儒嚗𡁜<E59A97>璅∪<E79285><E288AA>斗鱏 */}
<div className="w-1/2 overflow-y-auto p-4 bg-gray-50">
<h3 className="font-bold mb-4">
12摮埈挾霂<EFBFBD>摯霂行<EFBFBD> - {field}
</h3>
<div className="space-y-6">
{/* DeepSeek<65>斗鱏 */}
<div className="model-panel border rounded-lg p-4 bg-white">
<h4 className="font-bold mb-3">DeepSeek</h4>
<FieldAssessmentDetail
assessment={result?.modelAResult[field]}
field={field}
/>
</div>
{/* Qwen<65>斗鱏 */}
<div className="model-panel border rounded-lg p-4 bg-white">
<h4 className="font-bold mb-3">Qwen</h4>
<FieldAssessmentDetail
assessment={result?.modelBResult[field]}
field={field}
/>
</div>
{/* <20>脩<EFBFBD><E884A9><EFBFBD>扇 */}
{result?.conflictFields?.includes(field) && (
<Alert
type="warning"
message="<22>峕芋<E5B395>见ế<E8A781>凋<EFBFBD>銝<EFBFBD><E98A9D>?
description="霂瑚<EFBFBD>蝏<EFBFBD>瓲撖孵<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>𡁜枂<EFBFBD><EFBFBD>蝏<EFBFBD><EFBFBD>蝑?
/>
)}
</div>
</div>
</div>
</Modal>
)
}
// 摮埈挾霂<E68CBE>摯霂行<E99C82>蝏<EFBFBD>辣
const FieldAssessmentDetail: React.FC<{
assessment: any
field: string
}> = ({ assessment, field }) => {
return (
<div className="field-detail">
<div className="mb-2">
<strong>摮睃銁<EFBFBD>改<EFBFBD></strong>
<Tag color={assessment.present ? 'green' : 'red'}>
{assessment.present ? '摮睃銁' : '銝滚<E98A9D><E6BB9A>?}
</Tag>
</div>
<div className="mb-2">
<strong>摰峕㟲<E5B395>改<EFBFBD></strong>
<Tag color={
assessment.completeness === '摰峕㟲' ? 'green' :
assessment.completeness === '銝滚<EFBFBD><EFBFBD>? ? 'orange' : 'red'
}>
{assessment.completeness}
</Tag>
</div>
{field === 'field9' && (
<div className="mb-2">
<strong><EFBFBD>舀<EFBFBD><EFBFBD>𡝗<EFBFBD>改<EFBFBD></strong>
<Tag color={assessment.extractable ? 'green' : 'red'}>
{assessment.extractable ? '<27>舀<EFBFBD><E88880>? : '銝滚虾<EFBFBD>𣂼<EFBFBD>'}
</Tag>
</div>
)}
<div className="mt-3 p-3 bg-gray-50 rounded">
<strong>霂湔<EFBFBD>嚗?/strong>
<p className="mt-1 text-sm">{assessment.note}</p>
</div>
</div>
)
}
8. 撘<><E69298>烐<EFBFBD><E78390>?
8.1 Week 1嚗𡁻<E59A97>𡁶鍂<F0A181B6>賢<EFBFBD>撅?+ <20>唳旿摨?+ <20>𡒊垢<F0A1928A>詨<EFBFBD>
Day 1嚗?025-11-22 <20>其<EFBFBD>嚗㚁<E59A97><E39A81>𡁶鍂<F0A181B6>賢<EFBFBD>撅?- PDF摮睃<E691AE> <20>?撌脣<E6928C><E884A3>?
**<2A>格<EFBFBD>**嚗𡁜<E59A97>鋆<EFBFBD>緵<EFBFBD>侨DF<44>賢<EFBFBD>銝箇<E98A9D>銝<EFBFBD><E98A9D>滚𦛚<E6BB9A>亙藁
**<2A>𩤃<EFBFBD> <20>滩<EFBFBD>嚗帋<EFBFBD><EFBFBD>舫<EFBFBD><EFBFBD>啣<EFBFBD><EFBFBD>𡢅<EFBFBD><EFBFBD>?*<2A><><EFBFBD><EFBFBD>唳<EFBFBD><E594B3>賢<EFBFBD> <20>?
隞餃𦛚嚗?- [x] <20>𥕦遣 PDFStorageService.ts嚗<EFBFBD><EFBFBD>銝<EFBFBD><EFBFBD>亙藁嚗<EFBFBD><EFBFBD>鋆<EFBFBD>緵<EFBFBD>㗇<EFBFBD><EFBFBD>∴<EFBFBD><EFBFBD>?- [x] 摰䂿緵 DifyPDFStorageAdapter.ts嚗<EFBFBD><EFBFBD><EFBFBD>求ifyClient嚗争<EFBFBD>
- 摰䂿緵
OSSPDFStorageAdapter.ts嚗<EFBFBD><EFBFBD><EFBFBD>辷<EFBFBD><EFBFBD>?- [x] 摰䂿緵PDFStorageFactory.ts嚗<EFBFBD>極<EFBFBD><EFBFBD>掩嚗争<EFBFBD> - <EFBFBD><EFBFBD><EFBFBD><EFBFBD>唳<EFBFBD><EFBFBD>滚𦛚嚗尠<EFBFBD>
- 憭滨鍂
ExtractionClient.ts嚗<EFBFBD>歇摰䂿緵嚗争<EFBFBD> - 憭滨鍂
DifyClient.ts嚗<EFBFBD>歇摰䂿緵嚗争<EFBFBD> - 憭滨鍂
storage嚗<EFBFBD>像<EFBFBD>啣抅蝖<EFBFBD>撅<EFBFBD><EFBFBD><EFBFBD>?- [x] 蝻硋<E89DBB><E7A18B>訫<EFBFBD>瘚贝<E7989A> <20>?- [x] <20>臬<EFBFBD><E887AC>滨蔭嚗?env嚗争<E59A97>
- 憭滨鍂
鈭批枂嚗?- 6銝芣<E98A9D>隞塚<E99A9E>~500銵䔶誨<E494B6><E8AAA8><EFBFBD><EFBFBD><EFBFBD>鉄types<65>𢁾ndex嚗?- 瘚贝<E7989A>閬<EFBFBD><E996AC><EFBFBD>?80% <20>?
**憭滨鍂皜<E98D82><E79A9C>**嚗?- <20>?backend/src/common/document/ExtractionClient.ts
- <EFBFBD>?
backend/src/common/rag/DifyClient.ts - <EFBFBD>?
backend/src/common/storage/ - <EFBFBD>?
extraction_service/嚗㇊ython敺格<EFBFBD><EFBFBD>∴<EFBFBD>
Day 2嚗?025-11-22 <20>其<EFBFBD>嚗㚁<E59A97><E39A81>𡁶鍂<F0A181B6>賢<EFBFBD>撅?- LLM<4C>滚𦛚 <20>?撌脣<E6928C><E884A3>?
**<2A>格<EFBFBD>**嚗𡁜<E59A97><F0A1819C>袂LM憭<4D><E686AD>12摮埈挾<E59F88>滚𦛚
隞餃𦛚嚗?- [x] <20>𥕦遣 LLM12FieldsService.ts <20>?- [x] 摰䂿緵screening璅∪<E79285>Prompt<70>㰘蝸 <20>?- [x] 摰䂿緵extraction璅∪<E79285>Prompt<70>㰘蝸嚗<E89DB8><E59A97><EFBFBD>辷<EFBFBD><E8BEB7>?- [x] <20><><EFBFBD>LLM<4C><4D><EFBFBD><EFBFBD>剁<EFBFBD>憭滨鍂<E6BBA8>唳<EFBFBD>嚗争<E59A97>
- DeepSeek-V3嚗<33>歇摰䂿緵嚗争<E59A97>
- Qwen3-Max嚗<78>歇摰䂿緵嚗争<E59A97>
- LLMFactory嚗<EFBFBD>歇摰䂿緵嚗争<EFBFBD>
- 摰䂿緵蝻枏<EFBFBD>蝑𣇉裦嚗<EFBFBD><EFBFBD><EFBFBD>牢ache<EFBFBD>滚𦛚嚗争<EFBFBD>
- 摰䂿緵<EFBFBD>鞉𧋦霈∠<EFBFBD> <20>?- [x] 蝻硋<E89DBB>Prompt璅⊥踎嚗?2摮埈挾screening嚗争<E59A97> <20>詨<EFBFBD>撌乩<E6928C> <20>?- [x] 蝻硋<E89DBB><E7A18B>訫<EFBFBD>瘚贝<E7989A> <20>?- [x] **憸嘥<E686B8>摰峕<E691B0>**嚗䥪romptBuilder<65>滚𦛚嚗<F0A69B9A>𢆡<EFBFBD><F0A286A1>rompt蝏<74><E89D8F>嚗争<E59A97>
- **憸嘥<E686B8>摰峕<E691B0>**嚗?撅<>SON閫<4E><E996AB>蝑𣇉裦嚗àson-repair<69><72><EFBFBD>嚗争<E59A97>
- **憸嘥<E686B8>摰峕<E691B0>**嚗䥪romise.allSettled<65>峕芋<E5B395>见捆<E8A781>?<3F>?
**憭滨鍂皜<E98D82><E79A9C>**嚗?- <20>?
backend/src/common/llm/adapters/DeepSeekAdapter.ts - <EFBFBD>?
backend/src/common/llm/adapters/QwenAdapter.ts - <EFBFBD>?
backend/src/common/llm/LLMFactory.ts - <EFBFBD>?
backend/src/common/cache/
鈭批枂嚗?- LLM12FieldsService.ts嚗ǚ400銵䕘<EFBFBD>
- 2銝枉rompt<EFBFBD><EFBFBD>辣嚗ǚ500銵䕘<EFBFBD>
- 瘚贝<EFBFBD>閬<EFBFBD><EFBFBD><EFBFBD>?80%
Day 3嚗?025-11-22 <20>其<EFBFBD>嚗㚁<E59A97><E39A81>𡁶鍂<F0A181B6>賢<EFBFBD>撅?- 撉諹<E69289><E8ABB9>滚𦛚 <20>?撌脣<E6928C><E884A3>?
**<2A>格<EFBFBD>**嚗𡁜<E59A97><F0A1819C>鞾<EFBFBD>霂<EFBFBD><E99C82><EFBFBD>∪<EFBFBD><E288AA>脩<EFBFBD>璉<EFBFBD>瘚?
隞餃𦛚嚗?- [x] <20>𥕦遣 MedicalLogicValidator.ts <20>? - [x] 5<>∪龫摮阡<E691AE>餉<EFBFBD>撉諹<E69289>閫<EFBFBD><E996AB> <20>? - [x] safeGetFieldValue摰寥<E691B0><E5AFA5>箏<EFBFBD> <20>?- [x] <20>𥕦遣 EvidenceChainValidator.ts <20>? - [x] 霂<>旿<EFBFBD>曉<EFBFBD><E69B89>湔<EFBFBD>折<EFBFBD>霂?<3F>? - [x] 撘閧鍂<E996A7>踹漲<E8B8B9>䔶<EFBFBD>蝵桅<E89DB5>霂?<3F>? - [x] null/undefined摰匧<E691B0>憭<EFBFBD><E686AD> <20>?- [x] <20>𥕦遣 ConflictDetectionService.ts <20>? - [x] 摰䂿緵screening<6E>脩<EFBFBD>璉<EFBFBD>瘚?<3F>? - [x] 摰䂿緵extraction<6F>脩<EFBFBD>璉<EFBFBD>瘚页<E7989A>憸<EFBFBD><E686B8>嚗争<E59A97>
- <EFBFBD>脩<EFBFBD>銝仿<EFBFBD>蝔见漲<EFBFBD><EFBFBD>漣 <20>? - [x] 憭齿瓲隡睃<E99AA1>蝥扯恣蝞?<3F>? - [x] logger摰寥<E691B0>靽桀<E99DBD> <20>?- [x] 蝻硋<E89DBB><E7A18B>訫<EFBFBD>瘚贝<E7989A> <20>?- [x] <20><><EFBFBD>瘚贝<E7989A>嚗<EFBFBD><E59A97>𡁶鍂<F0A181B6>賢<EFBFBD>撅<EFBFBD>㟲雿橒<E99BBF><E6A992>? - [x] integration-test.ts嚗<73><E59A97><EFBFBD>湔<EFBFBD>霂𤏪<E99C82><F0A48FAA>? - [x] quick-test.ts嚗<73>翰<EFBFBD><E7BFB0><EFBFBD>霂𤏪<E99C82><F0A48FAA>? - [x] cached-result-test.ts嚗<73>捆<EFBFBD>䠷<EFBFBD>霂<EFBFBD><E99C82><EFBFBD>? 鈭批枂嚗?- 3銝芷<E98A9D>霂<EFBFBD><E99C82><EFBFBD>∴<EFBFBD>~1,300銵䔶誨<E494B6><E8AAA8><EFBFBD>
- 3銝芣<EFBFBD>霂閙<EFBFBD>隞塚<EFBFBD>~600銵䔶誨<E494B6><E8AAA8><EFBFBD>
- <EFBFBD>𡁶鍂<EFBFBD>賢<EFBFBD>撅<EFBFBD>瓲敹<EFBFBD><EFBFBD><EFBFBD>鐥<EFBFBD>
**瘜?*嚗鋫syncTaskService撱嗅<E692B1><E59785>蚤ay 4摰䂿緵嚗<E7B7B5>鸌憭<E9B88C><E686AD><EFBFBD>滚𦛚銝哨<E98A9D>
Day 4嚗?025-11-28 <20>典<EFBFBD>嚗㚁<E59A97><E39A81>唳旿摨栞挽霈?+ <20>冽<EFBFBD>憭滨<E686AD>銝𡁜𦛚<F0A1819C>餉<EFBFBD>
**<2A>格<EFBFBD>**嚗𡁜<E59A97><F0A1819C>鞉㺭<E99E89>桀<EFBFBD>餈<EFBFBD>宏<EFBFBD>峕瓲敹<E793B2><E695B9><EFBFBD>⊿<EFBFBD>餉<EFBFBD>
**銝𠰴<E98A9D>隞餃𦛚嚗<F0A69B9A>㺭<EFBFBD>桀<EFBFBD>嚗?*嚗?- [ ] 霈曇恣Prisma Schema
AslFulltextScreeningTask銵? - [ ]AslFulltextScreeningResult銵? - [ ]AslLiterature銵冽凒<E586BD>堆<EFBFBD>PDF摮睃<E691AE>摮埈挾嚗?- [ ] <20>扯<EFBFBD>餈<EFBFBD>宏嚗䫤npx prisma migrate dev --name add_fulltext_screening`- 撉諹<EFBFBD>銵函<EFBFBD><EFBFBD>?
**銝见<E98A9D>隞餃𦛚嚗<F0A69B9A><E59A97><EFBFBD>⊿<EFBFBD>餉<EFBFBD>嚗?*嚗?- [ ] <20>𥕦遣
FulltextScreeningService.tsprocessTask()- 隞餃𦛚憭<F0A69B9A><E686AD><EFBFBD>亙藁screenLiterature()- <20>閧<EFBFBD><E996A7><EFBFBD>讃蝑偦<E89D91>? - [ ]updateTaskProgress()- 餈𥕦漲<F0A595A6>湔鰵completeTask()- 隞餃𦛚摰峕<E691B0>
- <EFBFBD><EFBFBD><EFBFBD><EFBFBD>𡁶鍂<EFBFBD>賢<EFBFBD>撅<EFBFBD><EFBFBD><EFBFBD>? 鈭批枂嚗?- 3銝芣㺭<E88AA3>株”
FulltextScreeningService.ts嚗ǚ600銵䕘<EFBFBD>
Day 5嚗?025-11-29 <20>其<EFBFBD>嚗㚁<E59A97><E39A81>𡒊垢API摰䂿緵
**<2A>格<EFBFBD>**嚗𡁜<E59A97><F0A1819C>𣂼<EFBFBD><F0A382BC><EFBFBD><EFBFBD>蝑𡩅PI嚗?銝芣瓲敹<E793B2>𦻖<EFBFBD><F0A6BB96><EFBFBD>
隞餃𦛚嚗?- [ ] <20>𥕦遣 FulltextScreeningController.ts
createTask()- <20>𥕦遣隞餃𦛚getTaskProgress()- <20>瑕<EFBFBD>餈𥕦漲getTaskResults()- <20>瑕<EFBFBD>蝏𤘪<E89D8F>updateDecision()- 鈭箏極摰⊥瓲<E28AA5>喟<EFBFBD>exportExcel()- 撖澆枂Excel- <EFBFBD>𥕦遣
fulltext-screening.ts頝舐眏 - API瘚贝<EFBFBD>嚗㇊ostman嚗?- [ ] <20>躰秤憭<E7A7A4><E686AD>摰<EFBFBD><E691B0>
鈭批枂嚗?- 5銝服PI<50>亙藁
- API瘚贝<EFBFBD><EFBFBD>朞<EFBFBD>
- <EFBFBD>𡒊垢摰峕<EFBFBD><EFBFBD>?
8.2 Week 2嚗𡁜<E59A97>蝡臬<E89DA1><E887AC>?+ <20>磰<EFBFBD>瘚贝<E7989A>
Day 6嚗?025-12-02 <20>其<EFBFBD>嚗㚁<E59A97><E39A81>滨垢<E6BBA8>詨<EFBFBD>蝏<EFBFBD>辣嚗<E8BEA3><E59A97>嚗?
**<2A>格<EFBFBD>**嚗𡁜<E59A97><F0A1819C>啗挽蝵桅△<E6A185>W<EFBFBD>摰⊥瓲撌乩<E6928C><E4B9A9>啣抅蝖<E68A85>
隞餃𦛚嚗?- [ ] <20>𥕦遣 FulltextScreeningSettings.tsx
- PICOS<EFBFBD><EFBFBD><EFBFBD>撅閧內
- <EFBFBD>冽<EFBFBD><EFBFBD>瑕<EFBFBD>蝞∠<EFBFBD>銵冽聢
- 撘<EFBFBD>憪见<EFBFBD>蝑𥟇<EFBFBD><EFBFBD>?- [ ] <20>𥕦遣
ScreeningTable.tsx嚗<EFBFBD><EFBFBD><EFBFBD>系itle-screening嚗? - [ ] <20>諹<EFBFBD>銵典仍 - 銝餉<EFBFBD>皜脫<EFBFBD>
- 撅訫<EFBFBD>銵峕葡<EFBFBD>? - [ ] <20>斗鱏<E69697>暹<EFBFBD><E69AB9>孵稬
- API<EFBFBD><EFBFBD><EFBFBD>嚗<EFBFBD><EFBFBD>撱箔遙<EFBFBD>∴<EFBFBD>
鈭批枂嚗?- 2銝芷△<E88AB7>Y<EFBFBD>隞?- 銵冽聢蝏<E881A2>辣<EFBFBD>箇<EFBFBD>摰峕<E691B0>
Day 7嚗?025-12-03 <20>其<EFBFBD>嚗㚁<E59A97><E39A81>滨垢<E6BBA8>詨<EFBFBD>蝏<EFBFBD>辣嚗<E8BEA3><E59A97>嚗?
**<2A>格<EFBFBD>**嚗𡁜<E59A97><F0A1819C>𣂼恣<F0A382BC>詨極雿𨅯蝱<F0A885AF>釶DF<44>亦<EFBFBD><E4BAA6>?
隞餃𦛚嚗?- [ ] <20>𥕦遣 FulltextScreeningWorkbench.tsx
- 餈𥕦漲<EFBFBD>? - [ ] 隞餃𦛚<E9A483>嗆<EFBFBD><E59786>蔭霂? - [ ] 蝏𤘪<E89D8F><F0A498AA>㰘蝸
- <EFBFBD>𥕦遣
PDFViewer.tsx嚗ǐeact-pdf嚗? - [ ] PDF皜脫<E79A9C>- 憿菟𢒰蝧駁△
- 霂<EFBFBD>旿擃䀝漁
- <EFBFBD>𥕦遣
ReviewModal.tsx- <EFBFBD>諹<EFBFBD><EFBFBD>曉<EFBFBD>撅<EFBFBD>
- 12摮埈挾霂行<EFBFBD>撅閧內
- <EFBFBD>脩<EFBFBD><EFBFBD><EFBFBD>扇
- API<EFBFBD><EFBFBD><EFBFBD>嚗<EFBFBD>繮<EFBFBD>𤥁<EFBFBD>摨艾<EFBFBD><EFBFBD>繮<EFBFBD>𣇉<EFBFBD><EFBFBD>頣<EFBFBD>
鈭批枂嚗?- 摰⊥瓲撌乩<E6928C><E4B9A9>啣<EFBFBD><E595A3>?- PDF<44>亦<EFBFBD><E4BAA6>典<EFBFBD><E585B8>?- <20>諹<EFBFBD><E8ABB9>暹芋<E69AB9><E88A8B><EFBFBD>摰峕<E691B0>
Day 8嚗?025-12-04 <20>其<EFBFBD>嚗㚁<E59A97><E39A81>滨垢蝏𤘪<E89D8F>憿菟𢒰 + Excel撖澆枂
**<2A>格<EFBFBD>**嚗𡁜<E59A97><F0A1819C>𣂼<EFBFBD>蝑𤤿<E89D91><F0A4A4BF>𣈯△<F0A388AF>?
隞餃𦛚嚗?- [ ] <20>𥕦遣 FulltextScreeningResults.tsx
- 蝏蠘恣<EFBFBD>∠<EFBFBD>
- PRISMA<EFBFBD>㘾膄蝏蠘恣<EFBFBD>曇”
- Tab<EFBFBD><EFBFBD>揢嚗<EFBFBD>熙<EFBFBD>?<3F>㘾膄嚗? - [ ] 蝏𤘪<E89D8F><F0A498AA>𡑒”
- <EFBFBD>𥕦遣
ExcelExporter.ts- <EFBFBD>玺heet撖澆枂嚗<EFBFBD>熙<EFBFBD>?<3F>㘾膄嚗? - [ ] PRISMA蝏蠘恣Sheet
- API<EFBFBD><EFBFBD><EFBFBD>嚗<EFBFBD>紡<EFBFBD>慟xcel嚗?- [ ] 頝舐眏<E88890>滨蔭
鈭批枂嚗?- 蝏𤘪<E89D8F>憿菟𢒰摰峕<E691B0>
- Excel撖澆枂摰峕<EFBFBD>
- <EFBFBD>滨垢摰峕<EFBFBD><EFBFBD>?
Day 9嚗?025-12-05 <20>典<EFBFBD>嚗㚁<E59A97><E39A81>滚<EFBFBD>蝡航<E89DA1>靚?
**<2A>格<EFBFBD>**嚗𡁜<E59A97><F0A1819C>湔<EFBFBD>蝔𧢲<E89D94>霂? 隞餃𦛚嚗?- [ ] <20><><EFBFBD>瘚贝<E7989A><E8B49D>唳旿嚗?0蝭<30><E89DAD><EFBFBD>殷<EFBFBD>
- 摰峕㟲瘚<EFBFBD><EFBFBD>瘚贝<EFBFBD>
- <EFBFBD>𥕦遣隞餃𦛚
- PDF銝𠹺<EFBFBD>嚗<EFBFBD><EFBFBD>霂䉪ify<EFBFBD><EFBFBD><EFBFBD>嚗? - [ ] 隞餃𦛚<E9A483>扯<EFBFBD>嚗<EFBFBD><E59A97>霂訫<E99C82>璅∪<E79285>靚<EFBFBD>鍂嚗? - [ ] 餈𥕦漲<F0A595A6>湔鰵
- 摰⊥瓲撌乩<EFBFBD><EFBFBD>堆<EFBFBD>瘚贝<EFBFBD><EFBFBD>脩<EFBFBD>璉<EFBFBD>瘚页<EFBFBD>
- 鈭箏極<EFBFBD>喟<EFBFBD>
- 蝏𤘪<EFBFBD>撖澆枂
- Bug靽桀<EFBFBD>
- <EFBFBD>扯<EFBFBD>隡睃<EFBFBD>嚗<EFBFBD><EFBFBD>摮塩<EFBFBD><EFBFBD>僎<EFBFBD>𡢅<EFBFBD>
鈭批枂嚗?- 瘚贝<E7989A><E8B49D>亙<EFBFBD>
- Bug<EFBFBD>𡑒”
Day 10嚗?025-12-06 <20>其<EFBFBD>嚗㚁<E59A97>隡睃<E99AA1> + <20><>﹝
**<2A>格<EFBFBD>**嚗帋誨<E5B88B><E8AAA8><EFBFBD><EFBFBD>硋<EFBFBD><E7A18B><EFBFBD>﹝摰<EFB99D><E691B0>
隞餃𦛚嚗?- [ ] 隞<><E99A9E>隡睃<E99AA1>
- <EFBFBD>躰秤憭<EFBFBD><EFBFBD>摰<EFBFBD><EFBFBD>
- <EFBFBD>亙<EFBFBD>颲枏枂隡睃<EFBFBD>
- 蝐餃<EFBFBD>摰帋<EFBFBD>摰<EFBFBD><EFBFBD>
- UI/UX隡睃<E99AA1>
- <EFBFBD>㰘蝸<EFBFBD>嗆<EFBFBD>? - [ ] 蝛箇𠶖<E7AE87>? - [ ] <20>躰秤<E8BAB0>鞟內
- <EFBFBD><EFBFBD>﹝蝻硋<EFBFBD>
- API<EFBFBD><EFBFBD>﹝
- <EFBFBD>冽<EFBFBD>雿輻鍂<EFBFBD>见<EFBFBD>
- 撘<EFBFBD><EFBFBD>𤏸<EFBFBD><EFBFBD><EFBFBD>獢?- [ ] 隞<><E99A9E>Review
鈭批枂嚗?- 隞<><E99A9E>韐券<E99F90><E588B8>𣂼<EFBFBD>
- 摰峕㟲<EFBFBD><EFBFBD>﹝
- <EFBFBD>冽<EFBFBD>憭滨<EFBFBD>MVP摰峕<EFBFBD><EFBFBD>?
9. <20><><EFBFBD>航<EFBFBD><E888AA>?
9.1 <20>詨<EFBFBD><E8A9A8><EFBFBD><EFBFBD>舀<EFBFBD><E88880>?
9.1.1 LLM<4C>鞉𧋦<E99E89>批<EFBFBD>
**<2A>桅<EFBFBD>**嚗𡁜<E59A97>璅∪<E79285><E288AA>冽<EFBFBD>蝑偦<E89D91>厰<EFBFBD>閬<EFBFBD>綉<EFBFBD>嗆<EFBFBD><E59786>穿<EFBFBD>DeepSeek-V3 + Qwen3-Max嚗? **閫<><E996AB><EFBFBD>寞<EFBFBD>**嚗?1. 銝厩漣蝻枏<EFBFBD>蝑𣇉裦
- L1: <20><><EFBFBD>蝻枏<E89DBB>嚗<EFBFBD><E59A97><EFBFBD>𤑳㴓憓<E3B493><E68693>
- L2: Redis蝻枏<E89DBB>嚗<EFBFBD><E59A97>鈭抒㴓憓<E3B493><E68693>
- L3: <20>唳旿摨梶<E691A8>摮矋<E691AE>瘞訾<E7989E>摮睃<E691AE>嚗?
-
蝏煺<EFBFBD>璅∪<EFBFBD><EFBFBD>滨蔭嚗㇈VP<EFBFBD>嗆挾嚗? ```typescript // MVP<56>嗆挾嚗𡁶<E59A97>銝<EFBFBD>雿輻鍂<E8BCBB>鞉𧋦<E99E89>见末<E8A781><E69CAB>芋<EFBFBD>讠<EFBFBD><E8AEA0>? const DEFAULT_MODELS = { modelA: 'deepseek-v3', // 瞼1/<2F>曆<EFBFBD>tokens嚗峕<E59A97>找遠瘥娍<E798A5>擃? modelB: 'qwen-max' // 瞼4/<2F>曆<EFBFBD>tokens嚗䔶葉<E494B6><E89189><EFBFBD>憟? }
// <20>鞉𧋦撖寞<E69296>嚗? // - DeepSeek-V3 + Qwen-Max: <20><>?.05/蝭<><E89DAD>10K tokens嚗? // - GPT-4o + Claude-4.5: <20><>?.2/蝭<><E89DAD>10K tokens嚗? // <20><><EFBFBD><EFBFBD>鞉𧋦嚗?4<>?潃? ```
-
<EFBFBD>寥<EFBFBD>憸<EFBFBD><EFBFBD>
- 銝<EFBFBD>甈⊥<EFBFBD>扯繮<EFBFBD>硋<EFBFBD>蝭<EFBFBD><EFBFBD><EFBFBD>桀<EFBFBD><EFBFBD>? - <20>誩<EFBFBD>API靚<49>鍂甈⊥㺭
9.1.2 Serverless頞<73>𧒄<EFBFBD>桅<EFBFBD>
**<2A>桅<EFBFBD>**嚗?0蝭<30><E89DAD><EFBFBD>桃<EFBFBD><E6A183>匧虾<E58CA7>賡<EFBFBD>閬?5-20<32><30><EFBFBD>嚗諹<E59A97>餈𦺋erverless<73>𣂼<EFBFBD>嚗?0蝘𡜐<E89D98>
**閫<><E996AB><EFBFBD>寞<EFBFBD>**嚗?- <20>?**撘<>郊隞餃𦛚璅∪<E79285>**嚗<>歇摰䂿緵嚗? - <20>𥕦遣隞餃𦛚蝡见朖餈𥪜<E9A488>嚗?1蝘𡜐<E89D98>
- <EFBFBD>𤾸蝱撘<EFBFBD>郊憭<EFBFBD><EFBFBD>
- <EFBFBD>滨垢頧株砭餈𥕦漲
9.1.3 PDF憭<46><E686AD><EFBFBD>扯<EFBFBD>
**<2A>桅<EFBFBD>**嚗䥪DF<44>𣂼<EFBFBD><F0A382BC>航<EFBFBD>颲<EFBFBD><E9A2B2>嚗𠃊ougat<61><74>閬?0蝘?蝭<><E89DAD>
**閫<><E996AB><EFBFBD>寞<EFBFBD>**嚗?1. <EFBFBD>箄<EFBFBD><EFBFBD>𣂼<EFBFBD>蝑𣇉裦
// 霂剛<E99C82>璉<EFBFBD>瘚?<3F>?<3F>㗇𥋘<E39787>𣂼<EFBFBD><F0A382BC>寞<EFBFBD>
if (language === 'chinese') {
method = 'pymupdf' // 敹恍<E695B9><E6818D><EFBFBD>2蝘?蝭<><E89DAD>
} else {
try {
method = 'nougat' // 擃䁅捶<E48185>𧶏<EFBFBD>40蝘?蝭<><E89DAD>
} catch {
method = 'pymupdf' // <20>滨漣
}
}
- <EFBFBD>𣂼<EFBFBD><EFBFBD>寥<EFBFBD><EFBFBD>𣂼<EFBFBD>
- <EFBFBD><EFBFBD><EFBFBD><EFBFBD>萘<EFBFBD>摰峕<EFBFBD><EFBFBD>𠬍<EFBFBD><EFBFBD>𤾸蝱<EFBFBD>芸𢆡<EFBFBD>𣂼<EFBFBD><EFBFBD>冽<EFBFBD>
- <EFBFBD>冽<EFBFBD>憭滨<EFBFBD><EFBFBD>嗥凒<EFBFBD>乩蝙<EFBFBD>?
9.2 <20>唳旿銝<E697BF><E98A9D>湔<EFBFBD>找<EFBFBD>霂?
9.2.1 鈭见𦛚憭<F0A69B9A><E686AD>
// 雿輻鍂Prisma鈭见𦛚靽肽<E99DBD>銝<EFBFBD><E98A9D>湔<EFBFBD>?await prisma.$transaction(async (tx) => {
// 1. <20>𥕦遣蝑偦<E89D91>厩<EFBFBD><E58EA9>? const result = await tx.aslFulltextScreeningResult.create({ ... })
// 2. <20>湔鰵<E6B994><E9B0B5>讃<EFBFBD>嗆挾
await tx.aslLiterature.update({
where: { id: literatureId },
data: { stage: 'fulltext_screened' }
})
// 3. <20>湔鰵隞餃𦛚蝏蠘恣
await tx.aslFulltextScreeningTask.update({
where: { id: taskId },
data: { processedCount: { increment: 1 } }
})
})
9.2.2 憭梯揖<E6A2AF>滩<EFBFBD>
// <20><>㺭<EFBFBD><E3BAAD><EFBFBD>輸<EFBFBD>霂?async function retryWithBackoff<T>(
fn: () => Promise<T>,
maxRetries: number = 3
): Promise<T> {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn()
} catch (error) {
if (i === maxRetries - 1) throw error
const delay = Math.pow(2, i) * 1000 // 1s, 2s, 4s
await sleep(delay)
}
}
}
9.3 <20>滨垢<E6BBA8>扯<EFBFBD>隡睃<E99AA1>
9.3.1 <20>𡁏<EFBFBD>皛𡁜𢆡
// 雿輻鍂react-window憭<77><E686AD>憭折<E686AD><E68A98><EFBFBD>讃
import { FixedSizeList } from 'react-window'
<FixedSizeList
height={600}
itemCount={results.length}
itemSize={100}
width="100%"
>
{({ index, style }) => (
<div style={style}>
<ResultRow result={results[index]} />
</div>
)}
</FixedSizeList>
9.3.2 <20>鍦<EFBFBD>頧?
// PDF<44>亦<EFBFBD><E4BAA6>冽<EFBFBD><E586BD>㰘蝸
const PDFViewer = lazy(() => import('./PDFViewer'))
<Suspense fallback={<Spin />}>
<PDFViewer pdfUrl={url} />
</Suspense>
10. 憌𡡞埯銝擧釣<E693A7>譍<EFBFBD>憿?
10.1 <20><><EFBFBD>舫<EFBFBD><E888AB>?
| 憌𡡞埯 | 蝑厩漣 | 蝻栞圾<EFBFBD>芣鴌 |
|---|---|---|
| Dify摮睃<EFBFBD><EFBFBD>𣂼<EFBFBD> | <EFBFBD>椬 銝? | <EFBFBD><EFBFBD><EFBFBD><EFBFBD>冽芋撘誯<EFBFBD><EFBFBD>両SS餈<EFBFBD>宏 |
| *LLM<EFBFBD>鞉𧋦頞<EFBFBD><EFBFBD>蝞? | <EFBFBD>椬 銝? | 銝厩漣蝻枏<EFBFBD> + <20>箄<EFBFBD>璅∪<E79285><E288AA>㗇𥋘 |
| Python敺格<EFBFBD><EFBFBD>∩<EFBFBD>蝔喳<EFBFBD> | <EFBFBD>椬 銝? | <EFBFBD>滩<EFBFBD><EFBFBD>箏<EFBFBD> + <20>滨漣蝑𣇉裦 |
| <EFBFBD>滨垢PDF皜脫<EFBFBD><EFBFBD>扯<EFBFBD> | <EFBFBD>叚 雿? | react-pdf + <20>𡁏<EFBFBD>皛𡁜𢆡 |
10.2 撘<><E69298>烐釣<E78390>譍<EFBFBD>憿?
10.2.1 鈭穃<E988AD><E7A983>蠘<EFBFBD><E8A098>?
// <20>?甇<>&嚗帋蝙<E5B88B>典像<E585B8>唳<EFBFBD><E594B3>?import { storage, logger, cache, jobQueue } from '@/common'
// <20>?<3F>躰秤嚗𡁏𧋦<F0A1818F>唳<EFBFBD>隞嗅<E99A9E><E59785>?fs.writeFileSync('./uploads/file.pdf', buffer) // 蝳<>迫嚗?```
#### 10.2.2 <EFBFBD>唳旿摨栞挽霈?
```prisma
// <20>?甇<>&嚗𡁏<E59A97>摰锭chema
model AslFulltextScreeningTask {
@@schema("asl_schema") // 敹<>◆<EFBFBD><E29786><EFBFBD>
// ...
}
// <20>?<3F>躰秤嚗帋<E59A97><E5B88B><EFBFBD><EFBFBD>Schema
model FulltextScreeningTask {
// 隡𡁏𦆮<F0A1818F>郡ublic嚗諹<E59A97><E8ABB9>漤<EFBFBD>蝳餃<E89DB3><E9A483>?}
10.2.3 Git<69>𣂷漱閫<E6BCB1><E996AB>
# <20>?甇<>&嚗𡁜<E59A97><F0A1819C>𣂼<EFBFBD><F0A382BC>賢<EFBFBD>蝏煺<E89D8F><E785BA>𣂷漱
git commit -m "feat(asl): 摰峕<E691B0><E5B395>冽<EFBFBD>憭滨<E686AD><E6BBA8>蠘<EFBFBD>
- 摰䂿緵<E482BF>𡁶鍂<F0A181B6>賢<EFBFBD>撅<EFBFBD><E69285>PDF摮睃<E691AE><E79D83><EFBFBD>LM<4C>滚𦛚<E6BB9A><F0A69B9A><EFBFBD>蝒<EFBFBD><E89D92>瘚页<E7989A>
- 摰䂿緵<E482BF>冽<EFBFBD>憭滨<E686AD>銝𡁜𦛚<F0A1819C>餉<EFBFBD>
- 摰峕<E691B0><E5B395>滨垢摰⊥瓲撌乩<E6928C><E4B9A9>?- 瘛餃<E7989B>Excel撖澆枂<E6BE86>蠘<EFBFBD>
Tested: 摰峕㟲瘚<E39FB2><E7989A>瘚贝<E7989A><E8B49D>朞<EFBFBD>"
# <20>?<3F>躰秤嚗𡁻<E59A97>蝜<EFBFBD><E89D9C><EFBFBD><EFBFBD><EFBFBD><EFBFBD>𣂷漱
git commit -m "fix bug" # 瘥𤩺㺿銝<E3BABF>甈∪停<E288AA>𣂷漱
10.3 <20>芣䔉餈<E49489>宏瘜冽<E7989C>鈭钅★
10.3.1 Dify <20>?OSS餈<53>宏
# <20>芷<EFBFBD>靽格㺿<E6A0BC>臬<EFBFBD><E887AC>㗛<EFBFBD>
PDF_STORAGE_TYPE=oss # 隞𥮴ify<66>嫣蛹oss
# 銝𡁜𦛚隞<F0A69B9A><E99A9E><EFBFBD>嗆㺿<E59786>兩<EFBFBD>
# <20>唳旿摨枏<E691A8>畾萄<E795BE>摰嫖<E691B0>嚗īdfStorageType: 'dify' or 'oss'嚗?```
#### 10.3.2 <20>冽<EFBFBD><E586BD>𣂼<EFBFBD>璅∪<E79285>憭滨鍂
```typescript
// <20>冽<EFBFBD><E586BD>𣂼<EFBFBD>璅∪<E79285><E288AA>臭誑<E887AD>湔𦻖憭滨鍂<E6BBA8>𡁶鍂<F0A181B6>賢<EFBFBD>撅?import {
PDFStorageService, // <20>?憭滨鍂
LLM12FieldsService, // <20>?憭滨鍂嚗<E98D82><E59A97><EFBFBD>W<EFBFBD>extraction璅∪<E79285>嚗? ConflictDetectionService, // <20>?憭滨鍂
AsyncTaskService // <20>?憭滨鍂
} from '@/modules/asl/common/services'
// <20>芷<EFBFBD>撘<EFBFBD><E69298>𦦨xtraction<6F>孵<EFBFBD><E5ADB5><EFBFBD><EFBFBD><EFBFBD>⊿<EFBFBD>餉<EFBFBD>
export class DataExtractionService {
// 靚<>鍂<EFBFBD>𡁶鍂<F0A181B6>滚𦛚
private llmService = new LLM12FieldsService()
async extractLiterature(literatureId: string) {
// 雿輻鍂extraction璅∪<E79285>
const result = await this.llmService.process12Fields(
LLM12FieldsMode.EXTRACTION, // <20>?<3F><>揢璅∪<E79285>
'gpt-4o',
fullText,
context
)
}
}
<EFBFBD><EFBFBD><EFBFBD>
A. <20>詨<EFBFBD><E8A9A8><EFBFBD>﹝
| <EFBFBD><EFBFBD>﹝ | 頝臬<EFBFBD> | 霂湔<EFBFBD> |
|---|---|---|
| <EFBFBD>冽<EFBFBD>憭滨<EFBFBD><EFBFBD><EFBFBD>瘙? | 01-<2D><>瘙<EFBFBD><E79899><EFBFBD>?03-<2D>冽<EFBFBD>憭滨<E686AD><E6BBA8><EFBFBD>瘙<EFBFBD>祕餈?md |
<EFBFBD>蠘<EFBFBD><EFBFBD><EFBFBD>瘙? |
| 12摮埈挾璅⊥踎 | 01-<2D><>瘙<EFBFBD><E79899><EFBFBD>?<3F>冽<EFBFBD>憭滨<E686AD><E6BBA8>𠰴<EFBFBD><F0A0B0B4><EFBFBD><EFBFBD><EFBFBD>𡝗芋<F0A19D97>?txt |
敺芾<EFBFBD><EFBFBD>餃郎璅⊥踎 |
| UI<EFBFBD>笔<EFBFBD> | 03-UI霈曇恣/AI<41>箄<EFBFBD><E7AE84><EFBFBD>讃-<2D>冽<EFBFBD>憭滨<E686AD>.html |
<EFBFBD>滨垢<EFBFBD>笔<EFBFBD> |
| 鈭穃<EFBFBD><EFBFBD>蠘<EFBFBD><EFBFBD>? | docs/04-撘<><E69298>𤏸<EFBFBD><F0A48FB8>?08-鈭穃<E988AD><E7A983>笔<EFBFBD><E7AC94>𤏸<EFBFBD><F0A48FB8>?md |
敹<EFBFBD>粉 |
| 蝟餌<EFBFBD><EFBFBD>嗆<EFBFBD> | docs/00-蝟餌<E89D9F><E9A48C>颱<EFBFBD>霈曇恣/00-蝟餌<E89D9F>敶枏<E695B6><E69E8F>嗆<EFBFBD><E59786><EFBFBD>撘<EFBFBD><E69298>烐<EFBFBD><E78390>?md |
敹<EFBFBD>粉 |
B. <20>臬<EFBFBD><E887AC>滨蔭蝷箔<E89DB7>
# .env.development
# PDF摮睃<E691AE>嚗㇈VP<56>嗆挾雿輻鍂Dify嚗?PDF_STORAGE_TYPE=dify
DIFY_API_KEY=app-xxx
DIFY_BASE_URL=http://localhost:5001/v1
DIFY_ASL_DATASET_ID=dataset-xxx
# Python敺格<E695BA><E6A0BC>?EXTRACTION_SERVICE_URL=http://localhost:8000
# LLM<4C>滨蔭
CLOSEAI_API_KEY=sk-xxx
CLOSEAI_OPENAI_BASE_URL=https://api.openai-proxy.org/v1
CLOSEAI_CLAUDE_BASE_URL=https://api.openai-proxy.org/anthropic
# 蝻枏<E89DBB><E69E8F>滨蔭
CACHE_TYPE=memory # memory/redis
REDIS_URL=redis://localhost:6379
# <20>唳旿摨?DATABASE_URL=postgresql://user:pass@localhost:5432/asl_db
C. 瘚贝<E7989A><E8B49D>唳旿<E594B3><E697BF><EFBFBD>
-- <20><><EFBFBD>30蝭<30><E89DAD>霂閙<E99C82><E99699>?INSERT INTO asl_schema."AslLiterature" (
id, project_id, pmid, title, abstract,
has_pdf, pdf_storage_type, pdf_storage_ref, pdf_status,
full_text, full_text_token_count, stage
) VALUES
('lit-001', 'proj-123', 'PMID12345', '...', '...',
true, 'dify', 'doc-001', 'ready',
'<27>冽<EFBFBD><E586BD><EFBFBD>捆...', 8500, 'pdf_acquired'),
-- ... 29<32>∟扇敶?```
---
**<EFBFBD><EFBFBD>﹝蝏湔擪<EFBFBD><EFBFBD><EFBFBD>** ASL撘<EFBFBD><EFBFBD>穃𣪧<EFBFBD>?
**<EFBFBD><EFBFBD><EFBFBD>擧凒<EFBFBD>堆<EFBFBD>** 2025-11-22
**<EFBFBD><EFBFBD>﹝<EFBFBD>嗆<EFBFBD><EFBFBD><EFBFBD>** <EFBFBD>?撌脣<EFBFBD><EFBFBD>琜<EFBFBD>敺<EFBFBD><EFBFBD><EFBFBD>?
**<EFBFBD><EFBFBD> <EFBFBD><EFBFBD>𧋦<EFBFBD><EFBFBD>蟮嚗?*
- V1.0 (2025-11-22): <EFBFBD>嘥<EFBFBD><EFBFBD><EFBFBD>𧋦嚗<EFBFBD><EFBFBD><EFBFBD>游<EFBFBD><EFBFBD>𤏸恣<EFBFBD>?