软工团队实践第二次作业
2026秋软工实践 蛋仔派队——项目原型设计+概要设计
| 项目 | 内容 |
|---|---|
| 作业所属课程 | H202601 软件工程与软件工程实践 |
| 作业要求 | 2026秋软工实践团队作业-第二次(项目原型设计+概要设计) |
| 作业的目标 | 在第一次需求分析的基础上,完成可点击原型、系统分层架构、UML 与数据库设计,说明领域智能体与业务功能的协作方式,并通过 GitHub 留下可追踪的任务、评审与交付记录 |
| 团队名称 | 蛋仔派队 |
| 团队成员学号-名字 | 张诗培-102402145、张家祥-102402144、郑仕廷-102402146、郑宇嘉-102402147、叶腾骏-102402142、周凯-102402148、周山翔-102402149、周子钦-102402150、朱铭浩-102402151、韩尔洋-102402154、崔佳静-102402155、王昊-102402156 |
| 项目来源仓库 | https://github.com/zsp-c/medical-agent |
| 原型地址(墨刀) | https://modao.cc/proto/nCt2gfA6to4u6sNYrJRyP7/sharing?view_mode=read_only |
一、原型设计
1.1 十屏与任务流
十屏按五个阶段排列,全部面向终端用户:
访客 ── P01 登录 / 注册
│
问答 ── P02 工作台 ──┬── P03 检索中(流式)── P04 回答与证据 ── P07 图谱实体详情
└── P08 附件上传与解析
练习 ── P05 互动题卡 · 作答 ── P06 判题讲解
回顾 ── P10 练习中心 · 答题统计
配置 ── P09 设置(模型 / 记忆)
1.2 各屏说明
P01 登录 / 注册

P02 工作台 · 新会话

P03 问答 · 检索中(流式)

P04 问答 · 回答与证据

P05 互动题卡 · 作答

P06 互动题卡 · 判题讲解

P07 图谱 · 实体详情与子图

P08 附件 · 上传与解析

P09 设置 · 模型与记忆

P10 练习中心 · 答题统计

二、概要设计
2.1 系统分层结构
系统切成七层。

自上而下是接入层、接口层、服务层、领域层、数据访问层、存储层和外部依赖。接入层负责渲染、消费流式响应、在本地保管用户的 Key,不缓存业务数据。接口层是 FastAPI 的路由,管鉴权、参数校验、流式编码、统一错误码,不写查询语句也不含业务分支。服务层由七个服务组成,编排用例、划事务边界、把失败翻译成错误码,但不拼查询语句。领域层就是 ReActAgent、工具集、提示词模板和那几层硬约束,负责推理循环和上下文裁剪,不直接碰连接。数据访问层是唯一的数据读写出口,包括会话、消息、用户、附件、练习记录的仓储,图谱的 Cypher 模板,以及向量库的客户端,不含业务判断。再往下是 SQLite、Neo4j、两个向量集合,以及模型服务和嵌入服务两个外部依赖。。
2.2 模块划分
功能按业务内聚性拆成七个域、二十八个功能项,粒度定在可以独立指派给一个人并且能独立验收的程度。

| 域 | 功能项 | 约束 |
|---|---|---|
| M1 身份与权限 | 注册、登录、令牌校验、角色与停用 | 令牌有效期 720 分钟,停用即拒绝 |
| M2 对话与推理 | 会话管理、流式问答、工具调度、上下文裁剪 | 五类流式事件,迭代预算硬上限 |
| M3 知识检索 | 实体检索、关系查询、子图展开、证据回填 | 深度不超过 2,单次不超过 50 条,超限标记截断 |
| M4 练习闭环 | 出题、题卡下发、作答判定、讲解生成 | 答案不下发,一题一提交 |
| M5 附件与记忆 | 上传解析、附件注入、记忆检索、记忆沉淀、记忆清除 | 单文件不超过 20 MB,检索超时 5 秒按未命中处理 |
| M6 练习统计 | 作答聚合、学科正确率、薄弱知识点、练习记录 | 口径固定,只统计已提交作答 |
| M7 数据准备 | 图谱导入、题库向量化、索引重建、参数调整 | 本地脚本执行,不做界面 |
2.3 用例图

参与者三类:访客、个人用户、医学生(个人用户的特化)。外部系统三个:模型服务、图数据库、向量库。三个主用例都写了完整的用例描述表,这里各说要点。
医疗问答的前置条件是已登录。主成功场景是提问、检索图谱一到三次、组织回答、回填证据实体。异常分支有三条:图谱不可达时返回 503 并提示知识库暂不可用;模型超时返回已生成的部分并标记截断;幂等键命中时直接返回首次结果,写入条数为零。
出题练习与问答的区别在于它会改变对话形态。模型调用展示型工具下发题卡,然后停止展开讲解、等待用户作答。这条"暂停"的语义必须同时写进提示词和状态机,否则模型会在发题卡的同时把答案讲出来。
证据追溯从回答的证据标签进入实体详情,再展开一跳子图,它包含图谱检索用例,因为详情页的关系分组和子图走的是同一套查询模板。
上传附件和配置 Key 被明确标为可选用例。它们在主流程之外,不配置也能正常问答,这样可以避免把边缘功能画成必经步骤。
2.4 活动图

问答主流程的活动图重点不在主干,主干就是提问、检索、回答三步,重点在四个判断上。一是令牌是否有效,二是会话是否属于当前用户,两条都走拒绝分支,分别返回 401 与 403。三是是否需要调用工具,不需要就直接组织回答,省掉一轮空转。四是本轮轮次是否小于八、与已执行过的查询是否重复,任一条不满足就强制基于已有信息作答,并在回答里标注证据不足。
第二点值得展开。ReAct 循环必须有硬约束,一个是迭代次数上限八轮,一个是单轮图谱查询上限八次,还有一个是同轮重复查询拦截,参数完全相同的查询直接返回"请勿重复查询"。在这三条之外还有一层软约束,提示词里写明一般四次工具调用就够,引导模型尽早收敛。缺任何一层,模型都可能在一个模糊问题上反复查同一张图,单请求成本从几分钱涨到几块钱。约束不是限制能力,是让成本可预测。

题卡闭环单独画一张,因为它的状态比问答复杂:出题、下发(不含答案)、等待作答(模型挂起)、收到选择、检索答案、判定、锁定题卡、逐项解析、记录作答。这里有两个容易混淆的分支,「换一题」丢弃本题且不写作答记录,「再来一题」会写入上一题的结果。这条规则不画出来,实现时两边一定不一致,统计页的数据就废了。
2.5 类图

类图只画有行为的类,纯数据模型用注释带过,否则图会被二三十个数据结构淹没。结构上几个决定是这样的。
智能体服务是 ReActAgent 的唯一持有者,实例池按用户和模型两个维度缓存。智能体不放在请求作用域里,因为一次推理要跨多个工具调用,重复初始化会把工具注册的开销乘以调用次数。响应对象由智能体服务组装,不由接口层拼装,因为流式和非流式两条路径必须返回同一个结构,拼装逻辑只能有一处。图谱服务依赖仓储层的查询模板而不依赖驱动,驱动只在装配文件里出现一次,换驱动不改业务。
记忆服务的接口里没有"由智能体决定记什么"这样的方法。记忆的读是同步的,超过五秒按未命中处理,不再等待;写是异步的,回答完成后才沉淀。这个不对称是刻意的:读在关键路径上必须有时限,写不在关键路径上,慢一点没关系,但不能阻塞用户看到答案。
2.6 时序图

从浏览器发出流式请求到连接关闭,一共十一步消息。有三点值得单独说。
流式事件的顺序是契约,不是实现细节。工具调用、工具结果循环若干次,然后正文增量若干次,最后结束。前端的状态机完全建立在这个顺序上:收到工具调用就把步骤条推到检索图谱,收到第一个正文增量就推到组织回答。如果把工具结果提前或者合并,前端就会出现已经在输出正文、步骤条却还停在检索的错乱。
结束事件携带完整响应,包括证据实体列表和元信息。证据没有在正文增量里带,是因为要等所有工具调用结束才能确定,边流边发会让证据列表反复变化,用户看到标签闪烁。
消息落库只在结束之后写一次。流式中途断开时,已经发给用户的文字会以截断标记落库,保证用户看到的和库里存的一致。如果边流边写,断线就会留下一堆半截消息。

长期记忆单独画一张,因为它是唯一一条跨请求的链路:本次回答结束后异步沉淀,下次提问时同步检索注入。图里用分隔线把第 N 轮和第 N+1 轮切开。隔离键是用户和场景两层,用户层保证 A 的记忆不会被 B 检索到,场景层让同一用户在不同课程或不同会话组下有独立的记忆空间。检索超过五秒按未命中处理,不阻塞回答,因为记忆是增强不是前提。
2.7 协作图

协作图和时序图信息等价但视角不同,时序图看时间,协作图看耦合。这张图能直接读出三件事。
问答服务的扇出是六,分别连向智能体、图谱、题库、记忆、会话、附件六个服务,它必然是一个薄编排层,一旦它开始有业务判断,就说明职责漏到上层了。会话仓储是唯一被多方引用的仓储,问答、附件、统计都会碰消息,所以它的接口必须最稳定。记忆服务和练习服务之间没有连线,这是有意的:练习记录和长期记忆是两套数据,不能因为都跟用户有关就混在一起,否则清空记忆会顺手删掉练习统计。
三、数据库设计
完整内容见《蛋仔派队_数据库设计说明书》,这里说清几条主线。
3.1 四类存储各管一段
这个系统没有"一个数据库解决所有问题"的条件。四类数据形态不同,硬塞进一种存储必然有一类是残废的。
| 存储 | 承载 | 为什么用它 |
|---|---|---|
| SQLite | 用户、会话、消息、附件元数据、练习记录 | 单机部署、事务简单、免安装,读多写少且数据量可控 |
| Neo4j | 60,689 个实体、354,752 条关系 | 多跳可达性查询,关系型数据库要写递归查询,图库一句话 |
| Chroma 两个集合 | 题库向量(39,307 条、346 学科)、记忆向量 | 语义检索是近似最近邻问题,字符串匹配解决不了同义表达 |
| 对象卷 | 附件原文 | 大文本不进数据库,避免备份体积失控 |
3.2 ER 图与关系模型

核心五张表是用户、会话、消息、附件和练习记录,关系以一对多为主。用户到会话是一对多,删用户级联删会话。会话到消息是一对多,会话是消息的聚合根。用户到附件是一对多,附件仅本人可见,跨用户访问返回 403。用户到练习记录是一对多,练习记录独立于消息。
3.3ORM 映射
用 SQLAlchemy 2.0 的声明式映射,类型注解配 mapped_column,关系用双向显式声明。
class Message(Base):
__tablename__ = "messages"
id: Mapped[int] = mapped_column(primary_key=True)
session_id: Mapped[str] = mapped_column(
ForeignKey("sessions.id", ondelete="CASCADE"), index=True)
role: Mapped[str] = mapped_column(String(16)) # user | assistant
content: Mapped[str] = mapped_column(Text)
request_id: Mapped[str | None] = mapped_column(String(64), unique=True)
truncated: Mapped[bool] = mapped_column(default=False)
created_at: Mapped[datetime] = mapped_column(server_default=func.now())
session: Mapped["Session"] = relationship(back_populates="messages")
图谱和向量不走 ORM。Cypher 和向量库的查询语义与关系模型不同构,硬套 ORM 只会得到一层无意义的包装。这两处用参数化的查询模板和向量库客户端,都只暴露领域语义的方法,比如找实体、搜题目。
四、团队协作记录
4.1 开发计划
计划从 2026 年 11 月 9 日开始,九周做到可演示。每一周的验收标准是「当周结束时仓库里有一个能跑的版本」,不做无法验证的并行铺开。
| 周次 | 日期 | 阶段 | 主要工作 | 产出 |
|---|---|---|---|---|
| W1 | 11.09–11.15 | 起步 | 仓库结构、依赖与配置、SQLite 建表、十屏静态页 | 作业二提交,骨架能启动 |
| W2 | 11.16–11.22 | 数据层 | 图谱分批导入与校验,题库向量化,四个仓储与单测 | 图谱与题库查得通 |
| W3 | 11.23–11.29 | 对话主链路 | 推理循环、白名单工具、三层硬约束、五类流式事件 | 流式问答跑通 |
| W4 | 11.30–12.06 | 练习闭环 | 题卡出题、作答判题、逐项讲解、练习记录落库 | 出题到统计闭环 |
| W5 | 12.07–12.13 | 记忆与附件 | 记忆召回与沉淀、清空、附件解析与按需注入 | 两个增强特性可用 |
| W6 | 12.14–12.20 | 前端联调 | 十屏按原型还原、统计页接真实数据、错误态补齐 | 功能冻结 |
| W7 | 12.21–12.27 | 测试 | 单测、端到端走查、异常路径(拔图库、停嵌入服务)、修缺陷 | 主流程无阻断缺陷 |
| W8 | 12.28–01.03 | 文档与部署 | 部署说明、用户手册、演示录屏、答辩材料 | 可交付版本 |
| W9 | 01.04–01.10 | 收尾 | 答辩、按反馈修缺陷、补测试 | 最终提交 |
五、附页
通过网盘分享的文件:蛋仔派队_数据库设计说明书.pdf
链接: https://pan.baidu.com/s/16j4_mPG8yniDU9WWxjV4Cw?pwd=jzq4 提取码: jzq4
通过网盘分享的文件:蛋仔派队_系统设计说明书.pdf
链接: https://pan.baidu.com/s/1Ihyz4TXNNZ2DQOD-tpEQQw?pwd=za68 提取码: za68

浙公网安备 33010602011771号