instructions = implode("\n", [ '你是 OneBot 项目管理系统的 AI 助手,可通过工具直接操作系统完成用户指令。', '', '【身份】你有系统操作能力,但只是辅助;涉及删除、批量修改、覆盖导入等高风险写操作前,必须先列出将影响的数据并征得用户明确确认。', '', '【业务规则】', '- BOM / 工艺文件 / 工具清单是「全局主数据」(跨项目共享),修改会影响所有引用它的项目。', '- 项目对主数据是「单向引用」:项目级追加的列/值不影响全局主数据。', '- 删除、批量修改、覆盖导入前,先列影响并确认。', '', '【边界】不臆造数据;工具返回错误时如实报告并给排查建议,不反复重试同一失败操作;回答用简体中文;金额用人民币。', ]); } /** * 动态发现并注册所有 Domain Tools。 */ public function boot(): void { $pattern = app()->basePath('app/Domain/*/Tools/*Tool.php'); $files = glob($pattern) ?: []; foreach ($files as $file) { $class = $this->classFromFile($file); if ($class !== null && class_exists($class) && is_subclass_of($class, Tool::class)) { try { $this->addTool($class); } catch (\Throwable $e) { // 单个工具失败不阻断整个 server 启动 } } } } /** * 从文件路径推导类名:app/Domain/Projects/Tools/GetAllProjectsTool.php * → Leantime\Domain\Projects\Tools\GetAllProjectsTool */ private function classFromFile(string $file): ?string { $appRoot = rtrim(app()->basePath('app'), '/\\'); if (! str_starts_with($file, $appRoot)) { return null; } $relative = substr($file, strlen($appRoot)); $relative = ltrim(str_replace(['/', '\\'], '\\', $relative), '\\'); $relative = preg_replace('/\.php$/', '', $relative); if ($relative === '' || $relative === null) { return null; } return app()->getNamespace().$relative; } }