@nocobase/plugin-ai 3.0.0-alpha.3 → 3.0.0-alpha.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/ai/docs/nocobase/ai-employees/dev/ai-employee/complete-example.md +206 -0
  2. package/dist/ai/docs/nocobase/ai-employees/dev/ai-employee/define-ai-employee.md +149 -0
  3. package/dist/ai/docs/nocobase/ai-employees/dev/ai-employee/define-skill.md +138 -0
  4. package/dist/ai/docs/nocobase/ai-employees/dev/ai-employee/define-tool.md +189 -0
  5. package/dist/ai/docs/nocobase/ai-employees/dev/ai-employee/frontend-tool-ui.md +296 -0
  6. package/dist/ai/docs/nocobase/ai-employees/dev/ai-employee/index.md +126 -0
  7. package/dist/ai/docs/nocobase/ai-employees/dev/ai-employee/internationalization.md +88 -0
  8. package/dist/ai/docs/nocobase/ai-employees/dev/ai-employee/troubleshooting.md +86 -0
  9. package/dist/ai/docs/nocobase/ai-employees/{knowledge-base/dev → dev/knowledge-base}/external-knowledge-base.md +2 -2
  10. package/dist/ai/docs/nocobase/ai-employees/knowledge-base/knowledge-base/index.md +1 -1
  11. package/dist/ai/docs/nocobase/development/index.md +1 -1
  12. package/dist/client/{244.8631eb5cf3f05e21.js → 244.145eb763339a109b.js} +1 -1
  13. package/dist/client/{264.cc98933ef65608b6.js → 264.6ca5f2eeb4173d86.js} +1 -1
  14. package/dist/client/index.js +2 -2
  15. package/dist/client-v2/{244.c428e645ecb94414.js → 244.64cb9d3ee27dcdc6.js} +1 -1
  16. package/dist/client-v2/{264.be92ff6a4d2612f1.js → 264.3c7d8e90aa7f46ee.js} +1 -1
  17. package/dist/client-v2/index.js +2 -2
  18. package/dist/client-v2/pages/LLMServicesPage.d.ts +9 -4
  19. package/dist/externalVersion.js +15 -15
  20. package/dist/locale/de-DE.json +2 -1
  21. package/dist/locale/en-US.json +3 -1
  22. package/dist/locale/es-ES.json +2 -1
  23. package/dist/locale/fr-FR.json +2 -1
  24. package/dist/locale/hu-HU.json +2 -1
  25. package/dist/locale/id-ID.json +2 -1
  26. package/dist/locale/it-IT.json +2 -1
  27. package/dist/locale/ja-JP.json +2 -1
  28. package/dist/locale/ko-KR.json +2 -1
  29. package/dist/locale/nl-NL.json +2 -1
  30. package/dist/locale/pt-BR.json +2 -1
  31. package/dist/locale/ru-RU.json +2 -1
  32. package/dist/locale/tr-TR.json +2 -1
  33. package/dist/locale/uk-UA.json +2 -1
  34. package/dist/locale/vi-VN.json +2 -1
  35. package/dist/locale/zh-CN.json +3 -1
  36. package/dist/locale/zh-TW.json +2 -1
  37. package/dist/node_modules/@langchain/mistralai/package.json +1 -1
  38. package/dist/node_modules/@langchain/xai/package.json +1 -1
  39. package/dist/node_modules/fs-extra/package.json +1 -1
  40. package/dist/node_modules/jsonrepair/package.json +1 -1
  41. package/dist/node_modules/just-bash/package.json +1 -1
  42. package/dist/node_modules/nodejs-snowflake/package.json +1 -1
  43. package/dist/node_modules/openai/package.json +1 -1
  44. package/dist/node_modules/zod/package.json +1 -1
  45. package/dist/swagger/index.d.ts +8 -0
  46. package/dist/swagger/llm.d.ts +8 -0
  47. package/dist/swagger/llm.js +8 -0
  48. package/package.json +2 -2
@@ -0,0 +1,206 @@
1
+ ---
2
+ title: "Complete Example: Create a Built-in AI Employee"
3
+ description: "Use a complete example to define a Tool, Skill, system prompt, and built-in AI employee in a NocoBase plugin."
4
+ keywords: "NocoBase,Dev Helper,AI employee example,defineTools,defineAIEmployee,SKILLS.md"
5
+ ---
6
+
7
+ # Complete Example: Create a Built-in AI Employee
8
+
9
+ The following complete example creates a built-in AI employee that guides plugin development. The example names the employee `Dev Helper` and configures its Tool, Skill, and system prompt. When a user says “Please greet Alice,” the employee loads the `welcome-developer` Skill, calls the `greetDeveloper` Tool to confirm the name, and then generates a greeting in the user's current language.
10
+
11
+ :::tip Prerequisites
12
+
13
+ - [Define a server-side Tool](./define-tool.md) — Learn the basic structure of `defineTools()` and Tools
14
+ - [Define a Skill](./define-skill.md) — Learn about `SKILLS.md` and Tool binding
15
+ - [Define a built-in AI employee](./define-ai-employee.md) — Learn about `defineAIEmployee()` and employee directories
16
+
17
+ :::
18
+
19
+ ## Final result
20
+
21
+ When complete, the plugin provides these capabilities:
22
+
23
+ - Creates a built-in AI employee named `Dev Helper`
24
+ - Automatically binds the `welcome-developer` Skill to the employee
25
+ - Uses the Skill to call the `greetDeveloper` Tool and confirm the developer's name
26
+ - Generates a greeting and follow-up question in the user's current language
27
+
28
+ <!-- 需要一张 AI 员工管理页中 Dev Helper 被标记为内置员工的截图 -->
29
+
30
+ ## Final directory structure
31
+
32
+ ```text
33
+ src/ai/ai-employees/dev-helper/
34
+ ├── index.ts
35
+ ├── prompt.md
36
+ └── skills/
37
+ └── welcome-developer/
38
+ ├── SKILLS.md
39
+ └── tools/
40
+ └── greetDeveloper.ts
41
+ ```
42
+
43
+ This example requires no frontend code and no manual registration in `src/server/plugin.ts`.
44
+
45
+ ## Step 1: Define the Tool
46
+
47
+ Create `src/ai/ai-employees/dev-helper/skills/welcome-developer/tools/greetDeveloper.ts`:
48
+
49
+ ```ts
50
+ import type { Context } from '@nocobase/actions';
51
+ import { defineTools } from '@nocobase/ai';
52
+ import { z } from 'zod';
53
+
54
+ export default defineTools({
55
+ scope: 'SPECIFIED',
56
+ defaultPermission: 'ALLOW',
57
+ introduction: {
58
+ title: '{{t("ai.tools.greetDeveloper.title", { ns: "@nocobase/plugin-developer-helper" })}}',
59
+ about: '{{t("ai.tools.greetDeveloper.about", { ns: "@nocobase/plugin-developer-helper" })}}',
60
+ },
61
+ definition: {
62
+ name: 'greetDeveloper',
63
+ description: 'Validate the developer name before the assistant writes a welcome message.',
64
+ schema: z.object({
65
+ name: z.string().min(1).describe('The developer name provided by the user.'),
66
+ }),
67
+ },
68
+ invoke: async (_ctx: Context, args: { name: string }) => {
69
+ return {
70
+ status: 'success',
71
+ content: {
72
+ name: args.name,
73
+ },
74
+ };
75
+ },
76
+ });
77
+ ```
78
+
79
+ ## Step 2: Define the Skill
80
+
81
+ Create `src/ai/ai-employees/dev-helper/skills/welcome-developer/SKILLS.md`:
82
+
83
+ ```md
84
+ ---
85
+ scope: SPECIFIED
86
+ name: welcome-developer
87
+ description: Greet a developer by name and guide them to the next NocoBase plugin-development step.
88
+ introduction:
89
+ title: '{{t("ai.skills.welcomeDeveloper.title", { ns: "@nocobase/plugin-developer-helper" })}}'
90
+ about: '{{t("ai.skills.welcomeDeveloper.about", { ns: "@nocobase/plugin-developer-helper" })}}'
91
+ ---
92
+
93
+ You welcome developers who are starting NocoBase plugin development.
94
+
95
+ # Workflow
96
+
97
+ 1. Read the developer name from the user's request.
98
+ 2. If the name is missing, ask the user for it.
99
+ 3. Call `greetDeveloper` exactly once.
100
+ 4. Wait for a tool result with `status: "success"`.
101
+ 5. Use `content.name` to write a short welcome message in the same language as the user.
102
+ 6. Ask which plugin capability the developer wants to build next, using the same language as the user.
103
+
104
+ # Constraints
105
+
106
+ - Do not invent a name.
107
+ - Do not claim the Tool succeeded before receiving its result.
108
+ - Write both the welcome message and the follow-up question in the same language as the user.
109
+ ```
110
+
111
+ Because `greetDeveloper.ts` is in the current Skill's `tools/` directory, you do not need to add `tools: [greetDeveloper]`.
112
+
113
+ ## Step 3: Define the AI employee profile
114
+
115
+ Create `src/ai/ai-employees/dev-helper/index.ts`:
116
+
117
+ ```ts
118
+ import { defineAIEmployee } from '@nocobase/ai';
119
+
120
+ export default defineAIEmployee({
121
+ username: 'developer-helper-dev-assistant',
122
+ category: 'developer',
123
+ description: 'AI employee for helping developers start NocoBase plugin development.',
124
+ avatar: 'nocobase-002-male',
125
+ nickname: 'Dev Helper',
126
+ position: 'Plugin development guide',
127
+ bio: 'Welcomes developers and guides them into a small, verifiable plugin-development task.',
128
+ greeting: 'Hello, I can help you begin a NocoBase plugin development task. Who are we welcoming today?',
129
+ });
130
+ ```
131
+
132
+ `username` is the unique identifier in the database. Do not change it casually after release, or NocoBase will treat the new value as a different built-in AI employee.
133
+
134
+ :::warning Note
135
+
136
+ The `username` must remain stable and must not conflict with another plugin or existing AI employee. If the database already contains the same `username`, loading the plugin updates that record instead of creating an isolated new employee.
137
+
138
+ When the plugin reloads, the `category`, `nickname`, `position`, `avatar`, `bio`, `greeting`, default system prompt, Skill and Tool bindings, `chatSettings`, and `sort` from the code may all be written to the database again. For production plugins, use a plugin-prefixed name such as `developer-helper-dev-assistant`.
139
+
140
+ :::
141
+
142
+ ## Step 4: Define the system prompt
143
+
144
+ Create `src/ai/ai-employees/dev-helper/prompt.md`:
145
+
146
+ ```md
147
+ You are Dev Helper, a NocoBase plugin development guide.
148
+
149
+ Help users begin with a small, verifiable task.
150
+
151
+ When the user asks you to greet or welcome a developer, load the `welcome-developer` skill and follow its workflow.
152
+
153
+ Never claim that a Tool succeeded before receiving its result.
154
+ ```
155
+
156
+ The directory relationships now provide automatic binding:
157
+
158
+ ```text
159
+ greetDeveloper Tool
160
+ → welcome-developer Skill
161
+ → dev-helper AI employee
162
+ ```
163
+
164
+ ## Step 5: Enable and verify
165
+
166
+ Rebuild or restart the development service and confirm that the plugin containing these files is enabled. Then check the AI employee management page:
167
+
168
+ - `Dev Helper` is listed
169
+ - The employee is marked as built-in
170
+ - The employee's dedicated Skills include `welcome-developer`
171
+ - `greetDeveloper` is available after the Skill loads
172
+
173
+ Enter the following in a conversation:
174
+
175
+ ```text
176
+ 请向 Alice 打个招呼。
177
+ ```
178
+
179
+ The expected sequence is:
180
+
181
+ ```text
182
+ 加载 welcome-developer
183
+ → 调用 greetDeveloper({ name: "Alice" })
184
+ → 收到 status: "success" 和 content.name
185
+ → Skill 使用用户当前语言生成问候语
186
+ → 询问接下来要开发什么插件能力
187
+ ```
188
+
189
+ Set `defaultPermission: 'ALLOW'` if the Tool should not ask the user for confirmation before every call. For Tools that delete data, modify records in bulk, or have external side effects, keeping the default `ASK` is more appropriate.
190
+
191
+ ## Summary
192
+
193
+ | File | Responsibility |
194
+ | --- | --- |
195
+ | `greetDeveloper.ts` | Validates input and returns a structured Tool result |
196
+ | `SKILLS.md` | Defines the Tool call and response workflow |
197
+ | `prompt.md` | Defines the employee role and global constraints |
198
+ | `index.ts` | Defines the built-in AI employee profile |
199
+
200
+ ## Related links
201
+
202
+ - [AI employee plugin development](./index.md) — Understand the relationship between Tools, Skills, and built-in AI employees
203
+ - [Define a server-side Tool](./define-tool.md) — See the complete `defineTools()` configuration
204
+ - [Define a Skill](./define-skill.md) — See the fields and format of `SKILLS.md`
205
+ - [Define a built-in AI employee](./define-ai-employee.md) — Learn about `defineAIEmployee()` and directory binding
206
+ - [Internationalization for AI employee plugins](./internationalization.md) — Add translations for management-interface text in this example
@@ -0,0 +1,149 @@
1
+ ---
2
+ title: "Define a Built-in AI Employee"
3
+ description: "Explains how NocoBase plugins use defineAIEmployee, prompt.md, skills, and tools directories to create built-in AI employees."
4
+ keywords: "NocoBase,built-in AI employee,defineAIEmployee,prompt.md,AIEmployeeOptions,Nathan"
5
+ ---
6
+
7
+ # Define a Built-in AI Employee
8
+
9
+ A built-in AI employee is registered with its plugin. When the plugin loads for the first time, NocoBase creates the corresponding employee record and marks it as built-in. On later plugin loads, the employee's default profile, prompt, Skills, and Tools are updated from the code.
10
+
11
+ ## Single-file and directory forms
12
+
13
+ Use a single file when the profile is simple and does not need a separate prompt or dedicated resources:
14
+
15
+ ```text
16
+ src/ai/ai-employees/lina.ts
17
+ ```
18
+
19
+ Use a directory when you need `prompt.md`, dedicated Skills, or dedicated Tools:
20
+
21
+ ```text
22
+ src/ai/ai-employees/nathan/
23
+ ├── index.ts
24
+ ├── prompt.md
25
+ ├── skills/
26
+ └── tools/
27
+ ```
28
+
29
+ The directory form is better suited to long-term maintenance.
30
+
31
+ ## Use `defineAIEmployee()`
32
+
33
+ In `index.ts`, use `defineAIEmployee()` from `@nocobase/ai`:
34
+
35
+ ```ts
36
+ import { defineAIEmployee } from '@nocobase/ai';
37
+
38
+ export default defineAIEmployee({
39
+ username: 'developer-helper-dev-assistant',
40
+ category: 'developer',
41
+ description: 'AI employee for helping developers start NocoBase plugin development.',
42
+ avatar: 'nocobase-002-male',
43
+ nickname: 'Dev Helper',
44
+ position: 'Plugin development guide',
45
+ bio: 'Helps developers understand plugin structure and complete small development tasks.',
46
+ greeting: 'Hello, I can help you start a NocoBase plugin development task. What would you like to build?',
47
+ });
48
+ ```
49
+
50
+ The main fields are:
51
+
52
+ | Field | Purpose |
53
+ | --- | --- |
54
+ | `username` | Unique AI employee identifier; required and must remain stable over time |
55
+ | `category` | Employee category, such as `developer` or `business` |
56
+ | `description` | Internal description and retrieval information |
57
+ | `avatar` | Avatar identifier |
58
+ | `nickname` | Name displayed to users |
59
+ | `position` | Job title |
60
+ | `bio` | Short biography |
61
+ | `greeting` | Greeting for a new conversation |
62
+ | `systemPrompt` | Default system prompt |
63
+ | `skills` | Explicitly bound Skill names |
64
+ | `tools` | Explicitly bound Tool configuration |
65
+ | `chatSettings` | Chat settings such as whether Skills and Tools are enabled and which system-prompt mode is used |
66
+ | `sort` | Sort order for built-in employees |
67
+
68
+ The current type of `tools` is an array of objects:
69
+
70
+ ```ts
71
+ tools: [
72
+ { name: 'greetDeveloper' },
73
+ { name: 'customDataExporter', autoCall: true }, // customDataExporter 的 scope 必须是 CUSTOM
74
+ ]
75
+ ```
76
+
77
+ `autoCall` only overrides the current AI employee's call permission for a `CUSTOM` Tool. For `GENERAL` and `SPECIFIED` Tools, runtime behavior still follows the Tool's own `defaultPermission`. If a `CUSTOM` Tool has no employee-level configuration, it also falls back to the Tool's own `defaultPermission`.
78
+
79
+ Tools discovered automatically in the directory are normalized to `{ name: 'toolName' }`.
80
+
81
+ ## Put long prompts in `prompt.md`
82
+
83
+ When an AI employee uses the directory form, put its system prompt in the sibling `prompt.md` file:
84
+
85
+ ```text
86
+ src/ai/ai-employees/dev-helper/prompt.md
87
+ ```
88
+
89
+ ```md
90
+ You are Dev Helper, a NocoBase plugin development guide.
91
+
92
+ Help the user break a plugin requirement into small, verifiable steps.
93
+
94
+ When the user asks you to welcome a developer, load the `welcome-developer` skill and follow it.
95
+
96
+ Never claim that a Tool succeeded before receiving its result.
97
+ ```
98
+
99
+ When `prompt.md` exists, it overrides `systemPrompt` in `index.ts`. Long prompts are easier to review in a Markdown file and avoid escaping problems in TypeScript template strings.
100
+
101
+ ## Built-in AI employee example: Nathan
102
+
103
+ The employee profile in `packages/plugins/@nocobase/plugin-flow-engine/src/ai/ai-employees/nathan/index.ts` is short:
104
+
105
+ ```ts
106
+ export default defineAIEmployee({
107
+ username: 'nathan',
108
+ category: 'developer',
109
+ description: 'AI employee for coding',
110
+ avatar: 'nocobase-002-male',
111
+ nickname: 'Nathan',
112
+ position: 'Frontend code engineer',
113
+ greeting: 'Hello, I’m Nathan, your frontend code engineer...',
114
+ });
115
+ ```
116
+
117
+ Nathan's full capabilities come from other resources in the same directory:
118
+
119
+ ```text
120
+ nathan/
121
+ ├── index.ts
122
+ ├── prompt.md
123
+ └── skills/
124
+ └── frontend-developer/
125
+ ├── SKILLS.md
126
+ └── tools/
127
+ ├── getContextApis.ts
128
+ ├── getContextEnvs.ts
129
+ ├── getContextVars.ts
130
+ ├── lintAndTestJS.ts
131
+ ├── patchJSCode.ts
132
+ ├── readJSCode.ts
133
+ └── writeJSCode.ts
134
+ ```
135
+
136
+ The loading process automatically completes three levels of binding:
137
+
138
+ 1. Files in `tools/` are registered as Tools
139
+ 2. The Tools are automatically bound to the `frontend-developer` Skill
140
+ 3. The Skill is automatically bound to Nathan
141
+
142
+ As a result, `index.ts` does not need to list the complete `skills` and `tools` configuration again.
143
+
144
+ ## Related links
145
+
146
+ - [AI employee plugin development](./index.md) — Understand the relationship between built-in AI employees, Tools, and Skills
147
+ - [Define a Skill](./define-skill.md) — Create a dedicated Skill for an employee
148
+ - [Complete example: Create a built-in AI employee](./complete-example.md) — See the complete employee directory and registration process
149
+ - [Internationalization for AI employee plugins](./internationalization.md) — Learn how localization differs for employee profiles and Tool and Skill text
@@ -0,0 +1,138 @@
1
+ ---
2
+ title: "Define a Skill"
3
+ description: "Explains the frontmatter, prompt content, Tool binding, and automatic directory discovery for NocoBase AI employee SKILLS.md files."
4
+ keywords: "NocoBase,AI employee Skill,SKILLS.md,Skill Tool binding,business-analysis-report"
5
+ ---
6
+
7
+ # Define a Skill
8
+
9
+ A Skill does not execute code. It is an operating guide provided to the model that specifies the workflow, available Tools, validation steps, and output requirements.
10
+
11
+ ## Skill directory
12
+
13
+ Each Skill uses its own directory:
14
+
15
+ ```text
16
+ src/ai/skills/business-analysis-report/
17
+ ├── SKILLS.md
18
+ └── tools/
19
+ └── businessReportGenerator.ts
20
+ ```
21
+
22
+ Where:
23
+
24
+ - `SKILLS.md` defines metadata and prompt content
25
+ - `tools/` contains Tools used only with this Skill
26
+ - Tools discovered in `tools/` are automatically added to this Skill's Tool list
27
+
28
+ ## `SKILLS.md` frontmatter
29
+
30
+ A minimal Skill looks like this:
31
+
32
+ ```md
33
+ ---
34
+ scope: SPECIFIED
35
+ name: welcome-developer
36
+ description: Greet a developer by name and explain the next step for starting NocoBase plugin development.
37
+ introduction:
38
+ title: '{{t("ai.skills.welcomeDeveloper.title", { ns: "@nocobase/plugin-developer-helper" })}}'
39
+ about: '{{t("ai.skills.welcomeDeveloper.about", { ns: "@nocobase/plugin-developer-helper" })}}'
40
+ ---
41
+
42
+ You help welcome developers who are starting NocoBase plugin development.
43
+
44
+ When the user asks you to greet or welcome a developer:
45
+
46
+ 1. Extract the developer name from the request.
47
+ 2. Call `greetDeveloper` exactly once.
48
+ 3. Return the greeting from the tool result.
49
+ 4. Ask which plugin capability the developer wants to build next.
50
+
51
+ Do not claim that the greeting was generated until the tool returns `status: "success"`.
52
+ ```
53
+
54
+ Common frontmatter fields include:
55
+
56
+ | Field | Purpose |
57
+ | --- | --- |
58
+ | `scope` | Availability scope of the Skill; defaults to `SPECIFIED` when omitted |
59
+ | `name` | Unique name of the Skill |
60
+ | `description` | Helps the model decide when to load this Skill |
61
+ | `introduction.title` | Title displayed in the management interface |
62
+ | `introduction.about` | Description displayed in the management interface |
63
+ | `tools` | List of additional Tool names to bind |
64
+
65
+ The Skill content is stored unchanged and added to the model context after the Skill is loaded. Focus the content on workflow and constraints; do not copy Tool implementation details into it.
66
+
67
+ ## Bind Tools to a Skill
68
+
69
+ There are two methods.
70
+
71
+ The first is to declare them explicitly in the frontmatter:
72
+
73
+ ```yaml
74
+ tools:
75
+ - getSkill
76
+ - businessReportGenerator
77
+ ```
78
+
79
+ The second is to put the Tool in the current Skill's `tools/` directory:
80
+
81
+ ```text
82
+ src/ai/skills/welcome-developer/
83
+ ├── SKILLS.md
84
+ └── tools/
85
+ └── greetDeveloper.ts
86
+ ```
87
+
88
+ The loader automatically discovers `greetDeveloper` and merges it into the Skill's Tool list. By default, put a Tool that belongs exclusively to one Skill under that Skill's directory. The file location then expresses the binding relationship.
89
+
90
+ ## How to write an effective Skill
91
+
92
+ A practical Skill usually includes:
93
+
94
+ 1. Role and task boundaries
95
+ 2. Required processing order
96
+ 3. Which Tool to call at each step
97
+ 4. When to ask the user for confirmation
98
+ 5. How to handle Tool failures
99
+ 6. Final output structure and validation conditions
100
+
101
+ If a Tool modifies data, the Skill must explicitly require the model to wait for a successful Tool result. It must not claim that the operation is complete before the call returns.
102
+
103
+ ## Built-in Skill example: `business-analysis-report`
104
+
105
+ `packages/plugins/@nocobase/plugin-ai/src/ai/skills/business-analysis-report/SKILLS.md` breaks business analysis into a clear workflow:
106
+
107
+ ```yaml
108
+ ---
109
+ scope: GENERAL
110
+ name: business-analysis-report
111
+ description: Analyze business data with the data-query workflow and generate stakeholder-facing reports with markdown and ECharts.
112
+ introduction:
113
+ title: '{{t("ai.skills.businessAnalysisReport.title", { ns: "@nocobase/plugin-ai" })}}'
114
+ about: '{{t("ai.skills.businessAnalysisReport.about", { ns: "@nocobase/plugin-ai" })}}'
115
+ tools:
116
+ - getSkill
117
+ - businessReportGenerator
118
+ ---
119
+ ```
120
+
121
+ Instead of merely saying “generate a business report,” the content goes on to require the following:
122
+
123
+ - Understand the decision objective, audience, time range, and metrics first
124
+ - When business data is involved, the first ToolCall must load the `data-query` Skill
125
+ - Never guess tables, relationship paths, or query results
126
+ - Call `businessReportGenerator` only after the data is ready
127
+ - Generate charts and the Markdown report in the same ToolCall
128
+ - Determine success from the returned `status`, `chartCount`, `errors`, and `warnings`
129
+ - Retry a chart failure only once, then fall back to a Markdown-only report
130
+
131
+ These rules are the main value of a Skill—they narrow “what the model can do” into a repeatable, verifiable process.
132
+
133
+ ## Related links
134
+
135
+ - [AI employee plugin development](./index.md) — Understand where Skills fit into AI employee extensions
136
+ - [Define a server-side Tool](./define-tool.md) — Define a Tool that a Skill can call
137
+ - [Define a built-in AI employee](./define-ai-employee.md) — Bind a Skill to a fixed employee
138
+ - [Complete example: Create a built-in AI employee](./complete-example.md) — See a complete Skill and Tool binding example
@@ -0,0 +1,189 @@
1
+ ---
2
+ title: "Define a Server-side Tool"
3
+ description: "Explains defineTools, scope, schema, invoke, permissions, and directory registration for NocoBase AI employee server-side Tools."
4
+ keywords: "NocoBase,AI employee Tool,defineTools,ToolsOptions,Zod,invoke"
5
+ ---
6
+
7
+ # Define a Server-side Tool
8
+
9
+ ## Minimal Tool structure
10
+
11
+ Define a server-side Tool with `defineTools()` from `@nocobase/ai`. The following Tool accepts a name and returns a greeting:
12
+
13
+ ```ts
14
+ import type { Context } from '@nocobase/actions';
15
+ import { defineTools } from '@nocobase/ai';
16
+ import { z } from 'zod';
17
+
18
+ export default defineTools({
19
+ scope: 'SPECIFIED',
20
+ defaultPermission: 'ALLOW',
21
+ introduction: {
22
+ title: '{{t("ai.tools.greetDeveloper.title", { ns: "@nocobase/plugin-developer-helper" })}}',
23
+ about: '{{t("ai.tools.greetDeveloper.about", { ns: "@nocobase/plugin-developer-helper" })}}',
24
+ },
25
+ definition: {
26
+ name: 'greetDeveloper',
27
+ description: 'Generate a short greeting for the developer named by the user.',
28
+ schema: z.object({
29
+ name: z.string().min(1).describe('The developer name to greet.'),
30
+ }),
31
+ },
32
+ invoke: async (_ctx: Context, args: { name: string }) => {
33
+ return {
34
+ status: 'success',
35
+ content: `Hello ${args.name}, welcome to NocoBase plugin development!`,
36
+ };
37
+ },
38
+ });
39
+ ```
40
+
41
+ If the file path is `src/ai/tools/greetDeveloper.ts`, the loader uses the filename `greetDeveloper` as the final Tool name. Even if `definition.name` contains another value, registration overrides it with the filename.
42
+
43
+ Therefore, keep the filename, `definition.name`, the name referenced by the Skill, and the name registered on the frontend consistent by default.
44
+
45
+ ## Tool options
46
+
47
+ The main `defineTools()` options are:
48
+
49
+ | Option | Purpose | Default |
50
+ | --- | --- | --- |
51
+ | `scope` | Determines the Tool's availability scope | Required |
52
+ | `execution` | Specifies whether the logic runs on the `backend` or `frontend` | `backend` |
53
+ | `defaultPermission` | Allows the Tool directly or asks for confirmation before a call | `ASK` |
54
+ | `silence` | Hides the Tool call indicator in the conversation | `false` |
55
+ | `introduction` | Title and description displayed in the management interface | Uses the Tool name |
56
+ | `definition` | Provides the name, description, and parameter schema to the model | Required |
57
+ | `invoke` | Actual execution logic of the Tool | Required |
58
+
59
+ The selected `scope` directly affects how the Tool enters an AI employee's context:
60
+
61
+ | `scope` | Usage |
62
+ | --- | --- |
63
+ | `GENERAL` | Shared by all AI employees, usually for general foundational capabilities |
64
+ | `SPECIFIED` | Available only to a Skill or AI employee that binds the Tool |
65
+ | `CUSTOM` | Administrators can add it manually in AI employee configuration and set it to “Ask” or “Allow” |
66
+
67
+ Use `SPECIFIED` by default. Use `GENERAL` only when every AI employee definitely needs the capability. Use `CUSTOM` when administrators should select it per employee.
68
+
69
+ ## `definition` is written for the model
70
+
71
+ `definition.description` and `definition.schema` affect whether the model selects the Tool and how it constructs arguments. The description should explain three things:
72
+
73
+ - When to call the Tool
74
+ - What each argument represents
75
+ - Which tasks the Tool should not handle
76
+
77
+ Zod is recommended for the parameter schema:
78
+
79
+ ```ts
80
+ schema: z.object({
81
+ query: z.string().describe('A specific search query.'),
82
+ limit: z.number().int().min(1).max(20).default(5).describe('Maximum number of records to return.'),
83
+ })
84
+ ```
85
+
86
+ The Tool name must also remain stable. Skills, AI employee configurations, frontend cards, and saved chat messages all locate it by name.
87
+
88
+ ## What `invoke()` receives
89
+
90
+ A server-side `invoke()` receives three arguments:
91
+
92
+ ```ts
93
+ invoke: async (ctx, args, runtime) => {
94
+ // ctx:当前 NocoBase action Context
95
+ // args:模型根据 schema 生成的参数
96
+ // runtime.toolCallId:当前 ToolCall ID
97
+ // runtime.writer(chunk):流式写出中间结果
98
+ }
99
+ ```
100
+
101
+ Through `ctx`, you can access the current application, database, authentication information, and action arguments. For example:
102
+
103
+ ```ts
104
+ const repository = ctx.app.db.getRepository('posts');
105
+ const currentUser = ctx.auth?.user;
106
+ const values = ctx.action?.params?.values;
107
+ ```
108
+
109
+ A Tool should return a structure that clearly indicates success or failure. Built-in Tools commonly use this shape:
110
+
111
+ ```ts
112
+ return {
113
+ status: 'success',
114
+ content: result,
115
+ };
116
+ ```
117
+
118
+ For expected business failures, return a clear status and reason instead of making the model guess whether the operation succeeded.
119
+
120
+ ## Directory-based Tools
121
+
122
+ A Tool can use a directory instead of a single file:
123
+
124
+ ```text
125
+ src/ai/tools/documentSearch/
126
+ ├── index.ts
127
+ └── description.md
128
+ ```
129
+
130
+ `index.ts` exports the result of `defineTools()` by default. When `description.md` exists, its entire content overrides `definition.description`, making it suitable for longer Tool instructions.
131
+
132
+ The directory name `documentSearch` becomes the final registered name.
133
+
134
+ ## Built-in Tool example: `subAgentWebSearch`
135
+
136
+ `packages/plugins/@nocobase/plugin-ai/src/ai/tools/subAgentWebSearch.ts` demonstrates a complete server-side Tool:
137
+
138
+ ```ts
139
+ export default defineTools({
140
+ scope: 'SPECIFIED',
141
+ defaultPermission: 'ALLOW',
142
+ introduction: {
143
+ title: '{{t("Web search")}}',
144
+ about: '{{t("Use web search to quickly find up-to-date information from the internet.")}}',
145
+ },
146
+ definition: {
147
+ name: 'subAgentWebSearch',
148
+ description: 'Search the web for current information...',
149
+ schema: z.object({
150
+ query: z.array(z.string()),
151
+ }),
152
+ },
153
+ invoke: async (ctx, args) => {
154
+ // 获取 AI 插件和当前会话使用的模型配置。
155
+ const pluginAI = ctx.app.pm.get('ai') as PluginAIServer;
156
+ const { model } = ctx.action?.params?.values ?? {};
157
+ const { provider } = await pluginAI.aiManager.getLLMService({
158
+ ...model,
159
+ webSearch: true,
160
+ reasoning: { mode: 'off' },
161
+ });
162
+
163
+ // 独立查询并行执行,最后统一返回。
164
+ const result = await Promise.all(
165
+ args.query.map(async (query) => {
166
+ const content = await provider.invoke(/* messages */);
167
+ return { query, result: content.text };
168
+ }),
169
+ );
170
+
171
+ return { status: 'success', content: result };
172
+ },
173
+ });
174
+ ```
175
+
176
+ This implementation demonstrates several reusable practices:
177
+
178
+ - Use `SPECIFIED` to limit the Tool to designated employees or Skills
179
+ - Use Zod to constrain model-generated arguments
180
+ - Read the current AI conversation configuration from `ctx.action.params.values`
181
+ - Put multiple independent queries into one ToolCall and run them concurrently with `Promise.all()`
182
+ - Return structured results with clear provenance for the higher-level model to process
183
+
184
+ ## Related links
185
+
186
+ - [AI employee plugin development](./index.md) — Choose which capability layer to extend
187
+ - [Define a Skill](./define-skill.md) — Use a Skill to organize the call flow for multiple Tools
188
+ - [Complete example: Create a built-in AI employee](./complete-example.md) — See a runnable Tool example
189
+ - [Add frontend interaction to a Tool](./frontend-tool-ui.md) — Add confirmation and selection UI to a ToolCall