Files
yunhai-agent-service/src/main/java/com/yunhai/agent/config/AgentSettings.java
T
lijing 8303b857e9
build / build-push (push) Successful in 40s
feat: 提交校园智能助手后端工程源码(Spring Boot 2.7.18 + 平台 starter + JPA + Nacos)
- com.yunhai.agent,端口 8093,context-path /agent,制品 target/agent.jar;与 NoticeService 同构
- CAS 单点登录 + 三层角色权限(学生/教师/管理员/访客)+ 会话与工具调用审计落库
- DeepSeek 大模型编排 + 7 个业务模块工具(请假/公告/宿舍/活动/作业/资源库/教师发展)
- 浏览器代执行协议(服务端挂起 → 前端用登录态代调 → toolResult 续跑)+ 调用失败深链兜底
- 登出修复:服务端真正 session.invalidate() + 前端两步登出 + logout-guard
- 保留 projman 注入的 .gitea/workflows/build.yml 与 docker/Dockerfile;docker/application-prod.yml、run-app.sh 随工程入库
2026-10-12 11:18:29 +08:00

172 lines
7.8 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package com.yunhai.agent.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* 智能体配置:大模型 + 各业务模块基地址 + 角色权限映射。
*
* <p>全部落在 {@code application.yml} 的 {@code agent.*} 下,便于运维按环境改配置而不动代码。
*/
@Data
@Component
@ConfigurationProperties(prefix = "agent")
public class AgentSettings {
/** 大模型配置 */
private Llm llm = new Llm();
/** 编排参数 */
private Loop loop = new Loop();
/** 角色权限映射(学生 / 教师 / 管理员 的角色号) */
private Roles roles = new Roles();
/**
* 业务模块基地址表:key = 模块标识(工具定义里引用),value = 该模块的根地址(不含 /api)。
* <p>例:{@code homeleave: http://base.yunhaixingtong.com/home-leave}
* <p>本地联调把 value 改成 {@code http://localhost:8106} 之类即可。
*/
private Map<String, String> modules = new LinkedHashMap<>();
/**
* 各业务模块的「默认落地页」——即该模块前端的一个路由(如 {@code /my})。
*
* <p><b>★ 深链兜底(2026-10 新增)</b>:业务模块的用户态接口 {@code /<模块>/api/*} 只认
* <b>浏览器 CAS 会话</b>,服务端调用拿不到,必然回 {@code code=302}(详见 README 第 11 节)。
* 此时智能体不再只丢一句报错,而是把<b>模块页面地址</b>随轨迹回传前端,
* 让用户用<b>自己的浏览器登录态</b>一键打开真实页面看数据 —— 这比"死等打通"务实得多。
*
* <p>取值已按各模块前端 {@code src/router/routes.ts} 的真实路由核对(2026-10-10):
* <pre>
* homeleave → /my 我的请假
* campusnotice → / 我的公告(收件箱)
* dormnotice → /par/notices 宿舍通知箱
* familyactivity → /par/activities 活动广场
* coursehomework → /stu/today 今日作业
* resourcelibrary → /resource 资源管理
* profession → /home 发展首页
* </pre>
* 运维可在 {@code application.yml} 用 {@code agent.module-landing.<模块>=<路由>} 覆盖单个模块;
* 工具自身声明的 {@code landingPath}({@code write(...)} / {@code readAt(...)})优先级更高。
*/
private Map<String, String> moduleLanding = defaultModuleLanding();
/** 模块默认落地页的出厂值(见 {@link #moduleLanding}) */
private static Map<String, String> defaultModuleLanding() {
Map<String, String> m = new LinkedHashMap<>();
m.put("homeleave", "/my");
m.put("campusnotice", "/");
m.put("dormnotice", "/par/notices");
m.put("familyactivity", "/par/activities");
m.put("coursehomework", "/stu/today");
m.put("resourcelibrary", "/resource");
m.put("profession", "/home");
return m;
}
/**
* 是否按「用户已授权模块」过滤工具(modId 维度)。
* <p>默认 false:不向模型隐藏工具,越权与否由各业务模块按透传身份自行裁决。
* <p>置 true 时:用平台 GetUserMod 的 modId 与 {@code ToolDef.moduleId} 比对,未授权模块的工具不暴露。
*/
private boolean filterToolsByAuthorizedModule = false;
/**
* 是否开启「角色强校验」。
* <p>默认 true:模型若调用了当前角色无权使用的工具,服务端直接拒绝执行(不发真实请求),
* 并在轨迹里留下 FORBIDDEN 记录 —— 这是「分学生、教师权限」的硬约束。
* <p>置 false 可临时放开(排障用),此时仅按角色过滤列表,不做执行拦截。
*/
private boolean enforceRolePermission = true;
/**
* 浏览器代执行(方案 A,2026-10-12 新增)。
*
* <p>业务模块的用户态接口只认<b>浏览器 CAS 会话</b>,服务端调用必然 {@code code=302}
* (README 第 11 节的四条实测证据)。开启后,模块类工具不再由服务端发请求,而是:
* <ol>
* <li>模型决定调用 → 服务端挂起,把「接口描述(方法/路径/参数)」下发前端;</li>
* <li>前端用<b>用户自己的登录态</b>调用模块接口(同站部署 ⇒ 同源,Cookie 自动携带);</li>
* <li>结果回传服务端 → 喂回模型 → 生成最终回答。</li>
* </ol>
* 本地 dev(localhost:8081)因跨域取不到 base 域会话,代执行会失败并自动落回
* 「去系统里查看」的深链兜底;同站部署后即真实生效。
*/
private BrowserExec browserExec = new BrowserExec();
@Data
public static class BrowserExec {
/** 是否启用浏览器代执行(false = 维持服务端直调的旧行为) */
private boolean enabled = true;
}
@Data
public static class Llm {
/** OpenAI 兼容的 chat/completions 地址(DeepSeek 官方:https://api.deepseek.com/chat/completions) */
private String host = "https://api.deepseek.com/chat/completions";
/** 多模态/推理兼用的兜底地址(可选,默认同上) */
private String fallbackHost;
private String apiKey;
/** 默认模型:deepseek-flash(DeepSeek-V4.1-Flash,支持 function calling)。
* 以官方实时清单为准:GET https://api.deepseek.com/models(2026-10-10:deepseek-flash / deepseek-v4-pro) */
private String model = "deepseek-flash";
private int maxTokens = 2048;
private double temperature = 0.3;
/** 单次 HTTP 超时(毫秒)。⚠️ 实际出站超时已由平台统一客户端接管,见 {@code yh.http.read-timeout-ms};本字段保留仅作文档参考 */
private int timeout = 60000;
/** 是否开启深度思考(旧的 deepseek-reasoner 独立模型已下线;V4 系列改为在请求里带 effort 档位,本开关暂未接线) */
private boolean thinking = false;
}
@Data
public static class Loop {
/** 单轮对话内最多允许的「模型思考 → 调工具」循环次数,防死循环 */
private int maxRounds = 6;
/** 每个工具调用结果塞回模型时的最大字符数(防撑爆上下文) */
private int toolResultMaxChars = 6000;
/** 会话上下文最多保留的历史消息条数 */
private int historyMaxMessages = 20;
/** 会话空闲过期时间(分钟) */
private int sessionTtlMinutes = 120;
}
/**
* 角色映射:UserBase 的角色号(roleId)→ 智能体角色。
* <p><b>★ 上线前必须按本校 UserBase 的角色体系校准</b>:
* 平台 {@code BoaUser.getType() == 1} 恒为超管/校管;其余按 {@code roleIds} 命中下面的集合判定。
* 一个用户可同时命中多个(如既是教师又是管理员),能力取并集。
*/
@Data
public static class Roles {
/** 学生角色号(默认 1;请按 UserBase 校准) */
private List<Integer> student = new ArrayList<>(Arrays.asList(1));
/** 教师角色号(默认 2=教师、3=班主任;请按 UserBase 校准) */
private List<Integer> teacher = new ArrayList<>(Arrays.asList(2, 3));
/** 管理员角色号(默认 4=校管、5=分管领导、6=教务、7=校长;请按 UserBase 校准) */
private List<Integer> admin = new ArrayList<>(Arrays.asList(4, 5, 6, 7));
/** 管理员是否天然拥有教师能力(默认 true:校管/领导可用教师侧工具) */
private boolean adminAlsoTeacher = true;
}
}