[{"data":1,"prerenderedAt":167},["ShallowReactive",2],{"content:project:gym-agent":3},{"id":4,"title":5,"body":6,"cover":146,"date":147,"demoUrl":148,"description":149,"draft":150,"extension":151,"meta":152,"navigation":153,"path":154,"repoUrl":155,"seo":156,"status":157,"stem":158,"tags":159,"updated":165,"__hash__":166},"projects\u002Fprojects\u002Fgym-agent.md","Gym-Agent：把 Agent 和 RAG 塞进一个真实的预约系统",{"type":7,"value":8,"toc":135},"minimark",[9,13,22,25,32,45,48,52,55,62,65,73,77,80,95,98,112,115],[10,11,12],"h2",{"id":12},"为什么是体育场馆",[14,15,16,17,21],"p",{},"我自己除了写代码，剩下的时间基本都在健身房和球场上，所以第一个从 0 到 1 的项目就想做这个。\n业务本身不新，场馆预约系统满地都是，我想要的是一条",[18,19,20],"strong",{},"足够完整的后端主链路","：\n用户、场馆、场地、时段、订单、公告、评论、收藏、通知，每一环都得自己接一遍。\n链路跑通之后，再往上叠 Agent 和 RAG——这样 AI 部分是长在真实业务上的，不是一个悬浮的聊天框。",[10,23,24],{"id":24},"先说预约这条主链路",[14,26,27,28,31],{},"真正的难点不在增删改查，在",[18,29,30],{},"时段","：",[33,34,35,39,42],"ul",{},[36,37,38],"li",{},"场地按日期 + 时间段开放，同一场地同一时段不能被约两次，冲突校验落在服务层，不依赖前端置灰；",[36,40,41],{},"前端做了一张预约时段可视化表，可约、已占、已停用一个颜色说清楚，不用用户自己算时间；",[36,43,44],{},"订单要能区分预约订单和商城订单，状态流转、取消、退款各自有流水记录（支付是模拟的，这点不装）。",[14,46,47],{},"外围接了 Redis（缓存、验证码、登录态）、MinIO（图片和文件）、Mail（预约\u002F取消\u002F退款通知）、\nWebSocket、EasyExcel 导出，Maven 拆成 parent + admin \u002F model \u002F common 三个模块。\n前端是 Vue3 + Element Plus + Vite，后台管理端和用户前台是两个工程，\n本地跑起来分别是 3001 和 3002，后端在 9601。",[10,49,51],{"id":50},"agent-只出草稿确认权在人手里","Agent 只出草稿，确认权在人手里",[14,53,54],{},"这是整个项目里我最在意的一条设计。",[14,56,57,58,61],{},"用户说「帮我约明晚七点到九点的一个羽毛球场地」，Agent 会调工具去查场馆、查可预约时段，\n然后",[18,59,60],{},"生成一份预约草稿","返回给前端；前端把它渲染成一张卡片，用户点确认按钮，才真正走预约接口。\n中间那一步不能省——模型理解错了时间或者场地，代价是一次错误的真实预约。",[14,63,64],{},"配套还做了：",[33,66,67,70],{},[36,68,69],{},"「我的预约」这类高频意图走直达路由，不把所有问题都塞给模型，准确性和响应速度都更好；",[36,71,72],{},"每次工具调用的名称、参数、结果都记进日志，带 traceId，后台能直接看到 Agent 到底干了什么——\n调试 AI 功能时，这一步比看代码有用得多。",[10,74,76],{"id":75},"rag-用来回答平台规则这类问题","RAG 用来回答「平台规则」这类问题",[14,78,79],{},"知识库走 PostgreSQL + pgvector：文档切片入库、向量化、检索、拼进提示词。\n两个细节值得单独说：",[81,82,83,89],"ol",{},[36,84,85,88],{},[18,86,87],{},"命中来源展示","：回答下面会列出标题、标签、知识范围、相似度和命中的文本片段。\n没有来源的 RAG 回答，用户没法判断能不能信；",[36,90,91,94],{},[18,92,93],{},"单条知识增量索引","：后台改一条公告，不需要把整个知识库重跑一遍。",[10,96,97],{"id":97},"我知道哪里还不够",[33,99,100,103,106,109],{},[36,101,102],{},"支付是模拟的，没有真实资金链路；",[36,104,105],{},"场馆别名（「奥体」到底指哪个馆）还没做解析，靠模型硬猜；",[36,107,108],{},"多轮上下文的裁剪比较粗，长对话会丢信息；",[36,110,111],{},"没有评测集。Agent 现在只能靠人工试，改一版提示词不知道是变好还是变坏——\n这也是我后来做 Job-Agent 时把 Eval 平台补上的原因。",[10,113,114],{"id":114},"怎么跑起来",[14,116,117,118,122,123,126,127,130,131,134],{},"需要 JDK 17、Maven 3.8+、MySQL 8、Redis 7、Node.js 16+。\n先执行 ",[119,120,121],"code",{},"db\u002Fgym.sql"," 初始化数据库，再把 ",[119,124,125],{},"Gym-admin\u002Fsrc\u002Fmain\u002Fresources\u002Fapplication.yml"," 换成自己的环境，\n敏感配置单独放在 ",[119,128,129],{},"application-local.yml","（这个文件不进仓库），然后启动 ",[119,132,133],{},"AdminApplication"," 和两个前端工程。",{"title":136,"searchDepth":137,"depth":137,"links":138},"",3,[139,141,142,143,144,145],{"id":12,"depth":140,"text":12},2,{"id":24,"depth":140,"text":24},{"id":50,"depth":140,"text":51},{"id":75,"depth":140,"text":76},{"id":97,"depth":140,"text":97},{"id":114,"depth":140,"text":114},"\u002Fimages\u002Fcovers\u002Fgym-agent.jpg","2026-03-03",null,"Spring Boot 3 + Vue3 的体育场馆预约平台，在常规预约链路之外接了 LangChain4j Agent 与 pgvector 知识库；Agent 只生成预约草稿，必须由用户点确认才真正落库。",false,"md",{},true,"\u002Fprojects\u002Fgym-agent","https:\u002F\u002Fgithub.com\u002FHFJ0624\u002FGym-Agent",{"title":5,"description":149},"maintenance","projects\u002Fgym-agent",[160,161,162,163,164],"Spring Boot","Vue3","LLM 应用","RAG","Agent","2026-05-27","dxEJCYfD87DdmP0OByKK2vwB5uM4YMI5c9AQ4S5LJ24",1792063306707]