feat(ai): 业务上下文感知与 A2UI 链路统一
- 新增代码内业务上下文元数据层(实体字典 + 三大闭环工作流) - 新增 get_business_context / get_entity_schema / get_pending_tasks 运行时工具 - SYSTEM_PROMPT 与技能目录改为先查业务流程/待办再执行 - A2UI 增加 ai_a2ui_submissions 幂等表、表单过期、ui.artifact 事件 - 提交回灌携带 submissionId / fieldErrors / 下一步建议 - 前端 uiArtifacts 归一化与过期表单禁用
This commit is contained in:
90
apps/server/src/agent-context/get-business-context.tool.ts
Normal file
90
apps/server/src/agent-context/get-business-context.tool.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import type { AgentToolContext, ToolDef, ToolInputResult } from '../agent-tools/agent-tool.types';
|
||||
import { rejectUnknownKeys, optionalString } from '../agent-tools/tools/tool-input';
|
||||
import type { BusinessContextService } from './business-context.service';
|
||||
|
||||
interface GetBusinessContextInput {
|
||||
workflowKey?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 让 Agent 获取当前角色可见的业务流程、实体字典与依赖规则。
|
||||
*/
|
||||
@Injectable()
|
||||
export class GetBusinessContextTool implements ToolDef<GetBusinessContextInput> {
|
||||
readonly name = 'get_business_context';
|
||||
readonly skillKey = 'assistant';
|
||||
readonly requiredPermission = 'ai:chat:use';
|
||||
readonly description =
|
||||
'获取当前账号可见的业务流程(学生教学/住宿计费/教室租赁)、实体字典、阶段依赖与下一步建议。写入或导入前先调用本工具确认前置数据要求。';
|
||||
readonly inputSchema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
workflowKey: {
|
||||
type: 'string',
|
||||
description: '可选:聚焦某个闭环(student_teaching / dormitory_billing / classroom_rental)',
|
||||
maxLength: 50,
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
};
|
||||
|
||||
constructor(private readonly service: BusinessContextService) {}
|
||||
|
||||
validate(input: Record<string, unknown>): ToolInputResult<GetBusinessContextInput> {
|
||||
const invalid = rejectUnknownKeys(input, ['workflowKey']);
|
||||
if (invalid) return invalid;
|
||||
const workflowKey = optionalString(input.workflowKey, 'workflowKey', 50);
|
||||
if (!workflowKey.ok) return workflowKey;
|
||||
return { ok: true, value: { workflowKey: workflowKey.value } };
|
||||
}
|
||||
|
||||
async execute(input: GetBusinessContextInput, context: AgentToolContext): Promise<unknown> {
|
||||
return this.service.getBusinessContext(
|
||||
{ permissions: context.permissions, isSuperAdmin: context.isSuperAdmin },
|
||||
input.workflowKey,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 让 Agent 获取指定业务实体的字段字典与关系,用于生成准确表单。
|
||||
*/
|
||||
@Injectable()
|
||||
export class GetEntitySchemaTool implements ToolDef<{ entityKey: string }> {
|
||||
readonly name = 'get_entity_schema';
|
||||
readonly skillKey = 'assistant';
|
||||
readonly requiredPermission = 'ai:chat:use';
|
||||
readonly description =
|
||||
'获取指定业务实体(student/class/room/occupancy/expense/bill/deposit/classroom/organization/rental 等)的字段字典、枚举来源与关系,render_form 前可按需调用以生成正确字段。';
|
||||
readonly inputSchema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
entityKey: { type: 'string', minLength: 1, maxLength: 50 },
|
||||
},
|
||||
required: ['entityKey'],
|
||||
additionalProperties: false,
|
||||
};
|
||||
|
||||
constructor(private readonly service: BusinessContextService) {}
|
||||
|
||||
validate(input: Record<string, unknown>): ToolInputResult<{ entityKey: string }> {
|
||||
const invalid = rejectUnknownKeys(input, ['entityKey']);
|
||||
if (invalid) return invalid;
|
||||
if (typeof input.entityKey !== 'string' || !input.entityKey.trim()) {
|
||||
return { ok: false, error: 'entityKey 必须是字符串' };
|
||||
}
|
||||
const entityKey = input.entityKey.trim();
|
||||
if (entityKey.length > 50) {
|
||||
return { ok: false, error: 'entityKey 长度不能超过 50' };
|
||||
}
|
||||
return { ok: true, value: { entityKey } };
|
||||
}
|
||||
|
||||
async execute(input: { entityKey: string }, context: AgentToolContext): Promise<unknown> {
|
||||
return this.service.getEntitySchema(
|
||||
{ permissions: context.permissions, isSuperAdmin: context.isSuperAdmin },
|
||||
input.entityKey,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user