关于 objectstack-ai
objectstack-ai 介绍如何为 ObjectStack 应用定义 AI 技能和工具,面向编写 AI 元数据的开发者。开放版通过 MCP 将应用提供给外部 Agent,不包含应用内 Agent 运行时。
主要功能
- 01定义包含工具和指令的技能
- 02配置程序化触发条件
- 03将明确启用的操作公开为工具
- 04通过 MCP 发布 ObjectStack 应用
- 05配置知识来源与适配器
- 06验证技能中的工具引用
适用场景
- 01为 ObjectStack 应用编写技能
- 02向外部 Agent 提供应用操作
- 03让外部 Agent 访问应用数据和操作
- 04配置已索引的知识来源
MCP 相关
文档中的 @objectstack/mcp 插件通过 MCP 公开 ObjectStack 应用,包括架构检查、记录操作和明确启用的业务操作。调用受 OAuth 作用域、调用者权限和行级安全控制。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ objectstack-ai/ objectstack --skill objectstack-ai安装后在 Claude Code 中输入/objectstack-ai确认已加载。
附带文件evals/skills-tools-knowledge.jsonreferences/_index.md
详细介绍
AI Agent 设计 — ObjectStack AI 协议
版本范围。☁️ 表示云端 / Enterprise 功能,以下不再重复标注。 UI 内的 AI 运行时——
ask/buildAgent、产品内聊天、/api/v1/ai/*(@objectstack/service-ai)——仅随云端 / EE 提供,不属于开源框架。@objectstack/spec/ai中的架构定义保持开放,因此无论哪种情况,你都可以将*.skill.ts/*.tool.ts编写为源代码,但它们只能在云端 / EE 主机中执行。在开源版本中没有产品内 Agent:通过 MCP(见下文)向你自己的 AI 开放应用,并使用 AI 编码 Agent(Claude Code、Cursor)以源代码模式编写元数据。
Agent → Skill → Tool
│ │ │
│ │ └─ Atomic operation (query, action, flow, API call)
│ └─ Capability bundle with instructions & trigger conditions
└─ Autonomous actor with role, instructions, and guardrails
Agent 是平台内部功能(☁️ ask / build;参考内容见底部)。Skill 是你的入口——通过编写 Skill 来扩展平台,并在 Skill 中指定工具来调用它们。
Skill 配置
Skill 是一组带有专属指令和触发条件的命名工具集合。
| 必填 | 类型 | 描述 |
|---|---|---|
name | snake_case | 唯一的 Skill ID(/^[a-z_][a-z0-9_]*$/) |
label | string | 显示名称 |
tools | string[] | 此 Skill 授予的工具名称(允许使用尾随通配符,例如 action_*)——每个名称都必须能解析;详见下文的解析层级 |
| 可选 | 用途 |
|---|---|
surface | 'ask' | 'build' | 'both'——平台 Agent 可以绑定此 Skill 的范围(默认 'ask')。加载时在 resolveActiveSkills 中强制执行:不兼容的绑定会导致快速加载错误(ADR-0063 §3) |
description | Skill 的用途——Agent 会读取它来判断何时使用该 Skill |
instructions | 针对此 Skill 上下文的提示词指导。开源版本会读取的唯一字段:@objectstack/mcp 会将其映射到 MCP prompts 基元,因此,instructions 为空或 active: false 的 Skill 根本不会作为提示词列出 |
triggerConditions | 以编程方式定义的激活规则(见下文) |
active | 是否启用(默认 true) |
Skill 没有
permissions键——该键已在 17.0.0 中移除,因为它没有任何限制作用。应在 Agent 上进行限制(在defineAgent上设置access/permissions,由聊天路由强制执行),或限制工具调用的操作(ADR-0066)。
指定工具名称:解析层级
skill.tools[] 条目会针对三个来源的并集进行解析(packages/lint/src/validate-ai-tool-references.ts 中的 collectToolUniverse):
stack.tools[].name——你自己的 stack 所声明的记录。ADR-0109:默认的第三方路径不声明任何工具(见下文的 工具元数据)。PLATFORM_PROVIDED_TOOL_NAMES——静态注册的平台工具,从packages/spec/src/system/constants/platform-tool-names.ts中的PLATFORM_TOOLS_BY_PACKAGE展开而来:来自service-ai的数据 / 知识工具,以及来自service-ai-studio的架构 / 元数据 / 包工具。请查看该文件以获取准确集合:这是 lint 规则检查的注册表。action_<name>——你自己的 stack 中每个向 AI 开放的 Action 对应一个工具(PLATFORM_TOOL_FAMILY_PREFIXES)。任何应用功能的默认路径。
不属于上述三个来源的名称会触发 ai-skill-tool-unresolved(警告,ADR-0078 先以建议方式执行)。设置此规则的原因是:有一个经过审计的应用,通过 validate / lint 检查,却在 6 个 Skill 中发布了 10 个虚构工具,最终成为一个声称具备自身并不具备能力的副驾驶。
Skill 示例
import { defineSkill } from '@objectstack/spec';
export default defineSkill({
name: 'case_management',
label: 'Case Management',
description: 'Triage, query, and escalate support cases.',
instructions: `
When managing cases:
- Always check for duplicate cases before creating a new one.
- Set priority by customer tier: Enterprise → High, Pro → Medium, Free → Low.
- Escalated cases must summarise the actions already taken.
`,
// Every name resolves: two platform tools (rung 2) plus one action tool
// materialised from this stack's own `escalate_case` Action (rung 3).
tools: ['query_records', 'get_record', 'action_escalate_case'],
triggerConditions: [
{ field: 'objectName', operator: 'eq', value: 'support_case' },
],
active: true,
});
触发条件
每个条件的形式为 { field, operator, value },并与同级条件进行 AND 运算。value 的形式取决于 operator——不匹配会导致解析错误,不会进行类型转换(packages/spec/src/ai/skill.zod.ts:37-51, 139-202):
| 运算符 | 含义 | value 必须是 |
|---|---|---|
eq | 等于 | 字符串——数组采用引用相等,因此永远不会匹配 |
neq | 不等于 | 字符串——数组会始终触发 |
in | 在列表中 | 数组(允许空数组 [],且它是一个有效谓词) |
not_in | 不在列表中 | 数组 |
contains | 子字符串或数组子集 | 二者皆可——两种写法都会执行 |
短语无法匹配:将路由规则写在 triggerConditions 中,并将自然语言意图写在 description / instructions 中,模型会读取这些内容。
将操作用作 AI 工具 — 默认路径 ☁️
通常,调用操作时无需手动编写工具定义。通过 defineObject({ actions: [...] }) 附加到对象的 Action,只有在选择公开时才会变为名为 action_<actionName> 的工具——必须设置 ai.exposed: true(默认值为 false),并提供长度不少于 40 个字符的 ai.description;否则解析会拒绝该配置(ADR-0011)。registerActionsAsTools() 会遍历选择公开的操作;公开绝不会自动发生,也没有用于取消公开的键。
三种操作类型可在无头模式下分发:
action.type | 分发方式 | 接线配置 |
|---|---|---|
script | IDataEngine.executeAction(object, target, ctx) — 与 Studio 的行工具栏相同 | 无 |
api | 对 action.target 发起 HTTP 调用(默认基于 fetch) | AIServicePlugin({ apiActionBaseUrl, apiActionHeaders }) 或自定义 apiClient |
flow | IAutomationService.execute(target, { triggerData }) | 在内核中注册的 automation 服务 |
即使选择公开也会跳过: 仅限 UI 的类型(url、modal、form);以及危险变体——仅限声明了 mode: 'delete' / variant: 'danger' 的情况(confirmText 是对话框文案,不是破坏性操作的信号;ai.requiresConfirmation 无论上述条件如何都会覆盖此规则)——除非插件以 enableActionApproval: true 运行,此时这些操作会进入审批队列。
type:'api' 请求体组装(后者覆盖前者):用户参数 → recordIdParam(使用 recordIdField,默认值为 'id')→ bodyExtra。bodyShape: { wrap: 'data' } 会将用户参数嵌套在 data 下,同时让 recordIdParam 保持平级。
人在回路中。 AIServicePlugin({ enableActionApproval: true })(默认值为 false)不会执行危险操作,而是持久化一条 ai_pending_actions 记录,并返回 { status: 'pending_approval', pendingActionId };操作员可在 Studio 的 AI 待处理操作收件箱中(或通过 GET/POST /api/v1/ai/pending-actions/...)批准或拒绝。你的操作是否会被暂缓,完全由上述跳过规则决定——@objectstack/service-ai 中的 actionSkipReason(action, ctx) 和 actionRequiresApproval(action) 可分别回答这两个问题,Studio 的“AI 公开”诊断也使用相同的判定条件。
MCP — 开放版 AI 路径
@objectstack/mcp 通过模型上下文协议发布你的应用,让外部 Agent(Claude、Cursor、Codex 等)无需云端许可即可读取并操作它。(消费外部 MCP 服务是入站对应项:@objectstack/connector-mcp。)
import { MCPServerPlugin } from '@objectstack/mcp';
kernel.use(new MCPServerPlugin());
HTTP 接口默认启用,由运行时分发器按请求在 POST /api/v1/mcp 提供服务——无需启动任何内容(设置 OS_MCP_SERVER_ENABLED=false 可选择退出)。长驻的 stdio 传输有独立开关,且默认关闭:autoStart: true,或 OS_MCP_STDIO_ENABLED=true。(OS_MCP_SERVER_ENABLED=true 仍会启动它——此方式已弃用,并会在启动时发出警告。)
客户端随后可以看到的工具——来自 registerObjectTools / registerActionTools,每组工具都受调用方的 OAuth 作用域控制;每次调用均遵循调用方的权限和行级安全规则:
| 工具 | 功能 |
|---|---|
list_objects, describe_object | 架构自省(默认过滤系统对象) |
query_records, get_record | 读取——筛选、字段选择、排序、分页 |
aggregate_records | GROUP BY 汇总;仅当数据桥接实现了 aggregate 时注册 |
validate_expression | 在公式写入元数据之前,按照构建时配置检查 CEL |
create_record, update_record, delete_record | 写入;delete_record 标注为破坏性操作 |
list_actions, run_action | 供 AI 使用的业务 Actions——与上述相同,需选择启用 ai.exposed |
知识源(RAG)
RAG 原语是 KnowledgeSource(@objectstack/spec/ai 中的
KnowledgeSourceSchema):一种声明式元数据,用于将 要索引的内容 与负责执行工作的 IKnowledgeAdapter 的 ID 配对。没有用于这些对象的 defineStack
集合——它们在运行时注册,☁️
search_knowledge 工具会向 Agent 暴露已注册的源。
接线(开放包)。 @objectstack/service-knowledge 中的 KnowledgeServicePlugin({ sources }) 会在启动时向内核注册 IKnowledgeService 和每个源(之后仍可使用 service.registerSource)。adapter
ID 会解析到一个适配器插件——@objectstack/knowledge-memory
('memory',开发/测试)或 @objectstack/knowledge-ragflow('ragflow');
若没有适配器插件,该 ID 将无法解析。
| 属性 | 用途 |
|---|---|
id | 蛇形命名的源 ID |
label / description | 显示元数据 |
adapter | 适配器 ID(例如 'ragflow'、'memory'),通过 IKnowledgeService.registerAdapter 解析 |
adapterConfig | 适配器专属配置(服务不解析其内容) |
source | 要索引的内容——根据 kind 区分:'object' | 'file' | 'http' |
embedding | 可选的 EmbeddingModelSchema 引用(provider、model、dimensions)——在内部管理嵌入的适配器(RAGFlow、Dify、Vectara)可以忽略它 |
vectorStore | 可选的 VectorStoreSchema 引用(provider、collection)——同样需要注意 |
refresh | onRecordChange(对象源默认为 true)+ 可选的 cron(供外部调度器使用;不会自行调度) |
aiExposed | search_knowledge 是否可向 Agent 暴露此源(默认为 true)——设为 false 时会索引该源,但不会使其可供 Agent 搜索 |
source.kind | 字段 | 数据规范 |
|---|---|---|
object | object、contentFields[](至少 1 个;* = 每个可读文本字段)、metadataFields?、where?(ObjectQL where 语法) | 使用 where 将草稿和已归档行排除在索引之外;contentFields 只选择有意义的文本(绝不选择系统字段或 ID,谨慎使用 *);将查询时要用于筛选的字段(status、owner_id、tags)投影到 metadataFields 中 |
file | prefix(存储前缀,例如 kb/handbooks/)、mimeTypes? | 缩小前缀范围,而不是索引整个存储桶 |
http | urls[]、userAgent? | 明确列出 URL——没有爬虫 |
import { KnowledgeSourceSchema, type KnowledgeSource } from '@objectstack/spec/ai';
export const supportKb: KnowledgeSource = KnowledgeSourceSchema.parse({
id: 'support_kb',
label: 'Support Knowledge Base',
adapter: 'ragflow', // or 'memory' for dev/test
source: {
kind: 'object',
object: 'kb_article',
contentFields: ['title', 'body'], // concatenated into document content
metadataFields: ['category', 'owner_id'], // projected for search-time filtering
where: { published: true }, // index published articles only
},
refresh: { onRecordChange: true }, // re-index on record.* events
});
分块、top-K、分数阈值和重排器不属于平台元数据。 规范特意将它们排除在外(
embedding.zod.ts):它们属于 适配器(adapterConfig)或应用代码。平台只承载嵌入和向量原语,因此可以在其上构建任意 RAG 策略。
工具元数据(defineTool)——非默认路径
默认:不声明工具记录。 技能会指定一个平台工具或一个
action_<name>(见上文阶梯)。ADR-0109:stack.tools 记录“仅用于
AI 展示优化(阶段 2:大模型描述、参数收窄、流程暴露),在该功能推出前
没有运行时读取者”
(packages/spec/src/stack.zod.ts:595-602)。只有在需要优化现有能力的展示方式时才编写一条——绝不能用它新增能力,也绝不能让某项能力变得可执行:ToolSchema 没有 handler / implementation 字段,也没有框架执行器会加载通过元数据编写的工具。
ToolSchema 是严格的——未知键(拼写错误或已弃用的键)会导致解析错误,而不会被静默剔除。必填字段为 name / label / description 和 JSON Schema parameters 对象;可选字段为 objectName 和 outputSchema(⚠️ 实验性——工具记录中没有任何内容会读取它;应改为在操作上声明 ai.outputSchema,AI 运行时 ☁️ 会通过阻止不符合该模式的结果来强制执行)。在协议 17 中弃用:category、permissions、active、builtIn——这些字段都可以编写,但不起作用;与 requiresConfirmation 一同弃用。每项拒绝都会说明其替代方案。
应通过底层操作(action.requiredPermissions,ADR-0066)或工具所涉及的对象来限制工具能力;要撤销某项工具,只需将其从引用它的技能中移除。分类应设在操作上(action.ai.category——有效,但仅用于列表:会传递到工具上,从不发送给模型)。
模型配置
采样参数位于 Agent 的 model(AIModelConfigSchema)下——Agent 上没有顶层的 temperature / maxTokens(packages/spec/src/ai/agent.zod.ts:36-40):
| 键 | 约定 |
|---|---|
provider | 内联枚举 openai | azure_openai | anthropic | local(默认值为 openai)。模型注册表条目(ModelProviderSchema)接受更多选项:还包括 google、cohere、huggingface、custom |
model | 任意字符串——服务提供商自己的模型名称 |
temperature | min(0).max(2),默认值为 0.7。超出 0–2 会导致解析错误 |
maxTokens / topP | 可选数字,架构不设上限 |
常见陷阱
- 误把
guardrails当作关卡。 AI 运行时 ☁️ 会在每轮用户交互中执行guardrails——令牌和时间限制,以及被拒绝的屏蔽工具名称和类别——但它只是限制,不是审批;AI 运行时 ☁️ 也会执行memory——会回忆并反思长期笔记;longTerm.enabled为 true 时,必须设置maxEntries和reflectionInterval,且没有可选的存储后端——这两者都不是关卡。若要使用强制执行的关卡,请使用enableActionApproval: true(审批队列 ☁️)、操作上的ai.requiresConfirmation,或 MCP 工具绑定上的approval: 'always'。AI 元数据编辑本身已有审批关卡:编辑会以草稿形式提交,必须由人工发布(ADR-0033)。 ⚠️ 工具上的requiresConfirmation已移除(ADR-0033 §2)——没有任何执行路径读取它,因此不会暂停执行;ToolSchema是严格的,因此现在编写该字段会导致解析失败。不存在requireApprovalFor字段。 - 索引所有内容。 缺少
where过滤条件和精心挑选的contentFields的知识源,会让草稿和模板内容填满索引,污染检索。元数据负责保证来源整洁;相关性调优属于适配器的职责。
App AI 蓝图
| 层 | 文件 | 模式 |
|---|---|---|
| 可复用技能 | src/skills/lead-qualification.skill.ts | defineSkill — instructions + 触发条件 + 每个名称都能解析的工具集;选择一个 surface |
| 知识源 | src/knowledge/sales-kb.ts | KnowledgeSourceSchema 元数据,通过 KnowledgeServicePlugin({ sources }) 在启动时注册 |
| 集中注册 | defineStack({ skills: [...] }) | agents / tools / skills 是 AI 技术栈仅有的集合——知识源不属于其中,Agent 由平台提供,而 tools 并非默认路径 |
元数据应用的默认做法:将业务能力放入 技能,将业务逻辑作为 AI 可调用的 Actions 暴露,通过 知识源 接入领域知识。
平台 Agent — 仅供参考 ☁️
agent 类型对第三方关闭(allowRuntimeCreate:false、
allowEnvironmentOverlay:false;ADR-0063 §2),且 os g agent 已弃用:由 stack 编写的 Agent 会被
listAgents() 过滤,被 loadAgent() 直接拒绝,并在聊天时返回 404。扩展平台要用技能,绝不要
添加 Agent。AgentSchema 的完整字段列表见
references/_index.md。
运行时恰好提供两个 Agent,按使用界面绑定——用户不会从列表中选择,所在的界面会决定使用哪个 Agent。ask 是
数据产品(≈ Claude Chat):面向终端用户,可对记录进行对话式读取 / 查询 / 探索,并运行应用公开的业务操作,
受 RLS 限制,规范 ID 为 ask(ASK_AGENT_NAME),也是任何未设置 app.defaultAgent 的应用的隐式副驾驶。build
是创作产品(≈ Claude Code):面向构建者,通过规划 → 起草 → 验证 → 发布,以 Agent 方式创作元数据,受治理机制约束,
由 Studio 通过 app.defaultAgent 固定。不存在逐轮意图分类器——发给 ask 的 build 类请求会被拒绝并引导至其他界面,
绝不会悄然转入创作流程(ADR-0063 §1/§5)。data_chat→ask 和
metadata_assistant→build 会通过别名表解析,以兼容旧书签和持久化的 agent_id;它们不是规范术语——始终使用 ask /
build。
Agent 的工具集是其兼容当前界面的技能所拥有工具的并集——不存在全局回退机制(ADR-0064),因此从设计上讲,ask 无法进行
创作;停用某项技能(active: false)会撤销所有引用该技能的 Agent 的相应能力。内置技能:
| 技能 | surface | 所属工具 |
|---|---|---|
schema_reader | both | list_objects、describe_object、query_data |
data_explorer | ask | query_records、get_record、aggregate_data、visualize_data |
actions_executor | ask | action_*(对象公开的业务操作) |
metadata_authoring + solution_design | build | 元数据起草 / 验证 / 发布 + 蓝图提议 / 应用 |
这四项都是云运行时技能——在开源版中均无法解析,因此你自定义技能中的 surface / tools / triggerConditions 面向云端编写,
在开源版中不会生效;MCP 提供的是它们的 instructions。
visualize_data 是唯一会绘制图表的内置技能(内嵌的 data-chart 部分),且仅在接入分析服务(IAnalyticsService)时自动注册——
query_data / aggregate_data 返回数字,而非图表。
AgentSchema 中有两个键是墓碑字段,不是旧版选项——编写任一字段都会导致解析错误,并附带迁移说明
(packages/spec/src/ai/agent.zod.ts:234, :251):
| 已弃用的键 | 在 protocol 17 中已移除 — 原因及替代方案 |
|---|---|
agent.tools | 内联字段会在完整注册表中按名称解析,不检查界面兼容性。这不是重命名:迁移会删除该键,并列出其中的每个工具;请将每个工具重新声明在技能中(ADR-0064) |
agent.knowledge | 在 Agent 上声明来源 / 索引并不会限定检索范围——search_knowledge 会从模型的工具调用参数中获取 sourceIds。请在知识服务 / 来源级别限制范围;在 instructions 中说明预期的依据 |
验证你的工作
编写 *.skill.ts / *.tool.ts 后,在报告完成前运行作者时检查:
os validate # Zod schema + CEL predicate validation + bindings (no artifact)
# or: os build # the same gates, plus emits dist/
此检查会确认技能 / 工具元数据符合协议。此领域没有 CEL 站点——模型注册表中的
promptTemplate.system / .user 使用 template 方言({{var}});ToolSchema 不包含任何类型的表达式字段。在脚手架生成的项目中,
该检查命令为 npm run validate。参见 objectstack-platform → 验证你的工作。
参考资料
完整的 Zod 架构列表(附一行说明)请参阅 references/_index.md——其中包含指向 node_modules/@objectstack/spec/src/ 的索引。请始终 Read 源文件以确认字段的准确结构;不要凭记忆猜测属性名称。