山东大学创新实训个人博客6:康复智能体开发记录
这周我主要做的是康复智能体的后端开发。
一、康复智能体设计思路
我把康复智能体的目标定得很简单,就是让用户在问诊结束后,能继续收到清楚、能执行、能维护的康复安排,把吃药、复查、禁忌这些内容变成日程。
我一开始的思路是分两步走。
第一步,先生成康复建议消息。
这一步会结合用户的消息、处方单文字、历史对话,再让大模型整理出一段适合用户看的康复内容。第二步,再把这段内容转成真正的日程记录。
用户不是每次都想要全部内容,所以我让前端做多选题,用户选吃药、复查、禁忌中的一项或多项,后端只生成勾选的那部分日程。
我觉得这样做比较符合实际使用场景。用户不需要一次接受太多信息,也能自己控制康复计划生成的范围。
然后我根据我的想法与前端同学进行了交流,确认了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());
}
三、总结
这周我做完以后,康复智能体就不只是“生成一句回复”了。
它已经能把康复建议、用户选择、日程生成、状态维护、手动增删这些内容串成一条完整链路。
我觉得这部分最重要的地方,是把康复流程做成了一个能落地的闭环。
后面前端接入后,用户就可以很自然地查看、生成和维护自己的康复日程了。
更多推荐




所有评论(0)