openserv-agent-sdk
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseOpenServ Agent SDK
OpenServ Agent SDK
Build and deploy custom AI agents for the OpenServ platform using TypeScript.
Reference files:
- - Quick reference for common patterns
reference.md - - Common issues and solutions
troubleshooting.md - - Complete code examples
examples/
使用TypeScript为OpenServ平台构建并部署自定义AI Agent。
参考文件:
- - 常见模式快速参考
reference.md - - 常见问题与解决方案
troubleshooting.md - - 完整代码示例
examples/
What's New in v2.3
v2.3版本新特性
- Env Var - Set
DISABLE_TUNNELfor production deployments (e.g. Cloud Run).DISABLE_TUNNEL=truestarts only the HTTP server, no WebSocket tunnel.run() - Env Var - Set
FORCE_TUNNELto force tunnel mode even when anFORCE_TUNNEL=trueis configured.endpointUrl - Public Health Check - The route now responds before auth middleware, so the platform health cron can reach it without credentials.
/health - Binary Tunnel Responses - Tunnel responses are sent as binary frames, preserving gzip, images, and other binary payloads transparently.
- 环境变量 - 生产部署(如Cloud Run)时设置
DISABLE_TUNNEL。DISABLE_TUNNEL=true仅启动HTTP服务器,不开启WebSocket隧道。run() - 环境变量 - 设置
FORCE_TUNNEL可强制启用隧道模式,即使已配置FORCE_TUNNEL=true。endpointUrl - 公开健康检查端点 - 路由现在会在身份验证中间件之前响应,因此平台健康检查定时任务无需凭证即可访问它。
/health - 二进制隧道响应 - 隧道响应以二进制帧形式发送,透明保留gzip、图像和其他二进制负载。
Earlier in v2.x
v2.x早期版本特性
- Built-in Tunnel - The function auto-connects to
run()agents-proxy.openserv.ai - No Endpoint URL Needed - Skip in
endpointUrlduring developmentprovision() - Automatic Port Fallback - If your port is busy, the agent finds an available one
- Direct Credential Binding - can bind credentials directly to agent instance via
provision()agent.instance - Method - Manually bind API key and auth token to agent
setCredentials()
- 内置隧道 - 函数会自动连接到
run()agents-proxy.openserv.ai - 无需端点URL - 开发期间调用时可省略
provision()endpointUrl - 自动端口回退 - 如果指定端口被占用,Agent会自动寻找可用端口
- 直接凭证绑定 - 可通过
provision()将凭证直接绑定到Agent实例agent.instance - 方法 - 手动将API密钥和身份验证令牌绑定到Agent
setCredentials()
Quick Start
快速开始
Installation
安装
bash
npm install @openserv-labs/sdk @openserv-labs/client zod openaiNote: The SDK requiresas a peer dependency.openai@^5.x
bash
npm install @openserv-labs/sdk @openserv-labs/client zod openai注意: SDK需要作为对等依赖。openai@^5.x
Minimal Agent
最简Agent示例
See for a complete runnable example.
examples/basic-agent.tsThe pattern is simple:
- Create an with a system prompt
Agent - Add capabilities with
agent.addCapability() - Call to register on the platform (pass
provision()to bind credentials)agent.instance - Call to start
run(agent)
完整可运行示例请查看。
examples/basic-agent.ts实现流程非常简单:
- 使用系统提示创建实例
Agent - 调用添加功能
agent.addCapability() - 调用在平台上注册(传入
provision()以绑定凭证)agent.instance - 调用启动Agent
run(agent)
Complete Agent Template
完整Agent模板
File Structure
文件结构
my-agent/
├── src/agent.ts
├── .env
├── .gitignore
├── package.json
└── tsconfig.jsonmy-agent/
├── src/agent.ts
├── .env
├── .gitignore
├── package.json
└── tsconfig.jsonDependencies
依赖安装
bash
npm init -y && npm pkg set type=module
npm i @openserv-labs/sdk @openserv-labs/client dotenv openai zod
npm i -D @types/node tsx typescriptNote: The project must usein"type": "module". Add apackage.jsonscript for local development."dev": "tsx src/agent.ts"
bash
npm init -y && npm pkg set type=module
npm i @openserv-labs/sdk @openserv-labs/client dotenv openai zod
npm i -D @types/node tsx typescript注意: 项目必须在中设置package.json。添加"type": "module"脚本用于本地开发。"dev": "tsx src/agent.ts"
.env
.env配置
env
OPENAI_API_KEY=your-openai-keyenv
OPENAI_API_KEY=your-openai-keyAuto-populated by provision():
由provision()自动填充:
WALLET_PRIVATE_KEY=
OPENSERV_API_KEY=
OPENSERV_AUTH_TOKEN=
PORT=7378
WALLET_PRIVATE_KEY=
OPENSERV_API_KEY=
OPENSERV_AUTH_TOKEN=
PORT=7378
Production: skip tunnel and run HTTP server only
生产环境:跳过隧道,仅运行HTTP服务器
DISABLE_TUNNEL=true
DISABLE_TUNNEL=true
Force tunnel even when endpointUrl is set
即使设置了endpointUrl也强制启用隧道
FORCE_TUNNEL=true
FORCE_TUNNEL=true
---
---Capabilities
功能模块(Capabilities)
Capabilities are functions your agent can execute. Each requires:
- - Unique identifier
name - - What it does (helps AI decide when to use it)
description - - Zod schema defining parameters
schema - - Function returning a string
run
See for basic capabilities.
examples/capability-example.ts功能模块是Agent可以执行的函数,每个模块需要包含:
- - 唯一标识符
name - - 功能说明(帮助AI判断何时使用该模块)
description - - 定义参数的Zod schema
schema - - 返回字符串的函数
run
基础功能模块示例请查看。
examples/capability-example.tsUsing Agent Methods
使用Agent方法
Access in capabilities to use agent methods like , , etc.
thisaddLogToTask()uploadFile()See for logging and file upload patterns.
examples/capability-with-agent-methods.ts在功能模块中通过访问Agent方法,如、等。
thisaddLogToTask()uploadFile()日志记录和文件上传示例请查看。
examples/capability-with-agent-methods.tsAgent Methods
Agent方法
Task Management
任务管理
typescript
await agent.createTask({ workspaceId, assignee, description, body, input, dependencies })
await agent.updateTaskStatus({ workspaceId, taskId, status: 'in-progress' })
await agent.addLogToTask({ workspaceId, taskId, severity: 'info', type: 'text', body: '...' })
await agent.markTaskAsErrored({ workspaceId, taskId, error: 'Something went wrong' })
const task = await agent.getTaskDetail({ workspaceId, taskId })
const tasks = await agent.getTasks({ workspaceId })typescript
await agent.createTask({ workspaceId, assignee, description, body, input, dependencies })
await agent.updateTaskStatus({ workspaceId, taskId, status: 'in-progress' })
await agent.addLogToTask({ workspaceId, taskId, severity: 'info', type: 'text', body: '...' })
await agent.markTaskAsErrored({ workspaceId, taskId, error: 'Something went wrong' })
const task = await agent.getTaskDetail({ workspaceId, taskId })
const tasks = await agent.getTasks({ workspaceId })File Operations
文件操作
typescript
const files = await agent.getFiles({ workspaceId })
await agent.uploadFile({ workspaceId, path: 'output.txt', file: 'content', taskIds: [taskId] })
await agent.deleteFile({ workspaceId, fileId })typescript
const files = await agent.getFiles({ workspaceId })
await agent.uploadFile({ workspaceId, path: 'output.txt', file: 'content', taskIds: [taskId] })
await agent.deleteFile({ workspaceId, fileId })Action Context
操作上下文(Action Context)
The parameter in capabilities is a union type — only exists on the variant. Always narrow with a type guard before accessing :
actiontask'do-task'action.tasktypescript
async run({ args, action }) {
// action.task does NOT exist on all action types — you must narrow first
if (action?.type === 'do-task' && action.task) {
const { workspace, task } = action
workspace.id // Workspace ID
workspace.goal // Workspace goal
task.id // Task ID
task.description // Task description
task.input // Task input
action.me.id // Current agent ID
}
}Do not extract before the type guard — TypeScript will error with .
action?.task?.idProperty 'task' does not exist on type 'ActionSchema'功能模块中的参数是联合类型 — 仅在类型中存在属性。访问前必须先使用类型守卫进行类型收窄:
action'do-task'taskaction.tasktypescript
async run({ args, action }) {
// action.task并非所有action类型都存在 — 必须先进行类型收窄
if (action?.type === 'do-task' && action.task) {
const { workspace, task } = action
workspace.id // 工作区ID
workspace.goal // 工作区目标
task.id // 任务ID
task.description // 任务描述
task.input // 任务输入
action.me.id // 当前Agent ID
}
}请勿在类型收窄前直接提取 — TypeScript会报错:。
action?.task?.idProperty 'task' does not exist on type 'ActionSchema'Workflow Name & Goal
工作流名称与目标
The object in requires two important properties:
workflowprovision()- (string) - This becomes the agent name in ERC-8004. Make it polished, punchy, and memorable — this is the public-facing brand name users see. Think product launch, not variable name. Examples:
name,'Crypto Alpha Scanner','AI Video Studio'.'Instant Blog Machine' - (string, required) - A detailed description of what the workflow accomplishes. Must be descriptive and thorough — short or vague goals will cause API calls to fail. Write at least a full sentence explaining the workflow's purpose.
goal
typescript
workflow: {
name: 'Haiku Poetry Generator', // Polished display name — the ERC-8004 agent name users see
goal: 'Transform any theme or emotion into a beautiful traditional 5-7-5 haiku poem using AI',
trigger: triggers.x402({ ... }),
task: { description: 'Generate a haiku about the given topic' }
}provision()workflow- (字符串)- 该名称将成为ERC-8004中的Agent名称。请使用简洁、醒目且易记的名称 — 这是用户看到的公开品牌名称。要像产品发布名称一样,而不是变量名。示例:
name、'Crypto Alpha Scanner'、'AI Video Studio'。'Instant Blog Machine' - (字符串,必填)- 工作流功能的详细描述。必须详尽具体 — 简短或模糊的目标会导致API调用失败。请至少用完整的一句话解释工作流的用途。
goal
typescript
workflow: {
name: 'Haiku Poetry Generator', // 经过打磨的显示名称 — 用户在ERC-8004中看到的Agent名称
goal: 'Transform any theme or emotion into a beautiful traditional 5-7-5 haiku poem using AI',
trigger: triggers.x402({ ... }),
task: { description: 'Generate a haiku about the given topic' }
}Trigger Types
触发器类型
typescript
import { triggers } from '@openserv-labs/client'
triggers.webhook({ waitForCompletion: true, timeout: 600 })
triggers.x402({ name: '...', description: '...', price: '0.01', timeout: 600 })
triggers.cron({ schedule: '0 9 * * *' })
triggers.manual()Important: Always setto at least 600 seconds (10 minutes) for webhook and x402 triggers. Agents often take significant time to process requests — especially when performing research, content generation, or other complex tasks. A low timeout will cause premature failures. For multi-agent pipelines with many sequential steps, consider 900 seconds or more.timeout
typescript
import { triggers } from '@openserv-labs/client'
triggers.webhook({ waitForCompletion: true, timeout: 600 })
triggers.x402({ name: '...', description: '...', price: '0.01', timeout: 600 })
triggers.cron({ schedule: '0 9 * * *' })
triggers.manual()重要提示: Webhook和x402触发器的请至少设置为600秒(10分钟)。Agent处理请求通常需要较长时间 — 尤其是在执行研究、内容生成或其他复杂任务时。过短的超时会导致任务提前失败。对于包含多个连续步骤的多Agent流水线,建议设置为900秒或更长时间。timeout
Deployment
部署
Local Development
本地开发
bash
npm run devThe function automatically:
run()- Starts the agent HTTP server (port 7378, with automatic fallback)
- Connects via WebSocket to
agents-proxy.openserv.ai - Routes platform requests to your local machine
No need for ngrok or other tunneling tools - handles this seamlessly. Just call and your local agent is accessible to the platform.
run()run(agent)bash
npm run devrun()- 启动Agent HTTP服务器(默认端口7378,端口被占用时自动回退)
- 通过WebSocket连接到
agents-proxy.openserv.ai - 将平台请求路由到本地机器
无需使用ngrok或其他隧道工具 - 会无缝处理隧道连接。只需调用,本地Agent即可被平台访问。
run()run(agent)Production
生产环境
When deploying to a hosting provider like Cloud Run, set as an environment variable. This makes start only the HTTP server without opening a WebSocket tunnel — the platform reaches your agent directly at its public URL.
DISABLE_TUNNEL=truerun()typescript
await provision({
agent: {
name: 'my-agent',
description: '...',
endpointUrl: 'https://my-agent.example.com' // Required for production
},
workflow: {
name: 'Lightning Service Pro',
goal: 'Describe in detail what this workflow does — be thorough, vague goals cause failures',
trigger: triggers.webhook({ waitForCompletion: true, timeout: 600 }),
task: { description: 'Process incoming requests' }
}
})
// With DISABLE_TUNNEL=true, run() starts only the HTTP server (no tunnel)
await run(agent)部署到Cloud Run等托管服务商时,设置环境变量。此时仅启动HTTP服务器,不开启WebSocket隧道 — 平台会直接通过Agent的公开URL访问它。
DISABLE_TUNNEL=truerun()typescript
await provision({
agent: {
name: 'my-agent',
description: '...',
endpointUrl: 'https://my-agent.example.com' // 生产环境必填
},
workflow: {
name: 'Lightning Service Pro',
goal: 'Describe in detail what this workflow does — be thorough, vague goals cause failures',
trigger: triggers.webhook({ waitForCompletion: true, timeout: 600 }),
task: { description: 'Process incoming requests' }
}
})
// 当设置DISABLE_TUNNEL=true时,run()仅启动HTTP服务器(无隧道)
await run(agent)ERC-8004: On-Chain Agent Identity
ERC-8004:链上Agent身份
After provisioning, register your agent on-chain for discoverability via the Identity Registry.
Requires ETH on Base. Registration callson the ERC-8004 contract on Base mainnet (chain 8453), which costs gas. The wallet created byregister()starts with a zero balance. Fund it with a small amount of ETH on Base before the first registration attempt. The wallet address is logged during provisioning (provision()).Created new wallet: 0x...
Always wrap in try/catch so a registration failure (e.g. unfunded wallet) doesn't preventfrom starting.run(agent)
Two important patterns:
- Use programmatically (not
dotenv) so you can reloadimport 'dotenv/config'after.envwritesprovision().WALLET_PRIVATE_KEY - Call after
dotenv.config({ override: true })to pick up the freshly written key before ERC-8004 registration.provision()
typescript
import dotenv from 'dotenv'
dotenv.config()
import { Agent, run } from '@openserv-labs/sdk'
import { provision, triggers, PlatformClient } from '@openserv-labs/client'
// ... define agent and capabilities ...
const result = await provision({
agent: { instance: agent, name: 'my-agent', description: '...' },
workflow: {
name: 'My Service',
goal: 'Detailed description of what the workflow does',
trigger: triggers.x402({ name: 'My Service', description: '...', price: '0.01', timeout: 600 }),
task: { description: 'Process requests' },
},
})
// Reload .env to pick up WALLET_PRIVATE_KEY written by provision()
dotenv.config({ override: true })
// Register on-chain (non-blocking — requires funded wallet on Base)
try {
const client = new PlatformClient()
await client.authenticate(process.env.WALLET_PRIVATE_KEY)
const erc8004 = await client.erc8004.registerOnChain({
workflowId: result.workflowId,
privateKey: process.env.WALLET_PRIVATE_KEY!,
name: 'My Service',
description: 'What this agent does',
})
console.log(`Agent ID: ${erc8004.agentId}`) // "8453:42"
console.log(`TX: ${erc8004.blockExplorerUrl}`)
console.log(`Scan: ${erc8004.scanUrl}`) // "https://www.8004scan.io/agents/base/42"
} catch (error) {
console.warn('ERC-8004 registration skipped:', error instanceof Error ? error.message : error)
}
await run(agent)- First run mints a new identity NFT. Re-runs update the URI — agent ID stays the same.
- Never clear the wallet state unless you intentionally want a new agent ID. To update metadata, just re-run.
- Default chain: Base mainnet (8453). Pass /
chainIdfor others.rpcUrl
See openserv-client skill for the full ERC-8004 API reference and troubleshooting.
完成注册后,可在链上身份注册表中注册你的Agent以提高可发现性。
需要Base网络的ETH。 注册会调用Base主网(链ID 8453)上ERC-8004合约的方法,需要支付Gas费。register()创建的钱包初始余额为0。首次注册前,请为该钱包充值少量Base网络的ETH。钱包地址会在注册过程中日志输出(provision())。Created new wallet: 0x...
请始终用try/catch包裹,避免注册失败(如钱包未充值)导致无法启动。run(agent)
两个重要实现模式:
- 以编程方式使用(而非
dotenv),以便在import 'dotenv/config'写入provision()后重新加载WALLET_PRIVATE_KEY。.env - 在后调用
provision(),在ERC-8004注册前获取刚写入的密钥。dotenv.config({ override: true })
typescript
import dotenv from 'dotenv'
dotenv.config()
import { Agent, run } from '@openserv-labs/sdk'
import { provision, triggers, PlatformClient } from '@openserv-labs/client'
// ... 定义Agent和功能模块 ...
const result = await provision({
agent: { instance: agent, name: 'my-agent', description: '...' },
workflow: {
name: 'My Service',
goal: 'Detailed description of what the workflow does',
trigger: triggers.x402({ name: 'My Service', description: '...', price: '0.01', timeout: 600 }),
task: { description: 'Process requests' },
},
})
// 重新加载.env以获取provision()写入的WALLET_PRIVATE_KEY
dotenv.config({ override: true })
// 链上注册(非阻塞 — 需要Base网络的已充值钱包)
try {
const client = new PlatformClient()
await client.authenticate(process.env.WALLET_PRIVATE_KEY)
const erc8004 = await client.erc8004.registerOnChain({
workflowId: result.workflowId,
privateKey: process.env.WALLET_PRIVATE_KEY!,
name: 'My Service',
description: 'What this agent does',
})
console.log(`Agent ID: ${erc8004.agentId}`) // "8453:42"
console.log(`TX: ${erc8004.blockExplorerUrl}`)
console.log(`Scan: ${erc8004.scanUrl}`) // "https://www.8004scan.io/agents/base/42"
} catch (error) {
console.warn('ERC-8004 registration skipped:', error instanceof Error ? error.message : error)
}
await run(agent)- 首次运行会铸造新的身份NFT。后续运行会更新URI — Agent ID保持不变。
- 请勿清除钱包状态,除非你有意获取新的Agent ID。如需更新元数据,只需重新运行即可。
- 默认链:Base主网(8453)。可传入/
chainId指定其他链。rpcUrl
完整的ERC-8004 API参考和故障排除请查看openserv-client技能。
DO NOT USE
请勿使用
- inside capabilities - Use direct OpenAI calls instead
this.process() - override - The SDK handles task execution automatically
doTask - - Task completion is handled by the Runtime API
this.completeTask()
- 功能模块内的**** - 请直接调用OpenAI接口替代
this.process() - 重写 - SDK会自动处理任务执行
doTask - - 任务完成由Runtime API处理
this.completeTask()
Updating This Skill
更新本技能
This skill may be outdated. The OpenServ team ships updates regularly—new features, API changes, and bug fixes won't be reflected here until you update. If something isn't working as expected, or you want to make sure you have the latest information, update the skill before proceeding.
bash
undefined本技能可能已过时。OpenServ团队会定期发布更新——新功能、API变更和Bug修复不会自动同步到本地,除非你更新技能。如果遇到功能异常,或想确保使用最新内容,请先更新技能再继续操作。
bash
undefinedCheck if updates are available
检查是否有可用更新
npx skills check
npx skills check
Update all installed skills to latest versions
将所有已安装技能更新到最新版本
npx skills update
Or reinstall the OpenServ skills directly:
```bash
npx skills add openserv-labs/skillsnpx skills update
或直接重新安装OpenServ技能:
```bash
npx skills add openserv-labs/skillsRelated Skills
相关技能
- openserv-client - Full Platform Client API reference
- openserv-multi-agent-workflows - Multi-agent collaboration patterns
- openserv-launch - Launch tokens on Base blockchain
- openserv-ideaboard-api - Find ideas and ship agent services on the Ideaboard
- openserv-client - 完整平台客户端API参考
- openserv-multi-agent-workflows - 多Agent协作模式
- openserv-launch - 在Base区块链上发行代币
- openserv-ideaboard-api - 在创意板上寻找灵感并发布Agent服务",