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%)
2187 lines
71 KiB
Markdown
2187 lines
71 KiB
Markdown
# 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>g<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>X踎嚗<E8B88E>虾<EFBFBD>睃<EFBFBD>嚗?*/}
|
||
<PICOSPanel project={task?.project} collapsible />
|
||
|
||
{/* 銵冽聢<E586BD>硋恣<E7A18B>貊<EFBFBD><E8B28A>?*/}
|
||
<ScreeningTable
|
||
taskType="fulltext"
|
||
results={results}
|
||
expandedRows={expandedRows}
|
||
onToggleExpand={(id) => toggleExpanded(id)}
|
||
onClickJudge={(result, field) => {
|
||
setCurrentReview({ result, field })
|
||
setReviewModalVisible(true)
|
||
}}
|
||
onDecisionChange={(resultId, decision) =>
|
||
updateDecision(resultId, decision)
|
||
}
|
||
/>
|
||
|
||
{/* <20>諹<EFBFBD><E8ABB9>曉<EFBFBD><E69B89><EFBFBD>恣<EFBFBD>交芋<E4BAA4><E88A8B><EFBFBD> */}
|
||
<ReviewModal
|
||
visible={reviewModalVisible}
|
||
result={currentReview?.result}
|
||
field={currentReview?.field}
|
||
onClose={() => setReviewModalVisible(false)}
|
||
/>
|
||
</div>
|
||
)
|
||
}
|
||
```
|
||
|
||
#### 7.2.2 ScreeningTable - 銵冽聢<E586BD>硋恣<E7A18B>?
|
||
```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>W<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>Y<EFBFBD>隞?- 銵冽聢蝏<E881A2>辣<EFBFBD>箇<EFBFBD>摰峕<E691B0>
|
||
|
||
---
|
||
|
||
#### Day 7嚗?025-12-03 <20>其<EFBFBD>嚗㚁<E59A97><E39A81>滨垢<E6BBA8>詨<EFBFBD>蝏<EFBFBD>辣嚗<E8BEA3><E59A97>嚗?
|
||
**<2A>格<EFBFBD>**嚗𡁜<E59A97><F0A1819C>𣂼恣<F0A382BC>詨極雿𨅯蝱<F0A885AF>釶DF<44>亦<EFBFBD><E4BAA6>?
|
||
**隞餃𦛚**嚗?- [ ] <20>𥕦遣 `FulltextScreeningWorkbench.tsx`
|
||
- [ ] 餈𥕦漲<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>W<EFBFBD>extraction璅∪<E79285>嚗? ConflictDetectionService, // <20>?憭滨鍂
|
||
AsyncTaskService // <20>?憭滨鍂
|
||
} from '@/modules/asl/common/services'
|
||
|
||
// <20>芷<EFBFBD>撘<EFBFBD><E69298>𦦨xtraction<6F>孵<EFBFBD><E5ADB5><EFBFBD><EFBFBD><EFBFBD>⊿<EFBFBD>餉<EFBFBD>
|
||
export class DataExtractionService {
|
||
// 靚<>鍂<EFBFBD>𡁶鍂<F0A181B6>滚𦛚
|
||
private llmService = new LLM12FieldsService()
|
||
|
||
async extractLiterature(literatureId: string) {
|
||
// 雿輻鍂extraction璅∪<E79285>
|
||
const result = await this.llmService.process12Fields(
|
||
LLM12FieldsMode.EXTRACTION, // <20>?<3F><>揢璅∪<E79285>
|
||
'gpt-4o',
|
||
fullText,
|
||
context
|
||
)
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## <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>?
|