Files
AIclinicalresearch/docs/03-业务模块/ASL-AI智能文献/04-开发计划/04-全文复筛开发计划.md
HaHafeng 1b53ab9d52 feat(aia): Complete AIA V2.0 with universal streaming capabilities
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%)
2026-01-14 19:15:01 +08:00

71 KiB
Raw Blame History

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>

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><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>踎嚗<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.ts
    • processTask() - 隞餃𦛚憭<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><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><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>嚗?
  1. 蝏煺<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<>?潃? ```

  2. <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>滨漣
  }
}
  1. <EFBFBD>𣂼<EFBFBD><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><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>?