# 开发业务模块
业务侧接入 Harness 只有两条面,工程里已经放了一套可直接照抄的示例,全部在 alinesno-infra-harness-business 模块下:
| 接入面 | 示例类 | 位置 |
|---|---|---|
| Agent FC 工具(让大模型能调用你的业务方法) | SampleEmployeeToolProvider | business/tools/sample/ |
| @ 快速定位(用户在对话框 @ 出你的业务对象) | SampleEmployeeLocatorContributor | business/locator/sample/ |
| 两条面共用的数据源 | SampleEmployeeData | business/tools/sample/ |
做法就是把这三个示例复制一份、改名、把假数据换成你自己的 Service;需要管理页时再在前端 ui 工程加一个页面。
# 本篇目录
# 一、接入 Agent FC 工具
作用:把业务方法注册成大模型可调用的工具(Function Calling)。大模型读工具描述,自己决定何时调用、传什么参数。
步骤
- 在
business/tools/下建一个类,类上加@CopilotToolProvider,声明它属于哪个工具分组:
@CopilotToolProvider(category = "biz_employee", categoryLabel = "雇员信息域",
categoryDesc = "公司雇员查询。适用:用户 @ 了某个雇员或询问雇员信息时")
public class SampleEmployeeToolProvider {
// 工具方法写在这里
}
category是分组名,自定义字面量即可,新增分组不用改框架任何文件。- 分组工具默认注册进目录、由
load_tools元工具按需激活;category留空则视为 core 常驻工具。
- 每个对外暴露的工具 = 一个加
@CopilotTool的方法,参数用@ToolParam描述:
@CopilotTool(displayName = "搜索雇员",
description = "按姓名/部门/职位关键字搜索公司雇员。当用户 @ 了某个雇员、询问某人信息时调用")
public ToolCallResult employee_search(
@ToolParam(value = "搜索关键字(人名/部门/职位)", example = "张伟") String keyword,
@ToolParam(value = "最大返回数量(默认10)", required = false) Integer limit) {
// ... 查询业务数据 ...
return ToolCallResult.success("找到 N 名雇员:...");
}
写法要点(直接决定大模型会不会用你的工具):
description是大模型判断「何时调用」的唯一依据,必须写清 用途 + 调用时机。- 参数说明、
example、enumValues(枚举约束)都会自动生成 JSON Schema 暴露给大模型。 - 方法返回
ToolCallResult.success(...)/ToolCallResult.failure(...)。 - 业务 Service 用常规
@Autowired注入;需要任务级上下文(如orgId)时,通过CopilotToolContext作为方法参数注入。
# 二、接入 @ 快速定位
作用:用户在对话框输入 @ 时,能像示例「部门 › 雇员」这样逐级钻取、选中你的业务对象;选中后对象作为结构化引用注入到大模型上下文,与上面的 FC 工具形成「@ 定位 → 引用注入 → 工具消费」闭环。
步骤
实现接口 ICopilotLocatorContributor,类上加 @Component。示例实现了四个方法:
@Component
public class SampleEmployeeLocatorContributor implements ICopilotLocatorContributor {
@Override
public CopilotLocatorModuleDto module() {
// 声明模块卡片:id、名称、图标、副标题、管理页路由(link)
}
@Override
public List<CopilotLocatorNodeDto> children(CopilotLocatorChildrenQueryDto query) {
// 分级钻取:祖先键链为空出第一级(部门),键链长度 1 出第二级(雇员),
// 层级深度与终止条件由业务自己判断,框架不感知层级
}
@Override
public List<CopilotLocatorSearchResultDto> search(String keyword, Long orgId, Long operatorId) {
// 参与跨模块全局搜索
}
@Override
public String renderReference(CopilotLocatorReferenceDto ref) {
// 可选:自定义引用注入提示词时的渲染文本;不实现则框架用通用格式
}
}
写法要点:
- 节点的
payload要带上 FC 工具需要的字段(如employeeId、deptId),大模型读到引用后会直接把这些字段当工具参数传入。 - 引用
type命名约定:模块.对象(如biz.employee),配合referenceTypes()声明本模块支持的类型。 - 全部数据由后端产出,前端浮层零改动。
module()里的dto.setLink("/harness/xxx/index")就是「去看看」跳转的管理页路由,与第四节的页面路由对应;留空则不展示该引导。
# 三、替换成真实业务
示例的假数据都集中在 SampleEmployeeData。接入时:
- 删除
SampleEmployeeData,改为注入你自己的EmployeeService(或对应业务 Service)。 - FC 工具类和定位贡献者各自调用同一个 Service。
- 务必按
orgId做数据权限过滤,只返回当前用户/组织可见的数据。
# 四、页面结合开发(管理页)
Agent 侧的能力(@ 定位、FC 工具)跑通后,通常还需要一个前端管理页做常规增删改查,并作为 @ 定位「去看看」的落地页。前端在 alinesno-infra-harness-ui(Vue 3 + Element Plus + Vite)里开发,一个业务模块固定三处落点:
| 落点 | 路径 | 作用 |
|---|---|---|
| 接口封装 | src/api/harness/<模块>.js | 用 request 调后端 REST |
| 页面组件 | src/views/harness/<模块>/index.vue(列表)、Editor.vue(表单) | 列表 + 新增/编辑 |
| 路由注册 | src/router/configRoute.js | 挂到菜单、声明标题/图标 |
步骤
- 封装接口
src/api/harness/employee.js,参照同目录llmModel.js的写法:统一prefix指向后端接口,按 CRUD 导出方法:
import request from '@/utils/request'
var prefix = '/api/infra/harness/employee/'
export function listEmployees(query) { // 分页列表
return request({ url: prefix + 'datatables', method: 'post', params: query })
}
export function getEmployee(id) { // 详情
return request({ url: prefix + 'detail/' + id, method: 'get' })
}
export function createEmployee(data) { // 新增
return request({ url: prefix + 'create', method: 'post', data })
}
export function updateEmployee(data) { // 修改
return request({ url: prefix + 'update', method: 'put', data })
}
分页列表统一走
datatables(POST + params),与框架通用列表约定一致;出入参、成功/失败响应遵循框架统一封装。
写页面
src/views/harness/employee/:index.vue:查询条件 + 表格 + 分页,调listEmployees;操作列放「编辑 / 删除」,按钮级权限用指令控制显隐。Editor.vue:新增/编辑表单,字段校验用框架统一的表单校验机制,提交调createEmployee/updateEmployee。- 可直接参照
src/views/harness/llmModel/这套现成的「列表 + Editor」结构改。
注册路由 在
src/router/configRoute.js加一条(path要和第二节module.link对齐):
{
path: '/harness/employee/index',
name: 'harness.employee',
component: () => import('@/views/harness/employee/index.vue'),
meta: { title: '雇员管理', icon: 'fa-solid fa-users', affix: false, chat: true }
}
meta.title 是菜单/面包屑显示名,meta.icon 用 Font Awesome 类名;菜单与访问权限由后端 sa-token 下发,前端不硬编码。
# 与 @ 定位的衔接
把第二节 module() 里的 dto.setLink('/harness/employee/index') 指向上一步的路由,用户在对话框 @ 雇员时,模块卡片上的「去看看」就能跳到这个管理页,形成「管理页维护数据 ↔ @ 定位/FC 工具消费数据」一体。
# 五、本地验证
- 按「本地工程导入」启动后端与前端。
- 打开前端 Copilot 对话框:
- 输入
@,应能看到你的模块卡片,能逐级钻取、选中对象;卡片「去看看」能跳到管理页。 - 选中后提问(如「张伟的部门是什么」),观察大模型是否调用了你注册的
employee_search/employee_get_detail工具。
- 输入
- 打开管理页,验证列表分页、搜索、新增、编辑、删除均可用。
- 看后端日志里工具被扫描注册、被调用的记录,确认闭环打通。
# 目录约定
后端 alinesno-infra-harness-business/src/main/java/.../business/
├── tools/ # FC 工具提供者(@CopilotToolProvider + @CopilotTool)
│ └── sample/ # 官方示例,照抄改名
└── locator/ # @ 定位贡献者(ICopilotLocatorContributor)
└── sample/ # 官方示例,照抄改名
前端 alinesno-infra-harness-ui/src/
├── api/harness/<模块>.js # 接口封装
├── views/harness/<模块>/ # index.vue 列表 + Editor.vue 表单
└── router/configRoute.js # 路由与菜单注册