Files
AIclinicalresearch/docs/03-业务模块/DC-数据清洗整理/02-技术设计/技术设计文档:工具 B - 病历结构化机器人 (The AI Structurer).md
HaHafeng 1b53ab9d52 feat(aia): Complete AIA V2.0 with universal streaming capabilities
Major Changes:
- Add StreamingService with OpenAI Compatible format
- Upgrade Chat component V2 with Ant Design X integration
- Implement AIA module with 12 intelligent agents
- Update API routes to unified /api/v1 prefix
- Update system documentation

Backend (~1300 lines):
- common/streaming: OpenAI Compatible adapter
- modules/aia: 12 agents, conversation service, streaming integration
- Update route versions (RVW, PKB to v1)

Frontend (~3500 lines):
- modules/aia: AgentHub + ChatWorkspace (100% prototype restoration)
- shared/Chat: AIStreamChat, ThinkingBlock, useAIStream Hook
- Update API endpoints to v1

Documentation:
- AIA module status guide
- Universal capabilities catalog
- System overview updates
- All module documentation sync

Tested: Stream response verified, authentication working
Status: AIA V2.0 core completed (85%)
2026-01-14 19:15:01 +08:00

178 lines
7.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# **<2A><><EFBFBD>航挽霈⊥<E99C88><EFBFBD><E78DA2>撌亙<E6928C> B \- <20><><EFBFBD>蝏𤘪<E89D8F><F0A498AA>𡝗㦤<F0A19D97>其犖 (The AI Structurer)**
| <20><>﹝蝐餃<E89D90> | Technical Design Document (TDD) |
| :---- | :---- |
| **撖孵<E69296> PRD** | **PRD\_撌亙<E6928C>B\_<><5F><EFBFBD>蝏𤘪<E89D8F><F0A498AA>𡝗㦤<F0A19D97>其犖\_V2.md** |
| **<EFBFBD><EFBFBD>𧋦** | **V2.0** (<28><EFBFBD><E59786><EFBFBD>漣嚗𡁜<E59A97><E79285>鈭文<E988AD>撉諹<E69289>) |
| **<EFBFBD><EFBFBD>?* | Draft |
| **<EFBFBD><EFBFBD><EFBFBD><EFBFBD>** | <20><>遣銝<E981A3>銝芷<E98A9D><E88AB7>臭縑摨衣<E691A8><E8A1A3><EFBFBD><E9A48C><EFBFBD>𧋦蝏𤘪<E89D8F><F0A498AA><EFBFBD><E7A18B>𠬍<EFBFBD><F0A0AC8D><EFBFBD>**<2A>峕芋<E5B395><EFBFBD>DeepSeek & Qwen嚗匧僎<E58CA7><EFBFBD><E78390>?*銝?*<2A>芸𢆡鈭文<E988AD>撉諹<E69289>**嚗諹圾<E8ABB9>?AI 撟餉<E6929F><E9A489><EFBFBD><E6A185>?|
## **1\. <20><EFBFBD><E9A2B1><EFBFBD>霈曇恣 (Architecture Overview)**
蝟餌<EFBFBD><EFBFBD><EFBFBD>隞𢛶<EFBFBD>𨅯<EFBFBD>蝥踵<EFBFBD><EFBFBD>瘞渡瑪<EFBFBD><EFBFBD>蝥找蛹 **<EFBFBD><EFBFBD>见僎<EFBFBD><EFBFBD>瘞渡瑪<EFBFBD>?*<2A><><EFBFBD><EFBFBD><E6A0AA><EFBFBD><EFBFBD><E59A97><EFBFBD>𤑳<EFBFBD>銝支葵銝滚<E98A9D><E6BB9A>?LLM 璅<E79285>撟嗉<E6929F><EFBFBD><E686AD>嚗𣬚<E59A97><F0A3AC9A>𨀣<EFBFBD><F0A880A3>𡁜<EFBFBD><F0A1819C>𨅯<EFBFBD><EFBFBD><E89D92>瘚见<E7989A><E8A781>𢛶<EFBFBD><EFBFBD>銵峕<E98AB5>撖對<E69296><E5B08D><EFBFBD><EFBFBD><EFBFBD><E88898><EFBFBD>鈭箏極撉諹<E69289>蝵烐聢<E78390>?
### **1.1 蝟餌<E89D9F><E9A48C><EFBFBD><E59786>?*
graph TD
Client\[React <20>滨垢 (Grid & Drawer UI)\]
subgraph API\_Server \[Fastify API <20>滚𦛚\]
JobAPI\[隞餃𦛚銝擧芋<E693A7><E88A8B>𦻖<EFBFBD>α]
VerifyAPI\[<5B>冽艶蝵烐聢<E78390>亙藁\]
end
subgraph Async\_Cluster \[<5B>𤾸蝱 Worker <20><>黎\]
BullMQ\[BullMQ 隞餃𦛚<E9A483><EFBFBD>\]
Orchestrator\[隞餃𦛚蝻𡝗<E89DBB><F0A19D97>沔]
PII\_Engine\[<5B><EFBFBD><E99E9F><EFBFBD>撘閙<E69298>\]
subgraph Dual\_LLM\_Engine \[<5B>𣬚𤩅<F0A3AC9A>𣂼<EFBFBD>撘閙<E69298>\]
ClientA\[DeepSeek 摰<E691B0>蝡珮]
ClientB\[Qwen 摰<E691B0>蝡珮]
end
CrossValidator\[鈭文<E988AD>撉諹<E69289>/<2F><EFBFBD><EFBFBD>瘚见膥\]
end
subgraph Storage \[<5B>唳旿摮睃<E691AE>\]
PG\[(PostgreSQL \- 銝𡁜𦛚<F0A1819C>唳旿)\]
VectorDB\[(pgvector \- <20><EFBFBD><EFBFBD><E39A81><EFBFBD>霂凋<E99C82>瘥𥪜笆)\]
Redis\[(Redis \- <20><EFBFBD>)\]
end
Client \--1.銝𠹺<E98A9D>&雿𤘪<E99BBF>--\> JobAPI
JobAPI \--2.<2E>𥕦遣撟嗅<E6929F>隞餃𦛚--\> BullMQ
BullMQ \--3.瘨<>晶--\> Orchestrator
Orchestrator \--4.<2E><EFBFBD>--\> PII\_Engine
PII\_Engine \--5.撟嗉<E6929F><EFBFBD>鍂--\> ClientA & ClientB
ClientA & ClientB \--6.餈𥪜<E9A488>JSON--\> CrossValidator
CrossValidator \--7.霈∠<E99C88><EFBFBD><E98A9D><EFBFBD>?-\> PG
Client \--8.<2E><EFBFBD>蝵烐聢<E78390>唳旿--\> VerifyAPI
VerifyAPI \--9.鈭箏極鋆<E6A5B5><E98B86>--\> PG
## **2\. <20><><EFBFBD><EFBFBD><EFBFBD> (Tech Stack)**
| 撅<>漣 | <20><><EFBFBD><EFBFBD>隞?| <20><EFBFBD><E58CA7><EFBFBD>眏 |
| :---- | :---- | :---- |
| **<EFBFBD>𡒊垢獢<EFBFBD>沲** | **Fastify 5.x** | 擃䀹<E69383><EFBFBD><EFBFBD>郊 I/O嚗屸<E59A97><E5B1B8><EFBFBD><EFBFBD><E686AD>擃睃僎<E79D83>烐芋<E78390><EFBFBD><E8B49D><EFBFBD>?|
| **璅<E79285><E288AA><EFBFBD>** | **LangChain.js** | 蝏煺<E89D8F><EFBFBD><E692A0> DeepSeek <20>?Qwen <20><><EFBFBD><EFBFBD>冽𦻖<E586BD><F0A6BB96><EFBFBD>靘蹂<E99D98><E8B982><EFBFBD>揢璅<E79285><E288AA>?|
| **隞餃𦛚<E9A483><EFBFBD>** | **BullMQ** | <20><EFBFBD><EFBFBD><EFBFBD><E8BEA3>2 <20><><EFBFBD><EFBFBD>?Flow <20><EFBFBD><E8A098>𡝗<EFBFBD><F0A19D97><EFBFBD><E587BD>埝䔉摰䂿緵<E482BF>𦦵<EFBFBD><EFBFBD>舅銝芣芋<E88AA3><EFBFBD>餈𥪜<E9A488><F0A5AA9C><EFBFBD><E89098><EFBFBD><E9A489>?|
| **<EFBFBD><EFBFBD><EFBFBD>瘚?* | **Lodash (<28><EFBFBD>) \+ Dice Coefficient (餈偦𧫴)** | <20><EFBFBD>瘥𥪜笆銝支葵 JSON 撖寡情<E5AFA1><E68385><EFBFBD>畾萄榆撘<E6A686><E69298><EFBFBD><EFBFBD><EFBFBD>祉㮾隡澆漲<E6BE86>臭蝙<E887AD><EFBFBD><E587BD><EFBFBD> Dice 蝟餅㺭<E9A485>?Levenshtein 頝萘氖嚗峕<E59A97>銝漤<E98A9D><EFBFBD><E996AC><EFBFBD><EFBFBD><E8A781><EFBFBD><E8AAA9>?|
| **<EFBFBD>唳旿摨?* | **PostgreSQL 15** | 摮睃<E691AE> JSONB <20><EFBFBD><E6BE86><EFBFBD><EFBFBD><E79285>蝏𤘪<E89D8F><F0A498AA>?|
| **<EFBFBD>滨垢鈭支<EFBFBD>** | **React \+ TanStack Table** | V2 <20>嫣蛹<E5ABA3>冽艶蝵烐聢嚗峕㺭<E5B395><EFBFBD>憭扳𧒄<E689B3><F0A79284>閬?TanStack Table (Headless) <20><EFBFBD><E6BB9A>𡁏<EFBFBD>皛𡁜𢆡<F0A1819C>?|
## **3\. <20><EFBFBD><EFBFBD><E7989A>霈曇恣 (Core Logic)**
### **3.1 <20><EFBFBD>雿𤘪<E99BBF> (Health Check Logic)**
* **閫血<E996AB><E8A180>嗆㦤嚗?* <20><EFBFBD><E586BD><EFBFBD>蝡舫<E89DA1>㗇𥋘<E39787>𨀣<EFBFBD><F0A880A3><EFBFBD><E7A08D><EFBFBD><E89098>祇𡢿<E7A587>?
* **<2A><EFBFBD><E689AF><EFBFBD>嚗?*
1. <20>𡒊垢霂餃<E99C82>霂亙<E99C82><E4BA99><EFBFBD><EFBFBD> 100 銵䕘<E98AB5>銝滩粉<E6BBA9><EFBFBD>嚗剹<E59A97>?
2. 霈∠<E99C88>蝏蠘恣<E8A098><E681A3><EFBFBD>嚗?
* emptyRate: 蝛箏<E89D9B>?/ <20><EFBFBD><E9A489><EFBFBD>?
* avgLength: <20>䂿征銵𣬚<E98AB5>撟喳<E6929F>摮㛖泵<E39B96><EFBFBD>?
3. **<EFBFBD>行⏛蝑𣇉裦嚗?* <20>?emptyRate \> 0.8 <20>?avgLength \< 10嚗諹<E59A97><E8ABB9>?status: 'BAD'<27>?
4. **Token 憸<>摯嚗?* totalRows \* avgLength \* 1.5 (蝎㛖裦隡啁<E99AA1>)<29>?
### **3.2 <20>𣬚𤩅<F0A3AC9A>𣂼<EFBFBD>銝𦒘漱<F0A69298><EFBFBD>霂?(Double-Blind & Validation)**
餈蹱糓 V2 <20><><EFBFBD><EFBFBD><EFBFBD>?
#### **A. <20>鞟內霂滚極蝔?(Prompt Engineering)**
銝箔<EFBFBD><EFBFBD>嫣噶瘥𥪜笆嚗<EFBFBD><EFBFBD>憿餃撩<EFBFBD>嗡舅銝芣芋<EFBFBD><EFBFBD><EFBFBD>?*摰<><E691B0><EFBFBD><E98A9D><EFBFBD> JSON 蝏𤘪<E89D8F>**<2A>?
* **System Prompt:** "You are a medical structural extraction assistant..."
* **Constraint:** "Output strictly in JSON format. Keys must be: \['tumor\_size', 'lymph\_node', ...\]."
* **Temperature:** 霈曆蛹 0嚗諹蕭瘙<E895AD><E79899>憭抒摰𡁏<E691B0><EFBFBD>?
#### **B. 鈭文<E988AD>撉諹<E69289>蝞埈<E89D9E> (The Judge)**
敶?Model A (DeepSeek) <20>?Model B (Qwen) 餈𥪜<E9A488>蝏𤘪<E89D8F><F0A498AA>𠬍<EFBFBD><F0A0AC8D><EFBFBD>瘥𥪜笆嚗?
function validate(jsonA, jsonB) {
const conflicts \= \[\];
const keys \= Object.keys(jsonA);
for (const key of keys) {
const valA \= normalize(jsonA\[key\]); // 敶雴<E695B6><E99BB4><EFBFBD><E59094>駁膄蝛箸聢<E7AEB8><E881A2>蓮撠誩<E692A0><E8AAA9><EFBFBD><EFBFBD>閫鍦<E996AB>
const valB \= normalize(jsonB\[key\]);
// 1\. 蝎曄<E69B84><EFBFBD>
if (valA \=== valB) continue;
// 2\. <20><EFBFBD><EFBFBD><EFBFBD><E98A9D>硋龪<E7A18B>?(憒?"3cm" vs "3.0cm")
if (isNumber(valA) && isNumber(valB) && parse(valA) \=== parse(valB)) continue;
// 3\. (<28><EFBFBD>? 霂凋<E99C82><E5878B>訾撮摨血龪<E8A180>?
// if (similarity(valA, valB) \> 0.95) continue;
conflicts.push(key);
}
return conflicts.length \=== 0 ? 'CLEAN' : 'CONFLICT';
}
## **4\. <20>唳旿摨栞挽霈?(Database Schema)**
V2 <20><><EFBFBD><E996AC><EFBFBD>其舅隞?AI 蝏𤘪<E89D8F>隞亙<E99A9E><E4BA99><EFBFBD><E586BD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E5969F><EFBFBD>?
### **Prisma Schema <20>湔鰵**
// 隞餃𦛚銵?
model ExtractionJob {
id String @id @default(uuid())
// ...<2E><EFBFBD>摮埈挾
diseaseType String // <20><EFBFBD>蝐餃<E89D90> (<28><EFBFBD>)
reportType String // <20><EFBFBD>蝐餃<E89D90> (<28><><EFBFBD>)
targetFields Json // <20><EFBFBD>摮埈挾摰帋<E691B0> \[{name: "<22>輻𠈔憭批<E686AD>", desc: "..."}\]
}
// <20><EFBFBD>霈啣<E99C88>銵?
model ExtractionItem {
id String @id @default(uuid())
jobId String
originalText String @db.Text
// V2 <20><EFBFBD>摮埈挾
resultA Json? // DeepSeek 蝏𤘪<E89D8F> { "size": "3cm" }
resultB Json? // Qwen 蝏𤘪<E89D8F> { "size": "3.0 cm" }
// <20><EFBFBD><EFBFBD>瘚讠<E7989A><E8AEA0>?
status ItemStatus // PENDING, CLEAN, CONFLICT, RESOLVED
conflictFields String\[\] // \["size"\] 霈啣<E99C88><E595A3><EFBFBD>摮埈挾<E59F88><EFBFBD>鈭?
// <20><><EFBFBD><E89D8F>蝥喟<E89DA5><E5969F>?(<28><EFBFBD><EFBFBD><E98B86><EFBFBD>𤾸<EFBFBD><F0A4BEB8><EFBFBD><E4BC90>𤥁<EFBFBD><F0A4A581><EFBFBD><EFBFBD>湔𧒄<E6B994>芸𢆡<E88AB8><EFBFBD>)
finalResult Json?
}
## **5\. <20>亙藁霈曇恣 (API Endpoints)**
### **5.1 璅∠<E79285>銝𡡞<E98A9D>蝵?*
* GET /api/templates: <20><EFBFBD><EFBFBD><EFBFBD><E68CBD>𪆴<EFBFBD><F0AA86B4><EFBFBD><EFBFBD><EFBFBD>璅∠<E79285><E288A0>𡑒”<F0A19192>?
* POST /api/jobs: <20>𥕦遣隞餃𦛚嚗釶ayload 銝剝<E98A9D><E5899D><EFBFBD>鉄 diseaseType <20>?reportType嚗䔶噶鈭𤾸<E988AD>蝡舐<E89DA1>鋆?Prompt<70>?
### **5.2 蝵烐聢撉諹<E69289> (Grid Verification)**
* GET /api/jobs/:id/rows: <20><><EFBFBD><EFBFBD>撉諹<E69289><E8ABB9>唳旿<E594B3>?
* **Response:** 餈𥪜<E9A488> originalText, resultA, resultB, conflictFields<64>?
* POST /api/items/:id/resolve: <20><EFBFBD><EFBFBD><E98B86><EFBFBD>?
* **Payload:** { field: "tumor\_size", chosenValue: "3cm" }<7D>?
* **Logic:** <20>湔鰵 finalResult嚗<74><E59A97><EFBFBD>𡏭砲銵峕<E98AB5><E5B395><EFBFBD><EFBFBD><E89D92>畾菟<E795BE>撌脰圾<E884B0><EFBFBD>撠?status <20>湔鰵銝?RESOLVED<45>?
## **6\. <20>滨垢霂衣<E99C82>霈曇恣 (Frontend)**
### **6.1 <20>冽艶撉諹<E69289>蝵烐聢 (Verification Grid)**
* **蝏<><EFBFBD><EFBFBD>嚗?* 靘萘<E99D98><E89098><EFBFBD> **TanStack Table** (<28><EFBFBD>撅? \+ **UI 蝏<>辣摨?* (皜脫<E79A9C>撅?<3F>?
* **<2A><EFBFBD><E884A9><EFBFBD><E8A8AB>潭葡<E6BDAD><EFBFBD>**
* 敶?conflictFields.includes(column.id) <20><EFBFBD><E5A19A><EFBFBD><E8A8AB>潭葡<E6BDAD>㮖蛹**撖寞<E69296><E79285>**<2A>?
* <20>曄內銝支葵撠𤩺<E692A0><F0A4A9BA><EFBFBD>\[DS: 3cm\] <20>?\[QW: 3.0cm\]<5D>?
* <20><EFBFBD><E586BD>孵稬隞颱<E99A9E><E9A2B1>厰僼嚗諹圻<E8ABB9>?resolve API嚗<49><E59A97>蝡臭<E89DA1><EFBFBD><EFBFBD><EFBFBD>Optimistic Update嚗劐蛹<E58A90>劐葉<E58A90><EFBFBD><E59786><EFBFBD>?
### **6.2 靘扯器<E689AF><EFBFBD><E8AAA9>?(Context Drawer)**
* **閫血<E996AB>嚗?* <20>孵稬銵冽聢銵𣬚<E98AB5>蝛箇蒾憭<E892BE><E686AD><EFBFBD>𨀣䰻<F0A880A3><EFBFBD><E8A781><EFBFBD><EFBFBD>嘥㦛<E598A5><E3A69B><EFBFBD>?
* **<2A><EFBFBD>嚗?* 撅閧內 originalText<78>?
* **擃䀝漁隡睃<E99AA1>嚗?* 蝞<><E89D9E><EFBFBD><E8A8AB>?String.indexOf <20>交𪄳敶枏<E695B6>摮埈挾<E59F88><E68CBE><EFBFBD>澆僎<E6BE86><E5838E><EFBFBD><EFBFBD>?
## **7\. 憌𡡞埯<F0A1A19E><EFBFBD>銝擧<E98A9D><EFBFBD>隡睃<E99AA1>**
| 瞏𨅯銁憌𡡞埯 | 閫<><E996AB><EFBFBD><EFBFBD> |
| :---- | :---- |
| **<EFBFBD><EFBFBD><EFBFBD>?Token <20>鞉𧋦** | 1\. 暺䁅恕雿輻鍂 DeepSeek (<28><><EFBFBD><EFBFBD>鞉𧋦) \+ Qwen (雿擧<E99BBF><E693A7>? 蝏<><E89D8F><EFBFBD>?2\. <20><EFBFBD><EFBFBD><EFBFBD><E79289>嗪𧫴畾萎艇<E8908E>潭㜃<E6BDAD><EFBFBD><E88AA3><EFBFBD><EFBFBD><EFBFBD>?|
| **憭<><E686AD><EFBFBD>笔漲<E7AC94>?* | 銝支葵璅<E79285><EFBFBD>◆ **撟嗅<E6929F><EFBFBD>鍂 (Promise.all)**嚗諹<E59A97><EFBFBD><E494B6>臭葡銵䎚<E98AB5><E48E9A>㟲雿栞<E99BBF>埈𧒄<E59F88><EFBFBD>鈭擧<E988AD><E693A7><EFBFBD><EFBCB9><EFBFBD>葵璅<E79285><E288AA>?|
| **璅<E79285><E288AA><EFBFBD>銝滚𨯬霂?* | Prompt 銝剖<E98A9D><E58996>?Few-Shot (撠烐甅<E78390>? 蝷箔<E89DB7>嚗峕<E59A97>蝖桀<E89D96>蝷?JSON <20><EFBFBD><E6BE86><EFBFBD><EFBFBD><EFBFBD>?JSON 閫<><E996AB>憭梯揖嚗諹䌊<E8ABB9><EFBFBD>霂?1 甈<E79488>?|
| **<EFBFBD>滨垢蝵烐聢<EFBFBD>⊿▼** | 憒<><E68692><EFBFBD>唳旿頞<E697BF><E9A09E> 1000 <20><EFBFBD><EFBFBD><E69298>?Virtual Scrolling (<28>𡁏<EFBFBD>皛𡁜𢆡)<29>?|