# **IIT Manager Agent V2.9 补充设计:主动性与用户画像增强** **目标:** 响应智能化评估中的 P0 需求,补齐“主动服务”和“个性化”短板,并增强复杂任务处理能力。 **原则:** 坚持 **Postgres-Only**,复用现有的 **Skill (JSON)** 和 **ToolsService**。 ## **1\. 主动提醒机制设计 (The Proactive Engine)** 我们不需要写死一堆 cron 脚本,而是将“主动提醒”抽象为一种特殊的 **Skill**。 ### **1.1 架构复用逻辑** * **触发源**:pg-boss 的定时任务 (Scheduler)。 * **规则载体**:iit\_skills 表(新增 trigger\_type: 'cron')。 * **执行者**:QcEngineService (复用现有的逻辑)。 ### **1.2 数据库 Schema 扩展** 在 iit\_skills 表中增加调度字段: model IitSkill { // ... 原有字段 triggerType String @default("webhook") // 'webhook' | 'cron' | 'event' cronSchedule String? // 例如: "0 9 \* \* \*" (每天9点) // 核心配置 (JSON) config Json } ### **1.3 Skill 配置示例:访视提醒** 这是一个“每天早上9点检查即将到期访视”的技能配置。 { "name": "即将到期访视提醒", "description": "扫描未来3天内需要访视的患者", // 核心:复用 Engine A 的逻辑来筛选数据 "hard\_rules": \[ // 这里我们稍微扩展一下 hard\_rules 的能力,支持 SQL-like 的筛选 // 或者直接调用工具获取列表 \], // 核心:复用 Engine B (LLM) 来生成人性化通知 "soft\_instructions": \[ { "instruction": "请调用 \`get\_upcoming\_visits(days=3)\` 工具。如果有数据,请根据【用户偏好】生成一条提醒消息。如果没有数据,什么都不做。", "tools": \["get\_upcoming\_visits", "send\_wechat\_msg"\] } \] } ### **1.4 代码实现 (SchedulerService.ts)** // SchedulerService.ts async initCronJobs() { // 1\. 从数据库加载所有 triggerType \= 'cron' 的 Skill const cronSkills \= await prisma.iitSkill.findMany({ where: { triggerType: 'cron', isActive: true } }); for (const skill of cronSkills) { // 2\. 注册到 pg-boss await this.boss.schedule( \`run-skill-${skill.id}\`, skill.cronSchedule, { skillId: skill.id } ); } } // Worker 处理逻辑 async handleCronSkill(job) { const { skillId } \= job.data; const skill \= await prisma.iitSkill.findUnique({ where: { id: skillId } }); // 3\. 复用现有的引擎! // 就像处理 Webhook 一样处理 Cron,架构极其统一 await this.qcEngine.runSoftAgent( skill.config.soft\_instructions\[0\].instruction, { source: 'cron\_job' } // Context ); } **效果:** 你只需要在后台配一个 JSON,Agent 就会每天早上 9 点醒来,查数据,然后发微信:“张医生,P005 明天该来复查了。” ## **2\. 用户画像增强 (User Profiling)** 我们已经在 V2.8 中设计了 iit\_project\_memory (Hot Memory),现在只需要把“画像”结构化。 ### **2.1 存储设计** 在 iit\_user\_preferences 表中,我们将 preferences 字段细分为两类: 1. **显性偏好 (Explicit)**:用户明确说的(“我要简报”)。 2. **隐性画像 (Implicit)**:系统分析出来的(“他是 PI,关注宏观数据”)。 // preferences 字段内容示例 { "communication\_style": "concise", // 简练 | 详尽 "role\_tag": "PI", // PI | CRC | CRA "focus\_areas": \["AE", "Enrollment"\], // 关注点 "notification\_time": "08:30", // 接收时间 "feedback\_history": { // 简单的反馈记录 "thumbs\_up": 12, "thumbs\_down": 1 } } ### **2.2 自动画像更新 (The Profiler)** 我们在 ChatService 结束一次对话后,异步触发一个微型任务:**“画像侧写”**。 // ProfilerService.ts async updateProfile(userId: string, lastConversation: any\[\]) { const currentProfile \= await this.getProfile(userId); // 让 LLM 当心理分析师 const prompt \= \` 基于刚刚的对话,更新用户画像。 对话:${JSON.stringify(lastConversation)} 旧画像:${JSON.stringify(currentProfile)} 提取规则: 1\. 如果用户抱怨"太长了",将 style 设为 concise。 2\. 如果用户问了"安全性",将 AE 加入 focus\_areas。 返回新的 JSON。 \`; const newProfile \= await this.llm.chat(prompt); await this.saveProfile(userId, newProfile); } ## **3\. 反馈循环 (Feedback Loop \- Lite)** 不要做复杂的强化学习(RLHF),只做简单的\*\*“点赞/点踩”\*\*。 ### **3.1 交互设计** 在 Agent 回复的每条消息下面(如果是小程序/网页),加两个小按钮:👍 / 👎。 ### **3.2 逻辑闭环** 1. **用户点踩 (👎)**。 2. **前端**:弹窗询问“哪里不好?”(选项:太啰嗦、不准确、没听懂)。 3. **后端**: * 将这条 Negative Feedback 写入 iit\_conversation\_history。 * **关键动作**:触发一次 Hot Memory 更新。 * **Prompt**:"用户对刚才的回答点了踩,原因是'太啰嗦'。请在 Hot Memory 中记下一条禁令:\[针对该用户,严禁输出超过 100 字的废话\]。" **结果**:Agent 下次遇到这个用户,真的会改。这就是最真实的“智能感”。 ## **4\. 多意图与任务规划增强 (Multi-Intent Handling)** 针对“先做 A,再做 B”的复杂指令(如:“查一下 P001 的状态,然后通知张医生”),我们不需要引入重型 Planner,而是通过 **ReAct \+ Prompt Engineering** 实现。 ### **4.1 核心策略:ReAct 自然涌现** **结论**:ReAct 引擎本身就是一个轻量级 Planner。 不需要额外的 Planner 模块,只需要在 **System Prompt** 中显式教导 Agent 如何拆解任务。 ### **4.2 Prompt 增强设计** 在 backend/src/modules/iit-manager/engines/ReActEngine.ts 的系统提示词中加入以下**思维链(Chain of Thought)指令**: const REACT\_SYSTEM\_PROMPT \= \` 你是一个临床研究助手。你可以使用工具来回答问题。 核心原则: 1\. \*\*任务拆解\*\*:如果用户请求包含多个步骤(例如"先...再...","查完...发给..."),请务必分步执行。 \- 不要试图在一个步骤里做完所有事。 \- 比如:先调用查询工具,获得结果后,再调用发送工具。 2\. \*\*依赖管理\*\*:如果步骤 B 依赖步骤 A 的结果,必须先执行 A,观察 A 的结果,再执行 B。 3\. \*\*完成检查\*\*:在输出 Final Answer 之前,检查是否完成了用户的所有指令。 思考格式示例: Thought: 用户想查 P001 并通知张医生。我有两个任务。首先,我需要查 P001 的状态。 Action: 调用 read\_clinical\_data(id='P001')... Observation: {"status": "Enrolled"} Thought: 我已经查到了状态。现在我需要执行第二个任务:发消息给张医生。 Action: 调用 send\_wechat\_msg(to='Dr. Zhang', content='P001 status is Enrolled')... ... \`; ### **4.3 为什么不用 Planner?** 对于 2 人团队,显式 Planner(先生成计划列表,再循环执行)过于沉重且难以维护状态。ReAct 的 **“边走边想”** 模式完全能够覆盖 90% 的“线性多步任务”。 ## **5\. 总结与建议** | 需求 | 解决方案 | 复杂度 | 推荐实施阶段 | | :---- | :---- | :---- | :---- | | **主动提醒** | **Cron Skill** (复用 pg-boss \+ QC Engine) | 低 | **Phase 4** (与周报一起做) | | **用户画像** | **Profiler Job** (对话后异步分析) | 中 | **Phase 5** (智能化增强) | | **反馈循环** | **点赞按钮 \+ 记忆写入** | 低 | **Phase 5** | | **多意图处理** | **ReAct Prompt 优化** (无需新模块) | 极低 | **Phase 5** (开发 ReActEngine 时同步完成) | ### **对你的建议** 1. **全盘接受 P0 建议**:主动提醒和用户画像是成本低、收益大的功能。 2. **技术上保持克制**: * 别写专门的 Alert Engine,用 Cron Skill 复用现有的引擎。 * 别搞复杂的推荐算法,用 JSON 存画像就够了。 * 别搞复杂的 Planner,用 **Prompt** 教会 ReAct 拆解任务就够了。 这样,你的 V2.6+ 架构就真正补齐了“右脑”的情商短板,变成了一个 **“有记忆、会主动、懂人情、能办事”** 的完整 Agent。