这周我主要做的是康复智能体的后端开发。

一、康复智能体设计思路

我把康复智能体的目标定得很简单,就是让用户在问诊结束后,能继续收到清楚、能执行、能维护的康复安排,把吃药、复查、禁忌这些内容变成日程。

我一开始的思路是分两步走。

第一步,先生成康复建议消息。
这一步会结合用户的消息、处方单文字、历史对话,再让大模型整理出一段适合用户看的康复内容。

第二步,再把这段内容转成真正的日程记录。
用户不是每次都想要全部内容,所以我让前端做多选题,用户选吃药、复查、禁忌中的一项或多项,后端只生成勾选的那部分日程。

我觉得这样做比较符合实际使用场景。用户不需要一次接受太多信息,也能自己控制康复计划生成的范围。

然后我根据我的想法与前端同学进行了交流,确认了api需求和前端界面设计如下:

二、康复智能体开发过程

1. /rehab/generate 生成康复消息

这个接口是康复智能体里最先做的部分。
我一开始没有让它直接落库,而是先让它生成一条“给用户看的康复消息”。
这样做的原因很简单:康复建议不能一上来就变成死板的日程,先把意思讲清楚,用户才更容易接受。

这个接口会先拿到 message_id、session_id、用户输入内容,还有可选的处方单图片。
如果用户上传了处方单,我会先让 Qwen 去识别图片里的文字,把处方内容转成文本。
然后我再把这段处方文字、用户消息、历史对话一起交给百川模型,让它先提取疾病名、药品名、检查名这些关键词。
提取完以后,我再去 kg_relation 表里做模糊查询,看看知识图谱里有没有相关记录。
最后把处方文字、用户消息、KG记录、历史对话一起喂给模型,让它生成一段中文康复回复。

POST /rehab/generate
{
  "message_id": 101,
  "session_id": "abc123",
  "content": "请根据我的处方安排康复计划",
  "prescription_files": [
    {
      "file_url": "http://127.0.0.1:8080/uploads/multimodal/xxx",
      "file_type": "PRESCRIPTION",
      "file_name": "处方单.jpg"
    }
  ]
}

我这里特别注意了一点,就是模型输出不能只是一段泛泛而谈的话。
我会要求它同时返回 reply、summary 和 tasks。
其中 reply 是给用户看的回复,summary 是简短总结,tasks 是后面生成日程要用的结构化任务信息。

Map<String, Object> result = integrationHttpService.generateRehabMessage(
        sessionId,
        userId,
        messageId,
        content,
        prescriptionFiles
);

这一步完成后,系统会把模型生成的回复保存成一条康复类型的聊天消息。
我觉得这一步很重要,因为它把智能体回答用户和后面生成日程分开了。
用户先看消息,确认方向没问题,再进入真正的日程创建阶段。

2. /rehab/plans 创建康复日程

这个接口是把上一阶段的康复消息,真正变成日程记录。
我这里没有直接让前端传一堆复杂数据,而是让前端先做一个多选题。
选项只有三个:吃药、复查、禁忌。
用户至少选一项,后端只会创建选中的那部分日程。

POST /rehab/plans
{
  "message_id": 101,
  "session_id": "abc123",
  "selected_task_types": ["MEDICATION", "CHECKUP"]
}

我在后端做了兼容,前端就算传中文“吃药、复查、禁忌”,或者传 1、2、3,我也能识别。
这一步先把用户选择标准化,再交给模型处理。

接着我会根据 message_id 找到那条康复消息,再把消息内容交给百川,让它把任务整理成 JSON。
模型生成的内容里会包括每个任务的类型、内容、时间和来源。
然后我再根据前端勾选的类型做一次硬过滤,保证只创建用户选中的日程,不会把没选的内容也一起写进去。

List<String> selectedTaskTypes = resolveSelectedRehabTaskTypes(
        request.get("selected_task_types"),
        request.get("task_types")
);

时间这块我也做了一个简单规则。
一天分早、中、晚三个时间段,分别对应 6 点、12 点、18 点。
一天 2 次就默认早晚各一次,一天 1 次就默认中午一次。
复查一般优先安排在当天早上,禁忌注意可以没有明确时间。

最后,模型输出的任务会被写入 rehab_schedule 表。
这样系统就从一条康复消息,真正落成了用户后面能看到、能打卡、能删除、能新增的日程数据。

这一步的重点是写入数据库,这样前端接入后,用户能拿到一个可执行的康复安排。

3. /rehab/schedules/{scheduleId}/check-in 单次打卡 / 取消打卡

这个接口是给用户日常维护用的。
我希望用户能像打卡一样操作日程,所以这里做了一个状态切换逻辑。

  • 0 待执行
  • 1 已完成
  • 2 已过期

如果当前是 0 或 2,我就切到 1。
如果当前已经是 1,我就根据当前时间判断是恢复成 0,还是变成 2。

if (currentStatus == 1) {
    nextStatus = executeTime.isBefore(LocalDateTime.now()) ? 2 : 0;
} else {
    nextStatus = 1;
}

4. /rehab/schedules/{scheduleId}/delete 删除日程

这个接口比较简单,就是删除当前用户自己的某条日程。
我做这一步的时候,重点不是删除本身,而是权限校验。

用户只能删自己的日程,不能删别人的。
所以我在仓库层加了 scheduleId + userId 的联合查找,避免越权操作。

long deleted = rehabScheduleRepository.deleteByScheduleIdAndUserId(
        scheduleId,
        authResult.getData().getUserId()
);

5. /rehab/schedules/add 手动新增日程

这个接口是给前端补录用的。
有些时候用户不想走模型生成,或者想自己补一条康复任务,这时候就可以直接新增。

支持传入任务类型、任务内容、执行时间和来源信息。

{
  "task_type": "MEDICATION",
  "task_content": "晚饭后服药",
  "execute_time": "2026-05-25T18:00:00",
  "status": 0,
  "source_report_id": "101"
}

我这里也做了默认状态处理。
如果用户传的状态不合法,后端会根据执行时间自动判断是待执行还是已过期。
这样数据会更稳,不容易乱。

6、自动维护

除了上面 5 个接口,我还加了一个每天凌晨自动跑的维护任务。
它会检查所有 status = 0 的日程,如果已经过期,就自动改成 2。

这一步虽然不是接口,但我觉得很有必要。
因为日程一旦多起来,靠用户手动维护会很麻烦,自动更新能让状态一直比较准确。

@Scheduled(cron = "0 1 0 * * ?")
public void markExpiredPendingSchedules() {
    rehabScheduleRepository.markExpiredPendingSchedules(LocalDateTime.now());
}

三、总结

这周我做完以后,康复智能体就不只是“生成一句回复”了。
它已经能把康复建议、用户选择、日程生成、状态维护、手动增删这些内容串成一条完整链路。

我觉得这部分最重要的地方,是把康复流程做成了一个能落地的闭环。
后面前端接入后,用户就可以很自然地查看、生成和维护自己的康复日程了。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐