feat(notice): 「发布校园公告」裸意图直达表单 + 修发文部门下拉与假的发布方式
build / build-push (push) Successful in 1m9s

- IntentShortcut 泛化成规则表(Rule{短语集 → Intent}):加新意图只需
  ① 一条 Intent ② 一个短语集 ③ RULES.add(...),不用动 AgentOrchestrator。
  新增 NOTICE_PUBLISH —— 仅教职工(notice.publish),学生无落点则落回模型如实说明。
  短语表只收带「发/发布」动词的说法(发布公告/发个公告/发布校园公告/发通知…),
  光说「公告」「通知」更像查询,不收,避免误判。
- ★ 修 prepareOptionCall 漏拼字典工具的 fixed 参数:公文部门字典是
  /api/dict/List?type=1,丢了 type 前端取回的是「没指定类型」的结果 → 空下拉。
- deptId 补 optionsKeys("k","v"):平台通用字典返回 {k,v} 而非 id/name;
  服务端兜底 fetchDictOptions 的候选键也补上 k/v。
- ★ 移除 notice.publish 的 publishType 与 deptName:后端 /api/notice/Create 就是
  直接发布(无 publishType 字段,是否转审批由学校「应用设置」绑定的流程决定),
  声明它会让用户以为选了「存草稿」却仍然直接发出;deptName 由后端按 deptId 反查字典。
- system prompt 加第 10 条说明。
- 测试:新增 NoticeIntentFormTest(7),IntentDirectFormTest 加教师/学生两条公告用例
  (仍用「一调就炸」的假 LLM 证明不过大模型);全量 71/71。
This commit is contained in:
lijing
2026-10-12 18:54:15 +08:00
parent c11d331371
commit d2c6c185b5
7 changed files with 352 additions and 47 deletions
+27 -10
View File
@@ -160,21 +160,34 @@ AgentService/
#### 5.1.0 裸意图直达(`service/IntentShortcut`)
「只有意图、没有细节」的一句话(典型:**「我要请假」**)由后端**直接**按身份弹表单,不经过大模型。
原因:同一句话交给模型可能得到三种都不合预期的结果 —— ① 先去调只读字典(用户看到一串类型列表、
没有表单);② 调了写工具却自行**编造**起止时间直接提交;③ 用文字反问。用户的预期是立刻看到表单。
「只有意图、没有细节」的一句话(典型:**「我要请假」**、**「我要发布校园公告」**)由后端**直接**按身份弹表单,
不经过大模型。原因:同一句话交给模型可能得到三种都不合预期的结果 —— ① 先去调只读字典(用户看到一串
类型列表、没有表单);② 调了写工具却自行**编造**参数直接提交;③ 用文字反问。用户的预期是立刻看到表单。
命中规则刻意做得**很窄**(宁可少直达、不可错直达):去掉空白与标点、剥掉「我要/帮我/麻烦…」前缀
与「吧/了/一下」后缀后,**正好等于**标准说法(`请假`/`请个假`/`请假申请`…)、且原句 ≤12 字。
因此带信息的句子(`我要请10月12日上午的病假,发烧就医`、`我请假那天作业怎么办`)**不会**被劫持,
照常交给模型(模型能一次填全参数,或由下面的缺参拦截补表单)。
与「吧/了/一下」后缀后,**正好等于**标准说法、且原句 ≤12 字。因此带信息的句子
(`我要请10月12日上午的病假,发烧就医`、`我请假那天作业怎么办`、`发一条关于社团招新的通知`)
**不会**被劫持,照常交给模型。
**加新意图只需三步**(不用动 `AgentOrchestrator`):① 一条 `Intent`(工具 + 引导语);
② 一个「标准说法」短语集;③ 往 `RULES` 里追加一条 `Rule`。
同一意图按身份落到不同工具:
| 身份 | 工具 | 表单字段 | 提交后 |
|---|---|---|---|
| 学生 | `leave.submit` | 请假类型 / 开始时间 / 结束时间 / 事由 | `type=2` 待班主任审核 |
| 教职工 | `leave.register` | **班级 → 学生**(联动下拉)/ 请假类型 / 起止时间 / 事由 | `type=1` 免审直接生效 |
| 意图 | 身份 | 工具 | 表单字段 | 提交后 |
|---|---|---|---|---|
| 请假 | 学生 | `leave.submit` | 请假类型 / 开始时间 / 结束时间 / 事由 | `type=2` 待班主任审核 |
| 请假 | 教职工 | `leave.register` | **班级 → 学生**(联动下拉)/ 请假类型 / 起止时间 / 事由 | `type=1` 免审直接生效 |
| 发布校园公告 | 教职工 | `notice.publish` | 公告标题 / 公告正文 / **发文部门**(下拉) | **立即发布、全站可见** |
| 发布校园公告 | 学生 | —(置 null) | — | 落回模型,如实说明需要教师/管理员身份 |
⚠️ **发布公告只认带「发/发布」动词的说法**(`发布公告`/`发个公告`/`我要发布校园公告`/`发通知`…):
光说「公告」「通知」更像**查询**,误判成"发布"会很突兀,故不进短语表。
⚠️ **不要再给 `notice.publish` 加「发布方式」参数**:后端 `/api/notice/Create` 就是**直接发布**
(是否转审批由学校「应用设置」里的流程绑定决定,请求侧绕不过),并不存在 `publishType` 字段 ——
声明它会让用户以为选了「存草稿」却仍然直接发出去。需要草稿/送审请走公告模块页面
(`/api/notice/SaveDraft` 与 `/api/notice/Submit`,后者还需要先有草稿 id,属另一条链路)。
#### 5.1.1 缺参 → 下发补全表单
`POST /api/agent/chat {"message":"我要请假"}`(学生身份)
@@ -224,6 +237,10 @@ AgentService/
> 结果是教师登记请假得手填 `classId` / `stuId` 编号,根本没法用。现在后端只下发**去哪儿取**
> (`optionCall`,依赖字段留 `{{占位符}}`),前端用用户自己的登录态拉回原始字典,
> 按后端给出的 `valueKey`/`labelKey` **确定性**映射成下拉项;取不到时才退化成可手填编号的输入框。
>
> **字典工具的 `fixed` 参数会被一并拼上** —— 多个模块的字典接口靠它选字典种类,
> 例如公告的发文部门取自 `/api/dict/List?type=1`(`{k,v}` 形状,故 `deptId` 声明
> `.optionsKeys("k","v")`)。早先漏拼固定参数,前端取回的是「没指定类型」的结果,下拉是空的。
**② 填完提交 → 直接执行**:`POST /api/agent/chat`