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

2187 lines
71 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# AI<41><EFBFBD><E7AE84><EFBFBD>讃 - <20><EFBFBD>憭滨<E686AD><EFBFBD><E69298>𤏸恣<F0A48FB8>?
> **<2A><><EFBFBD><EFB99D>𧋦嚗?* V1.2
> **<2A>𥕦遣<F0A595A6><EFBFBD>嚗?* 2025-11-22
> **<2A><><EFBFBD>擧凒<E693A7><EFBFBD>** 2025-11-23
> **<2A><><EFBFBD>嗆挾嚗?* MVP<56>嗆挾
> **憸<>恣撌交<E6928C>嚗?* 2<>?
> **蝏湔擪<E6B994><E693AA><EFBFBD>** ASL撘<4C><E69298>穃𣪧<E7A983>?
---
## <20><><><E69298>𤏸<EFBFBD>摨行<E691A8>閫?
**敶枏<E695B6><E69E8F><EFBFBD>?*嚗𡄻<E59A97>?Day 1-5 撌脣<E6928C><E884A3><EFBFBD><E7909C>𡒊垢<F0A1928A><EFBFBD>摰峕<E691B0>嚗㚁<E59A97><EFBFBD><E695BA>蝡臬<E89DA1><E887AC>?
| <20>嗆挾 | <20>園𡢿 | <20><EFBFBD>?| 摰峕<E691B0>摨?|
|------|------|------|---------|
| **Week 1** | 2025-11-22 ~ 2025-11-23 | <20>?撌脣<E6928C><E884A3>?| 100% |
| - Day 1: PDF摮睃<E691AE><E79D83>滚𦛚 | 2025-11-22 | <20>?撌脣<E6928C><E884A3>?| 100% |
| - Day 2: LLM 12摮埈挾<E59F88>滚𦛚 | 2025-11-22 | <20>?撌脣<E6928C><E884A3>?| 100% |
| - Day 3: 撉諹<E69289><E8ABB9>滚𦛚 | 2025-11-22 | <20>?撌脣<E6928C><E884A3>?| 100% |
| - Day 4銝𠰴<E98A9D>: <20>唳旿摨栞挽霈∩<E99C88><EFBFBD>宏 | 2025-11-23 | <20>?撌脣<E6928C><E884A3>?| 100% |
| - Day 4銝见<E98A9D>: <20><EFBFBD><E5ADB5><EFBFBD><EFBFBD><EFBFBD>?| 2025-11-23 | <20>?撌脣<E6928C><E884A3>?| 100% |
| - Day 5: API撘<49><E69298>?| 2025-11-23 | <20>?撌脣<E6928C><E884A3>?| 100% |
| **Week 2** | 2025-11-24 ~ 2025-11-27 | <20>?敺<><E695BA>憪?| 0% |
| - Day 6-7: <20>滨垢撘<E59EA2><E69298>?| 敺<><E695BA>憪?| <20>?敺<><E695BA>憪?| 0% |
| - Day 8: <20><EFBFBD>蝡航<E89DA1><EFBFBD><E99D9A>霂?| 敺<><E695BA>憪?| <20>?敺<><E695BA>憪?| 0% |
**撌脣<E6928C><E884A3>鞉瓲敹<E793B2><E695B9><EFBFBD>?*嚗?- <20>?PDF摮睃<E691AE>銝擧<E98A9D><E693A7>𡝗<EFBFBD><F0A19D97><EFBFBD><E288B4><EFBFBD><EFBFBD><EFBFBD><E69285>
- <20>?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>
- <20>?霂<><EFBFBD><EFBFBD><EFBFBD>
- <20>?<3F><EFBFBD><EFBFBD>瘚𧢲<E7989A><F0A7A2B2>?- <20>?<3F><><EFBFBD>瘚贝<E7989A><EFBFBD>
- <20>?<3F>唳旿摨廍chema霈曇恣嚗?撘㰘”嚗?- <20>?<3F>唳旿摨𤘪<E691A8><F0A498AA><EFBFBD>蝘餃<E89D98><E9A483>?- <20>?FulltextScreeningService嚗<65>鸌憭<E9B88C><E686AD><EFBFBD>滚𦛚嚗?- <20>?5銝芣瓲敹<E793B2>PI<50>亙藁
- <20>?Excel撖澆枂<E6BE86>滚𦛚嚗?銝杵heet嚗?- <20>?Zod<6F><64>㺭撉諹<E69289>
- <20>?REST Client瘚贝<E7989A><E8B49D><EFBFBD>嚗?1銝迎<E98A9D>
**銝衤<E98A9D>甇?*嚗鋽ay 6 <20>滨垢UI撘<49><E69298>?
---
## <20><> <20><EFBFBD>
- [1. 憿寧𤌍璁<F0A48C8D>](#1-憿寧𤌍璁<F0A48C8D>)
- [2. <20><EFBFBD>霈曇恣](#2-<2D><EFBFBD>霈曇恣)
- [3. <20>𡁶鍂<F0A181B6><EFBFBD><EFBFBD>挽霈∴<E99C88><E288B4><EFBFBD><E887AC><EFBFBD>](#3-<2D>𡁶鍂<F0A181B6><EFBFBD><EFBFBD>挽霈虾憭滨鍂)
- [4. <20>唳旿摨栞挽霈(#4-<2D>唳旿摨栞挽霈?
- [5. API霈曇恣](#5-api霈曇恣)
- [6. <20><EFBFBD>憭滨<E686AD>銝𡁜𦛚撅<F0A69B9A>挽霈(#6-<2D><EFBFBD>憭滨<E686AD>銝𡁜𦛚撅<F0A69B9A>挽霈?
- [7. <20>滨垢霈曇恣](#7-<2D>滨垢霈曇恣)
- [8. 撘<><E69298><EFBFBD><E78390>篏(#8-撘<><E69298><EFBFBD><E78390>?
- [9. <20><><EFBFBD><EFBFBD><E888AA>鉛(#9-<2D><><EFBFBD><EFBFBD><E888AA>?
- [10. 憌𡡞埯銝擧釣<E693A7><EFBFBD>憿鉛(#10-憌𡡞埯銝擧釣<E693A7><EFBFBD>憿?
---
## 1. 憿寧𤌍璁<F0A48C8D>
### 1.1 <20><EFBFBD>摰帋<E691B0>
**<EFBFBD><EFBFBD>憭滨<EFBFBD>**<2A>舐頂蝏蠘<E89D8F>隞?Meta<74><61><EFBFBD><EFBFBD><EFBFBD>洵鈭屸𧫴畾萇<E795BE><E89087><EFBFBD>撖寞<E69296>憸䀹<E686B8><EFBFBD><E996AC>蝑𥕦<E89D91>"<22>脲郊蝥喳<E89DA5>"<22><><EFBFBD><EFBFBD><EFBFBD><E6AEB7><EFBFBD>**<2A><EFBFBD><E586BD><EFBFBD>捆**餈𥡝<E9A488>銝交聢<E4BAA4><E881A2><EFBFBD>甈∠<E79488><E288A0><EFBFBD>?
### 1.2 <20><EFBFBD>隞瑕<E99A9E>?
| 蝏游漲 | 霂湔<E99C82> |
|------|------|
| **<EFBFBD><EFBFBD>** | <20>斗鱏<E69697><E9B18F><EFBFBD>唳旿<E594B3>?*摰峕㟲<E5B395><EFBFBD><E689B9>舐鍂<E88890>?*嚗𣬚摰𡁏<E691B0><EFBFBD><EFBFBD>?|
| **靘脲旿** | **12摮埈挾璅⊥踎**嚗<>抅鈭焾ochrane RoB 2.0<EFBFBD><EFBFBD><EFBFBD>嚗?|
| **颲枏<E9A2B2>** | <20><><EFBFBD><EFBFBD><EFBFBD><E48185><EFBFBD><E89098>?<3F>脲郊蝥喳<E89DA5>"<22><>讃嚗<E8AE83><EFBFBD><EFBFBD><E79195><EFBFBD>PDF嚗?|
| **颲枏枂** | <20><><EFBFBD><EFBFBD><EFBFBD>銵?+ <20>㘾膄<E398BE>𡑒” + PRISMA蝏蠘恣 + 摰峕㟲霂<E39FB2><EFBFBD>?|
| **<EFBFBD>𡒊賒** | <20><><EFBFBD><EFBFBD><EFBFBD><E4BAA6><EFBFBD>讃餈𥕦<E9A488>"<22><EFBFBD><E586BD>唳旿<E594B3>𣂼<EFBFBD>"<22>嗆挾 |
| **韐券<E99F90><E588B8><EFBFBD><EFBFBD>** | <20><><EFBFBD><EFBC86>竉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>
```
**<EFBFBD><EFBFBD>憭滨<EFBFBD><EFBFBD><EFBFBD><EFBFBD>?*嚗朞<E59A97>隡啗<E99AA1>12銝芸<E98A9D>畾萇<E795BE>**摮睃銁<E79D83><EFBFBD><E689BC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E689BC><EFBFBD>𣂼<EFBFBD><F0A382BC>?*嚗<>抅鈭?*Cochrane<6E><EFBFBD>𡁻<EFBFBD><F0A181BB><EFBFBD>隡唳<E99AA1><E594B3>?*<2A>斗鱏<E69697>臬炏蝥喳<E89DA5><E596B3>?
**<EFBFBD>喲睸摮埈挾<EFBFBD><EFBFBD><EFBFBD>摯**嚗<><E59A97><EFBFBD><EFBFBD>ochrane RoB 2.0嚗㚁<EFBFBD>
- <20>𤩺㦤<F0A4A9BA>𡝗䲮瘜𤏪<E7989C>摨誩<E691A8><E8AAA9><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E6BCA4>𧶏<EFBFBD>
- <20><EFBFBD><EFBFBD><EFBFBD>脣笆鞊<E99E8A><EFBD9E><EFBFBD>脫䲮瘜𤏪<E7989C>
- 蝏𤘪<E89D8F>摰峕㟲<E5B395><EFBFBD>憭梯挪<E6A2AF><E68CAA><EFBFBD><EFBFBD>TT<54><54><EFBFBD>嚗?- <20>㗇𥋘<E39787>扳𥁒<E689B3>𠺪<EFBFBD>瘜典<E7989C><E585B8><EFBFBD>撖寞<E69296>嚗?
### 1.4 銝𤾸<E98A9D><F0A4BEB8><EFBFBD><EFBFBD><EFBFBD>𣇉<EFBFBD><F0A38789>喟頂
| 蝏游漲 | <20><EFBFBD>憭滨<E686AD> | <20><EFBFBD><E586BD>𣂼<EFBFBD> |
|------|---------|---------|
| **<EFBFBD><EFBFBD>** | <20>斗鱏<E69697>臬炏<E887AC>舐鍂嚗<E98D82><EFBFBD>?<3F>㘾膄嚗?| <20>𣂼<EFBFBD><F0A382BC><EFBFBD><E7919A>唳旿嚗<E697BF>鍂鈭𤾸<E988AD><F0A4BEB8><EFBFBD> |
| **12摮埈挾<E59F88><EFBFBD>** | 霂<>摯**<2A>臬炏摰峕㟲** | <20>𣂼<EFBFBD>**<2A><EFBFBD><E7919A><EFBFBD>?* |
| **颲枏枂** | 鈭<><E988AD>蝐鳴<E89D90>蝥喳<E89DA5>/<2F>㘾膄嚗? <20><>眏 | 霂衣<E99C82><E8A1A3><EFBFBD><EFBFBD>畾萄<E795BE><EFBFBD><E6BD98>唳旿銵剁<E98AB5> |
| **摨訫<E691A8><E8A8AB><EFBFBD><EFBFBD>?* | **摰<><E691B0><EFBFBD><EFBFBD>**嚗㇊DF<44><46>LM<4C><4D><EFBFBD><EFBFBD><E89D92>瘚讠<E7989A>嚗?| **摰<><E691B0><EFBFBD><EFBFBD>** |
| **<EFBFBD>唳旿摨?API** | **<EFBFBD><EFBFBD>霈曇恣**嚗<><E59A97><EFBFBD><EFBFBD>餈𨥈<E9A488> | **<EFBFBD><EFBFBD>霈曇恣** |
**<EFBFBD><EFBFBD>霈曇恣<EFBFBD><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>
- <20>?**<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>?靚<>
<EFBFBD>𢞖<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><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/
<EFBFBD><EFBFBD><EFBFBD><EFBFBD> common/ # <20>𡁶鍂<F0A181B6><EFBFBD>撅?潃?<3F>航◤extraction憭滨鍂
<EFBFBD>? <20><EFBFBD><E98EBF><EFBFBD> pdf/ # <20>?PDF摮睃<E691AE><EFBFBD>歇摰峕<E691B0>Day 1嚗争<E59A97>
<EFBFBD>? <20>? <20><EFBFBD><E98EBF><EFBFBD> PDFStorageService.ts
<EFBFBD>? <20>? <20><EFBFBD><E98EBF><EFBFBD> PDFStorageFactory.ts
<EFBFBD>? <20>? <20><EFBFBD><E98EBF><EFBFBD> adapters/
<EFBFBD>? <20>? <20>? <20><EFBFBD><E98EBF><EFBFBD> DifyPDFStorageAdapter.ts
<EFBFBD>? <20>? <20>? <20><EFBFBD><E5A999><EFBFBD> OSSPDFStorageAdapter.ts
<EFBFBD>? <20>? <20><EFBFBD><E5A999><EFBFBD> types.ts
<EFBFBD>? <20><EFBFBD><E98EBF><EFBFBD> llm/ # <20>?LLM<4C>滚𦛚
<EFBFBD>? <20>? <20><EFBFBD><E98EBF><EFBFBD> LLM12FieldsService.ts # LLM憭<4D><E686AD>12摮埈挾
<EFBFBD>? <20>? <20><EFBFBD><E98EBF><EFBFBD> PromptBuilder.ts # Prompt<70><74>遣嚗𠄎ection-Aware嚗争<E59A97>
<EFBFBD>? <20>? <20><EFBFBD><E5A999><EFBFBD> types.ts
<EFBFBD>? <20><EFBFBD><E98EBF><EFBFBD> validation/ # <20>?撉諹<E69289><E8ABB9>滚𦛚嚗<F0A69B9A>鰵憓痹<E68693>潃?<3F>? <20>? <20><EFBFBD><E98EBF><EFBFBD> MedicalLogicValidator.ts # <20>餃郎<E9A483><EFBFBD>撉諹<E69289>
<EFBFBD>? <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>?撘<>郊隞餃𦛚
<EFBFBD>? <20>? <20><EFBFBD><E5A999><EFBFBD> AsyncTaskService.ts
<EFBFBD>? <20><EFBFBD><E5A999><EFBFBD> utils/
<EFBFBD>? <20><EFBFBD><E98EBF><EFBFBD> tokenCalculator.ts
<EFBFBD>? <20><EFBFBD><E5A999><EFBFBD> jsonParser.ts
<EFBFBD>?<3F><EFBFBD><E98EBF><EFBFBD> fulltext-screening/ # <20><EFBFBD>憭滨<E686AD><E79285> 潃?<3F>祆活撘<E6B4BB><E69298><EFBFBD><E785BE>?<3F>? <20><EFBFBD><E98EBF><EFBFBD> routes/
<EFBFBD>? <20>? <20><EFBFBD><E5A999><EFBFBD> fulltext-screening.ts # 5銝服PI<50>亙藁
<EFBFBD>? <20><EFBFBD><E98EBF><EFBFBD> controllers/
<EFBFBD>? <20>? <20><EFBFBD><E5A999><EFBFBD> FulltextScreeningController.ts
<EFBFBD>? <20><EFBFBD><E98EBF><EFBFBD> services/
<EFBFBD>? <20>? <20><EFBFBD><E5A999><EFBFBD> FulltextScreeningService.ts # 銝𡁜𦛚<F0A1819C><EFBFBD>
<EFBFBD>? <20><EFBFBD><E98EBF><EFBFBD> types/
<EFBFBD>? <20>? <20><EFBFBD><E5A999><EFBFBD> screening.types.ts # TypeScript蝐餃<E89D90>
<EFBFBD>? <20><EFBFBD><E5A999><EFBFBD> prompts/
<EFBFBD>? <20><EFBFBD><E98EBF><EFBFBD> system_prompt.md # System Prompt嚗𠄎ection-Aware嚗争<E59A97>
<EFBFBD>? <20><EFBFBD><E98EBF><EFBFBD> user_prompt_template.md # User Prompt璅⊥踎
<EFBFBD>? <20><EFBFBD><E98EBF><EFBFBD> cochrane_standards/ # Cochrane<6E><65><EFBFBD><EFBFBD>讛膩嚗<E886A9><E59A97>摮埈挾嚗争<E59A97>
<EFBFBD>? <20>? <20><EFBFBD><E98EBF><EFBFBD> <20>𤩺㦤<F0A4A9BA>𡝗䲮瘜?md
<EFBFBD>? <20>? <20><EFBFBD><E98EBF><EFBFBD> <20><EFBFBD>.md
<EFBFBD>? <20>? <20><EFBFBD><E98EBF><EFBFBD> 蝏𤘪<E89D8F>摰峕㟲<E5B395>?md
<EFBFBD>? <20>? <20><EFBFBD><E5A999><EFBFBD> ...
<EFBFBD>? <20><EFBFBD><E5A999><EFBFBD> few_shot_examples/ # Few-shot<6F>餃郎獢<E9838E><E78DA2>摨𣏾<E691A8>
<EFBFBD>? <20><EFBFBD><E98EBF><EFBFBD> 擃䁅捶<E48185>嗬CT.md
<EFBFBD>? <20><EFBFBD><E98EBF><EFBFBD> 韐券<E99F90>銝滩雲獢<E99BB2><E78DA2>.md
<EFBFBD>? <20><EFBFBD><E5A999><EFBFBD> 靽⊥<E99DBD><E28AA5>其葉<E585B6><EFBFBD>蝵格<E89DB5>靘?md # <20>?<3F><EFBFBD><E5ADB5><EFBFBD>
<EFBFBD>?<3F><EFBFBD><E5A999><EFBFBD> title-screening/ # <20><><EFBFBD><EFBFBD><EFBFBD><E48185><EFBFBD><EFBFBD>歇摰峕<E691B0>嚗? <20><EFBFBD><E5A999><EFBFBD> ...
frontend-v2/src/modules/asl/
<EFBFBD><EFBFBD><EFBFBD><EFBFBD> pages/
<EFBFBD>? <20><EFBFBD><E98EBF><EFBFBD> FulltextScreeningSettings.tsx # 霈曄蔭銝𤾸鍳<F0A4BEB8>?<3F>? <20><EFBFBD><E98EBF><EFBFBD> FulltextScreeningWorkbench.tsx # 摰⊥瓲撌乩<E6928C><E4B9A9>?潃?<3F><EFBFBD>
<EFBFBD>? <20><EFBFBD><E5A999><EFBFBD> FulltextScreeningResults.tsx # 憭滨<E686AD>蝏𤘪<E89D8F>
<EFBFBD>?<3F><EFBFBD><E98EBF><EFBFBD> components/
<EFBFBD>? <20><EFBFBD><E98EBF><EFBFBD> screening/
<EFBFBD>? <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>
<EFBFBD>? <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/
<EFBFBD>? <20><EFBFBD><E98EBF><EFBFBD> LiteratureAcquisitionTable.tsx # <20><EFBFBD><E586BD><EFBFBD>銵冽聢
<EFBFBD>? <20><EFBFBD><E5A999><EFBFBD> ExcelExporter.tsx # Excel撖澆枂
```
---
## 3. <20>𡁶鍂<F0A181B6><EFBFBD><EFBFBD>挽霈∴<E99C88><E288B4><EFBFBD><E887AC><EFBFBD>
> **潃?<3F><EFBFBD>霂湔<E99C82>**嚗𡁻<E59A97>𡁶鍂<F0A181B6><EFBFBD><EFBFBD><EFBFBD>祆活撘<E6B4BB><E69298>𤑳<EFBFBD><F0A491B3><EFBFBD><E6BBA9><EFBFBD>嚗諹<E59A97>鈭𥟇<E988AD><F0A59F87>?*<2A>芣䔉撠<E49489><EFBFBD><EFBFBD><E586BD>𣂼<EFBFBD><E79285>憭滨鍂**嚗<><E59A97>甇日<E79487><EFBFBD><E996AC>韐券<E99F90>摰䂿緵<E482BF>?
### 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銝𠹺<E98A9D>嚗㇄ify or OSS嚗? **憭滨鍂<E6BBA8><EFBFBD>storage<67>滚𦛚** <20>?- <20><EFBFBD><E586BD>𣂼<EFBFBD><EFBFBD><E59A97><EFBFBD>沌ython敺格<E695BA><E6A0BC><EFBFBD>- **憭滨鍂ExtractionClient** <20>?- PDF銝贝蝸/<2F>𣳇膄
- <20><EFBFBD><E88880><EFBFBD><EFBFBD><EFBFBD>冽芋撘𧶏<E69298><F0A7B68F>嗡誨<E597A1><E8AAA8><EFBFBD><EFBFBD><EFBFBD>
**<EFBFBD><EFBFBD>蝟餌<EFBFBD>撌脣<EFBFBD><EFBFBD>?*嚗?- <20>?Python敺格<E695BA><E6A0BC><EFBFBD>PyMuPDF + Nougat嚗?- <20>?ExtractionClient.ts嚗<73><E59A97>蝡舫<E89DA1><E888AB><EFBFBD>
- <20>?TokenService.ts嚗㇍oken霈∠<E99C88>嚗?- <20>?storage<67>滚𦛚嚗<F0A69B9A><EFBFBD>啣抅蝖<E68A85><EFBFBD><E69285>
**<EFBFBD>祆活撘<EFBFBD><EFBFBD>?*嚗𡁜蘨<F0A1819C><E898A8>**<2A><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E98A9D>亙藁**嚗<>漲200銵䔶誨<E494B6><E8AAA8><EFBFBD>
#### 3.1.2 <20><EFBFBD><E8A9A8>亙藁
```typescript
// 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>嚗?*嚗?
```typescript
// 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>滨蔭**嚗?
```bash
# .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>?*
```typescript
// 憭滨鍂<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'
}
}
```
**<EFBFBD><EFBFBD>蝟餌<EFBFBD><EFBFBD><EFBFBD>湔𦻖雿輻鍂嚗?*嚗?- <20>?`backend/src/common/document/ExtractionClient.ts`<EFBFBD>歇摰䂿緵嚗?- <20>?`extraction_service/`嚗㇊ython敺格<EFBFBD><EFBFBD><EFBFBD>撌脤<EFBFBD>蝵莎<EFBFBD>
- <20>?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璅<E79285>嚗朞<E59A97>隡?2摮埈挾摰峕㟲<E5B395><EFBFBD><E694B9><EFBFBD>Cochrane<6E><65><EFBFBD>嚗?- extraction璅<E79285>嚗𡁏<E59A97><F0A1818F>?2摮埈挾霂衣<E99C82><E8A1A3>唳旿嚗<E697BF>𧊋<EFBFBD><EFBFBD>
- <20>峕芋<E5B395>见僎銵䕘<E98AB5>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>亙藁
```typescript
// 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>蝑𣇉裦
```typescript
/**
* 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
- <20>?憭𡁏<E686AD><EFBFBD><E692A3><EFBFBD><EFBFBD><EFBFBD><E686AD>
#### 3.2.4 蝻枏<E89DBB>蝑𣇉裦
```typescript
// 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<43>𠉛弦敹<E5BCA6><EFBFBD><EFBFBD><E58EB0><EFBFBD><E7AE8F>讛膩
- <20>𣬚𤩅<F0A3AC9A>𠉛弦敹<E5BCA6>◆霂湔<E99C82><E6B994><EFBFBD>
- <20>瑟𧋦<E7919F><EFBFBD><E8AD8D>箇瑪<E7AE87>唳旿銝<E697BF><E98A9D><EFBFBD>?- <20>箇瑪銝滚像銵⊿<E98AB5><EFBFBD><E996AC><EFBFBD><EFBFBD><E6B8B8>?- ITT<54><54><EFBFBD>摰峕㟲<E5B395>?
#### 3.3.2 <20><EFBFBD><E8A9A8>亙藁
```typescript
// 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>亙藁
```typescript
// 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>嚗?
```typescript
/**
* 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>
- <20><EFBFBD>撟嗅<E6929F>嚗?撟嗅<E6929F>嚗?- 餈𥕦漲餈質葵
- 憭梯揖<E6A2AF><EFBFBD>
- 隞餃𦛚<E9A483>𡝗<EFBFBD>
#### 3.4.2 <20><EFBFBD><E8A9A8>亙藁
```typescript
// 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>隞餃𦛚銵?
```prisma
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>銵?
```prisma
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>湔鰵摮埈挾嚗?
```prisma
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>蝘?
**<2A>𩤃<EFBFBD><><E79E89>嚗?餈<>宏"撠望糓"<22>𥕦遣<F0A595A6>啗”"<22><><EFBFBD><EFBFBD>?*
<EFBFBD>牐蛹AI<EFBFBD><EFBFBD><EFBFBD><EFBFBD>讃璅<EFBFBD><EFBFBD>?*<2A>冽鰵<E586BD>?*嚗諹<E59A97>鈭𥡝”銋见<E98A8B>**銝滚<E98A9D><E6BB9A>?*嚗峕<E59A97>隞仙risma<6D>?migrate"<22><EFBFBD>摰鮋<E691B0>銝𠰴停<F0A0B0B4>?*<2A>𥕦遣<F0A595A6>啗”**<2A>?
```bash
# <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>𡑒”
```typescript
// <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>隞餃𦛚
```typescript
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>隞餃𦛚餈𥕦漲
```typescript
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>
```typescript
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>
```typescript
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
```typescript
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
```typescript
// 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
```text
// backend/src/modules/asl/fulltext-screening/prompts/12fields_screening_system.txt
<EFBFBD>冽糓敺芾<EFBFBD><EFBFBD>餃郎銝枏振嚗諹<EFBFBD><EFBFBD><EFBFBD>隡唳<EFBFBD><EFBFBD><EFBFBD><EFBFBD>唳旿摰峕㟲<EFBFBD><EFBFBD><EFBFBD>舐鍂<EFBFBD><EFBFBD>?
## 隞餃𦛚
<EFBFBD><EFBFBD>12摮埈挾霂<EFBFBD>摯獢<EFBFBD>沲嚗<EFBFBD>ế<EFBFBD><EFBFBD><EFBFBD>格糓<EFBFBD>血虾<EFBFBD><EFBFBD>Meta<EFBFBD><EFBFBD><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
```text
// backend/src/modules/asl/fulltext-screening/prompts/12fields_screening_user.txt
霂瑚<EFBFBD><EFBFBD><EFBFBD>霂颱誑銝𧢲<EFBFBD><EFBFBD><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>
<EFBFBD><EFBFBD><EFBFBD><EFBFBD> 摮鞱<E691AE><E99EB1>?嚗朞挽蝵桐<E89DB5><E6A190>臬𢆡
<EFBFBD>? <20><EFBFBD><E98EBF><EFBFBD> PICOS<4F><53><EFBFBD>撅閧內嚗<E585A7><E59A97><EFBFBD>𠉛弦<F0A0899B><EFBFBD>蝏扳㗁嚗?<3F>? <20><EFBFBD><E98EBF><EFBFBD> <20><EFBFBD><E586BD><EFBFBD>蝞∠<E89D9E>銵冽聢
<EFBFBD>? <20><EFBFBD><E5A999><EFBFBD><>憪见<E686AA>蝑𥟇<E89D91><F0A59F87>?<3F>?<3F><EFBFBD><E98EBF><EFBFBD> 摮鞱<E691AE><E99EB1>?嚗𡁜恣<F0A1819C>詨極雿𨅯蝱 潃?<3F><EFBFBD>
<EFBFBD>? <20><EFBFBD><E98EBF><EFBFBD> PICOS<4F><53><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E88880>𩤃<EFBFBD>
<EFBFBD>? <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>
<EFBFBD>? <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>?
```tsx
// 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>?
```tsx
// 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>?
```tsx
// 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={`<EFBFBD><EFBFBD><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>亙藁
**<EFBFBD>𩤃<EFBFBD> <20><EFBFBD>**嚗帋<E59A97><E5B88B><EFBFBD><E888AB><EFBFBD><E595A3>𡢅<EFBFBD><F0A1A285>?*<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>
- [x] 摰䂿緵 `OSSPDFStorageAdapter.ts`<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>?- [x] 摰䂿緵 `PDFStorageFactory.ts`<EFBFBD><EFBFBD><EFBFBD>掩嚗争<EFBFBD>
- [x] <20><><EFBFBD><EFBFBD><EFBFBD><E594B3>滚𦛚嚗尠<E59A97>
- [x] 憭滨鍂 `ExtractionClient.ts`<EFBFBD>歇摰䂿緵嚗争<EFBFBD>
- [x] 憭滨鍂 `DifyClient.ts`<EFBFBD>歇摰䂿緵嚗争<EFBFBD>
- [x] 憭滨鍂 `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`
- <20>?`backend/src/common/rag/DifyClient.ts`
- <20>?`backend/src/common/storage/`
- <20>?`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>
- [x] DeepSeek-V3嚗<33>歇摰䂿緵嚗争<E59A97>
- [x] Qwen3-Max嚗<78>歇摰䂿緵嚗争<E59A97>
- [x] LLMFactory嚗<79>歇摰䂿緵嚗争<E59A97>
- [x] 摰䂿緵蝻枏<E89DBB>蝑𣇉裦嚗<E8A3A6><E59A97><EFBFBD>牢ache<68>滚𦛚嚗争<E59A97>
- [x] 摰䂿緵<E482BF>鞉𧋦霈∠<E99C88> <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>
- [x] **憸嘥<E686B8>摰峕<E691B0>**嚗?撅<>SON閫<4E><E996AB>蝑𣇉裦嚗àson-repair<69><72><EFBFBD>嚗争<E59A97>
- [x] **憸嘥<E686B8>摰峕<E691B0>**嚗䥪romise.allSettled<65>峕芋<E5B395>见捆<E8A781>?<3F>?
**憭滨鍂皜<E98D82><E79A9C>**嚗?- <20>?`backend/src/common/llm/adapters/DeepSeekAdapter.ts`
- <20>?`backend/src/common/llm/adapters/QwenAdapter.ts`
- <20>?`backend/src/common/llm/LLMFactory.ts`
- <20>?`backend/src/common/cache/`
**鈭批枂**嚗?- `LLM12FieldsService.ts`嚗ǚ400銵䕘<EFBFBD>
- 2銝枉rompt<70><74>辣嚗ǚ500銵䕘<E98AB5>
- 瘚贝<E7989A><EFBFBD><E996AC><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>
- [x] <20><EFBFBD>銝仿<E98A9D>蝔见漲<E8A781><E6BCB2><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銝芣<E98A9D>霂閙<E99C82>隞塚<E99A9E>~600銵䔶誨<E494B6><E8AAA8><EFBFBD>
- <20>𡁶鍂<F0A181B6><EFBFBD><EFBFBD>瓲敹<E793B2><E695B9><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>
**<EFBFBD><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`
- [ ] 撉諹<E69289>銵函<E98AB5><E587BD>?
**銝见<E98A9D>隞餃𦛚嚗<F0A69B9A><E59A97><EFBFBD><EFBFBD><EFBFBD>嚗?*嚗?- [ ] <20>𥕦遣 `FulltextScreeningService.ts`
- [ ] `processTask()` - 隞餃𦛚憭<F0A69B9A><E686AD><EFBFBD>亙藁
- [ ] `screenLiterature()` - <20><EFBFBD><E996A7><EFBFBD>讃蝑偦<E89D91>? - [ ] `updateTaskProgress()` - 餈𥕦漲<F0A595A6>湔鰵
- [ ] `completeTask()` - 隞餃𦛚摰峕<E691B0>
- [ ] <20><><EFBFBD><EFBFBD>𡁶鍂<F0A181B6><EFBFBD><EFBFBD><E69285><EFBFBD>?
**鈭批枂**嚗?- 3銝芣㺭<E88AA3>株”
- `FulltextScreeningService.ts`嚗ǚ600銵䕘<EFBFBD>
---
#### Day 5嚗?025-11-29 <20><EFBFBD>嚗㚁<E59A97><E39A81>𡒊垢API摰䂿緵
**<EFBFBD><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
- [ ] <20>𥕦遣 `fulltext-screening.ts` 頝舐眏
- [ ] API瘚贝<E7989A>嚗㇊ostman嚗?- [ ] <20>躰秤憭<E7A7A4><E686AD><EFBFBD><E691B0>
**鈭批枂**嚗?- 5銝服PI<50>亙藁
- API瘚贝<E7989A><E8B49D><EFBFBD>
- <20>𡒊垢摰峕<E691B0><E5B395>?
---
### 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<4F><53><EFBFBD>撅閧內
- [ ] <20><EFBFBD><E586BD><EFBFBD>蝞∠<E89D9E>銵冽聢
- [ ]<>憪见<E686AA>蝑𥟇<E89D91><F0A59F87>?- [ ] <20>𥕦遣 `ScreeningTable.tsx`<EFBFBD><EFBFBD><EFBFBD>系itle-screening嚗? - [ ] <20><EFBFBD>銵典仍
- [ ] 銝餉<E98A9D>皜脫<E79A9C>
- [ ] 撅訫<E69285>銵峕葡<E5B395>? - [ ] <20>斗鱏<E69697><EFBFBD><E69AB9>孵稬
- [ ] API<50><49><EFBFBD><EFBFBD><E59A97>撱箔遙<E7AE94><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`
- [ ] 餈𥕦漲<F0A595A6>? - [ ] 隞餃𦛚<E9A483><EFBFBD><E59786>蔭霂? - [ ] 蝏𤘪<E89D8F><F0A498AA>㰘蝸
- [ ] <20>𥕦遣 `PDFViewer.tsx`嚗ǐeact-pdf嚗? - [ ] PDF皜脫<E79A9C>
- [ ] 憿菟𢒰蝧駁△
- [ ]<>旿擃䀝漁
- [ ] <20>𥕦遣 `ReviewModal.tsx`
- [ ] <20><EFBFBD><E8ABB9><EFBFBD><EFBFBD>
- [ ] 12摮埈挾霂行<E99C82>撅閧內
- [ ] <20><EFBFBD><E884A9><EFBFBD>
- [ ] API<50><49><EFBFBD><EFBFBD><EFBFBD>𤥁<EFBFBD>摨艾<E691A8><E889BE><EFBFBD>𣇉<EFBFBD><F0A38789><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撖澆枂
**<EFBFBD><EFBFBD>**嚗𡁜<E59A97><F0A1819C>𣂼<EFBFBD>蝑𤤿<E89D91><F0A4A4BF>𣈯△<F0A388AF>?
**隞餃𦛚**嚗?- [ ] <20>𥕦遣 `FulltextScreeningResults.tsx`
- [ ] 蝏蠘恣<E8A098><EFBFBD>
- [ ] PRISMA<4D>㘾膄蝏蠘恣<E8A098>曇”
- [ ] Tab<61><62>揢嚗<E68FA2><EFBFBD>?<3F>㘾膄嚗? - [ ] 蝏𤘪<E89D8F><F0A498AA>𡑒”
- [ ] <20>𥕦遣 `ExcelExporter.ts`
- [ ] <20>玺heet撖澆枂嚗<E69E82><EFBFBD>?<3F>㘾膄嚗? - [ ] PRISMA蝏蠘恣Sheet
- [ ] API<50><49><EFBFBD><EFBFBD><EFBFBD>慟xcel嚗?- [ ] 頝舐眏<E88890>滨蔭
**鈭批枂**嚗?- 蝏𤘪<E89D8F>憿菟𢒰摰峕<E691B0>
- Excel撖澆枂摰峕<E691B0>
- <20>滨垢摰峕<E691B0><E5B395>?
---
#### 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>
- [ ] 摰峕㟲瘚<E39FB2><E7989A>瘚贝<E7989A>
- [ ] <20>𥕦遣隞餃𦛚
- [ ] PDF銝𠹺<E98A9D><EFBFBD><E59A97>霂䉪ify<66><79><EFBFBD>嚗? - [ ] 隞餃𦛚<E9A483><EFBFBD><EFBFBD><E59A97>霂訫<E99C82><E79285><EFBFBD>鍂嚗? - [ ] 餈𥕦漲<F0A595A6>湔鰵
- [ ] 摰⊥瓲撌乩<E6928C><E4B9A9><EFBFBD>瘚贝<E7989A><E8B49D><EFBFBD><EFBFBD>瘚页<E7989A>
- [ ] 鈭箏極<E7AE8F><EFBFBD>
- [ ] 蝏𤘪<E89D8F>撖澆枂
- [ ] Bug靽桀<E99DBD>
- [ ] <20><EFBFBD>隡睃<E99AA1><EFBFBD><E59A97>摮塩<E691AE><E5A1A9><EFBFBD>𡢅<EFBFBD>
**鈭批枂**嚗?- 瘚贝<E7989A><E8B49D><EFBFBD>
- Bug<75>𡑒”
---
#### Day 10嚗?025-12-06 <20><EFBFBD>嚗㚁<E59A97>隡睃<E99AA1> + <20><>
**<EFBFBD><EFBFBD>**嚗帋誨<E5B88B><E8AAA8><EFBFBD><EFBFBD><EFBFBD><E7A18B><EFBFBD>﹝摰<EFB99D><E691B0>
**隞餃𦛚**嚗?- [ ] 隞<><E99A9E>隡睃<E99AA1>
- [ ] <20>躰秤憭<E7A7A4><E686AD><EFBFBD><E691B0>
- [ ] <20><EFBFBD>颲枏枂隡睃<E99AA1>
- [ ] 蝐餃<E89D90>摰帋<E691B0><EFBFBD><E691B0>
- [ ] UI/UX隡睃<E99AA1>
- [ ] <20>㰘蝸<E3B098><EFBFBD>? - [ ] 蝛箇𠶖<E7AE87>? - [ ] <20>躰秤<E8BAB0>鞟內
- [ ] <20><>﹝蝻硋<E89DBB>
- [ ] API<50><49>
- [ ] <20><EFBFBD>雿輻鍂<E8BCBB><EFBFBD>
- [ ]<><E69298>𤏸<EFBFBD><F0A48FB8><EFBFBD>獢?- [ ] 隞<><E99A9E>Review
**鈭批枂**嚗?- 隞<><E99A9E>韐券<E99F90><E588B8>𣂼<EFBFBD>
- 摰峕㟲<E5B395><E39FB2>
- **<2A><EFBFBD>憭滨<E686AD>MVP摰峕<E691B0>**<2A>?
---
## 9. <20><><EFBFBD><EFBFBD><E888AA>?
### 9.1 <20><EFBFBD><E8A9A8><EFBFBD><EFBFBD><EFBFBD><E88880>?
#### 9.1.1 LLM<4C>鞉𧋦<E99E89><EFBFBD>
**<EFBFBD><EFBFBD>**嚗𡁜<E59A97><E79285><E288AA><EFBFBD>蝑偦<E89D91><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E59786>穿<EFBFBD>DeepSeek-V3 + Qwen3-Max嚗?
**閫<><E996AB><EFBFBD><EFBFBD>**嚗?1. **銝厩漣蝻枏<E89DBB>蝑𣇉裦**
- L1: <20><><EFBFBD>蝻枏<E89DBB><EFBFBD><E59A97><EFBFBD>𤑳㴓憓<E3B493><E68693>
- L2: Redis蝻枏<E89DBB><EFBFBD><E59A97>鈭抒㴓憓<E3B493><E68693>
- L3: <20>唳旿摨梶<E691A8>摮矋<E691AE>瘞訾<E7989E>摮睃<E691AE>嚗?
2. **蝏煺<E89D8F><E79285><E288AA>滨蔭**嚗㇈VP<56>嗆挾嚗? ```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<>?潃? ```
3. **<2A><EFBFBD><EFBFBD><E686B8>**
- 銝<>甈⊥<E79488>扯繮<E689AF><EFBFBD><EFBFBD><E89DAD><EFBFBD><EFBFBD><E6A180>? - <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>
- <20>𤾸蝱撘<E89DB1>郊憭<E9838A><E686AD>
- <20>滨垢頧株砭餈𥕦漲
#### 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. **<2A><EFBFBD><E7AE84>𣂼<EFBFBD>蝑𣇉裦**
```typescript
// 霂剛<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>滨漣
}
}
```
2. **<2A>𣂼<EFBFBD><F0A382BC><EFBFBD><E5AFA5>𣂼<EFBFBD>**
- <20><><EFBFBD><EFBFBD><EFBFBD>摰峕<E691B0><E5B395>𠬍<EFBFBD><F0A0AC8D>𤾸蝱<F0A4BEB8>芸𢆡<E88AB8>𣂼<EFBFBD><F0A382BC><EFBFBD>
- <20><EFBFBD>憭滨<E686AD><E6BBA8>嗥凒<E597A5>乩蝙<E4B9A9>?
### 9.2 <20>唳旿銝<E697BF><E98A9D><EFBFBD><EFBFBD>霂?
#### 9.2.1 鈭见𦛚憭<F0A69B9A><E686AD>
```typescript
// 雿輻鍂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>
```typescript
// <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>皛𡁜𢆡
```tsx
// 雿輻鍂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>頧?
```tsx
// 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>?
| 憌𡡞埯 | 蝑厩漣 | 蝻栞圾<E6A09E>芣鴌 |
|------|------|---------|
| **Dify摮睃<E691AE><E79D83>𣂼<EFBFBD>** | <20>椬 銝?| <20><><EFBFBD><EFBFBD>冽芋撘誯<E69298><E8AAAF>両SS餈<53>宏 |
| **LLM<4C>鞉𧋦頞<F0A78BA6><E9A09E>蝞?* | <20>椬 銝?| 銝厩漣蝻枏<E89DBB> + <20><EFBFBD><E79285><E288AA>㗇𥋘 |
| **Python敺格<E695BA><E6A0BC><EFBFBD>蝔喳<E89D94>** | <20>椬 銝?| <20><EFBFBD><E6BBA9><EFBFBD> + <20>滨漣蝑𣇉裦 |
| **<2A>滨垢PDF皜脫<E79A9C><E884AB><EFBFBD>** | <20>叚 雿?| react-pdf + <20>𡁏<EFBFBD>皛𡁜𢆡 |
### 10.2 撘<><E69298>烐釣<E78390><EFBFBD>憿?
#### 10.2.1 鈭穃<E988AD><E7A983><EFBFBD><E8A098>?
```typescript
// <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 <20>唳旿摨栞挽霈?
```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>
```bash
# <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>
```bash
# <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
)
}
}
```
---
## <20><><EFBFBD>
### A. <20><EFBFBD><E8A9A8><EFBFBD>
| <20><>﹝ | 頝臬<E9A09D> | 霂湔<E99C82> |
|------|------|------|
| <20><EFBFBD>憭滨<E686AD><E6BBA8><EFBFBD>瘙?| `01-<2D><><EFBFBD><E79899><EFBFBD>?03-<2D><EFBFBD>憭滨<E686AD><E6BBA8><EFBFBD><EFBFBD>祕餈?md` | <20><EFBFBD><E8A098><EFBFBD>瘙?|
| 12摮埈挾璅⊥踎 | `01-<2D><><EFBFBD><E79899><EFBFBD>?<3F><EFBFBD>憭滨<E686AD><E6BBA8>𠰴<EFBFBD><F0A0B0B4><EFBFBD><EFBFBD><EFBFBD>𡝗芋<F0A19D97>?txt` | 敺芾<E695BA><E88ABE>餃郎璅⊥踎 |
| UI<55><EFBFBD> | `03-UI霈曇恣/AI<41><EFBFBD><E7AE84><EFBFBD>讃-<2D><EFBFBD>憭滨<E686AD>.html` | <20>滨垢<E6BBA8><EFBFBD> |
| 鈭穃<E988AD><E7A983><EFBFBD><E8A098>?| `docs/04-撘<><E69298>𤏸<EFBFBD><F0A48FB8>?08-鈭穃<E988AD><E7A983><EFBFBD><E7AC94>𤏸<EFBFBD><F0A48FB8>?md` | 敹<>粉 |
| 蝟餌<E89D9F><E9A48C><EFBFBD> | `docs/00-蝟餌<E89D9F><E9A48C><EFBFBD>霈曇恣/00-蝟餌<E89D9F>敶枏<E695B6><E69E8F><EFBFBD><E59786><EFBFBD><EFBFBD><E69298><EFBFBD><E78390>?md` | 敹<>粉 |
### B. <20><EFBFBD><E887AC>滨蔭蝷箔<E89DB7>
```bash
# .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>
```sql
-- <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撘<4C><E69298>穃𣪧<E7A983>?
**<EFBFBD><EFBFBD><EFBFBD>擧凒<EFBFBD><EFBFBD>** 2025-11-22
**<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>** <20>?撌脣<E6928C><E884A3><EFBFBD><EFBFBD><E695BA><EFBFBD>?
**<EFBFBD><EFBFBD> <20><>𧋦<EFBFBD><F0A78BA6>蟮嚗?*
- V1.0 (2025-11-22): <20><EFBFBD><E598A5><EFBFBD>𧋦嚗<F0A78BA6><E59A97><EFBFBD><EFBFBD><E6B8B8>𤏸恣<F0A48FB8>?