# 开发业务模块

业务侧接入 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)。大模型读工具描述,自己决定何时调用、传什么参数。

步骤

  1. 在 business/tools/ 下建一个类,类上加 @CopilotToolProvider,声明它属于哪个工具分组:
@CopilotToolProvider(category = "biz_employee", categoryLabel = "雇员信息域",
        categoryDesc = "公司雇员查询。适用:用户 @ 了某个雇员或询问雇员信息时")
public class SampleEmployeeToolProvider {
    // 工具方法写在这里
}
  • category 是分组名,自定义字面量即可,新增分组不用改框架任何文件。
  • 分组工具默认注册进目录、由 load_tools 元工具按需激活;category 留空则视为 core 常驻工具。
  1. 每个对外暴露的工具 = 一个加 @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。接入时:

  1. 删除 SampleEmployeeData,改为注入你自己的 EmployeeService(或对应业务 Service)。
  2. FC 工具类和定位贡献者各自调用同一个 Service。
  3. 务必按 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 挂到菜单、声明标题/图标

步骤

  1. 封装接口 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),与框架通用列表约定一致;出入参、成功/失败响应遵循框架统一封装。

  1. 写页面 src/views/harness/employee/:

    • index.vue:查询条件 + 表格 + 分页,调 listEmployees;操作列放「编辑 / 删除」,按钮级权限用指令控制显隐。
    • Editor.vue:新增/编辑表单,字段校验用框架统一的表单校验机制,提交调 createEmployee / updateEmployee。
    • 可直接参照 src/views/harness/llmModel/ 这套现成的「列表 + Editor」结构改。
  2. 注册路由 在 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 工具消费数据」一体。

# 五、本地验证

  1. 按「本地工程导入」启动后端与前端。
  2. 打开前端 Copilot 对话框:
    • 输入 @,应能看到你的模块卡片,能逐级钻取、选中对象;卡片「去看看」能跳到管理页。
    • 选中后提问(如「张伟的部门是什么」),观察大模型是否调用了你注册的 employee_search / employee_get_detail 工具。
  3. 打开管理页,验证列表分页、搜索、新增、编辑、删除均可用。
  4. 看后端日志里工具被扫描注册、被调用的记录,确认闭环打通。

# 目录约定

后端 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          # 路由与菜单注册
Last Updated: 9/25/2026, 7:10:09 AM