Appearance
工具选择、技能与安全执行
工具和 Skill 是 Agent 把模型输出转换为外部动作的边界。模型只能提出工具调用;真正能否执行,要经过 Agent 配置、能力策略、参数 Schema、租户上下文、超时、幂等 journal 和必要的 HITL。
1. 工具来源与统一形态
| 来源 | 注册/发现 | 运行时执行器 | 典型场景 |
|---|---|---|---|
| 本地 Spring/内置工具 | @Tool、BuiltinShellDispatcher | 本地方法、DocSpace/VFS/Sandbox | TODO、文件、命令 |
| 数据库业务工具 | ai_tool + serviceType | ToolExecutorFactory | Feign、HTTP、Java 服务 |
| Feign 工具 | @FeignToolRegistration 启动扫描 | FeignToolExecutor | 内部微服务调用 |
| 远程 MCP | Nacos/Registry + tools/list | McpRemoteToolExecutor、McpNacosToolExecutor | 外部能力服务 |
| 平台 Web Search | Agent 配置之外的能力策略 | WebSearchBuiltinDispatcher | 联网搜索 |
统一工具描述至少包含 name、description、参数 JSON Schema、serviceType、endpoint/方法、timeout、权限标签和 side-effect 级别。同步到 ai_tool 不代表所有 Agent 都能调用,还必须在 Agent 的 toolIds 或绑定关系中授权。
2. 工具进入模型前的过滤
LoadAgentConfigNode 解析 Agent 工具并把平台 web_search 从普通工具列表移除。LoopInit 再根据当前 Agent、Web Search 模式、Sandbox/VFS 能力和用户上下文构造模型可见的 tool specifications。
过滤顺序应理解为:
text
数据库存在
-> Agent 已绑定
-> 工具启用且版本有效
-> 当前租户/用户有权限
-> 当前 session 能提供依赖(MCP、Sandbox、文件房间)
-> 当前 WebSearchMode/side-effect 策略允许
-> 生成 JSON Schema 给 LLM缺少任何一层都不能把工具描述交给模型,否则模型会不断调用一个运行时必然失败的工具。
3. 工具调用节点执行链
ToolCallNode.executeDirect 有三条优先级:
- 有 session 时先交给
BuiltinShellDispatcher,处理 DocSpace、Sandbox、VFS 工具。 - 再由
WebSearchBuiltinDispatcher处理平台搜索,并校验WebSearchMode与平台能力。 - 最后查询
AiToolEntity,由ToolExecutorFactory按serviceType选择业务执行器。
业务工具执行有外层超时线程池,默认超时 60 秒;工具实体的正数 timeoutMs 会覆盖默认值。底层调用失败后最多三次尝试,最终转换为“执行失败: …”结果,并写 structured block、工具日志和 Agent 结果。轻量 executeDirect 与 Agentic Loop 入口不同:前者没有完整 state 时不能执行平台 Web Search,也不能依赖 HITL。
4. 智能体循环的日志与幂等
LoopActingNode 对每个 ToolExecutionRequest 以 toolCallId 为幂等键:
text
读取 ToolExecutionJournal
-> COMPLETED:复用结果
-> STARTED + 有副作用:结果未知,进入恢复审批
-> 未执行:写 STARTED
-> 调用工具
-> 成功写 COMPLETED / 失败写 FAILED
-> 写 ToolResultStore 和 ToolExecutionResultMessage内存 ToolResultStore 用于同一 runtime 的快速复用;journal 用于进程退出或 checkpoint 恢复。副作用调用不能只靠模型重复产生相同参数来判断安全,因为远端系统可能已经写入。
5. 高风险与人在回路(HITL)
当前检测规则覆盖递归删除、DROP TABLE、TRUNCATE、git push --force、git reset --hard 等模式,也会拦截上次进程退出时状态为 STARTED 的副作用工具。命中后 state 写入:
text
hitlPending=true
hitlRequest={requestType, promptMessage, toolName, toolCallId,
toolParams, triggerReason, iteration}图在 human_review 前中断。当前 /resume 请求只有 clientMessageId 和补充 message;普通工具恢复时 LoopHumanReviewNode 生成 approved=true 并带回原 toolCallId,checkpoint 暂停恢复不会批准工具。新增高风险工具时要明确 side-effect level、审批文本、拒绝策略和恢复后的下一节点。
6. 内置工具分发
| 内置名称 | 委托节点/服务 | 关键状态 |
|---|---|---|
UPDATE_TODO_LIST | TaskTodoManager | taskTodoList、todoUpdated |
RAG_SEARCH | RagNode | taskExecutionResult |
SKILL_CALL | SkillCallNode | activeSkill*、执行结果 |
SUB_AGENT_CALL / SUBMIT_SUBAGENT_TASK | SubAgentCallNode | taskId、父子会话回调 |
CHECK_TASK_STATUS | CheckTaskStatusNode | 子任务状态 |
READ_TOOL_RESULT | ToolResultStore | 按 toolCallId 读取大结果片段 |
PUBLISH_FILE | ChatArtifactService | Artifact 关系和产物事件 |
| Shell/DocSpace/Sandbox/VFS | ToolCallNode/内置 dispatcher | workspace、命令和文件事件 |
7. 技能调用协议
SkillCallNode 先从 currentTask.targetResource 读取名称;名称为空时按任务描述在 Agent 的 skills 列表中匹配。命中后:
- 校验 Skill 已绑定到 Agent。
- 通过
ISkillService.getSkillDetail获取当前详情。 - 官方 Skill 使用发布版本的
skillContent,并展示runtimePath、workspacePath和发布包文件索引。 - 如果请求带
path/filePath、offset、limit,按需读取 release 文件,限制索引条目和输出长度。 - 更新
LoopRuntime.activeSkillName/root/entryPath,下一轮注入 Skill reminder。 - 明确提示模型:
skill_call只激活/读取说明,不会自动执行脚本;脚本必须通过 Sandbox 工具按SKILL.md协议执行。
没有 Sandbox 或发布目录时,只能使用数据库中的 SKILL.md 内容,不能假定 scripts、references、assets 存在。
8. MCP 和 Feign 注册约束
Feign 自动注册从 FeignRegistryInitializer.registry 读取标注方法,生成 snake case 默认工具名、参数 Schema、接口方法、超时、权限和标签,saveOrUpdate 写入 ai_tool。修改注解后必须检查已有数据库记录。
Nacos MCP 同步先发现健康实例,再调用 tools/list,以 service@group 加锁防重复同步;实例变化由 listener 防抖,定时健康检查负责补偿。服务发现和工具授权是两件事:服务在线不表示某 Agent 可以调用。
9. 新工具上线检查表
- Schema 是否能被目标模型正确解析,必填字段和枚举是否明确。
- 是否声明读操作/副作用,是否需要 HITL。
- 超时、重试和取消时远端动作是否可控。
- 重试是否幂等,是否写 journal、审计和工具日志。
- 参数是否受租户、用户、session、workspace 限定。
- 结果是否有摘要、原文、分页读取和敏感字段脱敏策略。
- 是否能在 checkpoint 恢复后识别已完成、进行中和未知结果。