AIManage 中“技能”有两套并行机制:租户持久化的技能元数据,以及 Host 文件系统中的 Markdown 提示词文件。它们目前都没有接入 SendMessage/StreamMessage,也不存在反射调用或 Semantic Kernel tool registration,因此不能描述为“模型已经可以调用业务技能”。
1. 两类技能不要混为一谈
| 维度 | 持久技能 AISkill | 文件技能 FileSkillDefinition |
|---|---|---|
| 作用域 | TenantId | 整个进程/部署 |
| 存储 | 数据库 | AI_SKILLS_PATH 或 AI/Skills/*.md |
| 字段 | HandlerType、Method、Schema、Enabled | name/category/description/body |
| API | register/list/delete | list/reload |
| 权限 | Mediator ai/skill | 仅组级认证 |
| 执行 | 当前没有 | 当前没有 |
| 热更新 | 数据库下一次查询 | FileSystemWatcher 500ms 防抖 |
2. 持久技能注册
POST /api/ai/skillsContent-Type: application/json
{ "name": "CreateTicket", "description": "创建服务工单", "category": "Support", "handlerType": "Acme.Support.CreateTicketHandler, Acme.Support", "handlerMethod": "ExecuteAsync", "parameterSchemaJson": "{\"type\":\"object\",\"required\":[\"title\"]}"}Handler 只校验 Name 和 HandlerType 非空,然后保存字符串。没有验证程序集可加载、类型存在、方法签名、JSON Schema 合法、调用者对目标业务资源的权限、参数安全或返回类型。List 只返回摘要,Registry 查询启用项;Delete 实际是注销/禁用语义,以 Registry 实现为准。
3. 文件技能格式与扫描
---name: ContractReviewcategory: LegalOpsdescription: 识别合同中的履约风险---
你是合同复核助手。只输出风险、依据和需要人工确认的问题。扫描器用一个简单正则拆 front matter,再按行正则读取三个字段;它不是完整 YAML parser。正文为空时回退为 description。相同 Name 后加载的文件覆盖前项,列表顺序没有稳定保证。
启动 Start() 时扫描并创建 FileSystemWatcher。若目录启动时不存在,watcher 不会启动;手工 reload 只扫描,也不会立即 StartWatching。只有 timer 回调在 watcher 为 null 时尝试重建,因此“之后才创建目录”不一定自动被发现。文件系统事件可能丢失,Error 时 1 秒后重建。
文件列表/重载端点绕过 IAuthorizedRequest,任意认证用户都可查看文件名、描述和时间,并触发全进程重扫。GA 前应限制为运维/技能管理员,避免暴露部署文件结构与形成 DoS 面。
4. Semantic Kernel 不是自动 Agent
当前 SemanticKernelAdapter 只做聊天消息和执行参数转换。虽然公开 Kernel 属性供高级场景获取,但主路径没有添加 Plugin、KernelFunction、ToolCallBehavior,也没有逐工具授权、确认或审计。
一个安全工具执行闭环至少需要:
- 服务器 allowlist 解析工具,不信任模型给出的类型名;
- 每次调用重新执行租户、用户和目标资源授权;
- JSON Schema 验证、大小/枚举/格式限制;
- 只把最少参数与最少结果暴露给模型;
- 对写操作要求显式确认和稳定幂等键;
- 记录 request、tool、policy、actor、target、result 和耗时,不记录 Secret;
- 限制递归轮数、并发、Token、超时和总费用;
- 禁止自动重放有副作用调用。
5. RAG 端口
IRagSearchPort 提供 HybridSearch、GetSystemPrompt、IndexDocument 和 EnsureCollection。Application 默认注册 UnavailableRagSearchPort,生产只有在 AIManage:Provider 非空且非 None、并且连接器服务已注册时,Infrastructure 才 Replace 为 RagServiceAdapter。
RagServiceAdapter 将连接器 DTO 缩窄成平台结果,这是正确的隔离方向。但 GetSystemPromptAsync、IndexDocumentAsync 和集合创建的部分底层调用没有传播 cancellation token;EnsureCollection 通过异常消息包含 already exists/已存在 判断幂等,容易受 SDK 文案变化影响。
6. Agent 端口
IAgentServiceAdapter 同样默认 Unavailable。生产适配器包装外部 IAgentService:ChatInSessionAsync 管理持久会话,StreamMessageAsync 只透传流并明确不自动保存历史。注释提到 Guidance 可切换到它,但当前只提供桥,不代表 Guidance 或 AIManage 主聊天已使用。
SessionId 的租户隔离由调用方负责。建议强制结构化 SessionKey,而不是文档建议的字符串拼接;Store 谓词仍应绑定 TenantId,不能把 tenant|user|route 约定当安全边界。
7. RAG 数据安全
RAG 不是“把文档塞进向量库”这么简单。落地前必须定义:集合按租户还是按数据域隔离、metadata filter 是否由服务端强制、源文档权限如何在检索时重新验证、删除/权限变更如何传播到索引、Embedding Provider 是否允许接收该分类数据,以及检索片段如何标记为不可信内容防止 Prompt Injection。
// 调用方只传业务查询;TenantId 与访问范围由服务端上下文生成。var scope = await ragScope.ResolveAsync(currentUser, resourceIds, cancellationToken);var results = await rag.HybridSearchAsync( collectionName: scope.Collection, queryText: query, topK: Math.Min(topK, 20), metadataFilter: scope.RequiredFilter, cancellationToken);
// 返回模型前仍要逐条执行资源授权和内容分级。8. 能力成熟度
| 能力 | 当前状态 | 可宣称口径 |
|---|---|---|
| 租户技能目录 | 已持久化 | 可管理元数据 |
| 文件提示词目录 | 进程内扫描 | 可查看/热重载提示词文件 |
| 工具执行 | 未实现 | 不可宣称 |
| Semantic Kernel chat | 已接线 | 文本聊天适配 |
| RAG bridge | 条件注册 | 可供显式消费者调用 |
| Chat 自动 RAG | 未实现 | 不可宣称 |
| Agent bridge | 条件注册 | 可供显式消费者调用 |
| 多 Agent 编排 | 未实现 | 不可宣称 |
9. 测试缺口
现有测试验证 RAG/Agent 参数转发和 fail-closed 组合。还需补:文件技能权限、目录创建/丢事件/重复名、恶意 front matter、路径与大文件、技能 schema、工具逐次授权、跨租户 RAG、索引删除同步、Prompt Injection、取消传播、连接器错误类型和端到端 Chat/RAG/Tool 路径。
AIManage 总览 · 流式与用量 · 测试与 GA