Files
AIclinicalresearch/docs/03-业务模块/IIT Manager Agent/04-开发计划/Phase1.5-AI对话能力开发计划.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

83 KiB
Raw Blame History

IIT Manager Agent - Phase 1.5 AI撖寡<E69296><E5AFA1><EFBFBD><EFBFBD><E69298>𤏸恣<F0A48FB8>?

<EFBFBD><EFBFBD>𧋦: v3.0嚗<EFBFBD><EFBFBD><EFBFBD><EFBFBD>?+ 銝𠹺<E98A9D><F0A0B9BA><EFBFBD>扇敹?+ Dify<66><EFBFBD>摨橒<E691A8>
<EFBFBD>𥕦遣<EFBFBD><EFBFBD>: 2026-01-03
**<2A><><EFBFBD>唳凒<E594B3>?: 2026-01-04
**<2A><EFBFBD>?
: <20>?*撌脣<EFBFBD><EFBFBD><EFBFBD><EFBFBD>非ify<EFBFBD><EFBFBD><EFBFBD>嚗?
**摰鮋<E691B0>撌乩<E6928C><E4B9A9>?: ~2憭抬<E686AD><E68AAC><EFBFBD><EFBFBD><EFBFBD>?+ Dify<66><EFBFBD>摨橒<E691A8>
**<2A><EFBFBD>隞瑕<E99A9E>?
: PI<50>臬銁隡<E98A81><E99AA1>敺桐縑銝剛䌊<E5899B>嗅笆霂脲䰻霂㎞EDCap<61><EFBFBD><E7AC94>唳旿 + <20>𠉛弦<F0A0899B><EFBFBD><E5AF9E><EFBFBD>
<EFBFBD><EFBFBD><EFBFBD>𣂼停: <20>?REDCap<61>唳旿<E594B3><E697BF><EFBFBD> + <20>?銝𠹺<E98A9D><F0A0B9BA><EFBFBD>扇敹?+ <20>?閫<><E996AB>LLM撟餉<E6929F> + <20>?*Dify<EFBFBD><EFBFBD>摨𤘪毽<EFBFBD><EFBFBD><EFBFBD>蝝?


<EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>笔鍳<E7AC94><EFBFBD>1憭拐<E686AD>蝥選<E89DA5><E981B8>?<EFBFBD>𡁶鍂<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>

<EFBFBD><EFBFBD> <20>滚之<E6BB9A>𤑳緵嚗𡁻<E59A97>𡁶鍂<F0A181B6><EFBFBD><EFBFBD>歇摰<E6AD87><E691B0>嚗?

撟喳蝱<EFBFBD>啁𠶖嚗?026-01-03靚<33><E99D9A>蝏𤘪<E89D8F>嚗㚁<E59A97>

  • <EFBFBD>?LLMFactory 摰<><E691B0>撠梁貌嚗?蝘齿芋<E9BDBF><EFBFBD>DeepSeek/Qwen/GPT-5/Claude嚗㚁<E59A97><E39A81><EFBFBD><E79285>嚗屸妟<E5B1B8>滨蔭
  • <EFBFBD>?ChatContainer 摰<><E691B0>撠梁貌嚗鋫nt Design X蝏<58>辣嚗<E8BEA3><EFBFBD>汽ool C撉諹<E69289>嚗ǚ968銵䕘<E98AB5>
  • <EFBFBD>?<EFBFBD><EFBFBD><EFBFBD><EFBFBD>撌脤<EFBFBD>蝵?*嚗䫤DEEPSEEK_API_KEY<EFBFBD><EFBFBD>QWEN_API_KEY蝑?- <20>?<EFBFBD><EFBFBD>摰噼殿**嚗鋫SL<53><4C>C璅<E79285>撌脣之<E884A3>譍蝙<E8AD8D><EFBFBD>蝔喳<E89D94><E596B3><EFBFBD>

<EFBFBD><EFBFBD>隡睃飵嚗?```typescript // <20>𡒊垢LLM靚<4D>鍂嚗?銵䔶誨<E494B6><E8AAA8><EFBFBD> import { LLMFactory } from '@/common/llm/adapters/LLMFactory.js'; const llm = LLMFactory.getAdapter('deepseek-v3'); const response = await llm.chat(messages, { temperature: 0.7 });


### <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E8B3AA>?

<EFBFBD>?Day 1嚗?-6撠𤩺𧒄嚗? 摰䂿緵<E482BF><EFBFBD>撖寡<E69296> + 銝𠹺<E98A9D><F0A0B9BA><EFBFBD>扇敹?+ "typing"<22><EFBFBD>

  • 憭滨鍂LLMFactory嚗?撘<><E69298>𡢅<EFBFBD>
  • <EFBFBD>𥕦遣ChatService.ts嚗?撠𤩺𧒄嚗? - <20>𥕦遣SessionMemory.ts嚗?撠𤩺𧒄嚗? - 靽格㺿WechatCallbackController嚗?撠𤩺𧒄嚗?<3F>?Day 2嚗?-6撠𤩺𧒄嚗? Dify<66><EFBFBD>摨㯄<E691A8><E3AF84>?+ 瘛瑕<E7989B><EFBFBD><E89D9D>2026-01-04摰峕<E691B0>嚗? - <20><EFBFBD>憿寧𤌍銝𥟠ify<66><EFBFBD>摨橒<E691A8>1撠𤩺𧒄嚗? - <20><><EFBFBD>Dify璉<79><E89D9D>ChatService嚗?撠𤩺𧒄嚗? - 靽桀<E99DBD><E6A180>誩㦛霂<E3A69B><E99C82>銝擧㺭<E693A7>格釣<E6A0BC>半ug嚗?撠𤩺𧒄嚗? - 蝡臬<E89DA1>蝡舀<E89DA1>霂蓥<E99C82><E893A5><EFBFBD>﹝霈啣<E99C88>嚗?撠𤩺𧒄嚗?<3F>?<3F><><EFBFBD>摰䂿緵: <20>冽𥁒<E586BD><F0A58192><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ool Calling

### <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E6B2B2><EFBFBD>憭滨鍂<E6BBA8>𡁶鍂<F0A181B6><EFBFBD><EFBFBD><E69285>

PI<EFBFBD>鞾䔮 <20>?Node.js <20>?LLMFactory(deepseek-v3) <20>?<3F><><EFBFBD><EFBFBD><EFBFBD> <20>?隡<><E99AA1>敺桐縑<E6A190><EFBFBD>? <20>? <20>? SessionMemory RedcapAdapter嚗<72><EFBFBD><EFBFBD>


**<2A>喲睸<E596B2><EFBFBD>**嚗?- <20><> **憭滨鍂LLMFactory**嚗<><EFBFBD><EFBFBD><E39A81><EFBFBD><E59785>𡢅<EFBFBD><F0A1A285><EFBFBD>`deepseek-v3`嚗?- <20>?**<2A>芣䰻REDCap<61>唳旿**嚗<><EFBFBD>兴edcapAdapter嚗<72><E59A97><EFBFBD>典朖<E585B8><EFBFBD>
- <20>?**銝齿𦻖Dify**嚗<><E59A97>撠睲<E692A0>韏吔<E99F8F><E59094>惩翰撘<E7BFB0><E69298>𡢅<EFBFBD>
- <20>?**銝𠹺<E98A9D><F0A0B9BA><EFBFBD>扇敹?*嚗𠃊ode.js<6A><73><EFBFBD><EFBFBD><E59A97><EFBFBD><EFBFBD>餈?頧殷<E9A0A7>
- <20>?**甇<>銁颲枏<E9A2B2><E69E8F><EFBFBD>**嚗<><E59A97><EFBFBD><EFBFBD>"甇<><EFBFBD>亥砭..."嚗?- <20>?**<2A>閙郊頝舐眏**嚗<><E59A97><EFBFBD>沖eAct敺芰㴓嚗?
**憸<>摯撌乩<E6928C><E4B9A9>誩之撟<E4B98B><E6929F>雿?*嚗?-3憭?<3F>?**1憭?*嚗<><E59A97>銝摔LM靚<4D>鍂撅<E98D82>歇摰<E6AD87><E691B0>嚗争黾

---

## <20>㴓 銝<><E98A9D><EFBFBD>瓲敹<E793B2>𤌍<EFBFBD><F0A48C8D><EFBFBD>隞瑕<E99A9E>?
### 1.1 銝箔<E98A9D><EFBFBD><E98A8B><EFBFBD>I撖寡<E69296><E5AFA1><EFBFBD>嚗?
**敶枏<E695B6><E69E8F><EFBFBD>?*嚗㇄ay 3撌脣<E6928C><E884A3><EFBFBD>嚗?```
<0A>?REDCap敶訫<E695B6><E8A8AB>唳旿 <20>?Node.js<6A>閗繮 <20>?隡<><E99AA1>敺桐縑<E6A190><EFBFBD><E588B8><EFBFBD>𡁶䰻

**<2A><EFBFBD><E6A0BC><EFBFBD>?*嚗㇊hase 1.5嚗㚁<EFBFBD>

<EFBFBD>?PI<50><EFBFBD>銝𡁜凝靽∩葉<E288A9>鞾䔮 <20>?AI<41><49><EFBFBD>誩㦛 <20>?<3F>亥砭<E4BAA5>唳旿/<2F><><20>?<3F><EFBFBD><E7AE84><EFBFBD>

**<2A><EFBFBD>隞瑕<E99A9E>?*嚗?- <20><> 銝餃𢆡<EFBFBD>亥砭嚗䥪I銝滨鍂蝑厰<EFBFBD>𡁶䰻嚗屸<EFBFBD><EFBFBD>園䔮"<22>啣銁<E595A3><EFBFBD>憭𡁜<E686AD>鈭綽<E988AD>"

  • <EFBFBD><EFBFBD> **<2A>唳旿蝛輸<E89D9B>?*嚗𡁜<E59A97><F0A1819C>嗆䰻霂㎞EDCap<61>唳旿嚗<E697BF><E59A97><EFBFBD><EFBFBD><EFBFBD><E7A595><EFBFBD><EFBFBD><EFBFBD>抒𠶖<E68A92><F0A0B696><EFBFBD>
  • <EFBFBD><EFBFBD> **<2A><EFBFBD><EFBFBD>蝝?*嚗𡁏䰻霂<E99C82>蝛嗆䲮獢<E4B2AE><E78DA2><EFBFBD>RF銵冽聢<E586BD><E881A2><EFBFBD><EFBFBD><EFBFBD><E59F9D>?- <20><EFBFBD><EFBFBD><EFBFBD><EFBFBD>嚗朞䌊<EFBFBD>嗉祗閮<EFBFBD><EFBFBD>鞾䔮嚗峕<EFBFBD><EFBFBD><EFBFBD>霈啣<EFBFBD><EFBFBD>賭誘

<EFBFBD><EFBFBD> 鈭䎚<E988AD><E48E9A><EFBFBD><EFBFBD>舀沲<E88880><E6B2B2><EFBFBD><EFBFBD><EFBFBD><E7AE94>砍𧑐Dify嚗?

2.1 <20><EFBFBD><E6B8AF><EFBFBD><E59786>?

<EFBFBD>𢞖<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><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>?             PI (隡<><E99AA1>敺桐縑)                       <20>?<3F>? "P001<30><31><EFBFBD><EFBFBD><EFBFBD><E6B3B5><EFBFBD><EFBFBD><EFBFBD><E59F9D><EFBFBD><EFBFBD>嚗?                      <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><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><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>?     Node.js <20>𡒊垢 (撌脫<E6928C> WechatCallbackController) <20>?<3F>? <20>𢞖<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>?  <20>?<3F>? <20>?1. <20>交𤣰瘨<F0A4A3B0><E798A8> (handleCallback)              <20>?  <20>?<3F>? <20>?2. <20>誩㦛霂<E3A69B><E99C82> (Intent Router)               <20>?  <20>?<3F>? <20>?3. 撌亙<E6928C><EFBFBD>鍂 (Tool Executor)               <20>?  <20>?<3F>? <20>?4. 隡<><E99AA1>敺桐縑<E6A190><EFBFBD>?(WechatService)           <20>?  <20>?<3F>? <20><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>?  <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><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><EFBFBD><EFBFBD>?            <20>?       <20>𢞖<EFBFBD><F0A29E96><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E6B0AF><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>?       <20>?            <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>?<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>?<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>?<3F>? Dify        <20>?<3F>? REDCap      <20>?<3F>? PostgreSQL  <20>?<3F>? (<28>砍𧑐Docker)<29>?<3F>? API         <20>?<3F>? <20>唳旿摨?     <20>?<3F>?             <20>?<3F>?             <20>?<3F>?             <20>?<3F>?- <20>𠉛弦<F0A0899B><EFBFBD>   <20>?<3F>?- <20><><EFBFBD><EFBFBD><EFBFBD>?  <20>?<3F>?- 憿寧𤌍<E5AFA7>滨蔭   <20>?<3F>?- CRF銵冽聢    <20>?<3F>?- 韐冽綉<E586BD><EFBFBD>?  <20>?<3F>?- 摰∟恣<E2889F><EFBFBD>   <20>?<3F>?- <20>冽𥁒敶埝﹝   <20>?<3F>?- 銝滩<E98A9D><E6BBA9><EFBFBD>   <20>?<3F>?- <20><EFBFBD><E586BD><EFBFBD>   <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>?<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>?<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>?  <50ms           <100ms           <20ms
  <20>砍𧑐靚<F0A79190><20>砍𧑐靚<F0A79190><20>砍𧑐靚<F0A79190>

2.2 <20>喲睸<E596B2><E79DB8><EFBFBD><EFBFBD>蝑?

<EFBFBD><EFBFBD><EFBFBD>? <EFBFBD><EFBFBD> <EFBFBD><EFBFBD>
AI<EFBFBD><EFBFBD>撘閙<EFBFBD> DeepSeek-V3 (API) <EFBFBD>找遠瘥娪<EFBFBD>嚗峕𣈲<EFBFBD><EFBFBD>unction Calling
*<EFBFBD><EFBFBD>摨? Dify<EFBFBD>砍𧑐Docker 撌脤<EFBFBD>蝵莎<EFBFBD><EFBFBD>𣳇<EFBFBD>憭𡝗<EFBFBD><EFBFBD>穿<EFBFBD>撱嗉<EFBFBD>雿?
*<EFBFBD><EFBFBD><EFBFBD>唳旿摨? Dify<EFBFBD><EFBFBD>蔭Weaviate <EFBFBD>滨輕<EFBFBD><EFBFBD><EFBFBD>蝞勗朖<EFBFBD>?
頝舐眏蝑𣇉裦 <EFBFBD>閙郊<EFBFBD>誩㦛霂<EFBFBD><EFBFBD> MVP<EFBFBD>嗆挾蝞<EFBFBD><EFBFBD><EFBFBD>銝滨鍂ReAct敺芰㴓
<EFBFBD>唳旿<EFBFBD>亥砭 RedcapAdapter 撌脫<EFBFBD>嚗𣬚凒<EFBFBD><EFBFBD><EFBFBD>?

<EFBFBD>?鈭䎚<E988AD><E48E9A><EFBFBD><EFBFBD><E89D9E><EFBFBD><EFBFBD><EFBFBD>𤏸恣<F0A48FB8>𡜐<EFBFBD>2憭抬<E686AD>

<EFBFBD>㴓 Day 1嚗𡁜抅蝖<E68A85>撖寡<E69296><E5AFA1><EFBFBD>嚗?撠𤩺𧒄嚗?

<EFBFBD><EFBFBD><EFBFBD><EFBFBD>

*霈呸I<EFBFBD><EFBFBD>蝑𠉛鍂<EFBFBD>琿䔮憸矋<EFBFBD><EFBFBD>芣䰻REDCap<EFBFBD>唳旿嚗?

隞餃𦛚1.1嚗𡁜<EFBFBD>撱搴essionMemory嚗?0<><30><EFBFBD>嚗?

<EFBFBD><EFBFBD>辣雿滨蔭嚗䫤backend/src/modules/iit-manager/agents/SessionMemory.ts`

/**
 * 隡朞<E99AA1>霈啣<E99C88>蝞∠<E89D9E><E288A0><EFBFBD><E58981><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
 * 摮睃<E691AE><E79D83><EFBFBD><E586BD><EFBFBD>餈?頧桀笆霂嘅<E99C82><E59885><EFBFBD>銝𠹺<E98A9D><F0A0B9BA><EFBFBD><EFBFBD>閫? */
export class SessionMemory {
  // <20><><EFBFBD>摮睃<E691AE>嚗㝯 userId: ConversationHistory }
  private sessions: Map<string, ConversationHistory> = new Map();
  private readonly MAX_HISTORY = 3; // <20><EFBFBD><E88AAF><EFBFBD>餈?頧?
  /**
   * 瘛餃<E7989B>撖寡<E69296>霈啣<E99C88>
   */
  addMessage(userId: string, role: 'user' | 'assistant', content: string): void {
    if (!this.sessions.has(userId)) {
      this.sessions.set(userId, {
        userId,
        messages: [],
        createdAt: new Date(),
        updatedAt: new Date()
      });
    }

    const session = this.sessions.get(userId)!;
    session.messages.push({
      role,
      content,
      timestamp: new Date()
    });

    // <20><EFBFBD><E88AAF><EFBFBD>餈?頧殷<E9A0A7>6<EFBFBD><EFBFBD><E28AA5><EFBFBD>
    if (session.messages.length > this.MAX_HISTORY * 2) {
      session.messages = session.messages.slice(-this.MAX_HISTORY * 2);
    }

    session.updatedAt = new Date();
  }

  /**
   * <20><EFBFBD><E79195><EFBFBD>撖寡<E69296><E5AFA1><EFBFBD>   */
  getHistory(userId: string): ConversationMessage[] {
    const session = this.sessions.get(userId);
    return session?.messages || [];
  }

  /**
   * <20><EFBFBD><E79195><EFBFBD>銝𠹺<E98A9D><F0A0B9BA><EFBFBD><EFBFBD><EFBFBD><EFBFBD>餈睲<E9A488>頧殷<E9A0A7>
   */
  getContext(userId: string): string {
    const history = this.getHistory(userId);
    if (history.length === 0) return '';

    // <20><EFBFBD><E88AB8><EFBFBD>餈睲<E9A488>頧桀笆霂?    const recentMessages = history.slice(-2);
    return recentMessages
      .map(m => `${m.role}: ${m.content}`)
      .join('\n');
  }

  /**
   * 皜<><EFBFBD><EFBFBD>隡朞<E99AA1>
   */
  clearSession(userId: string): void {
    this.sessions.delete(userId);
  }

  /**
   * 皜<><E79A9C><EFBFBD><E9A488>隡朞<E99AA1><EFBFBD><E59A97>餈?撠𤩺𧒄<F0A4A9BA>芯蝙<E88AAF><EFBFBD>
   */
  cleanupExpiredSessions(): void {
    const now = Date.now();
    const ONE_HOUR = 3600000;

    for (const [userId, session] of this.sessions.entries()) {
      if (now - session.updatedAt.getTime() > ONE_HOUR) {
        this.sessions.delete(userId);
      }
    }
  }
}

interface ConversationHistory {
  userId: string;
  messages: ConversationMessage[];
  createdAt: Date;
  updatedAt: Date;
}

interface ConversationMessage {
  role: 'user' | 'assistant';
  content: string;
  timestamp: Date;
}

// <20><EFBFBD><E585B8><EFBFBD>
export const sessionMemory = new SessionMemory();

// 摰𡁏𧒄皜<F0A79284><E79A9C><EFBFBD><E9A488>隡朞<E99AA1><EFBFBD><E59A97>撠𤩺𧒄嚗?setInterval(() => {
  sessionMemory.cleanupExpiredSessions();
}, 3600000);

**撉峕𤣰<E5B395><F0A4A3B0><EFBFBD>**嚗?- <20>?<3F><EFBFBD><E887AC>典笆霂嘥<E99C82><E598A5>?- <20>?<3F>航繮<E888AA><EFBFBD>銝𧢲<E98A9D>

  • <EFBFBD>?<3F>芸𢆡皜<F0A286A1><E79A9C><EFBFBD><E9A488>隡朞<E99AA1>

隞餃𦛚1.2嚗𡁜<EFBFBD>撱慢hatService嚗?撠𤩺𧒄嚗争黾 憭滨鍂LLMFactory

<EFBFBD><EFBFBD>辣雿滨蔭嚗䫤backend/src/modules/iit-manager/services/ChatService.ts`

import { LLMFactory } from '../../../common/llm/adapters/LLMFactory.js';
import { Message } from '../../../common/llm/adapters/types.js';
import { logger } from '../../../common/logging/index.js';
import { sessionMemory } from '../agents/SessionMemory.js';

/**
 * AI撖寡<E69296><E5AFA1>滚𦛚嚗<F0A69B9A><E59A97><EFBFBD><EFBFBD>𡁶鍂<F0A181B6><EFBFBD><EFBFBD>LMFactory嚗? * 憭<><E686AD><EFBFBD><E99AA1>敺桐縑<E6A190><EFBFBD><EFBFBD><E798A8>嚗峕𣈲<E5B395><F0A388B2><EFBFBD>銝𧢲<E98A9D>霈啣<E99C88>
 */
export class ChatService {
  private llm;

  constructor() {
    // <20>?憭滨鍂<E6BBA8>𡁶鍂<F0A181B6><EFBFBD><EFBFBD>LMFactory嚗<79><EFBFBD>滨蔭嚗?    this.llm = LLMFactory.getAdapter('deepseek-v3');
  }

  /**
   * 霂<><E99C82><EFBFBD><EFBFBD><E586BD>誩㦛嚗<E3A69B>蒂銝𠹺<E98A9D><F0A0B9BA><EFBFBD><EFBFBD>
   */
  async route(
    userMessage: string,
    userId: string,
    projectId: string
  ): Promise<IntentRouteResult> {
    try {
      // 1. <20><EFBFBD>銝𠹺<E98A9D><F0A0B9BA>?      const context = sessionMemory.getContext(userId);

      logger.info('[SimpleIntentRouter] Routing with context', {
        message: userMessage.substring(0, 50),
        hasContext: !!context,
        userId
      });

      // 2. <20><>遣Prompt嚗<74><E59A97><EFBFBD><EFBFBD>銝𧢲<E98A9D>嚗?      const systemPrompt = this.buildSystemPrompt();
      const userPrompt = context 
        ? `<60>𣂷<EFBFBD>銝𧢲<E98A9D><F0A7A2B2>髢n${context}\n\n<EFBFBD>𣂼<EFBFBD><EFBFBD>漤䔮憸塩<EFBFBD>髢n${userMessage}`
        : userMessage;

      // 3. 靚<>鍂LLM
      const response = await this.llm.chat.completions.create({
        model: 'deepseek-chat',
        messages: [
          { role: 'system', content: systemPrompt },
          { role: 'user', content: userPrompt }
        ],
        tools: [this.getDataQueryTool()],
        tool_choice: 'auto',
        temperature: 0.1,
        max_tokens: 500
      });

      const message = response.choices[0].message;

      // 4. 憒<><E68692>LLM<4C><EFBFBD><EFBFBD>鍂撌亙<E6928C>
      if (message.tool_calls && message.tool_calls.length > 0) {
        const toolCall = message.tool_calls[0];
        const toolArgs = JSON.parse(toolCall.function.arguments);

        // <20>?銝𠹺<E98A9D><F0A0B9BA><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E68692>args銝剜<E98A9D><EFBFBD><E99A9E><EFBFBD><E59A97>霂蓥<E99C82>銝𠹺<E98A9D><F0A0B9BA><EFBFBD>葉閫<E89189><E996AB>
        if (context && this.hasPronouns(userMessage)) {
          toolArgs.patient_id = this.extractPatientIdFromContext(context, toolArgs);
        }

        return {
          needsToolCall: true,
          toolName: 'query_clinical_data',
          toolArgs,
          rawResponse: message
        };
      }

      // 5. <20>湔𦻖<E6B994><EFBFBD>
      return {
        needsToolCall: false,
        directAnswer: message.content || '<27><EFBFBD>嚗峕<E59A97>瘝⊥<E7989D><E28AA5><EFBFBD><EFBFBD><EFBFBD><E587BD><EFBFBD>',
        rawResponse: message
      };
    } catch (error: any) {
      logger.error('[SimpleIntentRouter] Routing failed', {
        error: error.message
      });

      return {
        needsToolCall: false,
        directAnswer: '<27><EFBFBD>嚗峕<E59A97><E5B395><EFBFBD><EFBFBD><EFBFBD><E988AD>鈭偦䔮憸矋<E686B8>霂瑞<E99C82><E7919E>𤾸<EFBFBD>霂?,
        error: error.message
      };
    }
  }

  /**
   * <20><>遣System Prompt
   */
  private buildSystemPrompt(): string {
    return `# 閫坿𠧧
雿䭾糓銝游<EFBFBD><EFBFBD>𠉛弦憿寧𤌍<EFBFBD><EFBFBD><EFBFBD><EFBFBD>周I<EFBFBD>亥砭憿寧𤌍<EFBFBD>唳旿<EFBFBD>?
# <20><EFBFBD>
雿惩虾隞交䰻霂㎞EDCap<EFBFBD>唳旿摨橒<EFBFBD><EFBFBD><EFBFBD>𡠺嚗?1. 憿寧𤌍蝏蠘恣嚗<E681A3><E59A97><EFBFBD><EFBFBD><EFBFBD><E5959C><EFBFBD><EFBFBD><E6A180><EFBFBD>嚗?2. <20><><EFBFBD><EFBFBD><EFBFBD><E7A595><EFBFBD>敶訫<E695B6><E8A8AB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>砌縑<E7A08C><EFBFBD>
3. 韐冽綉<E586BD><EFBFBD><E59786><EFBFBD><EFBFBD>唳旿<E594B3><EFBFBD>嚗?
# 銝𠹺<E98A9D><F0A0B9BA><EFBFBD><EFBFBD>閫?- 憒<><E68692><EFBFBD><EFBFBD>霂?隞?<3F>?餈嗘葵<E59798><E891B5><EFBFBD>?蝑劐誨霂㵪<E99C82>霂瑟覔<E7919F><EFBFBD>𣂷<EFBFBD>銝𧢲<E98A9D><F0A7A2B2>睲葉<E79DB2>𣂼<EFBFBD><F0A382BC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>?- 憒<><E68692>銝𠹺<E98A9D><F0A0B9BA><EFBFBD>葉瘝⊥<E7989D><E28AA5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>霂瑁<E99C82><EFBFBD><EFBFBD><EFBFBD>靘?
# 蝥行<E89DA5>
- 銝亦<E98A9D>蝻㚚<E89DBB>䭾㺭<E4ADBE>?- <20><EFBFBD><E88ABE>亥砭REDCap<61>唳旿嚗䔶<E59A97><E494B6>賣䰻霂<E99C82>獢?- <20><EFBFBD><EFBFBD><E996AC><EFBFBD><E798A3>銝䫤;
  }

  /**
   * 摰帋<E691B0><E5B88B>唳旿<E594B3>亥砭撌亙<E6928C>
   */
  private getDataQueryTool(): any {
    return {
      type: "function",
      function: {
        name: "query_clinical_data",
        description: `<60>亥砭REDCap銝游<E98A9D><E6B8B8>唳旿<E594B3>?<3F><><EFBFBD>箸艶嚗?- <20>桅★<E6A185><EFBFBD>霈∴<E99C88><E288B4>啣銁<E595A3><EFBFBD>憭𡁜<E686AD>鈭綽<E988AD><E7B6BD>唳旿韐券<E99F90><EFBFBD><E68692>嚗?- <20><EFBFBD><E6A0BC><EFBFBD><EFBFBD><E7A595><EFBFBD>P001<30><31><EFBFBD><EFBFBD><EFBFBD>摰䔶<E691B0><E494B6><EFBFBD><E6A2B9><EFBFBD><E58A90><EFBFBD>摨𥪜<E691A8>嚗?- <20>株捶<E6A0AA>抒𠶖<E68A92><F0A0B696><EFBFBD><EFBFBD>匧𪑛鈭𥡝捶<F0A5A19D>折䔮憸矋<E686B8>`,
        parameters: {
          type: "object",
          properties: {
            intent: {
              type: "string",
              enum: ["project_stats", "patient_detail", "qc_status"],
              description: `<60>亥砭<E4BAA5>誩㦛嚗?- project_stats: 憿寧𤌍蝏蠘恣
- patient_detail: <20><><EFBFBD><EFBFBD><EFBFBD>?- qc_status: 韐冽綉<E586BD><EFBFBD><E59786>
            },
            patient_id: {
              type: "string",
              description: "<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>001嚗㚁<E59A97>敶𧗽ntent=patient_detail<69><EFBFBD>憛?
            }
          },
          required: ["intent"]
        }
      }
    };
  }

  /**
   * 璉<><E79289><EFBFBD><E4BAA4>臭葉<E887AD>臬炏<E887AC>劐誨霂?   */
  private hasPronouns(message: string): boolean {
    const pronouns = ['?, '憟?, '餈嗘葵<EFBFBD><EFBFBD><EFBFBD>?, '霂交<E99C82><E4BAA4>?, '餈嗘<EFBFBD>', '<EFBFBD><EFBFBD><EFBFBD>'];
    return pronouns.some(p => message.includes(p));
  }

  /**
   * 隞𦒘<E99A9E>銝𧢲<E98A9D>銝剜<E98A9D><E5899C>𡝗<EFBFBD><F0A19D97><EFBFBD>D
   */
  private extractPatientIdFromContext(context: string, toolArgs: any): string {
    // 蝞<><E89D9E>閙迤<E99699><EFBFBD><E8B9B1>𡝗<EFBFBD><F0A19D97><EFBFBD><EFBFBD><EFBFBD>?    const match = context.match(/P\d{3,}/);
    return match ? match[0] : toolArgs.patient_id;
  }
}

export interface IntentRouteResult {
  needsToolCall: boolean;
  toolName?: string;
  toolArgs?: any;
  directAnswer?: string;
  error?: string;
  rawResponse?: any;
}

**撉峕𤣰<E5B395><F0A4A3B0><EFBFBD>**嚗?- <20>?<3F><EFBFBD><E888AA>急䰻霂<E99C82><EFBCB8>?- <20>?<3F><EFBFBD>銝𠹺<E98A9D><F0A0B9BA><EFBFBD><EFBFBD><EFBFBD><E996AB><EFBFBD><E99A9E><EFBFBD><E996AB>嚗?- <20>?<3F>躰秤憭<E7A7A4><E686AD><EFBFBD><E691B0>


隞餃𦛚1.3嚗𡁶<EFBFBD><EFBFBD>巁oolExecutor嚗?.5撠𤩺𧒄嚗?

<EFBFBD><EFBFBD>辣雿滨蔭嚗䫤backend/src/modules/iit-manager/agents/SimpleToolExecutor.ts`

import { RedcapAdapter } from '../adapters/RedcapAdapter.js';
import { logger } from '../../../common/logging/index.js';
import { prisma } from '../../../config/database.js';

/**
 * 蝞<><E89D9E>𣇉<EFBFBD>撌亙<E6928C><E4BA99><EFBFBD><E689AF>? * <20><EFBFBD>銵朙EDCap<61>唳旿<E594B3>亥砭
 */
export class SimpleToolExecutor {
  /**
   * <20><EFBFBD><E689AF>亥砭銝游<E98A9D><E6B8B8>唳旿
   */
  async execute(
    toolArgs: {
      intent: 'project_stats' | 'patient_detail' | 'qc_status';
      patient_id?: string;
    },
    context: {
      projectId: string;
      userId: string;
    }
  ): Promise<ToolExecutionResult> {
    try {
      logger.info('[SimpleToolExecutor] Executing query', {
        intent: toolArgs.intent,
        patientId: toolArgs.patient_id,
        projectId: context.projectId
      });

      // 1. <20><EFBFBD>憿寧𤌍<E5AFA7>滨蔭
      const project = await prisma.iitProject.findUnique({
        where: { id: context.projectId },
        select: {
          name: true,
          redcapApiUrl: true,
          redcapApiToken: true
        }
      });

      if (!project) {
        return {
          success: false,
          data: null,
          error: '憿寧𤌍銝滚<E98A9D><E6BB9A>?
        };
      }

      // 2. <20><EFBFBD><E598A5>餸edcapAdapter
      const redcap = new RedcapAdapter(
        project.redcapApiUrl,
        project.redcapApiToken
      );

      // 3. <20>寞旿intent<6E><EFBFBD><E689AF>亥砭
      switch (toolArgs.intent) {
        case 'project_stats':
          return await this.getProjectStats(redcap, project.name);

        case 'patient_detail':
          if (!toolArgs.patient_id) {
            return {
              success: false,
              data: null,
              error: '霂瑟<EFBFBD>靘𥟇<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>P001嚗?
            };
          }
          return await this.getPatientDetail(redcap, toolArgs.patient_id);

        case 'qc_status':
          return await this.getQCStatus(context.projectId);

        default:
          return {
            success: false,
            data: null,
            error: '<27>芰䰻<E88AB0><E4B0BB>䰻霂<EFBCB9>?
          };
      }
    } catch (error: any) {
      logger.error('[SimpleToolExecutor] Execution failed', {
        error: error.message
      });

      return {
        success: false,
        data: null,
        error: error.message
      };
    }
  }

  /**
   * <20><EFBFBD>憿寧𤌍蝏蠘恣
   */
  private async getProjectStats(
    redcap: RedcapAdapter,
    projectName: string
  ): Promise<ToolExecutionResult> {
    const records = await redcap.exportRecords();

    return {
      success: true,
      data: {
        type: 'project_stats',
        projectName,
        stats: {
          totalRecords: records.length,
          enrolled: records.length,
          completed: records.filter((r: any) => r.complete === '2').length,
          dataQuality: this.calculateDataQuality(records)
        }
      }
    };
  }

  /**
   * <20><EFBFBD><E79195><EFBFBD><EFBFBD><EFBFBD><EFBFBD>?   */
  private async getPatientDetail(
    redcap: RedcapAdapter,
    patientId: string
  ): Promise<ToolExecutionResult> {
    const records = await redcap.exportRecords([patientId]);

    if (records.length === 0) {
      return {
        success: false,
        data: null,
        error: `<60>芣𪄳<E88AA3><EFBFBD><E594B3>?${patientId}`
      };
    }

    const record = records[0];

    return {
      success: true,
      data: {
        type: 'patient_detail',
        patientId,
        details: {
          age: record.age,
          gender: record.gender,
          bmi: record.bmi,
          complete: record.complete === '2' ? '撌脣<EFBFBD><EFBFBD>? : '餈𥡝<E9A488>銝?,
          lastUpdate: new Date().toISOString()
        }
      }
    };
  }

  /**
   * <20><EFBFBD>韐冽綉<E586BD><EFBFBD>?   */
  private async getQCStatus(projectId: string): Promise<ToolExecutionResult> {
    const logs = await prisma.iitAuditLog.findMany({
      where: {
        projectId,
        actionType: 'quality_issue'
      },
      orderBy: { createdAt: 'desc' },
      take: 10
    });

    return {
      success: true,
      data: {
        type: 'qc_status',
        issueCount: logs.length,
        recentIssues: logs.map(log => ({
          recordId: log.entityId,
          issue: log.details,
          createdAt: log.createdAt
        }))
      }
    };
  }

  /**
   * 霈∠<E99C88><E288A0>唳旿韐券<E99F90><EFBFBD><E59A97><EFBFBD><EFBFBD>瘜𤏪<E7989C>
   */
  private calculateDataQuality(records: any[]): string {
    if (records.length === 0) return '0%';

    const completedCount = records.filter((r: any) => r.complete === '2').length;
    const quality = (completedCount / records.length) * 100;

    return `${quality.toFixed(1)}%`;
  }
}

export interface ToolExecutionResult {
  success: boolean;
  data: any;
  error?: string;
}

**撉峕𤣰<E5B395><F0A4A3B0><EFBFBD>**嚗?- <20>?<3F>舀䰻霂<EFBD81><EFBFBD>霈?- <20>?<3F>舀䰻霂<E99C82><EFBCB8><EFBFBD><EFBFBD>?- <20>?<3F>舀䰻霂<EFBCBA>抒𠶖<E68A92>?

隞餃𦛚1.4嚗𡁶<EFBFBD><EFBFBD>䨝nswerGenerator嚗?撠𤩺𧒄嚗?

<EFBFBD><EFBFBD>辣雿滨蔭嚗䫤backend/src/modules/iit-manager/agents/SimpleAnswerGenerator.ts`

import { ToolExecutionResult } from './SimpleToolExecutor.js';
import { logger } from '../../../common/logging/index.js';

/**
 * 蝞<><E89D9E>𣇉<EFBFBD>蝑娍<E89D91><E5A88D><EFBFBD><EFBFBD><EFBFBD>? * 雿輻鍂璅⊥踎<E28AA5><E8B88E><EFBFBD><EFBFBD><EFBFBD>嚗䔶<E59A97><EFBFBD>鍂LLM嚗<4D><E59A97><EFBFBD><EFBFBD><EFBFBD><EFBFBD>穿<EFBFBD>
 */
export class SimpleAnswerGenerator {
  /**
   * <20><><EFBFBD><EFBFBD><EFBFBD>
   */
  generate(
    userQuestion: string,
    toolResult: ToolExecutionResult
  ): string {
    try {
      logger.info('[SimpleAnswerGenerator] Generating answer', {
        success: toolResult.success,
        dataType: toolResult.data?.type
      });

      // 憒<><E68692>撌亙<E6928C><E4BA99><EFBFBD>憭梯揖
      if (!toolResult.success) {
        return this.generateErrorMessage(toolResult.error);
      }

      // <20>寞旿<E5AF9E>唳旿蝐餃<E89D90><E9A483><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
      const dataType = toolResult.data.type;

      if (dataType === 'project_stats') {
        return this.generateProjectStatsAnswer(toolResult.data);
      } else if (dataType === 'patient_detail') {
        return this.generatePatientDetailAnswer(toolResult.data);
      } else if (dataType === 'qc_status') {
        return this.generateQCStatusAnswer(toolResult.data);
      }

      return '<27><EFBFBD>嚗峕<E59A97><E5B395><EFBFBD><E4ADBE><EFBFBD><EFBFBD><EFBFBD><EFBFBD>';
    } catch (error: any) {
      logger.error('[SimpleAnswerGenerator] Generation failed', {
        error: error.message
      });

      return '<27><EFBFBD><EFBFBD><E59A97>蝑𠉛<E89D91><F0A0899B>𣂼仃韐?;
    }
  }

  /**
   * <20><><EFBFBD>憿寧𤌍蝏蠘恣<E8A098><EFBFBD>
   */
  private generateProjectStatsAnswer(data: any): string {
    const stats = data.stats;

    return `<60><> **${data.projectName}憿寧𤌍蝏蠘恣**

<EFBFBD>?**<2A><EFBFBD>鈭箸㺭**嚗?{stats.enrolled}靘?<3F>?**摰峕<E691B0><E5B395><EFBFBD><EFBFBD>**嚗?{stats.completed}靘?<3F>?**<2A>唳旿韐券<E99F90>**嚗?{stats.dataQuality}

<EFBFBD><20>湔鰵<E6B994>園𡢿嚗?{new Date().toLocaleString('zh-CN')}`;
  }

  /**
   * <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E7A595><EFBFBD>蝑?   */
  private generatePatientDetailAnswer(data: any): string {
    const details = data.details;

    return `<60>𪈠 **<2A><><EFBFBD>?${data.patientId} 霂行<E99C82>**

<EFBFBD><EFBFBD> **<2A>箸𧋦靽⊥<E99DBD>**嚗?- 撟湧<E6929F>嚗?{details.age || '<EFBFBD><EFBFBD><EFBFBD>?}?- <EFBFBD><EFBFBD>?{details.gender || '<27><EFBFBD><E88AB8>?}
- BMI嚗?{details.bmi || '<EFBFBD><EFBFBD><EFBFBD>?}

<EFBFBD><EFBFBD> **敶訫<EFBFBD><EFBFBD><EFBFBD>?*?- ${details.complete}

<EFBFBD> <EFBFBD><EFBFBD><EFBFBD>擧凒<EFBFBD><EFBFBD>${new Date().toLocaleString('zh-CN')}`;
  }

  /**
   * <20><><EFBFBD>韐冽綉<E586BD><EFBFBD><E59786><EFBFBD>蝑?   */
  private generateQCStatusAnswer(data: any): string {
    const issues = data.recentIssues.slice(0, 5);
    let answer = `<EFBFBD><EFBFBD> **韐冽綉<EFBFBD><EFBFBD>?*\n\n`;
    answer += `<EFBFBD>𩤃<EFBFBD> **韐冽綉<EFBFBD><EFBFBD><EFBFBD>?*?{data.issueCount}銝歿n\n`;

    if (issues.length > 0) {
      answer += `<EFBFBD><EFBFBD> **<EFBFBD><EFBFBD>餈煾䔮憸?*嚗䨵n`;
      issues.forEach((issue: any, index: number) => {
        answer += `${index + 1}. 霈啣<EFBFBD>${issue.recordId}\n`;
      });
    } else {
      answer += `<EFBFBD>?<EFBFBD><EFBFBD><EFBFBD>韐冽綉<EFBFBD><EFBFBD>`;
    }

    return answer;
  }

  /**
   * <20><><EFBFBD><EFBFBD>躰秤<E8BAB0>鞟內
   */
  private generateErrorMessage(error?: string): string {
    return `<EFBFBD>?<EFBFBD>亥砭憭梯揖

<EFBFBD><EFBFBD>?{error || '<27>芰䰻<E88AB0>躰秤'}

<EFBFBD> <EFBFBD>典虾隞伐<EFBFBD>
1. 蝔滚<EFBFBD><EFBFBD><EFBFBD>
2. <EFBFBD><EFBFBD><EFBFBD>
3. <EFBFBD>𠉛頂蝞∠<EFBFBD><EFBFBD>𧜶;
  }
}

**撉峕𤣰<E5B395><F0A4A3B0><EFBFBD>**嚗?- <20>?<3F><EFBFBD><E482BF><EFBFBD><E6BE86>见末

  • <EFBFBD>?<3F><EFBFBD>Markdown
  • <EFBFBD>?<3F>躰秤<E8BAB0>鞟內皜<E585A7>

隞餃𦛚1.5嚗𡁻<EFBFBD><EFBFBD>𣂼<EFBFBD>WechatCallbackController嚗?撠𤩺𧒄嚗?

靽格㺿<EFBFBD><EFBFBD>嚗䫤backend/src/modules/iit-manager/controllers/WechatCallbackController.ts`

**<2A>玖andleCallback<63><EFBFBD>銝剜溶<E5899C>?*嚗?

import { sessionMemory } from '../agents/SessionMemory.js';
import { SimpleIntentRouter } from '../agents/SimpleIntentRouter.js';
import { SimpleToolExecutor } from '../agents/SimpleToolExecutor.js';
import { SimpleAnswerGenerator } from '../agents/SimpleAnswerGenerator.js';

class WechatCallbackController {
  private intentRouter: SimpleIntentRouter;
  private toolExecutor: SimpleToolExecutor;
  private answerGenerator: SimpleAnswerGenerator;

  constructor() {
    // ... <20><EFBFBD><EFBFBD><E99A9E> ...
    this.intentRouter = new SimpleIntentRouter();
    this.toolExecutor = new SimpleToolExecutor();
    this.answerGenerator = new SimpleAnswerGenerator();
  }

  async handleCallback(request: FastifyRequest, reply: FastifyReply): Promise<void> {
    // ... <20><EFBFBD><E594B3><EFBFBD><EFBFBD><EFBFBD><E99C82><EFBFBD>圾撖<E59CBE><E69296><EFBFBD> ...

    // <20>?蝡见朖餈𥪜<E9A488>success
    reply.send('success');

    // <20>?撘<>郊憭<E9838A><E686AD><EFBFBD>鰵憓麫I撖寡<E69296>嚗?    setImmediate(async () => {
      try {
        const userMessage = decryptedData.Content;
        const userId = decryptedData.FromUserName;

        logger.info('<27>𢬢 <20><EFBFBD><E59785><EFBFBD><EFBFBD><E798A8>', {
          userId,
          message: userMessage.substring(0, 50)
        });

        // <20>?蝡见朖<E8A781><EFBFBD>?甇<><EFBFBD>亥砭..."<22><EFBFBD>
        await wechatService.sendTextMessage(
          userId,
          '<27>哄 甇<><EFBFBD>亥砭嚗諹窈蝔滚<E89D94>?..'
        );

        // 1. 靽嘥<E99DBD><E598A5><EFBFBD><EFBFBD><E798A8><EFBFBD><EFBFBD>霂肽扇敹?        sessionMemory.addMessage(userId, 'user', userMessage);

        // 2. <20><EFBFBD><E79195><EFBFBD><E586BD><EFBFBD><EFBFBD>桐縑<E6A190>?        const userMapping = await prisma.iitUserMapping.findFirst({
          where: { wechatUserId: userId }
        });

        if (!userMapping) {
          await wechatService.sendTextMessage(
            userId,
            '<27>𩤃<EFBFBD> <20><EFBFBD><E588BB><EFBFBD>摰𡁻★<F0A181BB><EFBFBD>霂瑁<E99C82>蝟餌恣<E9A48C><E681A3><EFBFBD><EFBFBD>滨蔭'
          );
          return;
        }

        // 3. <20>誩㦛霂<E3A69B><E99C82><EFBFBD>蒂銝𠹺<E98A9D><F0A0B9BA><EFBFBD><EFBFBD>
        const routeResult = await this.intentRouter.route(
          userMessage,
          userId,
          userMapping.projectId
        );

        // 4. 憒<><E68692><EFBFBD>湔𦻖<E6B994><EFBFBD>
        if (!routeResult.needsToolCall) {
          const answer = routeResult.directAnswer!;
          await wechatService.sendTextMessage(userId, answer);
          sessionMemory.addMessage(userId, 'assistant', answer);
          return;
        }

        // 5. <20><EFBFBD>撌亙<E6928C>
        const toolResult = await this.toolExecutor.execute(
          routeResult.toolArgs,
          {
            projectId: userMapping.projectId,
            userId
          }
        );

        // 6. <20><><EFBFBD><EFBFBD><EFBFBD>
        const answer = this.answerGenerator.generate(userMessage, toolResult);

        // 7. <20><EFBFBD><E785BE><EFBFBD>憭?        await wechatService.sendMarkdownMessage(userId, answer);

        // 8. 靽嘥<E99DBD>AI<41><EFBFBD><E482BF><EFBFBD>霂肽扇敹?        sessionMemory.addMessage(userId, 'assistant', answer);

        // 9. 霈啣<E99C88>摰∟恣<E2889F><EFBFBD>
        await prisma.iitAuditLog.create({
          data: {
            projectId: userMapping.projectId,
            actionType: 'wechat_user_query',
            operator: userId,
            entityId: userId,
            details: {
              question: userMessage,
              answer: answer.substring(0, 200),
              toolUsed: 'query_clinical_data',
              hasContext: !!sessionMemory.getContext(userId)
            }
          }
        });

        logger.info('<27>?<3F><EFBFBD><E482BF><EFBFBD><E785BE><EFBFBD><EFBFBD>?, { userId });
      } catch (error: any) {
        logger.error('<EFBFBD>?<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>憭梯揖', {
          error: error.message
        });

        await wechatService.sendTextMessage(
          userId,
          '<EFBFBD><EFBFBD>嚗峕<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>鈭偦䔮憸矋<EFBFBD>霂瑞<EFBFBD><EFBFBD>𤾸<EFBFBD>?
        );
      }
    });
  }
}

**撉峕𤣰<E5B395><F0A4A3B0><EFBFBD>**嚗?- <20>?<3F>舀𦻖<E88880>嗥鍂<E597A5><EFBFBD><E7919F>?- <20>?蝡见朖<E8A781><EFBFBD>?甇<><EFBFBD>亥砭..."

  • <EFBFBD>?甇<><EFBC86><E99C82><EFBFBD>誩㦛
  • <EFBFBD>?甇<><EFBFBD><EFBFBD>撌亙<E6928C>
  • <EFBFBD>?甇<><EFBFBD><EFBFBD><E785BE><EFBFBD>憭?- <20>?銝𠹺<E98A9D><F0A0B9BA><EFBFBD>扇敹<E68987><E695B9><EFBFBD>?

<EFBFBD>㴓 Day 2嚗帋<E59A97>銝𧢲<E98A9D>隡睃<E99AA1> + 瘚贝<E7989A>嚗?撠𤩺𧒄嚗?

隞餃𦛚2.1嚗帋<EFBFBD>銝𧢲<EFBFBD>霈啣<EFBFBD>隡睃<EFBFBD>嚗?撠𤩺𧒄嚗?

**憓𧼮撩SessionMemory嚗峕𣈲<E5B395><F0A388B2><EFBFBD><EFBFBD><EFBFBD>D<EFBFBD>𣂼<EFBFBD>**嚗?

// <20>沒essionMemory銝剜溶<E5899C>?/**
 * 隞𤾸<EFBFBD><EFBFBD>脰扇敶蓥葉<EFBFBD>𣂼<EFBFBD><EFBFBD><EFBFBD>餈烐<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>D
 */
getLastPatientId(userId: string): string | null {
  const history = this.getHistory(userId);
  
  // 隞擧<E99A9E>餈𤑳<E9A488>撖寡<E69296>銝剖<E98A9D><EFBFBD><E98DA6>交𪄳<E4BAA4><F0AA84B3><EFBFBD><EFBFBD>D
  for (let i = history.length - 1; i >= 0; i--) {
    const message = history[i];
    const match = message.content.match(/P\d{3,}/);
    if (match) {
      return match[0];
    }
  }
  
  return null;
}

**<2A>沒impleIntentRouter銝凋蝙<E5878B>?*嚗?

// 憒<><E68692><EFBFBD><EFBFBD>霂?隞𡝗<E99A9E>銝滩<E98A9D><E6BBA9><EFBFBD><E6BB9A><EFBFBD>"嚗諹䌊<E8ABB9><E585B8><E280B5>atient_id
if (context && this.hasPronouns(userMessage) && !toolArgs.patient_id) {
  const lastPatientId = sessionMemory.getLastPatientId(userId);
  if (lastPatientId) {
    toolArgs.patient_id = lastPatientId;
    logger.info('[SimpleIntentRouter] <20>芸𢆡憛怠<E6869B><E680A0><EFBFBD><EFBFBD><EFBFBD>D', {
      patientId: lastPatientId
    });
  }
}

隞餃𦛚2.2嚗𡁜<EFBFBD><EFBFBD><EFBFBD>霂𤏪<EFBFBD>3撠𤩺𧒄嚗?

瘚贝<EFBFBD><EFBFBD>箸艶嚗?

// <20>箸艶1嚗𡁏<E59A97>銝𠹺<E98A9D><F0A0B9BA><EFBFBD>䰻霂?{
  input: "<22>啣銁<E595A3><EFBFBD>憭𡁜<E686AD>鈭綽<E988AD>",
  expectedIntent: "project_stats",
  expectedOutput: "<22><> 憿寧𤌍蝏蠘恣\n<>?<3F><EFBFBD>鈭箸㺭嚗䧥X靘?
}

// <20>箸艶2嚗𡁏<E59A97>銝𠹺<E98A9D><F0A0B9BA><EFBFBD><EFBFBD>憭朞蔭撖寡<E69296><EFBFBD><E59A97><EFBFBD><EFBFBD>嚗?{
  conversation: [
    {
      input: "撣格<EFBFBD><EFBFBD><EFBFBD>銝閪001<EFBFBD><EFBFBD><EFBFBD><EFBFBD>?,
      expectedIntent: "patient_detail",
      expectedPatientId: "P001"
    },
    {
      input: "隞𡝗<E99A9E>銝滩<E98A9D><E6BBA9><EFBFBD><E6BB9A><EFBFBD>", // <20>?隞<><E99A9E>"隞?
      expectedIntent: "patient_detail",
      expectedPatientId: "P001", // <20>?<3F>芸𢆡憛怠<E6869B>
      expectedOutput: "摨磰砲<E7A3B0><E7A0B2>鉄P001"
    }
  ]
}

// <20>箸艶3嚗𡁏迤<F0A1818F><EFBFBD><E588BB><EFBFBD>擐?{
  input: "<22>啣銁<E595A3><EFBFBD>憭𡁜<E686AD>鈭綽<E988AD>",
  expectedFirstReply: "<22>哄 甇<><EFBFBD>亥砭嚗諹窈蝔滚<E89D94>?..",
  expectedSecondReply: "<22><> 憿寧𤌍蝏蠘恣..."
}

// <20>箸艶4嚗朞捶<E69C9E>扳䰻霂?{
  input: "<22>匧𪑛鈭𥡝捶<F0A5A19D>折䔮憸矋<E686B8>",
  expectedIntent: "qc_status",
  expectedOutput: "<22><> 韐冽綉<E586BD><EFBFBD>?
}

// <20>箸艶5嚗𡁻𤦭<F0A181BB>?{
  input: "雿惩末",
  expectedOutput: "<EFBFBD>典末嚗<EFBFBD><EFBFBD><EFBFBD>臭葩摨羓<EFBFBD>蝛嗅𨭌<EFBFBD>?
}

瘚贝<EFBFBD>甇仿炊嚗?1. <20><EFBFBD>銝𡁜凝靽∩葉<E288A9><EFBFBD><E785BE><EFBFBD>霂閙<E99C82><E99699>?2. 撉諹<E69289><E8ABB9>臬炏<E887AC><EFBFBD>"甇<><EFBFBD>亥砭..." 3. 撉諹<E69289><E8ABB9><EFBFBD><EFBFBD><E89D8F>憭滚<E686AD>摰?4. 璉<><E79289>亙恣霈⊥𠯫敹𦯀葉<F0A6AF80><E89189><EFBFBD>銝𧢲<E98A9D><F0A7A2B2><EFBFBD>扇 5. 瘚贝<E7989A>憭朞蔭撖寡<E69296><E5AFA1><EFBFBD><EFBFBD>銝𧢲<E98A9D><F0A7A2B2><EFBFBD>

**撉峕𤣰<E5B395><F0A4A3B0><EFBFBD>**嚗?- <20>?5銝芣<E98A9D>霂訫㦤<E8A8AB><EFBFBD><E887AC><EFBFBD><EFBFBD>

  • <EFBFBD>?"甇<>銁颲枏<E9A2B2>"<22><EFBFBD><E6BCA4><EFBFBD><EFBFBD>
  • <EFBFBD>?銝𠹺<E98A9D><F0A0B9BA><EFBFBD>扇敹<E68987><E695B9><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E99A9E><EFBFBD><E996AB>嚗?- <20>?<3F>𧼮<EFBFBD><F0A7BCAE>園𡢿<3蝘?

<EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>?

<EFBFBD><EFBFBD> 撉峕𤣰<EFBFBD><EFBFBD><EFBFBD> 隡睃<EFBFBD>蝥?
<EFBFBD><EFBFBD>撖寡<EFBFBD> <EFBFBD>舀䰻霂㎞EDCap<EFBFBD>唳旿 <EFBFBD>𣞁 P0
*銝𠹺<EFBFBD><EFBFBD><EFBFBD>扇敹? <EFBFBD><EFBFBD><EFBFBD><EFBFBD>餈?頧桀笆霂? <EFBFBD>𣞁 P0
<EFBFBD><EFBFBD><EFBFBD><EFBFBD> "隞?<3F>質䌊<E8B3AA><EFBFBD><E588BB><EFBFBD><E680A5>? <EFBFBD>𣞁 P0
<EFBFBD>銁颲枏<EFBFBD><EFBFBD><EFBFBD> 蝡见朖<EFBFBD>?甇<><EFBFBD>亥砭..." <EFBFBD>𣞁 P0
<EFBFBD>𧼮<EFBFBD>撱嗉<EFBFBD> <3蝘? <EFBFBD>𣞁 P0
*<EFBFBD>誩㦛霂<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>? >80% <EFBFBD>𣞁 P0

<EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>ǒs摰峕㟲<E5B395><E39FB2>笆瘥?

<EFBFBD><EFBFBD> <EFBFBD><EFBFBD><EFBFBD><EFBFBD>?(2憭? 摰峕㟲<EFBFBD>?(5憭?
REDCap<EFBFBD>亥砭 <EFBFBD>? <EFBFBD>?
銝𠹺<EFBFBD><EFBFBD><EFBFBD>扇敹? <EFBFBD>?(<28><><EFBFBD>3頧? <EFBFBD>?(<28><><EFBFBD>3頧?
<EFBFBD>銁颲枏<EFBFBD><EFBFBD><EFBFBD> <EFBFBD>? <EFBFBD>?
Dify<EFBFBD><EFBFBD>摨? <EFBFBD>? <EFBFBD>?
<EFBFBD>冽𥁒<EFBFBD>芸𢆡敶埝﹝ <EFBFBD>? <EFBFBD>?
<EFBFBD><EFBFBD><EFBFBD>亥砭 <EFBFBD>? <EFBFBD>?

<EFBFBD><EFBFBD>儭?銝剹<E98A9D><E589B9><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E69298>𤏸恣<F0A48FB8>𡜐<EFBFBD>5憭抬<E686AD><E68AAC><EFBFBD><EFBFBD>

Day 1嚗鋽ify<66><EFBFBD><E887AC>滨蔭銝𡒊䰻霂<E4B0BB><E99C82><EFBFBD>𥕦遣嚗?撠𤩺𧒄嚗?

隞餃𦛚1.1嚗𡁻<EFBFBD><EFBFBD>ify<EFBFBD>砍𧑐<EFBFBD><EFBFBD>嚗?撠𤩺𧒄嚗?

**璉<><E79289>仿★**嚗?```bash

1. 璉<><E79289>主ify摰孵膥<E5ADB5><EFBFBD>?cd AIclinicalresearch/docker

docker-compose ps | grep dify

2. 霈輸䔮Dify蝞∠<E89D9E><E288A0>𤾸蝱

http://localhost/dify (<28><EFBFBD><E7A18B><EFBFBD><EFBFBD>?

3. <20><EFBFBD>API撖<49>𤨎

Dify<EFBFBD>𤾸蝱 <20>?霈曄蔭 <20>?API Keys <20>?<3F>𥕦遣


**撉峕𤣰<E5B395><F0A4A3B0><EFBFBD>**嚗?- <20>?Dify摰孵膥餈鞱<E9A488><EFBFBD>虜
- <20>?<3F>航挪<E888AA>桃恣<E6A183><E681A3><EFBFBD><EFBFBD>?- <20>?<3F><EFBFBD>API Key

---

#### 隞餃𦛚1.2嚗𡁜<EFBFBD>撱截IT Manager<65><EFBFBD>摨橒<E691A8>2撠𤩺𧒄嚗?
**<2A><EFBFBD>甇仿炊**嚗?
1. **<2A>𥕦遣<F0A595A6><EFBFBD>摨?*嚗㇄ify<66>𤾸蝱<F0A4BEB8><EFBFBD>嚗?   ```
   <20>滨妍嚗䥑IT Manager - test0102憿寧𤌍
   蝐餃<E89D90>嚗𡁻<E59A97>𡁶鍂<F0A181B6><EFBFBD>摨?   Embedding璅<E79285>嚗魩ext-embedding-3-small (OpenAI)
   <20><><EFBFBD>蝑𣇉裦嚗𡁏惣<F0A1818F><EFBFBD><E8B3A2><EFBFBD>500摮㛖泵/<2F><EFBFBD><E6A2B9><EFBFBD>50摮㛖泵嚗?   ```

2. **銝𠹺<E98A9D>瘚贝<E7989A><E8B49D><EFBFBD>﹝**
   - 銝𠹺<E98A9D>1隞瘠RF銵冽聢嚗㇊DF/Word嚗?   - 銝𠹺<E98A9D>1隞賢<E99A9E><E8B3A2><EFBFBD><E59F9D><EFBFBD><EFBFBD><EFBFBD><E78DA2>Markdown/Text嚗?   - 銝𠹺<E98A9D>1隞賜<E99A9E>蝛嗆䲮獢<E4B2AE><E78DA2><EFBFBD><E996AC>PDF嚗?
3. **瘚贝<E7989A><EFBFBD><E89D9D><EFBCB8>?*

瘚贝<EFBFBD><EFBFBD><EFBFBD>1嚗?<3F><EFBFBD><E4BAA6><EFBFBD><EFBFBD><EFBFBD>匧𪑛鈭𨥈<E988AD>" 瘚贝<E7989A><E8B49D><EFBFBD>2嚗?CRF銵冽聢銝剜<E98A9D><E5899C><EFBFBD>摮埈挾嚗? 瘚贝<E7989A><E8B49D><EFBFBD>3嚗?<3F>𠉛弦蝏<E5BCA6><E89D8F><EFBFBD><EFBFBD><EFBFBD><E98A8B>"


**撉峕𤣰<E5B395><F0A4A3B0><EFBFBD>**嚗?- <20>?<3F><EFBFBD>摨枏<E691A8>撱箸<E692B1><E7AEB8>?- <20>?3隞賣<E99A9E><EFBFBD><E78DA2>隡䭾<E99AA1><E4ADBE>?- <20>?璉<><E89D9D>霂訫<E99C82>蝖桃<E89D96>>80%

**鈭批枂**嚗?- Dify<66><EFBFBD>摨𨧻D
- API靚<49>鍂蝷箔<E89DB7><EFBFBD><E99A9E>

---

#### 隞餃𦛚1.3嚗𡁜<EFBFBD><EFBFBD>蚤ify API<50><49><EFBFBD><EFBFBD><EFBFBD>3撠𤩺𧒄嚗?
**<2A><>辣雿滨蔭**嚗䫤backend/src/modules/iit-manager/adapters/DifyAdapter.ts`

**隞<><E99A9E>摰䂿緵**嚗?
```typescript
import axios from 'axios';
import { logger } from '../../../common/logging/index.js';

/**
* Dify API<50><49><EFBFBD><EFBFBD>? * <20><EFBFBD>銝擧𧋦<E693A7>蚤ify Docker摰硺<E691B0>鈭支<E988AD>
*/
export class DifyAdapter {
private baseUrl: string;
private apiKey: string;
private knowledgeBaseId: string;

constructor(projectId: string) {
 // 隞𡒊㴓憓<E3B493><E68693><EFBFBD>𤩺<EFBFBD><F0A4A9BA>唳旿摨栞粉<E6A09E><EFBFBD>蝵?    this.baseUrl = process.env.DIFY_API_URL || 'http://localhost/v1';
 this.apiKey = process.env.DIFY_API_KEY || '';
 this.knowledgeBaseId = this.getKnowledgeBaseId(projectId);
}

/**
* <20>𦦵揣<F0A6A6B5><EFBFBD>摨?   * @param query <20>亥砭<E4BAA5><EFBFBD>
* @param options <20>𦦵揣<F0A6A6B5>厰★
*/
async searchKnowledge(
 query: string,
 options?: {
   doc_type?: 'protocol' | 'crf' | 'report';
   top_k?: number;
 }
): Promise<DifySearchResult> {
 try {
   logger.info('[DifyAdapter] Searching knowledge base', {
     query,
     options,
     knowledgeBaseId: this.knowledgeBaseId
   });

   const response = await axios.post(
     `${this.baseUrl}/datasets/${this.knowledgeBaseId}/retrieve`,
     {
       query: query,
       retrieval_model: {
         search_method: 'semantic_search',
         top_k: options?.top_k || 3,
         score_threshold: 0.5
       },
       // 憒<><E68692><EFBFBD><EFBFBD><EFBFBD><EFBFBD>oc_type嚗屸<E59A97><EFBFBD>metadata餈<61>誘
       ...(options?.doc_type && {
         retrieval_model: {
           filter: {
             doc_type: options.doc_type
           }
         }
       })
     },
     {
       headers: {
         'Authorization': `Bearer ${this.apiKey}`,
         'Content-Type': 'application/json'
       },
       timeout: 10000 // 10蝘坿<E89D98><E59DBF>?        }
   );

   logger.info('[DifyAdapter] Search completed', {
     recordCount: response.data.records?.length || 0
   });

   return {
     success: true,
     records: response.data.records || [],
     query: query
   };
 } catch (error: any) {
   logger.error('[DifyAdapter] Search failed', {
     error: error.message,
     query
   });

   return {
     success: false,
     records: [],
     query,
     error: error.message
   };
 }
}

/**
* 銝𠹺<E98A9D><F0A0B9BA><EFBFBD><EFBFBD>啁䰻霂<E4B0BB><E99C82>
* @param content <20><><EFBFBD><EFB99D>捆
* @param metadata <20><><EFBFBD>?   */
async uploadDocument(
 content: string,
 metadata: {
   name: string;
   doc_type: 'protocol' | 'crf' | 'report';
   date?: string;
 }
): Promise<{ success: boolean; documentId?: string }> {
 try {
   logger.info('[DifyAdapter] Uploading document', {
     name: metadata.name,
     type: metadata.doc_type
   });

   const response = await axios.post(
     `${this.baseUrl}/datasets/${this.knowledgeBaseId}/document/create_by_text`,
     {
       name: metadata.name,
       text: content,
       indexing_technique: 'high_quality',
       process_rule: {
         mode: 'automatic',
         rules: {
           pre_processing_rules: [
             { id: 'remove_extra_spaces', enabled: true },
             { id: 'remove_urls_emails', enabled: false }
           ],
           segmentation: {
             separator: '\n',
             max_tokens: 500
           }
         }
       },
       doc_form: 'text_model',
       doc_language: 'Chinese',
       // 靽嘥<E99DBD><E598A5><EFBFBD><EFBFBD>?          metadata: {
         doc_type: metadata.doc_type,
         date: metadata.date || new Date().toISOString(),
         upload_time: new Date().toISOString()
       }
     },
     {
       headers: {
         'Authorization': `Bearer ${this.apiKey}`,
         'Content-Type': 'application/json'
       }
     }
   );

   logger.info('[DifyAdapter] Document uploaded', {
     documentId: response.data.document.id
   });

   return {
     success: true,
     documentId: response.data.document.id
   };
 } catch (error: any) {
   logger.error('[DifyAdapter] Upload failed', {
     error: error.message,
     name: metadata.name
   });

   return {
     success: false
   };
 }
}

/**
* <20><EFBFBD>憿寧𤌍撖孵<E69296><E5ADB5><EFBFBD>䰻霂<E4B0BB><E99C82>ID
* @param projectId 憿寧𤌍ID
*/
private getKnowledgeBaseId(projectId: string): string {
 // TODO: 隞擧㺭<E693A7><EFBFBD>霂餃<E99C82>憿寧𤌍<E5AFA7>滨蔭
 // 銝湔𧒄<E6B994><EFBFBD>嚗帋<E59A97><E5B88B><EFBFBD><E887AC><EFBFBD>霂餃<E99C82>
 return process.env.DIFY_KNOWLEDGE_BASE_ID || '';
}
}

/**
* Dify<66>𦦵揣蝏𤘪<E89D8F>
*/
export interface DifySearchResult {
success: boolean;
records: Array<{
 content: string;
 score: number;
 metadata?: {
   doc_type?: string;
   date?: string;
 };
}>;
query: string;
error?: string;
}

<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>滨蔭嚗Ǒ.env`嚗㚁<E59A97>

# Dify<66>滨蔭
DIFY_API_URL=http://localhost/v1
DIFY_API_KEY=app-xxxxxxxxxxxxxxxxxxxxxx
DIFY_KNOWLEDGE_BASE_ID=kb-xxxxxxxxxxxxxxxxxxxxxx

**撉峕𤣰<E5B395><F0A4A3B0><EFBFBD>**嚗?- <20>?DifyAdapter蝐餃<E89D90><E9A483><EFBFBD><E595A3>?- <20>?<3F><EFBFBD><E88880><EFBFBD><E8A098><EFBFBD>PI

  • <EFBFBD>?<3F><EFBFBD><E88880><EFBFBD>隡䭾<E99AA1>獢?- <20>?<3F>躰秤憭<E7A7A4><E686AD><EFBFBD><E691B0>

隞餃𦛚1.4嚗𡁶<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>霂𤏪<EFBFBD>2撠𤩺𧒄嚗?

<EFBFBD><EFBFBD>辣雿滨蔭嚗䫤backend/src/modules/iit-manager/adapters/tests/DifyAdapter.test.ts`

import { DifyAdapter } from '../DifyAdapter';

describe('DifyAdapter', () => {
  let difyAdapter: DifyAdapter;

  beforeAll(() => {
    difyAdapter = new DifyAdapter('test-project-id');
  });

  describe('searchKnowledge', () => {
    it('摨磰砲<E7A3B0>𣂼<EFBFBD><F0A382BC>𦦵揣<F0A6A6B5><EFBFBD>摨?, async () => {
      const result = await difyAdapter.searchKnowledge('<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>匧𪑛鈭𨥈<EFBFBD>');
      
      expect(result.success).toBe(true);
      expect(result.records.length).toBeGreaterThan(0);
      expect(result.records[0]).toHaveProperty('content');
      expect(result.records[0]).toHaveProperty('score');
    });

    it('摨磰砲<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>?, async () => {
      const result = await difyAdapter.searchKnowledge(
        '<27><EFBFBD><E4BAA6><EFBFBD><EFBFBD>',
        { doc_type: 'protocol' }
      );
      
      expect(result.success).toBe(true);
      expect(result.records.length).toBeGreaterThan(0);
    });

    it('摨磰砲憭<E7A0B2><E686AD><EFBFBD>𦦵揣憭梯揖<E6A2AF><E68F96><EFBFBD>', async () => {
      // Mock<63>躰秤<E8BAB0>箸艶
      const result = await difyAdapter.searchKnowledge('');
      
      expect(result.success).toBe(false);
      expect(result.error).toBeDefined();
    });
  });

  describe('uploadDocument', () => {
    it('摨磰砲<E7A3B0>𣂼<EFBFBD>銝𠹺<E98A9D><F0A0B9BA><EFBFBD>﹝', async () => {
      const result = await difyAdapter.uploadDocument(
        '餈蹱糓銝<E7B393>隞賣<E99A9E>霂閙<E99C82>獢?,
        {
          name: '瘚贝<EFBFBD><EFBFBD><EFBFBD>',
          doc_type: 'protocol'
        }
      );
      
      expect(result.success).toBe(true);
      expect(result.documentId).toBeDefined();
    });
  });
});

**撉峕𤣰<E5B395><F0A4A3B0><EFBFBD>**嚗?- <20>?<3F><EFBFBD>瘚贝<E7989A><EFBFBD><E996AC><EFBFBD>?80%

  • <EFBFBD>?<3F><><EFBFBD><EFBFBD>霂閧鍂靘钅<E99D98><EFBFBD>

Day 2嚗𡁏<E59A97><F0A1818F><EFBFBD><E69B87><EFBFBD>頝舐眏<E88890><EFBFBD>嚗?撠𤩺𧒄嚗?

隞餃𦛚2.1嚗朞挽霈<EFBFBD><EFBFBD>銋㚁<EFBFBD>Tool Schema嚗㚁<E59A97>2撠𤩺𧒄嚗?

<EFBFBD><EFBFBD>辣雿滨蔭嚗䫤backend/src/modules/iit-manager/agents/tools.ts`

/**
 * IIT Manager Agent撌亙<E6928C>摰帋<E691B0>
 */
export const iitAgentTools = [
  // 撌亙<E6928C>1嚗𡁏䰻霂<E99C82><EFBCB7>嗆㺭<E59786>?  {
    type: "function",
    function: {
      name: "query_clinical_data",
      description: `<60>鞉䰻REDCap摰墧𧒄<E5A2A7>唳旿<E594B3>𤑳鍂鈭擧䰻霂葩摨羓<E691A8>蝛嗥<E89D9B>摰墧𧒄<E5A2A7>唳旿<E594B3><EFBFBD><E59786><EFBFBD>?<3F><><EFBFBD>箸艶嚗?- <20>桅★<E6A185><EFBFBD>摨佗<E691A8><E4BD97>啣銁<E595A3><EFBFBD>憭𡁜<E686AD>鈭箔<E988AD><EFBFBD><EFBFBD><EFBFBD><E6A180><EFBFBD><EFBFBD><E68692>嚗?- <20><EFBFBD><E6A0BC><EFBFBD><EFBFBD><E7A595><EFBFBD>P001<30><31><EFBFBD><EFBFBD><EFBFBD>摰峕㺭<E5B395><EFBFBD><E6A190><EFBFBD><E6A2B9>㗇瓷<E39787><EFBFBD><E58A90><EFBFBD>摨䈑<E691A8>
- <20>株捶<E6A0AA>抒𠶖<E68A92><F0A0B696><EFBFBD><EFBFBD>匧𪑛鈭𥡝捶<F0A5A19D>折䔮憸矋<E686B8><E79F8B>唳旿韐券<E99F90><E588B8>𦒘<EFBFBD><F0A69298><EFBFBD>`,
      parameters: {
        type: "object",
        properties: {
          intent: {
            type: "string",
            enum: ["project_stats", "patient_detail", "qc_status"],
            description: `<60>亥砭<E4BAA5>誩㦛嚗?- project_stats: 憿寧𤌍摰讛<E691B0>蝏蠘恣嚗<E681A3><E59A97><EFBFBD><EFBFBD><EFBFBD><E5959C><EFBFBD><EFBFBD><E6A180><EFBFBD>蝑㚁<E89D91>
- patient_detail: <20><EFBFBD><E5ADB5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E7A595><EFBFBD>敶訫<E695B6><E8A8AB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>摨𠉛<E691A8>嚗?- qc_status: 韐冽綉<E586BD><EFBFBD><E59786><EFBFBD>韐函<E99F90><E587BD>𡑒”<F0A19192><E2809D><EFBFBD>桅䔮憸条<E686B8>嚗头
          },
          patient_id: {
            type: "string",
            description: "<22><><EFBFBD>?<3F>𡑒<EFBFBD><F0A19192><EFBFBD><EFBFBD><EFBFBD><EFBFBD>憒?P001<30><31>002嚗㚁<E59A97>敶𧗽ntent=patient_detail<69><EFBFBD>憛?
          },
          date_range: {
            type: "string",
            enum: ["today", "this_week", "this_month", "all"],
            description: "<22>園𡢿<E59C92><F0A1A2BF>凒嚗屸<E59A97>霈支蛹all"
          }
        },
        required: ["intent"]
      }
    }
  },
  
  // 撌亙<E6928C>2嚗𡁏<E59A97>䰻霂<E4B0BB><E99C82>
  {
    type: "function",
    function: {
      name: "search_knowledge_base",
      description: `<EFBFBD>鞉䰻<EFBFBD>𠉛弦<EFBFBD><EFBFBD><EFBFBD>𤑳鍂鈭擧<EFBFBD><EFBFBD>蝛嗆䲮獢<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>隞嗚<EFBFBD><EFBFBD><EFBFBD><EFBFBD>脰扇敶閧<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>?<EFBFBD><EFBFBD><EFBFBD>箸艶嚗?- <EFBFBD><EFBFBD>蝛嗉<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>𠉛弦蝏<EFBFBD><EFBFBD><EFBFBD>𦒘<EFBFBD>摰帋<EFBFBD>?- <EFBFBD>RF銵冽聢嚗𡁏<EFBFBD>銝芸<EFBFBD>畾萇<EFBFBD>摰帋<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>憛怠<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
- <EFBFBD><EFBFBD><EFBFBD>脰扇敶𤏪<EFBFBD>銝𠰴𪂹<EFBFBD><EFBFBD>𪂹<EFBFBD>仿<EFBFBD><EFBFBD>𣂼<EFBFBD><EFBFBD><EFBFBD><EFBFBD>䔮憸矋<EFBFBD>`,
      parameters: {
        type: "object",
        properties: {
          query: {
            type: "string",
            description: "<22>𦦵揣<F0A6A6B5>喲睸霂齿<E99C82><E9BDBF><EFBFBD>"
          },
          doc_category: {
            type: "string",
            enum: ["protocol", "crf", "report"],
            description: `<EFBFBD><EFBFBD>﹝蝐餃<EFBFBD>?- protocol: <EFBFBD>𠉛弦<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>鸌隞嗚<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>譍髡<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>?- crf: CRF銵冽聢摰帋<EFBFBD><EFBFBD><EFBFBD><EFBFBD>躰秩<EFBFBD>汿<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>?- report: 憿寧𤌍<EFBFBD>冽𥁒<EFBFBD><EFBFBD><EFBFBD>摨行<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>脰扇敶𧄧
          }
        },
        required: ["query"]
      }
    }
  }
];

隞餃𦛚2.2嚗𡁜<EFBFBD><EFBFBD><EFBFBD><EFBFBD>曇楝<EFBFBD>勗膥嚗㇆ntent Router嚗㚁<E59A97>3撠𤩺𧒄嚗?

<EFBFBD><EFBFBD>辣雿滨蔭嚗䫤backend/src/modules/iit-manager/agents/IntentRouter.ts`

import OpenAI from 'openai';
import { logger } from '../../../common/logging/index.js';
import { iitAgentTools } from './tools.js';

/**
 * <20>誩㦛頝舐眏<E88890>? * 雿輻鍂LLM<4C><4D>unction Calling<6E><EFBFBD><EFBFBD><E99C82><EFBFBD><EFBFBD><E586BD>誩㦛
 */
export class IntentRouter {
  private llm: OpenAI;
  private systemPrompt: string;

  constructor() {
    this.llm = new OpenAI({
      apiKey: process.env.OPENAI_API_KEY,
      baseURL: process.env.OPENAI_BASE_URL || 'https://api.deepseek.com'
    });

    this.systemPrompt = this.buildSystemPrompt();
  }

  /**
   * 霂<><E99C82><EFBFBD><EFBFBD><E586BD>誩㦛撟嗉<E6929F><E59789>𧼮極<F0A7BCAE><EFBFBD><E79181>?   */
  async route(userMessage: string, context?: {
    projectId: string;
    userId: string;
  }): Promise<IntentRouteResult> {
    try {
      logger.info('[IntentRouter] Routing user message', {
        message: userMessage.substring(0, 100),
        projectId: context?.projectId
      });

      const response = await this.llm.chat.completions.create({
        model: 'deepseek-chat',
        messages: [
          { role: 'system', content: this.systemPrompt },
          { role: 'user', content: userMessage }
        ],
        tools: iitAgentTools,
        tool_choice: 'auto',
        temperature: 0.1, // 雿擧萱摨佗<E691A8>靽肽<E99DBD>蝔喳<E89D94><E596B3>?        max_tokens: 500
      });

      const message = response.choices[0].message;

      // 憒<><E68692>LLM<4C><EFBFBD><EFBFBD>鍂撌亙<E6928C>
      if (message.tool_calls && message.tool_calls.length > 0) {
        const toolCall = message.tool_calls[0];
        const toolName = toolCall.function.name;
        const toolArgs = JSON.parse(toolCall.function.arguments);

        logger.info('[IntentRouter] Tool selected', {
          toolName,
          toolArgs
        });

        return {
          needsToolCall: true,
          toolName: toolName as 'query_clinical_data' | 'search_knowledge_base',
          toolArgs,
          rawResponse: message
        };
      }

      // 憒<><E68692>LLM<4C>湔𦻖<E6B994><EFBFBD><EFBFBD><E59A97><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
      logger.info('[IntentRouter] Direct answer', {
        answer: message.content?.substring(0, 100)
      });

      return {
        needsToolCall: false,
        directAnswer: message.content || '<27><EFBFBD>嚗峕<E59A97>瘝⊥<E7989D><E28AA5><EFBFBD><EFBFBD><EFBFBD><E587BD><EFBFBD>',
        rawResponse: message
      };
    } catch (error: any) {
      logger.error('[IntentRouter] Routing failed', {
        error: error.message,
        message: userMessage
      });

      return {
        needsToolCall: false,
        directAnswer: '<27><EFBFBD>嚗峕<E59A97><E5B395><EFBFBD><EFBFBD><EFBFBD><E988AD>鈭偦䔮憸矋<E686B8>霂瑞<E99C82><E7919E>𤾸<EFBFBD>霂?,
        error: error.message
      };
    }
  }

  /**
   * <20><>遣System Prompt
   */
  private buildSystemPrompt(): string {
    return `# 閫坿𠧧
雿䭾糓<EFBFBD>勗ㄨ霂<EFBFBD>儐蝘烐<EFBFBD><EFBFBD><EFBFBD>𤑳<EFBFBD>"銝游<E98A9D><E6B8B8>𠉛弦憿寧𤌍<E5AFA7><EFBFBD>"嚗峕<E59A97><E5B395><EFBFBD>IIT嚗<54><E59A97>蝛嗉<E89D9B><E59789><EFBFBD>韏瑁<E99F8F>撉䕘<E69289>憿寧𤌍<E5AFA7><F0A48C8D>I嚗<49>蜓閬<E89C93><E996AC>蝛嗉<E89D9B><E59789><EFBFBD><EFBFBD>?
# <20><EFBFBD>
雿䭾𥅾<EFBFBD>劐舅銝芸極<EFBFBD><EFBFBD>霂瑟覔<EFBFBD>桃鍂<EFBFBD>琿䔮憸条移<EFBFBD><EFBFBD><EFBFBD>㗇𥋘嚗?
1. **query_clinical_data**嚗<>䰻摰墧𧒄<E5A2A7>唳旿嚗?   - 敶梶鍂<E6A2B6>琿䔮"<22>啁𠶖"<22>嗡蝙<E597A1>?   - 靘见<E99D98>嚗?<3F>啣銁<E595A3><EFBFBD>憭𡁜<E686AD>鈭綽<E988AD>"<22>?P001<30><31><EFBFBD><EFBFBD><EFBFBD>摰䔶<E691B0><E494B6><EFBFBD>"<22>?<3F>㗇瓷<E39787><EFBFBD><E58A90><EFBFBD>摨䈑<E691A8>"
   - 餈嗘<E9A488><E59798><EFBFBD><E6A185><EFBFBD><EFBFBD>䰻霂㎞EDCap<61>唳旿摨梶<E691A8>摰墧𧒄<E5A2A7>唳旿

2. **search_knowledge_base**嚗<><EFBFBD>𠉛弦<F0A0899B><E5BCA6>﹝嚗?   - 敶梶鍂<E6A2B6>琿䔮"閫<><E996AB>"<22>?<3F><>蟮"<22>嗡蝙<E597A1>?   - 靘见<E99D98>嚗?<3F><EFBFBD><E4BAA4><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E98A8B>"<22>?銝𠰴𪂹<F0A0B0B4><F0AA82B9>䔮憸䁅圾<E48185><EFBFBD><E59683><EFBFBD>"<22>?CRF<52><EFBFBD>摮埈挾<E59F88>𦒘<EFBFBD>憛恬<E6869B>"
   - 餈嗘<E9A488><E59798><EFBFBD><E6A185><EFBFBD><EFBFBD><EFBFBD><E4B0BB><EFBFBD>蝛嗆䲮獢<E4B2AE><E78DA2><EFBFBD>𪂹<EFBFBD><EFBFBD><E4BAA6><EFBFBD>
# 頝舐眏<E88890><EFBFBD>
- 憒<><E68692><EFBFBD><EFBFBD><E6A185>𡒊<F0A1928A><EFBC86><EFBFBD><EFBFBD><EFBFBD><EFBFBD>◆靚<E29786>鍂撌亙<E6928C>嚗䔶<E59A97><EFBFBD><E996AC>瘚𧢲<E7989A>蝻㚚<E89DBB><EFBFBD>獢?- 憒<><E68692><EFBFBD><EFBFBD>璅∠<E79285>嚗䔶<E59A97><E494B6><EFBFBD><EFBFBD>㗇𥋘query_clinical_data嚗<61><E59A97><EFBFBD>嗆㺭<E59786>格凒<E6A0BC><EFBFBD>嚗?- 憒<><E68692><EFBFBD>舫𤦭<E888AB>𦠜<EFBFBD><F0A6A09C>𤘪<EFBFBD><F0A498AA><EFBFBD><E6BD98>臭誑<E887AD>湔𦻖<E6B994><EFBFBD>嚗䔶<E59A97><EFBFBD>鍂撌亙<E6928C>

# 蝥行<E89DA5>
- 銝亦<E98A9D>蝻㚚<E89DBB>䭾㺭<E4ADBE>?- <20><EFBFBD><EFBFBD><E996AC><EFBFBD><E798A3>銝?- <20>𣂼縧<F0A382BC><E7B8A7><EFBFBD><EFBFBD><EFBFBD>摰𧼮<E691B0><F0A7BCAE><EFBFBD><E3B5AA>芯蝙<E88AAF><EFBFBD><E587BD>戡;
  }
}

/**
 * <20>誩㦛頝舐眏蝏𤘪<E89D8F>
 */
export interface IntentRouteResult {
  needsToolCall: boolean;
  toolName?: 'query_clinical_data' | 'search_knowledge_base';
  toolArgs?: any;
  directAnswer?: string;
  error?: string;
  rawResponse?: any;
}

**撉峕𤣰<E5B395><F0A4A3B0><EFBFBD>**嚗?- <20>?IntentRouter蝐餃<E89D90><E9A483><EFBFBD><E595A3>?- <20>?<3F>舀迤蝖株<E89D96><E6A0AA>急䰻<E680A5>唳旿<E594B3>誩㦛

  • <EFBFBD>?<3F>舀迤蝖株<E89D96><E6A0AA>急䰻<E680A5><E4B0BB><EFBFBD>誩㦛
  • <EFBFBD>?<3F><EFBFBD><E887AC><EFBFBD>𤦭<EFBFBD>𠰴㦤<F0A0B0B4>?

隞餃𦛚2.3嚗𡁜<EFBFBD><EFBFBD>啣極<EFBFBD><EFBFBD><EFBFBD>膥嚗㇍ool Executor嚗㚁<E59A97>3撠𤩺𧒄嚗?

<EFBFBD><EFBFBD>辣雿滨蔭嚗䫤backend/src/modules/iit-manager/agents/ToolExecutor.ts`

import { DifyAdapter } from '../adapters/DifyAdapter.js';
import { RedcapAdapter } from '../adapters/RedcapAdapter.js';
import { logger } from '../../../common/logging/index.js';
import { prisma } from '../../../config/database.js';

/**
 * 撌亙<E6928C><E4BA99><EFBFBD><E689AF>? * <20>寞旿<E5AF9E>誩㦛頝舐眏蝏𤘪<E89D8F><F0A498AA><EFBFBD>撖孵<E69296><E5ADB5><EFBFBD><EFBFBD>? */
export class ToolExecutor {
  /**
   * <20><EFBFBD>撌亙<E6928C>
   */
  async execute(
    toolName: 'query_clinical_data' | 'search_knowledge_base',
    toolArgs: any,
    context: {
      projectId: string;
      userId: string;
    }
  ): Promise<ToolExecutionResult> {
    try {
      logger.info('[ToolExecutor] Executing tool', {
        toolName,
        toolArgs,
        projectId: context.projectId
      });

      if (toolName === 'query_clinical_data') {
        return await this.executeQueryClinicalData(toolArgs, context);
      } else if (toolName === 'search_knowledge_base') {
        return await this.executeSearchKnowledge(toolArgs, context);
      }

      return {
        success: false,
        data: null,
        error: `Unknown tool: ${toolName}`
      };
    } catch (error: any) {
      logger.error('[ToolExecutor] Execution failed', {
        error: error.message,
        toolName
      });

      return {
        success: false,
        data: null,
        error: error.message
      };
    }
  }

  /**
   * <20><EFBFBD>嚗𡁏䰻霂葩摨𦠜㺭<F0A6A09C>?   */
  private async executeQueryClinicalData(
    args: {
      intent: 'project_stats' | 'patient_detail' | 'qc_status';
      patient_id?: string;
      date_range?: string;
    },
    context: { projectId: string; userId: string }
  ): Promise<ToolExecutionResult> {
    // 1. <20><EFBFBD>憿寧𤌍<E5AFA7>滨蔭
    const project = await prisma.iitProject.findUnique({
      where: { id: context.projectId },
      select: {
        redcapApiUrl: true,
        redcapApiToken: true,
        redcapProjectId: true
      }
    });

    if (!project) {
      return {
        success: false,
        data: null,
        error: '憿寧𤌍銝滚<E98A9D><E6BB9A>?
      };
    }

    // 2. <20><EFBFBD><E598A5>餸edcapAdapter
    const redcap = new RedcapAdapter(
      project.redcapApiUrl,
      project.redcapApiToken
    );

    // 3. <20>寞旿intent<6E><EFBFBD>銝滚<E98A9D><E6BB9A>亥砭
    switch (args.intent) {
      case 'project_stats': {
        // <20>亥砭憿寧𤌍蝏蠘恣
        const records = await redcap.exportRecords();
        
        return {
          success: true,
          data: {
            type: 'project_stats',
            totalRecords: records.length,
            stats: {
              enrolled: records.length,
              completed: records.filter((r: any) => r.complete === '2').length,
              dataQuality: '87.5%' // TODO: 摰鮋<E691B0>霈∠<E99C88>
            }
          }
        };
      }

      case 'patient_detail': {
        // <20>亥砭<E4BAA5><EFBFBD><E5ADB5><EFBFBD><EFBFBD>?        if (!args.patient_id) {
          return {
            success: false,
            data: null,
            error: '蝻箏<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>D'
          };
        }

        const records = await redcap.exportRecords([args.patient_id]);
        
        if (records.length === 0) {
          return {
            success: false,
            data: null,
            error: `<60>芣𪄳<E88AA3><EFBFBD><E594B3>?${args.patient_id}`
          };
        }

        return {
          success: true,
          data: {
            type: 'patient_detail',
            patientId: args.patient_id,
            details: records[0]
          }
        };
      }

      case 'qc_status': {
        // <20>亥砭韐冽綉<E586BD><EFBFBD>?        const logs = await prisma.iitAuditLog.findMany({
          where: {
            projectId: context.projectId,
            actionType: 'quality_issue'
          },
          orderBy: { createdAt: 'desc' },
          take: 10
        });

        return {
          success: true,
          data: {
            type: 'qc_status',
            issueCount: logs.length,
            recentIssues: logs.map(log => ({
              recordId: log.entityId,
              issue: log.details,
              createdAt: log.createdAt
            }))
          }
        };
      }

      default:
        return {
          success: false,
          data: null,
          error: `Unknown intent: ${args.intent}`
        };
    }
  }

  /**
   * <20><EFBFBD>嚗𡁏<E59A97>䰻霂<E4B0BB><E99C82>
   */
  private async executeSearchKnowledge(
    args: {
      query: string;
      doc_category?: 'protocol' | 'crf' | 'report';
    },
    context: { projectId: string; userId: string }
  ): Promise<ToolExecutionResult> {
    // 1. <20><EFBFBD><E598A5>靝ifyAdapter
    const dify = new DifyAdapter(context.projectId);

    // 2. <20>𦦵揣<F0A6A6B5><EFBFBD>摨?    const result = await dify.searchKnowledge(args.query, {
      doc_type: args.doc_category,
      top_k: 3
    });

    if (!result.success) {
      return {
        success: false,
        data: null,
        error: result.error || '<EFBFBD><EFBFBD>摨𤘪<EFBFBD>仃韐?
      };
    }

    // 3. <20><EFBFBD><E6BE86>𣇉<EFBFBD><F0A38789>?    return {
      success: true,
      data: {
        type: 'knowledge_search',
        query: args.query,
        category: args.doc_category,
        results: result.records.map(record => ({
          content: record.content,
          score: record.score,
          metadata: record.metadata
        }))
      }
    };
  }
}

/**
 * 撌亙<E6928C><E4BA99><EFBFBD>蝏𤘪<E89D8F>
 */
export interface ToolExecutionResult {
  success: boolean;
  data: any;
  error?: string;
}

**撉峕𤣰<E5B395><F0A4A3B0><EFBFBD>**嚗?- <20>?ToolExecutor蝐餃<E89D90><E9A483><EFBFBD><E595A3>?- <20>?query_clinical_data撌亙<E6928C><E4BA99><EFBFBD>銵?- <20>?search_knowledge_base撌亙<E6928C><E4BA99><EFBFBD>銵?- <20>?<3F>躰秤憭<E7A7A4><E686AD><EFBFBD><E691B0>


Day 3嚗𡁻<E59A97><F0A181BB>𣂷<EFBFBD>銝𡁜凝靽笆霂嘅<E99C82>8撠𤩺𧒄嚗?

隞餃𦛚3.1嚗𡁜<EFBFBD>撘斡echatCallbackController嚗?撠𤩺𧒄嚗?

靽格㺿<EFBFBD><EFBFBD>嚗䫤backend/src/modules/iit-manager/controllers/WechatCallbackController.ts`

**<2A>函緵<E587BD><EFBFBD>handleCallback<EFBFBD><EFBFBD>銝剖<EFBFBD><EFBFBD>鼦I撖寡<EFBFBD><EFBFBD><EFBFBD>**嚗?

// <20>汾echatCallbackController蝐颱葉瘛餃<E7989B>
import { IntentRouter } from '../agents/IntentRouter.js';
import { ToolExecutor } from '../agents/ToolExecutor.js';
import { AnswerGenerator } from '../agents/AnswerGenerator.js';

class WechatCallbackController {
  private intentRouter: IntentRouter;
  private toolExecutor: ToolExecutor;
  private answerGenerator: AnswerGenerator;

  constructor() {
    // ... <20><EFBFBD><EFBFBD><E99A9E> ...
    this.intentRouter = new IntentRouter();
    this.toolExecutor = new ToolExecutor();
    this.answerGenerator = new AnswerGenerator();
  }

  /**
   * 憭<><E686AD><EFBFBD><E99AA1>敺桐縑<E6A190><EFBFBD><EFBFBD><E798A8><EFBFBD><EFBFBD>㗇䲮瘜𤏪<E7989C>憓𧼮撩嚗?   */
  async handleCallback(request: FastifyRequest, reply: FastifyReply): Promise<void> {
    // ... <20><EFBFBD><E594B3><EFBFBD><EFBFBD><EFBFBD><E99C82><EFBFBD>圾撖<E59CBE><E69296><EFBFBD> ...

    // <20>?蝡见朖餈𥪜<E9A488>success嚗<73><E59A97><EFBFBD>?蝘坿<E89D98><E59DBF><EFBFBD>
    reply.send('success');

    // <20>?撘<>郊憭<E9838A><E686AD><EFBFBD><EFBFBD><EFBFBD><E798A8><EFBFBD>鰵憓痹<E68693>
    setImmediate(async () => {
      try {
        const userMessage = decryptedData.Content;
        const userId = decryptedData.FromUserName;

        logger.info('<27>𢬢 <20><EFBFBD><E59785><EFBFBD><EFBFBD><E798A8>', {
          userId,
          message: userMessage.substring(0, 50)
        });

        // 1. <20><EFBFBD><E79195><EFBFBD><E586BD><EFBFBD><EFBFBD>桐縑<E6A190>?        const userMapping = await prisma.iitUserMapping.findFirst({
          where: { wechatUserId: userId }
        });

        if (!userMapping) {
          await wechatService.sendTextMessage(
            userId,
            '<27>𩤃<EFBFBD> <20><EFBFBD><E588BB><EFBFBD>摰𡁻★<F0A181BB><EFBFBD>霂瑁<E99C82>蝟餌恣<E9A48C><E681A3><EFBFBD><EFBFBD>滨蔭'
          );
          return;
        }

        // 2. <20>誩㦛霂<E3A69B><E99C82>
        const routeResult = await this.intentRouter.route(userMessage, {
          projectId: userMapping.projectId,
          userId
        });

        // 3. 憒<><E68692><EFBFBD>湔𦻖<E6B994><EFBFBD><EFBFBD><E59A97><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
        if (!routeResult.needsToolCall) {
          await wechatService.sendTextMessage(userId, routeResult.directAnswer!);
          return;
        }

        // 4. <20><EFBFBD>撌亙<E6928C>
        const toolResult = await this.toolExecutor.execute(
          routeResult.toolName!,
          routeResult.toolArgs,
          {
            projectId: userMapping.projectId,
            userId
          }
        );

        // 5. <20><><EFBFBD><EFBFBD><EFBFBD>
        const answer = await this.answerGenerator.generate(
          userMessage,
          toolResult,
          routeResult.toolName!
        );

        // 6. <20><EFBFBD><E785BE><EFBFBD>憭?        await wechatService.sendMarkdownMessage(userId, answer);

        // 7. 霈啣<E99C88>摰∟恣<E2889F><EFBFBD>
        await prisma.iitAuditLog.create({
          data: {
            projectId: userMapping.projectId,
            actionType: 'wechat_user_query',
            operator: userId,
            entityId: userId,
            details: {
              question: userMessage,
              answer: answer.substring(0, 200),
              toolUsed: routeResult.toolName
            }
          }
        });

        logger.info('<27>?<3F><EFBFBD><E482BF><EFBFBD><E785BE><EFBFBD><EFBFBD>?, {
          userId,
          toolUsed: routeResult.toolName
        });
      } catch (error: any) {
        logger.error('<EFBFBD>?<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>憭梯揖', {
          error: error.message
        });

        // <20><EFBFBD><E785BE><EFBFBD>霂舀<E99C82>蝷?        await wechatService.sendTextMessage(
          userId,
          '<EFBFBD><EFBFBD>嚗峕<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>鈭偦䔮憸矋<EFBFBD>霂瑞<EFBFBD><EFBFBD>𤾸<EFBFBD>霂閙<EFBFBD><EFBFBD>𠉛頂蝞∠<EFBFBD><EFBFBD>?
        );
      }
    });
  }
}

**撉峕𤣰<E5B395><F0A4A3B0><EFBFBD>**嚗?- <20>?<3F>舀𦻖<E88880>嗥鍂<E597A5><EFBFBD><E7919F>?- <20>?<3F><EFBFBD><E888AA><EFBFBD><E586BD>曇楝<E69B87>?- <20>?<3F><EFBFBD><EFBFBD><EFBFBD>?- <20>?<3F><EFBFBD><E88890>𣂼<EFBFBD>蝑?- <20>?<3F><EFBFBD><E887AC><EFBFBD><EFBFBD>憭?

隞餃𦛚3.2嚗𡁜<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>𣂼膥嚗㇁nswer Generator嚗㚁<E59A97>2撠𤩺𧒄嚗?

<EFBFBD><EFBFBD>辣雿滨蔭嚗䫤backend/src/modules/iit-manager/agents/AnswerGenerator.ts`

import OpenAI from 'openai';
import { logger } from '../../../common/logging/index.js';
import { ToolExecutionResult } from './ToolExecutor.js';

/**
 * 蝑娍<E89D91><E5A88D><EFBFBD><EFBFBD><EFBFBD>? * 撠<><EFBFBD><EFBFBD>銵𣬚<E98AB5><F0A3AC9A>𡏭蓮<F0A18FAD><EFBCB6><EFBFBD><E586BD>见末<E8A781><E69CAB><EFBFBD>蝑? */
export class AnswerGenerator {
  private llm: OpenAI;

  constructor() {
    this.llm = new OpenAI({
      apiKey: process.env.OPENAI_API_KEY,
      baseURL: process.env.OPENAI_BASE_URL || 'https://api.deepseek.com'
    });
  }

  /**
   * <20><><EFBFBD><EFBFBD><EFBFBD>
   */
  async generate(
    userQuestion: string,
    toolResult: ToolExecutionResult,
    toolName: string
  ): Promise<string> {
    try {
      logger.info('[AnswerGenerator] Generating answer', {
        question: userQuestion.substring(0, 50),
        toolName,
        success: toolResult.success
      });

      // 憒<><E68692>撌亙<E6928C><E4BA99><EFBFBD>憭梯揖
      if (!toolResult.success) {
        return this.generateErrorMessage(toolResult.error);
      }

      // <20>寞旿銝滚<E98A9D>撌亙<E6928C>蝐餃<E89D90>嚗䔶蝙<E494B6><EFBFBD><E585B6>𣬚<EFBFBD><F0A3AC9A><EFBFBD>璅⊥踎
      if (toolName === 'query_clinical_data') {
        return this.generateDataAnswer(userQuestion, toolResult.data);
      } else if (toolName === 'search_knowledge_base') {
        return await this.generateKnowledgeAnswer(userQuestion, toolResult.data);
      }

      return '<27><EFBFBD>嚗峕<E59A97><E5B395><EFBFBD><E4ADBE><EFBFBD><EFBFBD><EFBFBD><EFBFBD>';
    } catch (error: any) {
      logger.error('[AnswerGenerator] Generation failed', {
        error: error.message
      });

      return '<27><EFBFBD><EFBFBD><E59A97>蝑𠉛<E89D91><F0A0899B>𣂼仃韐伐<E99F90>霂瑞<E99C82><E7919E>𤾸<EFBFBD>霂?;
    }
  }

  /**
   * <20><><EFBFBD><EFBFBD>唳旿<E594B3>亥砭<E4BAA5><E7A0AD><EFBFBD>蝑䈑<E89D91>雿輻鍂璅⊥踎嚗䔶<E59A97><EFBFBD>鍂LLM嚗?   */
  private generateDataAnswer(question: string, data: any): string {
    const type = data.type;

    if (type === 'project_stats') {
      return `<60><> **憿寧𤌍蝏蠘恣<E8A098>唳旿**

<EFBFBD>?**<2A><EFBFBD>鈭箸㺭**嚗?{data.stats.enrolled}靘?<3F>?**摰峕<E691B0><E5B395><EFBFBD><EFBFBD>**嚗?{data.stats.completed}靘?<3F>?**<2A>唳旿韐券<E99F90>**嚗?{data.stats.dataQuality}

<EFBFBD><20>唳旿<E594B3>湔鰵<E6B994>園𡢿嚗?{new Date().toLocaleString('zh-CN')}`;
    }

    if (type === 'patient_detail') {
      const details = data.details;
      return `<60>𪈠 **<2A><><EFBFBD>?${data.patientId} 霂行<E99C82>**

<EFBFBD><EFBFBD> **<2A>箸𧋦靽⊥<E99DBD>**嚗?- 撟湧<E6929F>嚗?{details.age || '<EFBFBD><EFBFBD><EFBFBD>?}?- <EFBFBD><EFBFBD>?{details.gender || '<27><EFBFBD><E88AB8>?}
- BMI嚗?{details.bmi || '<EFBFBD><EFBFBD><EFBFBD>?}

<EFBFBD><EFBFBD> **敶訫<EFBFBD><EFBFBD><EFBFBD>?*?- <EFBFBD>唳旿摰峕㟲摨佗<EFBFBD>${details.complete === '2' ? '<27>?撌脣<E6928C><E884A3>? : '<EFBFBD>?餈𥡝<EFBFBD>?}

<EFBFBD> <EFBFBD><EFBFBD><EFBFBD>擧凒<EFBFBD><EFBFBD>${new Date().toLocaleString('zh-CN')}`;
    }

    if (type === 'qc_status') {
      const issues = data.recentIssues.slice(0, 5);
      let answer = `<EFBFBD><EFBFBD> **韐冽綉<EFBFBD><EFBFBD>?*\n\n`;
      answer += `<EFBFBD>𩤃<EFBFBD> **韐冽綉<EFBFBD><EFBFBD><EFBFBD>?*?{data.issueCount}銝歿n\n`;
      
      if (issues.length > 0) {
        answer += `<EFBFBD><EFBFBD> **<EFBFBD><EFBFBD>餈煾䔮憸?*嚗䨵n`;
        issues.forEach((issue: any, index: number) => {
          answer += `${index + 1}. 霈啣<EFBFBD>${issue.recordId}?{JSON.stringify(issue.issue).substring(0, 50)}\n`;
        });
      } else {
        answer += `<EFBFBD>?<EFBFBD><EFBFBD><EFBFBD>韐冽綉<EFBFBD><EFBFBD>`;
      }

      return answer;
    }

    return JSON.stringify(data, null, 2);
  }

  /**
   * <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E89D9D><EFBCB9><EFBFBD><EFBFBD><E59A97><EFBFBD>汪LM蝏澆<E89D8F>嚗?   */
  private async generateKnowledgeAnswer(question: string, data: any): Promise<string> {
    const results = data.results;

    if (results.length === 0) {
      return `<EFBFBD><EFBFBD> **<EFBFBD><EFBFBD>摨𤘪<EFBFBD>?*

<EFBFBD>?<EFBFBD>芣𪄳<EFBFBD>啁㮾<EFBFBD><EFBFBD>?
<EFBFBD> 撱箄悅嚗?1. 撠肽<EFBFBD><EFBFBD><EFBFBD>喲睸霂?2. <EFBFBD><EFBFBD><EFBFBD>𠉛弦<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
3. <EFBFBD>𠉛頂憿寧𤌍<EFBFBD><EFBFBD><EFBFBD>𧜶;
    }

    // 撠<><E692A0><E89D9D><EFBCB9>𨀣𣄽<F0A880A3>乩蛹銝𠹺<E98A9D><F0A0B9BA>?    const context = results
      .map((r: any, index: number) => `[<5B><>${index + 1}] ${r.content}`)
      .join('\n\n');

    // 靚<>鍂LLM蝏澆<E89D8F><E6BE86><EFBFBD>
    const response = await this.llm.chat.completions.create({
      model: 'deepseek-chat',
      messages: [
        {
          role: 'system',
          content: `雿䭾糓銝游<E98A9D><E6B8B8>𠉛弦<F0A0899B><EFBFBD><E68B87><EFBFBD><EFBFBD><EFBFBD><E89D9D><EFBCB7><EFBFBD><EFBFBD><EFBFBD><E78DA2>摰對<E691B0><E5B08D><EFBFBD><E482BF><EFBFBD><E586BD><EFBFBD><E6A185>?閬<><E996AC>嚗?1. <20><EFBFBD><EFBFBD><E996AC>蝖柴<E89D96><E69FB4><EFBFBD>瘣?2. 撘閧鍂<E996A7><E98D82><EFBFBD><EFBFBD>瘜沍<E7989C><E6B28D>﹝X]
3. 憒<><E68692><EFBFBD><EFBFBD>﹝銝剜瓷<E5899C><EFBFBD>蝖桃<E89D96><EFBFBD><E78DA2>霂𡁜<E99C82>霂湔<E99C82>
4. 雿輻鍂Markdown<77><EFBFBD>`
        },
        {
          role: 'user',
          content: `<60><EFBFBD><E586BD><EFBFBD>嚗?{question}\n\n璉<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>摰對<EFBFBD>\n${context}`
        }
      ],
      temperature: 0.3,
      max_tokens: 800
    });

    const answer = response.choices[0].message.content || '<27><EFBFBD><E4ADBE><EFBFBD><EFBFBD><EFBFBD><EFBFBD>';

    return `<60><> **<2A><EFBFBD>摨𤘪䰻霂<E99C82><EFBCB9>?*\n\n${answer}`;
  }

  /**
   * <20><><EFBFBD><EFBFBD>躰秤<E8BAB0>鞟內
   */
  private generateErrorMessage(error?: string): string {
    return `<60>?<3F>亥砭憭梯揖

<EFBFBD><EFBFBD>嚗?{error || '<27>芰䰻<E88AB0>躰秤'}

<EFBFBD><20>典虾隞伐<E99A9E>
1. 蝔滚<E89D94><E6BB9A><EFBFBD>
2. <20><EFBCB6><EFBFBD>
3. <20>𠉛頂蝞∠<E89D9E><E288A0>𧜶;
  }
}

**撉峕𤣰<E5B395><F0A4A3B0><EFBFBD>**嚗?- <20>?<3F><EFBFBD><E88890>鞉㺭<E99E89>格䰻霂<E99C82>蝑?- <20>?<3F><EFBFBD><E88890>鞟䰻霂<E4B0BB><E99C82><E89D9D>蝑?- <20>?<3F><EFBFBD><E482BF><EFBFBD><E6BE86>见末嚗㇈arkdown嚗?- <20>?<3F>躰秤<E8BAB0>鞟內皜<E585A7>


隞餃𦛚3.3嚗𡁶垢<EFBFBD>啁垢瘚贝<EFBFBD>嚗?撠𤩺𧒄嚗?

瘚贝<EFBFBD><EFBFBD>箸艶嚗?

// 瘚贝<E7989A><E8B49D>箸艶1嚗𡁏䰻霂<EFBD81><EFBFBD>霈?{
  input: "<22>啣銁<E595A3><EFBFBD>憭𡁜<E686AD>鈭箔<E988AD>嚗?,
  expectedTool: "query_clinical_data",
  expectedIntent: "project_stats",
  expectedOutput: "<EFBFBD><EFBFBD> 憿寧𤌍蝏蠘恣<EFBFBD>唳旿\n<EFBFBD>?<EFBFBD><EFBFBD>鈭箸㺭嚗䧥X靘?
}

// 瘚贝<E7989A><E8B49D>箸艶2嚗𡁏䰻霂鸌摰𡁏<E691B0><F0A1818F>?{
  input: "P001<30><31><EFBFBD><EFBFBD><EFBFBD>摰峕㺭<E5B395><EFBFBD><E6A190><EFBFBD>",
  expectedTool: "query_clinical_data",
  expectedIntent: "patient_detail",
  expectedOutput: "<22>𪈠 <20><><EFBFBD>?P001 霂行<E99C82>"
}

// 瘚贝<E7989A><E8B49D>箸艶3嚗𡁏䰻霂<E99C82>蝛嗆䲮獢?{
  input: "<22><EFBFBD><E4BAA4><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E98A8B>",
  expectedTool: "search_knowledge_base",
  expectedCategory: "protocol",
  expectedOutput: "<22><> <20><EFBFBD>摨𤘪䰻霂<E99C82><EFBCB9>?
}

// 瘚贝<E7989A><E8B49D>箸艶4嚗𡁏䰻霂RF銵冽聢
{
  input: "BMI餈嗘葵摮埈挾<EFBFBD>𦒘<EFBFBD>憛恬<EFBFBD>",
  expectedTool: "search_knowledge_base",
  expectedCategory: "crf",
  expectedOutput: "<EFBFBD><EFBFBD> <EFBFBD><EFBFBD>摨𤘪䰻霂<EFBFBD><EFBFBD>?
}

// 瘚贝<E7989A><E8B49D>箸艶5嚗𡁻𤦭<F0A181BB>?{
  input: "雿惩末",
  expectedTool: null,
  expectedOutput: "<22>典末嚗<E69CAB><E59A97><EFBFBD>臭葩摨羓<E691A8>蝛嗅𨭌<E59785>?
}

瘚贝<EFBFBD>甇仿炊嚗?1. <20><EFBFBD>銝𡁜凝靽∩葉<E288A9><EFBFBD><E785BE><EFBFBD>霂閙<E99C82><E99699>?2. 閫<><E996AB><EFBFBD>𡒊垢<F0A1928A><EFBFBD> 3. 撉諹<E69289><E8ABB9>𧼮<EFBFBD><F0A7BCAE><EFBFBD>捆 4. 璉<><E79289>亙恣霈⊥𠯫敹? **撉峕𤣰<E5B395><F0A4A3B0><EFBFBD>**嚗?- <20>?5銝芣<E98A9D>霂訫㦤<E8A8AB><EFBFBD><E887AC><EFBFBD><EFBFBD>

  • <EFBFBD>?<3F>𧼮<EFBFBD><F0A7BCAE>園𡢿<3蝘?- <20>?<3F>𧼮<EFBFBD><F0A7BCAE><EFBFBD><EFBFBD><E68D86>
  • <EFBFBD>?摰∟恣<E2889F><EFBFBD>摰峕㟲

Day 4嚗𡁜𪂹<F0A1819C>亥䌊<E4BAA5><EFBFBD><EFBFBD><E78DA2>6撠𤩺𧒄嚗?

隞餃𦛚4.1嚗𡁜<EFBFBD><EFBFBD>啣𪂹<EFBFBD><EFBFBD><EFBFBD>𣂼膥嚗?撠𤩺𧒄嚗?

<EFBFBD><EFBFBD>辣雿滨蔭嚗䫤backend/src/modules/iit-manager/services/WeeklyReportGenerator.ts`

import { prisma } from '../../../config/database.js';
import { RedcapAdapter } from '../adapters/RedcapAdapter.js';
import { DifyAdapter } from '../adapters/DifyAdapter.js';
import { logger } from '../../../common/logging/index.js';
import { getISOWeek, startOfWeek, endOfWeek } from 'date-fns';

/**
 * <20>冽𥁒<E586BD><F0A58192><EFBFBD><EFBFBD>? * <20>芸𢆡<E88AB8><F0A286A1><EFBFBD>憿寧𤌍<E5AFA7>冽𥁒撟嗡<E6929F>隡惩<E99AA1>Dify<66><EFBFBD>摨? */
export class WeeklyReportGenerator {
  /**
   * <20><><EFBFBD>撟嗡<E6929F>隡惩𪂹<E683A9>?   */
  async generateAndUpload(projectId: string): Promise<{
    success: boolean;
    reportId?: string;
    error?: string;
  }> {
    try {
      const weekNumber = getISOWeek(new Date());
      const year = new Date().getFullYear();

      logger.info('[WeeklyReportGenerator] Starting generation', {
        projectId,
        year,
        weekNumber
      });

      // 1. 璉<><E79289>交糓<E4BAA4>血歇<E8A180><E6AD87><EFBFBD>
      const existing = await prisma.iitWeeklyReport.findFirst({
        where: {
          projectId,
          year,
          weekNumber
        }
      });

      if (existing) {
        logger.warn('[WeeklyReportGenerator] Report already exists', {
          reportId: existing.id
        });
        return {
          success: false,
          error: '<27>砍𪂹<E7A08D>冽𥁒撌脩<E6928C><E884A9>?
        };
      }

      // 2. <20><EFBFBD><E59C92>唳旿
      const reportData = await this.collectWeeklyData(projectId, year, weekNumber);

      // 3. <20><><EFBFBD>Markdown<77><6E>      const content = this.generateMarkdownContent(reportData, year, weekNumber);

      // 4. 靽嘥<E99DBD><E598A5>唳㺭<E594B3><EFBFBD>
      const report = await prisma.iitWeeklyReport.create({
        data: {
          projectId,
          year,
          weekNumber,
          content,
          stats: reportData.stats,
          createdAt: new Date()
        }
      });

      // 5. 銝𠹺<E98A9D><F0A0B9BA>蚤ify<66><EFBFBD>摨?      const dify = new DifyAdapter(projectId);
      const uploadResult = await dify.uploadDocument(content, {
        name: `<60>冽𥁒-${year}撟渡洵${weekNumber}<7D>灼,
        doc_type: 'report',
        date: `${year}-W${weekNumber.toString().padStart(2, '0')}`
      });

      if (!uploadResult.success) {
        logger.error('[WeeklyReportGenerator] Dify upload failed');
        // <20>唳旿摨枏歇靽嘥<E99DBD>嚗㷉ify銝𠹺<E98A9D>憭梯揖銝滚蔣<E6BB9A>?      }

      logger.info('[WeeklyReportGenerator] Generation completed', {
        reportId: report.id,
        difyUploaded: uploadResult.success
      });

      return {
        success: true,
        reportId: report.id
      };
    } catch (error: any) {
      logger.error('[WeeklyReportGenerator] Generation failed', {
        error: error.message,
        projectId
      });

      return {
        success: false,
        error: error.message
      };
    }
  }

  /**
   * <20><EFBFBD><E59C92>砍𪂹<E7A08D>唳旿
   */
  private async collectWeeklyData(
    projectId: string,
    year: number,
    weekNumber: number
  ): Promise<any> {
    const weekStart = startOfWeek(new Date(), { weekStartsOn: 1 });
    const weekEnd = endOfWeek(new Date(), { weekStartsOn: 1 });

    // 1. <20><EFBFBD>憿寧𤌍<E5AFA7>滨蔭
    const project = await prisma.iitProject.findUnique({
      where: { id: projectId },
      select: {
        name: true,
        redcapApiUrl: true,
        redcapApiToken: true
      }
    });

    // 2. 隞竃EDCap<61><EFBFBD>蝏蠘恣
    const redcap = new RedcapAdapter(
      project!.redcapApiUrl,
      project!.redcapApiToken
    );
    const allRecords = await redcap.exportRecords();

    // 3. 隞𤾸恣霈⊥𠯫敹𡑒繮<F0A19192>𡝗𧋦<F0A19D97>冽暑<E586BD>?    const weeklyLogs = await prisma.iitAuditLog.findMany({
      where: {
        projectId,
        createdAt: {
          gte: weekStart,
          lte: weekEnd
        }
      },
      orderBy: { createdAt: 'desc' }
    });

    // 4. 蝏蠘恣<E8A098>唳旿
    const stats = {
      totalRecords: allRecords.length,
      newRecordsThisWeek: weeklyLogs.filter(
        log => log.actionType === 'redcap_data_received'
      ).length,
      qualityIssues: weeklyLogs.filter(
        log => log.actionType === 'quality_issue'
      ).length,
      wechatNotifications: weeklyLogs.filter(
        log => log.actionType === 'wechat_notification_sent'
      ).length
    };

    return {
      projectName: project!.name,
      year,
      weekNumber,
      weekStart: weekStart.toISOString(),
      weekEnd: weekEnd.toISOString(),
      stats,
      recentActivities: weeklyLogs.slice(0, 20).map(log => ({
        actionType: log.actionType,
        entityId: log.entityId,
        createdAt: log.createdAt,
        details: log.details
      }))
    };
  }

  /**
   * <20><><EFBFBD>Markdown<77><6E>   */
  private generateMarkdownContent(data: any, year: number, weekNumber: number): string {
    return `# ${data.projectName} - ${year}撟渡洵${weekNumber}<7D>典𪂹<E585B8>?
## <20><> 蝏蠘恣<E8A098>唳旿

- **<2A>餉扇敶閙㺭**嚗?{data.stats.totalRecords}靘?- **<2A>砍𪂹<E7A08D><EFBFBD>**嚗?{data.stats.newRecordsThisWeek}靘?- **韐冽綉<E586BD><EFBFBD>**嚗?{data.stats.qualityIssues}銝?- **隡<><E99AA1>敺桐縑<E6A190>𡁶䰻**嚗?{data.stats.wechatNotifications}甈?
## <20><> <20>園𡢿<E59C92><F0A1A2BF>
- **撘<>憪𧢲𧒄<F0A7A2B2>?*嚗?{new Date(data.weekStart).toLocaleString('zh-CN')}
- **蝏𤘪<E89D8F><F0A498AA>園𡢿**嚗?{new Date(data.weekEnd).toLocaleString('zh-CN')}

## <20><> <20>砍𪂹銝餉<E98A9D>瘣餃𢆡

${data.recentActivities
  .map((activity: any, index: number) => {
    return `${index + 1}. **${activity.actionType}** - 霈啣<E99C88>${activity.entityId} (${new Date(activity.createdAt).toLocaleString('zh-CN')})`;
  })
  .join('\n')}

## <20><20><EFBFBD><E6BBA8>單釣

${data.stats.qualityIssues > 0 
  ? `<60>𩤃<EFBFBD> <20>砍𪂹<E7A08D>𤑳緵${data.stats.qualityIssues}銝芾捶<E88ABE>折䔮憸矋<E686B8>霂瑕<E99C82><E79195><EFBFBD><E59785><EFBFBD> 
  : '<EFBFBD>?<EFBFBD>砍𪂹<EFBFBD>㰘捶<EFBFBD>折䔮憸?}

---
*<EFBFBD>芸𢆡<EFBFBD><EFBFBD><EFBFBD><EFBFBD>園𡢿嚗?{new Date().toLocaleString('zh-CN')}*`;
  }
}

// <20>唳旿摨栞”摰帋<E691B0><EFBFBD><E59A97><EFBFBD><EFBFBD><EFBFBD>Prisma Schema嚗?/*
model IitWeeklyReport {
  id        String   @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
  projectId String   @db.Uuid
  year      Int
  weekNumber Int
  content   String   @db.Text
  stats     Json
  createdAt DateTime @default(now())

  project   IitProject @relation(fields: [projectId], references: [id])

  @@unique([projectId, year, weekNumber])
  @@map("weekly_reports")
  @@schema("iit_schema")
}
*/

**撉峕𤣰<E5B395><F0A4A3B0><EFBFBD>**嚗?- <20>?<3F><EFBFBD><E88890>𣂼𪂹<F0A382BC>仗arkdown

  • <EFBFBD>?<3F><EFBFBD>摮睃<E691AE><E79D83>唳旿摨?- <20>?<3F><EFBFBD>隡惩<E99AA1>Dify
  • <EFBFBD>?<3F>脫迫<E884AB><EFBFBD><E6BB9A><EFBFBD><EFBFBD>

隞餃𦛚4.2嚗𡁻<EFBFBD>蝵桀<EFBFBD><EFBFBD>嗡遙<EFBFBD><EFBFBD>2撠𤩺𧒄嚗?

<EFBFBD><EFBFBD>辣雿滨蔭嚗䫤backend/src/modules/iit-manager/index.ts`

import cron from 'node-cron';
import { WeeklyReportGenerator } from './services/WeeklyReportGenerator.js';

/**
 * <20><EFBFBD><E598A5>𦎾IT Manager璅<E79285>
 */
export async function initIitManager() {
  // ... <20><EFBFBD><E594B3><EFBFBD>orker瘜典<E7989C><EFBFBD><E99A9E> ...

  // <20>?<3F><EFBFBD>嚗𡁏釣<F0A1818F><E987A3>𪂹<EFBFBD><EFBFBD><E4BA99>嗡遙<E597A1>?  registerWeeklyReportCron();

  logger.info('<27>?IIT Manager initialized');
}

/**
 * 瘜典<E7989C><E585B8>冽𥁒摰𡁏𧒄隞餃𦛚
 * 瘥誩𪂹銝<F0AA82B9> 00:00 <20>芸𢆡<E88AB8><F0A286A1><EFBFBD>銝𠰴𪂹<F0A0B0B4>冽𥁒
 */
function registerWeeklyReportCron() {
  const generator = new WeeklyReportGenerator();

  // 瘥誩𪂹銝<F0AA82B9><E98A9D>峕膥0<E886A5><EFBFBD>銵?  cron.schedule('0 0 * * 1', async () => {
    logger.info('<27>?撘<>憪讠<E686AA><E8AEA0>𣂼𪂹<F0A382BC>?);

    try {
      // <20><EFBFBD><E79195><EFBFBD><EFBFBD>㗇暑頝<E69A91><EFBFBD>?      const projects = await prisma.iitProject.findMany({
        where: { status: 'active' }
      });

      for (const project of projects) {
        await generator.generateAndUpload(project.id);
      }

      logger.info('<EFBFBD>?<EFBFBD>冽𥁒<EFBFBD><EFBFBD><EFBFBD>摰峕<EFBFBD>', {
        projectCount: projects.length
      });
    } catch (error: any) {
      logger.error('<EFBFBD>?<EFBFBD>冽𥁒<EFBFBD><EFBFBD><EFBFBD>憭梯揖', {
        error: error.message
      });
    }
  }, {
    timezone: 'Asia/Shanghai'
  });

  logger.info('<EFBFBD>?<EFBFBD>冽𥁒摰𡁏𧒄隞餃𦛚撌脫釣<EFBFBD><EFBFBD>瘥誩𪂹銝<EFBFBD> 00:00嚗?);
}

**撉峕𤣰<E5B395><F0A4A3B0><EFBFBD>**嚗?- <20>?摰𡁏𧒄隞餃𦛚瘜典<E7989C><E585B8>𣂼<EFBFBD>

  • <EFBFBD>?<3F><EFBFBD><E88880>刻圻<E588BB><EFBFBD>霂?- <20>?<3F><EFBFBD>霈啣<E99C88>摰峕㟲

Day 5嚗𡁏<E59A97><EFBFBD><E78DA2><EFBFBD><EFBFBD>瘚贝<E7989A>嚗?撠𤩺𧒄嚗?

隞餃𦛚5.1嚗𡁶鍂<EFBFBD>瑚蝙<EFBFBD><EFBFBD><EFBFBD><EFBFBD>2撠𤩺𧒄嚗?

<EFBFBD><EFBFBD>辣雿滨蔭嚗䫤AIclinicalresearch/docs/03-銝𡁜𦛚璅<E79285>/IIT Manager Agent/05-雿輻鍂<E8BCBB><EFBFBD>/隡<><E99AA1>敺桐縑撖寡<E69296><E5AFA1><EFBFBD><EFBFBD>.md`

<EFBFBD><EFBFBD>捆憭抒熔嚗?1. <20><EFBFBD>隞讠<E99A9E> 2. <20><EFBFBD><E88880><EFBFBD>䰻霂<EFBCB9>?3. 撣貊鍂<E8B28A><EFBFBD>蝷箔<E89DB7> 4. 瘜冽<E7989C>鈭钅★ 5. 撣貉<E692A3><E8B289><EFBFBD>FAQ


隞餃𦛚5.2嚗䥪hase 1.5撘<EFBFBD><EFBFBD>𤏸扇敶𤏪<EFBFBD>2撠𤩺𧒄嚗?

<EFBFBD><EFBFBD>辣雿滨蔭嚗䫤AIclinicalresearch/docs/03-銝𡁜𦛚璅<E79285>/IIT Manager Agent/06-撘<><E69298>𤏸扇敶?Phase1.5-AI撖寡<E69296><E5AFA1><EFBFBD><EFBFBD><E69298><EFBFBD><E7A983>鞱扇敶?md`

<EFBFBD><EFBFBD>捆憭抒熔嚗?1. 撘<><E69298>𤑳𤌍<F0A491B3><F0A48C8D><EFBFBD><EFBFBD><EFBFBD> 2. <20><><EFBFBD><EFBFBD><E887AC><EFBFBD><E59581>?3. 瘚贝<E7989A>撉諹<E69289>蝏𤘪<E89D8F> 4. 撌脩䰻<E884A9>𣂼<EFBFBD>銝擧㺿餈𥡝恣<F0A5A19D>?

隞餃𦛚5.3嚗𡁜<EFBFBD><EFBFBD><EFBFBD>霂𤏪<EFBFBD>2撠𤩺𧒄嚗?

瘚贝<EFBFBD><EFBFBD>拚猐嚗?

<EFBFBD>箸艶 颲枏<EFBFBD> <EFBFBD><EFBFBD>撌亙<EFBFBD> <EFBFBD><EFBFBD>颲枏枂 <EFBFBD><EFBFBD>?
憿寧𤌍蝏蠘恣 "<22>啣銁<E595A3><EFBFBD>憭𡁜<E686AD>鈭綽<E988AD>" query_clinical_data <EFBFBD><EFBFBD><EFBFBD><EFBFBD>鈭箸㺭 <EFBFBD>?
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>? "P001敶訫<E695B6><EFBFBD><E988AD>嚗? query_clinical_data <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>𠶖<EFBFBD>? <EFBFBD>?
韐冽綉<EFBFBD><EFBFBD>? "<22>匧𪑛鈭𥡝捶<F0A5A19D>折䔮憸矋<E686B8>" query_clinical_data <EFBFBD><EFBFBD><EFBFBD>𡑒” <EFBFBD>?
<EFBFBD>𠉛弦<EFBFBD><EFBFBD> "<22><EFBFBD><E4BAA4><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E98A8B>" search_knowledge_base <EFBFBD><EFBFBD><EFBFBD><EFBFBD> <EFBFBD>?
CRF<EFBFBD>亥砭 "BMI<4D>𦒘<EFBFBD>憛恬<E6869B>" search_knowledge_base CRF霂湔<EFBFBD> <EFBFBD>?
<EFBFBD>冽𥁒<EFBFBD>亥砭 "銝𠰴𪂹餈𥕦<E9A488><EFBFBD><E68692>嚗? search_knowledge_base <EFBFBD>冽𥁒<EFBFBD><EFBFBD> <EFBFBD>?
<EFBFBD><EFBFBD> "雿惩末" <EFBFBD>? <EFBFBD>见末<EFBFBD>𧼮<EFBFBD> <EFBFBD>?

<EFBFBD><EFBFBD> <20><EFBFBD><E49C98><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>撉峕𤣰

4.1 <20><EFBFBD>摰峕㟲<E5B395>?

<EFBFBD><EFBFBD> 撉峕𤣰<EFBFBD><EFBFBD><EFBFBD> 隡睃<EFBFBD>蝥?
Dify<EFBFBD><EFBFBD><EFBFBD> <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>冽𧋦<EFBFBD>蚤ify API <EFBFBD>𣞁 P0
<EFBFBD>誩㦛霂<EFBFBD><EFBFBD> <EFBFBD><EFBFBD><EFBFBD>?80% <EFBFBD>𣞁 P0
<EFBFBD>唳旿<EFBFBD>亥砭 <EFBFBD>舀䰻霂㎞EDCap摰墧𧒄<EFBFBD>唳旿 <EFBFBD>𣞁 P0
*<EFBFBD><EFBFBD><EFBFBD>蝝? <EFBFBD><EFBFBD><EFBFBD>蝛嗆䲮獢<EFBFBD><EFBFBD>獢? <EFBFBD>𣞁 P0
<EFBFBD><EFBFBD>敺桐縑<EFBFBD>𧼮<EFBFBD> <EFBFBD>𧼮<EFBFBD><EFBFBD>園𡢿<3蝘? <EFBFBD>𣞁 P0
<EFBFBD>冽𥁒<EFBFBD>芸𢆡敶埝﹝ 瘥誩𪂹銝<EFBFBD><EFBFBD>芸𢆡<EFBFBD><EFBFBD><EFBFBD> <EFBFBD><EFBFBD> P1
摰∟恣<EFBFBD><EFBFBD> <EFBFBD><EFBFBD><EFBFBD>匧笆霂脲<EFBFBD><EFBFBD><EFBFBD> <EFBFBD><EFBFBD> P1

4.2 <20><EFBFBD><E689AF><EFBFBD><EFBFBD>

<EFBFBD><EFBFBD><EFBFBD> <EFBFBD><EFBFBD> 霂湔<EFBFBD>
<EFBFBD>𧼮<EFBFBD>撱嗉<EFBFBD> <3蝘? <EFBFBD><EFBFBD><EFBFBD>?<3F>?<3F><EFBFBD><E59785>𧼮<EFBFBD>
Dify<EFBFBD>亥砭撱嗉<EFBFBD> <500ms <EFBFBD>砍𧑐<EFBFBD>函蔡嚗<EFBFBD><EFBFBD>霂亙<EFBFBD>敹?
REDCap<EFBFBD>亥砭撱嗉<EFBFBD> <1蝘? 撌脫<EFBFBD>adapter嚗<EFBFBD>歇撉諹<EFBFBD>
*<EFBFBD>誩㦛霂<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>? >80% <EFBFBD><EFBFBD>瘚贝<EFBFBD><EFBFBD>拚猐撉諹<EFBFBD>
<EFBFBD><EFBFBD><EFBFBD><EFBFBD>蝖桃<EFBFBD> >70% 靘肽<EFBFBD><EFBFBD><EFBFBD>﹝韐券<EFBFBD>

4.3 隞<><E99A9E>韐券<E99F90>

  • <EFBFBD>?TypeScript蝐餃<E89D90>摰峕㟲
  • <EFBFBD>?<3F><EFBFBD>瘚贝<E7989A><EFBFBD><E996AC><EFBFBD>?70%
  • <EFBFBD>?<3F><><EFBFBD>瘚贝<E7989A><E8B49D><EFBFBD>
  • <EFBFBD>?<3F>躰秤憭<E7A7A4><E686AD><EFBFBD><E691B0>
  • <EFBFBD>?<3F><EFBFBD>霈啣<E99C88>摰峕㟲
  • <EFBFBD>?隞<><E99A9E>蝚血<E89D9A><EFBFBD><E996AB>

<EFBFBD><EFBFBD> 鈭𢛵<E988AD><F0A29BB5><EFBFBD>蝵脖<E89DB5>銝羓瑪

5.1 <20><EFBFBD><E887AC><EFBFBD><E3979B>滨蔭

# Dify<66>滨蔭
DIFY_API_URL=http://localhost/v1
DIFY_API_KEY=app-xxxxxxxxxxxxxxxxxxxxxx
DIFY_KNOWLEDGE_BASE_ID=kb-xxxxxxxxxxxxxxxxxxxxxx

# LLM<4C>滨蔭嚗㇄eepSeek嚗?OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxx
OPENAI_BASE_URL=https://api.deepseek.com

# 隡<><E99AA1>敺桐縑<E6A190>滨蔭嚗<E894AD><EFBFBD><EFBFBD>
WECHAT_CORP_ID=ww01cb7b72ea2db83c
WECHAT_CORP_SECRET=xxx
WECHAT_AGENT_ID=1000002

5.2 <20>唳旿摨栞<E691A8>蝘?

# 瘛餃<E7989B><E9A483>冽𥁒銵?npx prisma db push
npx prisma generate

5.3 <20>滚鍳<E6BB9A>滚𦛚

cd AIclinicalresearch/backend
npm run dev

<EFBFBD><EFBFBD> <20><EFBFBD><E58786><EFBFBD><EFBFBD><EFBFBD>箏𦛚銝擧㺿餈𥡝恣<F0A5A19D>?

6.1 敶枏<E695B6><E69E8F>𣂼<EFBFBD>嚗㇊hase 1.5嚗?

<EFBFBD>𣂼<EFBFBD> 敶勗<EFBFBD> <EFBFBD><EFBFBD><EFBFBD><EFBFBD>園𡢿
*<EFBFBD>閖★<EFBFBD>格𣈲<EFBFBD>? <EFBFBD><EFBFBD><EFBFBD>滚𦛚銝<EFBFBD>銝芷★<EFBFBD>? Phase 2
*<EFBFBD><EFBFBD>頧桀笆霂? 瘥𤩺活<EFBFBD><EFBFBD><EFBFBD><EFBFBD> Phase 2
<EFBFBD><EFBFBD>銝𧢲<EFBFBD>霈啣<EFBFBD> 銝滩扇敺𦯀<EFBFBD><EFBFBD><EFBFBD>撖寡<EFBFBD> Phase 2
蝖祉<EFBFBD><EFBFBD><EFBFBD>䰻霂<EFBFBD><EFBFBD>ID 銝齿𣈲<EFBFBD><EFBFBD><EFBFBD>憿寧𤌍 Phase 2
*<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>? 銝齿𣈲<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>? Phase 3

6.2 Phase 2 <20><EFBFBD><E99C88>

  1. *憭𡁻★<EFBFBD>格𣈲<EFBFBD>?

    • 瘥譍葵憿寧𤌍<EFBFBD><EFBFBD><EFBFBD><EFBFBD>摨? - <20><EFBFBD><E586BD><EFBFBD><EFBFBD>蝞∠<E89D9E>
    • 憿寧𤌍<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
  2. 憭朞蔭撖寡<EFBFBD>

    • 隡朞<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>? - 銝𠹺<E98A9D><F0A0B9BA><EFBFBD>扇敹<E68987><E695B9>Redis嚗? - 瞉<><E79E89>撘𤩺<E69298><F0A4A9BA>?
  3. *瘛瑕<EFBFBD><EFBFBD><EFBFBD>嚗㇌eAct嚗?

    • <EFBFBD><EFBFBD>憭齿<EFBFBD><EFBFBD>亥砭
    • 憭𡁜極<EFBFBD><EFBFBD><EFBFBD>? - <20>芯蜓<E88AAF><EFBFBD>敺芰㴓

<EFBFBD>?銝<><E98A9D><EFBFBD><EFBFBD><EFBFBD>

7.1 Phase 1.5<EFBFBD><EFBFBD>隞瑕<EFBFBD>?

**摰䂿緵<E482BF><EFBFBD>**嚗?- <20>?PI<50>臬銁隡<E98A81><E99AA1>敺桐縑銝剛䌊<E5899B>嗉祗閮<E7A597><E996AE>鞾䔮

  • <EFBFBD>?AI<41><EFBFBD><EFBFBD><E996AB><EFBFBD>曉僎<E69B89>亥砭<E4BAA5>唳旿/<2F><>
  • <EFBFBD>?<3F><EFBFBD><E482BF><EFBFBD><EFBFBD><EFBC86><EFBFBD><EFBFBD><EFBFBD><E5979A><EFBFBD>憟?- <20>?<3F>冽𥁒<E586BD>芸𢆡敶埝﹝嚗<EFB99D><EFBFBD>𤩺𧒄<F0A4A9BA>亥砭

**<2A><><EFBFBD>臭漁<E887AD>?*嚗?- <20><20><EFBFBD><E7AE94>砍𧑐Dify嚗峕<E59A97>API<50>鞉𧋦

  • <EFBFBD><EFBFBD> <20>閙郊<E99699>誩㦛霂<E3A69B><E99C82>嚗𣬚<E59A97><F0A3AC9A><EFBFBD><E99696>?- <20>圲 憭滨鍂<E6BBA8><EFBFBD>RedcapAdapter
  • <EFBFBD><EFBFBD> <20>冽𥁒<E586BD>芸𢆡<E88AB8><F0A286A1><EFBFBD>銝𤾸<E98A9D>獢?- <20><> 蝡臬<E89DA1>蝡臬辣餈?3蝘? **撘<><E69298><EFBFBD><E78390>?*嚗?- <20><><>摯撌乩<E6928C><E4B9A9>𧶏<EFBFBD>5憭?- <20><> <20><EFBFBD><EFBFBD><E99A9E>嚗鰺2000銵?- <20>妒 瘚贝<E7989A><EFBFBD><E996AC>嚗?70%
  • <EFBFBD><EFBFBD> <20><>﹝摰峕㟲嚗𡁶鍂<F0A181B6><EFBFBD><E7919F>?撘<><E69298>𤏸扇敶?

**銝衤<E98A9D>甇?*嚗䥪hase 2 - 憭𡁻★<F0A181BB>格𣈲<E6A0BC><F0A388B2><EFBFBD>擃条漣撖寡<E69296><E5AFA1><EFBFBD>


<EFBFBD><20><EFBFBD><E68092><EFBFBD><EFBFBD><E89D9E><EFBFBD>瓲敹<E793B2><EFBFBD><EFBFBD><EFBFBD>

8.1 銝箔<E98A9D><EFBFBD><E98A8B><EFBFBD><E89D9E><EFBFBD><EFBFBD><EFBFBD><EFBFBD>嚗?

**<2A><EFBFBD><E586BD><EFBFBD>**嚗?> "<22><><EFBFBD><EFBFBD><E6BBA9><EFBFBD><EFBFBD><E7B393>悟AI<41>賢笆霂嘅<E99C82><E59885><EFBFBD><E597A1><EFBFBD><E8B3A2><EFBFBD>颲?

**<2A><EFBFBD><E595A3><EFBFBD><EFBFBD>**嚗?- <20>?MVP<56>剔㴓撌脫<E6928C><E884AB>𡄯<EFBFBD>Day 3摰峕<E691B0>嚗?- <20>?隡<><E99AA1>敺桐縑<E6A190><EFBFBD><E588B8>歇撉諹<E69289>嚗?00%<25>𣂼<EFBFBD><F0A382BC><EFBFBD><EFBFBD>

  • <EFBFBD>?RedcapAdapter撌脣虾<E884A3><EFBFBD><E58981>湔𦻖憭滨鍂嚗?- <20>𩤃<EFBFBD> **蝻箏<E89DBB>**嚗䥪I<E4A5AA><EFBFBD>銝餃𢆡<E9A483>亥砭<E4BAA5>唳旿

**<2A><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>?*嚗?- <20><> **2憭拐<E686AD>蝥?*嚗𡁏<E59A97>敹怠<E695B9><E680A0>蚊I撖寡<E69296>

  • <EFBFBD>兛 **<2A><EFBFBD><E59786>?*嚗𡁜蘨<F0A1819C>充EDCap嚗䔶<E59A97><E494B6>求ify
  • <EFBFBD><EFBFBD> **<2A>㕑扇敹?*嚗𡁏𣈲<F0A1818F><F0A388B2><EFBFBD>頧桀笆霂嘅<E99C82>3頧殷<E9A0A7>
  • <EFBFBD>?**<2A><EFBFBD>擐?*嚗?甇<><EFBFBD>亥砭..."<22><EFBFBD><E8B8B9><EFBFBD><E586BD>西<EFBFBD>
  • <EFBFBD><EFBFBD><EFBFBD>憭毺鍂嚗𡁏說頞?0%<25><>䰻霂<E99C82>瘙?

8.2 銝匧之<E58CA7><EFBFBD><E8A9A8><EFBFBD><EFBFBD>抅鈭𡒊鍂<F0A1928A>瑕遣霈殷<E99C88>

<EFBFBD><EFBFBD>1嚗帋<EFBFBD>銝𧢲<EFBFBD>霈啣<EFBFBD> <20>?

**<2A><EFBFBD>**嚗? PI: "撣格<E692A3><E6A0BC><EFBFBD>銝閪001<30><31><EFBFBD><EFBFBD><E89D8F><EFBFBD>? AI: "P001撌脣<E6928C>蝏? PI: "隞𡝗<E99A9E>銝滩<E98A9D><E6BBA9><EFBFBD><E6BB9A><EFBFBD>" AI: <20>?"霂瑟<E99C82>靘𥟇<E99D98><F0A59F87><EFBFBD><EFBFBD><EFBFBD>?嚗<>仃敹<E4BB83><E695B9>嚗?

**閫<><E996AB>**嚗?```typescript // SessionMemory嚗𡁜<E59A97><F0A1819C><EFBFBD>餈?頧桀笆霂?sessionMemory.addMessage(userId, 'user', '撣格<E692A3><E6A0BC>仙001'); sessionMemory.addMessage(userId, 'assistant', 'P001撌脣<E6928C>蝏?);

// 銝𧢲活<F0A7A2B2>亥砭<E4BAA5><EFBFBD><E5A19A>芸𢆡憛怠<E6869B><E680A0><EFBFBD><EFBFBD><EFBFBD>D const lastPatientId = sessionMemory.getLastPatientId(userId); // P001


**<2A><><EFBFBD>**嚗?```
PI: "隞𡝗<E99A9E>銝滩<E98A9D><E6BBA9><EFBFBD><E6BB9A><EFBFBD>"
AI: <20>?"<22>亥砭P001嚗𡁏<E59A97>銝滩<E98A9D><E6BBA9><EFBFBD>霈啣<E99C88>"嚗<>扇敺埈糓P001嚗?```

---

#### <20><EFBFBD>2嚗𡁏迤<F0A1818F><EFBFBD><E588BB><EFBFBD>擐?<3F>?
**<2A><EFBFBD>**嚗?- AI憭<49><E686AD><EFBFBD><EFBFBD>閬?-8蝘?- <20><EFBFBD><E586BD><EFBFBD><EFBFBD><E798A8><EFBFBD>𠬍<EFBFBD><F0A0AC8D>𧢲㦤瘝<E7989D>摨?- <20><EFBFBD>隞乩蛹蝟餌<E89D9F><E9A48C><EFBFBD><EFBFBD>

**閫<><E996AB>**嚗?```typescript
// 蝡见朖<E8A781><EFBFBD><E785BE><EFBFBD><EFBFBD>擐?await wechatService.sendTextMessage(userId, '<27>哄 甇<><EFBFBD>亥砭嚗諹窈蝔滚<E89D94>?..');

// <20>齿<EFBFBD><E9BDBF><EFBFBD><EFBCB7>?const answer = await processQuery(userMessage);
await wechatService.sendMarkdownMessage(userId, answer);

**<2A><><EFBFBD>**嚗?- <20>?<3F><EFBFBD>蝡见朖<E8A781><EFBFBD><E8A781><EFBFBD>嚗?1蝘𡜐<E89D98>

  • <EFBFBD>?<3F>仿<EFBFBD>AI<41>典極雿?- <20>?銝滢<E98A9D><E6BBA2>西<EFBFBD>

<EFBFBD><EFBFBD>3嚗𡁏<EFBFBD><EFBFBD>隡睃<EFBFBD> <20>?

**<2A><EFBFBD>**嚗?- <20>蠘恣<E8A098>?憭拙<E686AD><E68B99>穃云<E7A983>?- Dify<66><79>𪂹<EFBFBD><EFBFBD><E4BAA6><EFBFBD><E8A098>𧼮<EFBFBD><F0A7BCAE><EFBFBD>

  • <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>嚗朞<EFBFBD>撖寡<EFBFBD>

**閫<><E996AB>**嚗?- <20>?<3F><EFBFBD>REDCap<61>亥砭嚗<E7A0AD><E59A97><EFBFBD>函緵<E587BD>学dapter嚗?- <20>?銝齿𦻖Dify嚗㇊hase 2<><EFBFBD>嚗?- <20>?銝滚<E98A9D><E6BB9A>冽𥁒嚗㇊hase 2<><EFBFBD>嚗?- <20>?2憭拐<E686AD>蝥? **<2A><><EFBFBD>**嚗?- <20>?敹恍<E695B9><EFBFBD><EFBFBD><EFBFBD>?- <20>?敹恍<E695B9><E6818D>𤣰<EFBFBD><F0A4A3B0><EFBFBD>擐?- <20>?敹恍<E695B9>蠘翮隞?

8.3 <20>滨垢<E6BBA8><EFBFBD>瞍磰<E79E8D>頝舐瑪

Phase 1.5嚗<EFBFBD><EFBFBD><EFBFBD><EFBFBD>:
<0A><EFBFBD><E98EBF><EFBFBD><><E99AA1>敺桐縑<E6A190><EFBFBD>撖寡<E69296><EFBFBD><E59A97>鈭页<E988AD>
<0A><EFBFBD><E98EBF><EFBFBD> <20>㰘䌊摰帋<E691B0>UI
<0A><EFBFBD><E5A999><EFBFBD> 銝𠹺<E98A9D><F0A0B9BA><EFBFBD><EFBFBD><EFBFBD>沐ode.js<6A><73><EFBFBD>

       <20>?嚗<><EFBFBD><EFBFBD>擐?+ <20><><EFBFBD><E79899><EFBFBD><EFBFBD>

Phase 3嚗<33>𧊋<EFBFBD><EFBFBD>:
<0A><EFBFBD><E98EBF><EFBFBD> <20><EFBFBD>H5/撠讐<E692A0>摨𧶏<E691A8>Taro 4.x嚗?<3F><EFBFBD><E98EBF><EFBFBD> Ant Design X蝞∠<E89D9E>銝𠹺<E98A9D><F0A0B9BA>?<3F><EFBFBD><E98EBF><EFBFBD> 銝啣<E98A9D><E595A3><EFBFBD>I蝏<49>辣嚗<E8BEA3><E59A97><EFBFBD><EFBFBD>蝷箝<E89DB7><E7AE9D><EFBFBD><EFBFBD>脰扇敶𨰻<E695B6><F0A8B0BB>䰻霂<E4B0BB><EFBFBD><E3A883><EFBFBD>
<0A><EFBFBD><E5A999><EFBFBD> <20>游末<E6B8B8><E69CAB><EFBFBD><EFBFBD>撉?```

**銝箔<E98A9D><EFBFBD><E98A8B>銝日𧫴畾蛛<E795BE>**
- <20>?Phase 1.5嚗𡁻<EFBFBD><EFBFBD>瓲敹<EFBFBD><EFBFBD><EFBFBD>AI<EFBFBD><EFBFBD>蝑娪䔮憸矋<EFBFBD>
- <20>?Phase 3嚗帋<E59A97><E5B88B>𣇉鍂<F0A38789><EFBFBD>撉䕘<E69289><E49598><EFBFBD><EFBFBD><E996AB><EFBFBD><EFBFBD><EFBFBD>嚗?- <20>?<3F><EFBFBD><EFBFBD>漲霈曇恣嚗<E681A3><E59A97><EFBFBD><EFBFBD>憟踝<E6869F>

---

### 8.4 蝡见朖銵<E69C96>𢆡<EFBFBD><F0A286A1><EFBFBD>

#### Step 1嚗𡁜<E59A97>撱箇洵銝<E6B4B5>銝芣<E98A9D>隞塚<E99A9E>5<EFBFBD><35><EFBFBD>嚗?
```bash
cd AIclinicalresearch/backend/src/modules/iit-manager
mkdir -p agents
touch agents/SessionMemory.ts

憭滚<EFBFBD>Day 1隞餃𦛚1.1<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> SessionMemory.ts

Step 2嚗朞<E59A97><EFBFBD><E98AB5><EFBFBD><EFBFBD><EFBFBD>霂𤏪<E99C82><F0A48FAA><EFBFBD><EFBFBD>

npm test agents/SessionMemory.test.ts

Step 3嚗𡁶誧蝏胖ay 1<><EFBFBD>隞餃𦛚

<EFBFBD><EFBFBD><EFBFBD><EFBFBD>﹝銝剔<EFBFBD>憿箏<EFBFBD>嚗?1. <20>?SessionMemory (30<33><30><EFBFBD>) 2. <20>?SimpleIntentRouter (2撠𤩺𧒄) 3. <20>?SimpleToolExecutor (1.5撠𤩺𧒄) 4. <20>?SimpleAnswerGenerator (1撠𤩺𧒄) 5. <20>?<3F><><EFBFBD><EFBFBD>訖echatCallbackController (1撠𤩺𧒄)

Step 4嚗鋽ay 1蝏𤘪<E89D8F><F0A498AA><EFBFBD>霂?

<EFBFBD><EFBFBD>銝𡁜凝靽∩葉<EFBFBD><EFBFBD><EFBFBD><EFBFBD>

"<22>啣銁<E595A3><EFBFBD>憭𡁜<E686AD>鈭綽<E988AD>"

<EFBFBD><EFBFBD>嚗?1. 蝡见朖<E8A781><EFBFBD>"<22>哄 甇<><EFBFBD>亥砭嚗諹窈蝔滚<E89D94>?.." 2. 3蝘鍦<E89D98><E98DA6><EFBFBD>"<22><> 憿寧𤌍蝏蠘恣..."

Step 5嚗鋽ay 2瘚贝<E7989A>憭朞蔭撖寡<E69296>

PI: "撣格<E692A3><E6A0BC><EFBFBD>銝閪001<30><31><EFBFBD><EFBFBD>?
AI: "<22>𪈠 <20><><EFBFBD>?P001 霂行<E99C82>..."

PI: "隞𡝗<E99A9E>銝滩<E98A9D><E6BBA9><EFBFBD><E6BB9A><EFBFBD>" <20>?瘚贝<E7989A>銝𠹺<E98A9D><F0A0B9BA>?AI: "<22>亥砭P001嚗𡁏<E59A97>銝滩<E98A9D><E6BBA9><EFBFBD>霈啣<E99C88>" <20>?摨磰砲<E7A3B0>芸𢆡霂<F0A286A1><E99C82>

8.5 <20>𣂼<EFBFBD><F0A382BC><EFBFBD><EFBFBD><EFBFBD><E59A97><EFBFBD><E89D9E><EFBFBD><EFBFBD>

<EFBFBD><EFBFBD>仿★ <EFBFBD><EFBFBD><EFBFBD> 撉諹<EFBFBD><EFBFBD><EFBFBD>
<EFBFBD>?<3F><EFBFBD>撖寡<E69296> <EFBFBD><EFBFBD>蝑?<3F><EFBFBD>憭𡁜<E686AD>鈭? <EFBFBD>凝瘚贝<EFBFBD>
<EFBFBD>?<3F><><EFBFBD><EFBFBD>䰻霂? <EFBFBD><EFBFBD>蝑?P001<30><31><EFBFBD>" <EFBFBD>凝瘚贝<EFBFBD>
<EFBFBD>?銝𠹺<E98A9D><F0A0B9BA><EFBFBD>扇敹? "隞𡝗<E99A9E>銝滩<E98A9D><E6BBA9><EFBFBD><E6BB9A>?<3F><EFBFBD><E8B3AA>促001 <EFBFBD>凝瘚贝<EFBFBD>
<EFBFBD>?甇<>銁颲枏<E9A2B2><E69E8F><EFBFBD> <1蝘埝𤣰<E59F9D>?甇<><EFBFBD>亥砭..." <EFBFBD>凝瘚贝<EFBFBD>
<EFBFBD>?<3F><><EFBFBD><E89D8F>憭? <3蝘埝𤣰<E59F9D><EFBFBD><E595A3><EFBFBD>獢? <EFBFBD>𡒊垢<EFBFBD><EFBFBD>
<EFBFBD>?摰∟恣<E2889F><EFBFBD> 霈啣<EFBFBD>銝𠹺<EFBFBD><EFBFBD><EFBFBD><EFBFBD>霈? <EFBFBD>唳旿摨𤘪<EFBFBD><EFBFBD>?

8.6 銝𤾸<E98A9D><F0A4BEB8><EFBFBD><E6B8A1><EFBFBD><EFBFBD>蝟?

**<2A><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>2憭抬<E686AD>**嚗?- <20><20><EFBFBD>嚗𡁏<E59A97>敹恍<E695B9><EFBFBD>I撖寡<E69296>隞瑕<E99A9E>?- <20>𣑐 <20><>凒嚗鑹EDCap<61>亥砭 + 銝𠹺<E98A9D><F0A0B9BA><EFBFBD>扇敹?- <20><20>鞉𧋦嚗𡁏<E59A97>憸嘥<E686B8><E598A5>鞉𧋦嚗<F0A78BA6><E59A97><EFBFBD>函緵<E587BD><EFBFBD>

  • <EFBFBD><EFBFBD> <20>笔漲嚗?憭拐<E686AD>蝥? **摰峕㟲<E5B395><E39FB2><EFBFBD>5憭抬<E686AD>**嚗?- <20><20><EFBFBD>嚗𡁜<E59A97><F0A1819C><EFBFBD>AI<41><EFBFBD><E68B87><EFBFBD>
  • <EFBFBD>𣑐 <20><>凒嚗? Dify<66><EFBFBD>摨?+ <20>冽𥁒敶埝﹝ + <20><><EFBFBD>亥砭
  • <EFBFBD><20>鞉𧋦嚗𡁻<E59A97><F0A181BB>滨蔭Dify嚗<79><EFBFBD><E6AD87>ocker嚗?- <20><> <20>笔漲嚗?憭拐<E686AD>蝥? 撱箄悅嚗?<3F>?**<2A><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>?*嚗?憭抬<E686AD>嚗屸<E59A97><EFBFBD><EFBFBD>? <0A>?<3F><EFBFBD><E59C92><EFBFBD><E586BD><EFBFBD>
    <0A>?<3F><EFBFBD>摰𡁏糓<F0A1818F><EFBFBD>摰峕㟲<E5B395>? **摰鮋<E691B0><E9AE8B><EFBFBD>嚗?<3F>?<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>歇摰峕<EFBFBD>**嚗?026-01-03嚗? <0A>?**Dify<66><EFBFBD>摨枏歇<E69E8F><E6AD87><EFBFBD>**嚗?026-01-04嚗? <0A>?瘛瑕<EFBFBD><EFBFBD>歇摰䂿緵嚗鑹EDCap摰墧𧒄<EFBFBD>唳旿 + Dify<66><79><EFBFBD><EFBFBD>摨?

<EFBFBD><EFBFBD> <20><EFBFBD><E68092>ify<66><EFBFBD>摨㯄<E691A8><E3AF84><EFBFBD>2026-01-04摰峕<E691B0>嚗?

8.7 <20><><EFBFBD><EFBFBD>峕艶

摰峕<EFBFBD><EFBFBD>園𡢿: 2026-01-04
<EFBFBD><EFBFBD>穃極雿𣈯<EFBFBD>: 4-6撠𤩺𧒄
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>: <20>沖EDCap摰墧𧒄<E5A2A7>唳旿<E594B3>亥砭<E4BAA5><EFBFBD>銝𠺪<E98A9D>憓𧼮<E68693><F0A7BCAE>𠉛弦<F0A0899B><EFBFBD><E5AF9E><EFBFBD><EFBFBD>亥砭<E4BAA5><EFBFBD>

**<2A><EFBFBD>隞瑕<E99A9E>?嚗?- <20><> <EFBFBD><EFBFBD><EFBFBD>亥砭: <20>亥砭<E4BAA5>𠉛弦<F0A0899B><EFBFBD><E5AF9E><EFBFBD>RF銵冽聢<E586BD><E881A2><EFBFBD><E68690><EFBFBD>隞?- <20><> **瘛瑕<E7989B><EFBFBD>蝝?: <20>峕𧒄<E5B395><EFBFBD>蝏𤘪<E89D8F><F0A498AA>𡝗㺭<F0A19D97><EFBFBD>REDCap嚗匧<E59A97><E58CA7><EFBFBD><E482BF><EFBFBD><EFBFBD><EFBFBD><EFBFBD>﹝嚗㇄ify嚗?- <20><EFBFBD><EFBFBD>頝舐眏: <20>寞旿<E5AF9E><EFBFBD><E586BD><EFBFBD><E6A185>芸𢆡<E88AB8>㗇𥋘<E39787>唳旿皞?

8.8 <20><><EFBFBD>舀䲮獢?

<EFBFBD><EFBFBD><EFBFBD>㗇𥋘

蝏游漲 <EFBFBD><EFBFBD><EFBFBD><EFBFBD>
*<EFBFBD><EFBFBD>摨𤘪沲<EFBFBD>? <EFBFBD>閖★<EFBFBD><EFBFBD><EFBFBD><EFBFBD>摨橒<EFBFBD>1銝杷IT憿寧𤌍 <20>?1銝枋ify Dataset嚗?
<EFBFBD><EFBFBD>﹝銝𠹺<EFBFBD> Dify Web<65>屸𢒰<E5B1B8>见𢆡銝𠹺<E98A9D>嚗㇈VP<56>嗆挾嚗?
憿寧𤌍<EFBFBD><EFBFBD> <EFBFBD><EFBFBD>蝏穃<EFBFBD>暺䁅恕憿寧𤌍嚗<EFBFBD><EFBFBD><EFBFBD>典銁iit_schema.projects.dify_dataset_id嚗?

<EFBFBD><EFBFBD>摰䂿緵

1. <20><EFBFBD><E68B99>誩㦛霂<E3A69B><E99C82>

<EFBFBD>灼ChatService.detectIntent()銝剜鰵憓裇query_protocol<EFBFBD>誩㦛嚗?

// 霂<><E99C82><EFBFBD><EFBFBD><EFBFBD>亥砭嚗<E7A0AD><E59A97>蝛嗆䲮獢<E4B2AE><E78DA2><EFBFBD><EFBFBD><E68690><EFBFBD><EFBFBD><EFBFBD><E4B0BB><EFBFBD><EFBFBD><EFBFBD><E8AD8C>RF蝑㚁<E89D91>
if (/(<28>𠉛弦<F0A0899B><EFBFBD>|隡衣<E99AA1>|<7C><EFBFBD><E4BAA4><EFBFBD>|CRF|<7C><><EFBFBD><EFBFBD><EFBFBD>銵育蝥喳<E89DA5>|<7C>仿<EFBFBD><EFBFBD>㘾膄|<7C><><EFBFBD>|<7C><EFBFBD><E4BAA6><EFBFBD><EFBFBD>|瘝餌<E7989D><E9A48C><EFBFBD>|霂閖<E99C82>霈曇恣|<7C>𠉛弦<F0A0899B><EFBFBD>|<7C>𠉛弦瘚<E5BCA6><E7989A>|閫<><E996AB><EFBFBD><EFBFBD><EFBFBD>|霂𦠜鱏<F0A6A09C><E9B18F><EFBFBD>|<7C><EFBFBD><E69B84><EFBFBD><EFBFBD>)/.test(message)) {
  return { intent: 'query_protocol' };
}

2. <20><EFBFBD>Dify<66>亥砭<E4BAA5><EFBFBD>

private async queryDifyKnowledge(query: string): Promise<string> {
  // 1. <20><EFBFBD>憿寧𤌍<E5AFA7><F0A48C8D>ifyDatasetId
  const project = await prisma.iitProject.findFirst({
    where: { status: 'active' },
    select: { name: true, difyDatasetId: true }
  });

  // 2. 靚<>鍂Dify API璉<49>蝝?  const retrievalResult = await difyClient.retrieveKnowledge(
    project.difyDatasetId,
    query,
    { retrieval_model: { search_method: 'semantic_search', top_k: 5 } }
  );

  // 3. <20><EFBFBD><E6BE86>𡝗<EFBFBD><E89D9D><EFBCB9>?  // 靽桀<E99DBD>bug嚗帋蝙<E5B88B>冽迤蝖桃<E89D96>摮埈挾頝臬<E9A09D> record.segment.document.name <20>?record.segment.content
  // ...
}

3. <20>湔鰵撖寡<E69296><EFBFBD><E7989A>

async handleMessage(userId: string, userMessage: string): Promise<string> {
  const { intent, params } = this.detectIntent(userMessage);

  // REDCap<61>亥砭
  let toolResult: any = null;
  if (intent === 'query_record') {
    toolResult = await this.queryRedcapRecord(params.recordId);
  }

  // Dify<66><EFBFBD>摨𤘪䰻霂?  let difyKnowledge: string = '';
  if (intent === 'query_protocol') {
    difyKnowledge = await this.queryDifyKnowledge(userMessage);
  }

  // <20><>遣LLM瘨<4D><E798A8><EFBFBD><E59A97><EFBFBD>嗆釣<E59786>充EDCap<61>唳旿<E594B3>㷉ify<66><EFBFBD>嚗?  const messages = this.buildMessagesWithData(
    userMessage, context, toolResult, difyKnowledge, userId
  );

  // 靚<>鍂LLM<4C><4D><EFBFBD><EFBFBD><EFBFBD>
  const response = await this.llm.chat(messages);
  // ...
}

8.9 <20><EFBFBD><E6A185>埝䰻銝𦒘耨憭?

<EFBFBD><EFBFBD>1: AI銝齿䰻霂¥ify嚗諹䌊撌梁<E6928C><E6A281><EFBFBD>獢?

<EFBFBD>啗情: <20><EFBFBD><E586BD>?蝥喳<E89DA5><E596B3><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E98A8B>"嚗淾I蝻㚚<E89DBB><EFBFBD>蝑娍<E89D91>嚗㷉ify<66><EFBFBD><E689B9><EFBFBD><E594B3>亥砭霈啣<E99C88>

<EFBFBD><EFBFBD>1: <20>誩㦛霂<E3A69B><E99C82><EFBFBD>喲睸霂滢<E99C82><E6BBA2>?- 蝻箏<EFBFBD>: "<22>仿<EFBFBD>?<3F>?霂𦠜鱏<F0A6A09C><E9B18F><EFBFBD>"<22>?<3F><EFBFBD><E69B84><EFBFBD><EFBFBD>"

  • <EFBFBD><EFBFBD>: <20><EFBFBD><E68B99>喲睸霂滚<E99C82>銵? <EFBFBD><EFBFBD>2: Dify API餈𥪜<E9A488>摮埈挾頝臬<E9A09D><E887AC>躰秤
  • <EFBFBD>躰秤: record.document_name<EFBFBD><EFBFBD>record.content <20>?餈𥪜<E9A488>undefined`
  • <EFBFBD>: record.segment.document.name<EFBFBD><EFBFBD>record.segment.content`
  • <EFBFBD><EFBFBD>: 靽格迤摮埈挾霈輸䔮頝臬<E9A09D>

<EFBFBD><EFBFBD><EFBFBD><EFBFBD>:

  1. <EFBFBD>𥕦遣debug-dify-injection.ts餈質葵<EFBFBD>唳旿瘜典<EFBFBD><EFBFBD><EFBFBD>
  2. <EFBFBD>𥕦遣inspect-dify-response.ts<EFBFBD><EFBFBD>Dify API摰鮋<E691B0>餈𥪜<E9A488>蝏𤘪<E89D8F>
  3. <EFBFBD>𤑳緵撟嗡耨憭滚<EFBFBD>畾菔楝敺<EFBFBD><EFBFBD>霂?

8.10 瘚贝<E7989A>撉諹<E69289>

瘚贝<EFBFBD><EFBFBD>箸艶 <EFBFBD><EFBFBD> <EFBFBD>唳旿皞? 蝏𤘪<EFBFBD>
<EFBFBD><EFBFBD><EFBFBD>亥砭 "餈嗘葵<E59798>𠉛弦<F0A0899B><E5BCA6><EFBFBD><EFBFBD><EFBFBD><E69697><EFBFBD>糓隞<E7B393><EFBFBD><E98A8B>" Dify <EFBFBD>?<3F>𣂼<EFBFBD>
CRF<EFBFBD>亥砭 "CRF銵冽聢銝剜<E98A9D><E5899C><EFBFBD><EFBFBD><E996AB><EFBFBD><EFBFBD><EFBFBD>嚗? Dify <EFBFBD>?<3F>𣂼<EFBFBD>
*<EFBFBD><EFBFBD><EFBFBD><EFBFBD>䰻霂? "ID 7<><37><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>? REDCap <EFBFBD>?<3F>𣂼<EFBFBD>
蝏蠘恣<EFBFBD>亥砭 "<22><EFBFBD><E6A180><EFBFBD><EFBFBD><E988AD>撠睲犖嚗? REDCap <EFBFBD>?<3F>𣂼<EFBFBD>
瘛瑕<EFBFBD><EFBFBD>亥砭 "餈嗘葵<E59798>𠉛弦<F0A0899B><E5BCA6>蜓閬<E89C93><E996AC>蝛嗥𤌍<E597A5><F0A48C8D>糓隞<E7B393><EFBFBD><E98A8B>" Dify <EFBFBD>?<3F>𣂼<EFBFBD>

8.11 <20><><EFBFBD><EFBFBD><EFBFBD>

**<2A><><EFBFBD>舀沲<E88880>?*:

<EFBFBD><EFBFBD><EFBFBD>鞾䔮 <20>?<3F>誩㦛霂<E3A69B><E99C82> <20>?<3F><EFBFBD> [query_protocol] <20>?Dify API <20>?<3F><><EFBFBD><EFB99D><20><EFBFBD> [query_record]   <20>?REDCap API <20>?<3F><><EFBFBD><EFBFBD><EFBFBD>?                       <20><EFBFBD> [count_records]  <20>?REDCap API <20>?蝏蠘恣<E8A098>唳旿
                              <20>?                    <20><>遣LLM Prompt嚗𠄎ystem + Data + Context嚗?                              <20>?                         DeepSeek-V3
                              <20>?                           AI<41><EFBFBD>

<EFBFBD><EFBFBD><EFBFBD><EFBFBD>:

  1. <EFBFBD>?**瘛瑕<E7989B><EFBFBD>蝝?*: <20>峕𧒄<E5B395><EFBFBD>蝏𤘪<E89D8F><F0A498AA>𡝗㺭<F0A19D97><EFBFBD><E6A180><EFBFBD><E482BF><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
  2. <EFBFBD>?<EFBFBD><EFBFBD>頝舐眏: <20>寞旿<E5AF9E>誩㦛<E8AAA9>芸𢆡<E88AB8>㗇𥋘<E39787>唳旿皞?3. <20>?<EFBFBD>脫迫撟餉<EFBFBD>: <20><><EFBFBD><EFBFBD>蝑𥪜抅鈭𡒊<E988AD>摰墧㺭<E5A2A7>?<3F><>
  3. <EFBFBD>?<EFBFBD><EFBFBD><EFBFBD><EFBFBD>: 皜<><EFBFBD><E88B8A><EFBFBD>唳旿<E594B3>亥䌊REDCap<61>靝ify

霂衣<EFBFBD>霈啣<EFBFBD>: <20><><EFBFBD> [Dify<66><EFBFBD>摨㯄<E691A8><E3AF84>𣂼<EFBFBD><F0A382BC>𤏸扇敶騟(../06-撘<><E69298>𤏸扇敶?2026-01-04-Dify<66><EFBFBD>摨㯄<E691A8><E3AF84>𣂼<EFBFBD><F0A382BC>𤏸扇敶?md)


<EFBFBD>?銋腈<E98A8B><E88588><EFBFBD><EFBFBD>

<EFBFBD><EFBFBD><EFBFBD>𣂼停嚗<EFBFBD><EFBFBD><EFBFBD><EFBFBD>?+ Dify<66><79><EFBFBD>嚗?

  1. <EFBFBD>?**2憭拐<E686AD>蝥?*嚗𡁏<E59A97>敹怠<E695B9><E680A0>蚊I撖寡<E69296><E5AFA1><EFBFBD><EFBFBD>鉄Dify<66><79><EFBFBD>嚗?2. <20>?**銝𠹺<E98A9D><F0A0B9BA><EFBFBD>扇敹?*嚗𡁏𣈲<F0A1818F><F0A388B2><EFBFBD>頧桀笆霂嘅<E99C82>3頧殷<E9A0A7>
  2. <EFBFBD>?**甇<>銁颲枏<E9A2B2><E69E8F><EFBFBD>**嚗𡁻<E59A97><F0A181BB>滨鍂<E6BBA8><EFBFBD><E7919E>?4. <20>?**隞<><E99A9E><EFBFBD><E996AB>**嚗?隞?<3F>質䌊<E8B3AA><EFBFBD><E588BB><EFBFBD><E680A5>?5. <20>?瘛瑕<EFBFBD><EFBFBD>蝝?*嚗𡁜<E59A97><F0A1819C>嗆𣈲<E59786><F0A388B2>EDCap摰墧𧒄<E5A2A7>唳旿 + Dify<66><79><EFBFBD><EFBFBD>摨?6. <20>?<EFBFBD>脫迫撟餉<EFBFBD>**嚗𡁏<E59A97><F0A1818F><EFBFBD>蝑𥪜抅鈭𡒊<E988AD>摰墧㺭<E5A2A7><EFBFBD>蝏苷<E89D8F>蝻㚚<E89DBB>?

<EFBFBD><EFBFBD><EFBFBD>臭漁<EFBFBD>?

  • <EFBFBD><EFBFBD> SessionMemory嚗𡁜<EFBFBD>摮睃<EFBFBD><EFBFBD><EFBFBD><EFBFBD>𣳇<EFBFBD>Redis
  • <EFBFBD><EFBFBD>閙郊頝舐眏嚗帋<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>eAct敺芰㴓
  • <EFBFBD>圲 **憭滨鍂<E6BBA8><EFBFBD>**嚗鑹edcapAdapter + WechatService
  • <EFBFBD>?**<2A><EFBFBD>靽肽<E99DBD>**嚗?3蝘垍垢<E59E8D>啁垢撱嗉<E692B1>
  • <EFBFBD><EFBFBD> 摰∟恣摰峕㟲嚗朞扇敶閙<EFBFBD><EFBFBD>匧笆霂?

<EFBFBD><EFBFBD>隞瑕<EFBFBD>?

**Before嚗㇄ay 3嚗?*嚗?- <20>?PI<50>臭誑<E887AD>交𤣰隡<F0A4A3B0><E99AA1>敺桐縑<E6A190>𡁶䰻

  • <EFBFBD>?PI<50><EFBFBD>銝餃𢆡<E9A483>亥砭<E4BAA5>唳旿
  • <EFBFBD>?<3F><><EFBFBD>蒈敶𥵃EDCap<61><EFBFBD>

**After嚗㇊hase 1.5 + Dify<66><79><EFBFBD>嚗?*嚗?- <20>?PI<50>臭誑<E887AD><EFBFBD>銝𡁜凝靽∩葉<E288A9>湔𦻖<E6B994>?<3F><EFBFBD>憭𡁜<E686AD>鈭?嚗㇌EDCap嚗?- <20>?PI<50>臭誑<E887AD>?P001<30><EFBFBD><E58A90><EFBFBD>摨𥪜<E691A8>"嚗㇌EDCap嚗?- <20>?PI<50>臭誑<E887AD>?<3F>𠉛弦<F0A0899B><E5BCA6><EFBFBD><EFBFBD><E4BAA4><EFBFBD><E69697><EFBFBD>糓隞<E7B393>銋?嚗㇄ify嚗?- <20>?PI<50>臭誑<E887AD>?CRF銵冽聢銝剜<E98A9D><E5899C><EFBFBD><EFBFBD><E996AB><EFBFBD><EFBFBD><EFBFBD>"嚗㇄ify嚗?- <20>?AI霈啣<E99C88>銝𠹺<E98A9D>頧桀笆霂嘅<E99C82><E59885><EFBFBD><EFBFBD><E99A9E>

  • <EFBFBD>?<3F>𧼮<EFBFBD>敹恍<E695B9><E6818D><EFBFBD><6蝘𡜐<E89D98>嚗峕<E59A97><E5B395><EFBFBD>
  • <EFBFBD>?AI<41><EFBFBD><E7AE94><EFBFBD><E7AC94>唳旿/<2F><><EFBFBD><EFBFBD>嚗䔶<E59A97>蝻㚚<E89DBB>?

<EFBFBD><EFBFBD> Phase 1.5 撘<><E69298><EFBFBD><E7A983><EFBFBD><EFBFBD> (2026-01-03 & 2026-01-04)

摰鮋<EFBFBD>摰峕<EFBFBD><EFBFBD><EFBFBD><EFBFBD>

  • <EFBFBD>?Day 1摰峕<E691B0> (2026-01-03): SessionMemory + ChatService + REDCap<61><70><EFBFBD>
  • <EFBFBD>?Day 2摰峕<E691B0> (2026-01-04): Dify<66><EFBFBD>摨㯄<E691A8><E3AF84>?+ 瘛瑕<E7989B><EFBFBD>蝝?- <20>?瘚贝<EFBFBD><EFBFBD><EFBFBD>: 隡<><E99AA1>敺桐縑撖寡<E69296> + <20><EFBFBD><E7AC94>唳旿<E594B3>亥砭 + <20><><EFBFBD>亥砭
  • <EFBFBD>?<EFBFBD><EFBFBD><EFBFBD>: 閫<><E996AB>LLM撟餉<E6929F><E9A489><EFBFBD> + 瘛瑕<E7989B><EFBFBD><EFBCB8>?

<EFBFBD>喲睸<EFBFBD><EFBFBD>

  1. <EFBFBD>?AI<41><EFBFBD>REDCap<61><EFBFBD><E7AC94>唳旿<E594B3><EFBFBD>嚗䔶<E59A97>蝻㚚<E89DBB>?2. <20>?AI<41><EFBFBD>Dify<66><EFBFBD>摨𤘪<E691A8><EFBFBD><E78DA2>蝑𠉛<E89D91>蝛嗆䲮獢<E4B2AE>䔮憸?3. <20>?瘛瑕<E7989B><EFBFBD><E89D9D><EFBD87>峕𧒄<E5B395><EFBFBD>蝏𤘪<E89D8F><F0A498AA>𡝗㺭<F0A19D97><EFBFBD><E6A180><EFBFBD><E482BF><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
  2. <EFBFBD>?隞擧㺭<E693A7><EFBFBD>霂餃<E99C82>憿寧𤌍<E5AFA7>滨蔭嚗ōest0102嚗?5. <20>?<3F>誩㦛霂<E3A69B><E99C82> + <20><EFBFBD>頝舐眏 + <20>唳旿<E594B3>亥砭 + LLM<4C><4D><EFBFBD>
  3. <EFBFBD>?銝𠹺<E98A9D><F0A0B9BA><EFBFBD>扇敹<E68987><E695B9><EFBFBD><EFBFBD>餈?頧桀笆霂嘅<E99C82>
  4. <EFBFBD>?<3F>單𧒄<E596AE><EFBFBD>嚗?甇<><EFBFBD>亥砭"嚗?

瘚贝<EFBFBD>撉諹<EFBFBD>

  • 憿寧𤌍: test0102
    • REDCap PID: 16, 11<31>∟扇敶? - Dify Dataset ID: b49595b2-bf71-4e47-9988-4aa2816d3c6f
    • <EFBFBD><EFBFBD>﹝: <20>𠉛弦<F0A0899B><EFBFBD><E5AF9E><EFBFBD>RF銵冽聢嚗?銝芣<E98A9D>隞塚<E99A9E>撌脣<E6928C><E884A3><EFBFBD><EFBFBD>
  • <EFBFBD>箸艶1: <20>亥砭ID 7<><37><EFBFBD><EFBFBD><EFBFBD><EFBFBD>REDCap嚗争<E59A97> <20>?摰<><E691B0><EFBFBD><EFBFBD><E5AFA5><EFBFBD><E7AC94>唳旿
  • <EFBFBD>箸艶2: <20>亥砭<E4BAA5>𠉛弦<F0A0899B>㘾膄<E398BE><E88684><EFBFBD>嚗㇄ify嚗争<E59A97> <20>?<3F><EFBFBD><E7AE94><EFBFBD><EFBFBD><EFB99D><EFBFBD><EFBFBD>
  • <EFBFBD>箸艶3: <20>亥砭CRF閫<46><E996AB><EFBFBD><EFBFBD><EFBFBD>嚗㇄ify嚗争<E59A97> <20>?<3F><EFBFBD><E7AE94><EFBFBD><EFBFBD><EFB99D><EFBFBD><EFBFBD>
  • <EFBFBD>箸艶4: 蝏蠘恣<E8A098><EFBFBD>鈭箸㺭嚗㇌EDCap嚗争<E59A97> <20>?<3F><>蝏蠘恣11鈭?- 蝏𤘪<EFBFBD>: <20>?<3F><><EFBFBD><EFBFBD>霂閖<E99C82><EFBFBD>嚗峕<E59A97>蝻㚚<E89DBB>?

霂衣<EFBFBD>霈啣<EFBFBD>


**蝏湔擪<E6B994>?*嚗䥑IT Manager撘<72><E69298>穃𣪧<E7A983>? **<2A><><EFBFBD>擧凒<E693A7>?*嚗?026-01-03
**<2A><><EFBFBD><EFBFBD>?*嚗尠<E59A97> Phase 1.5撌脣<EFBFBD><EFBFBD>?