扩展技能(ExtSkill)示例 · JS / 浏览器

扩展技能(ExtSkill)示例 · JS / 浏览器

⚠️ 尚未上线:本能力为 v1.5.0-SNAPSHOT 开发中特性,接口与协议可能调整,正式发布前请勿用于生产环境。

v1.5.0 新增。本页给出浏览器端扩展技能的最小可读 TypeScript 示例,展示 query(拉取清单)与 invoke(触发执行,两段回执)全流程。

最小示例:查询 + 触发

ts
import { createAgentSdk, AgentEventType } from 'linksoul-agentsdk';

const appId = '您通过灵心开放平台创建的应用获取的appId';
const agentId = '您通过灵心平台创建的二开智能体获取的agentId';
const upstreamUrl = 'wss://open.agibot.com/api/V1/open-portal/app/wss/agent-sdk';

let sdk = createAgentSdk(appId, {
  authProvider: async () => {
    const res = await fetch('/api/ws-auth', {
      method: 'POST',
      headers: { 'content-type': 'application/json' },
      body: JSON.stringify({ url: upstreamUrl, appId }),
    });
    return { url: (await res.json()).url };
  },
  reconnect: true,
});

// 监听下行结果
sdk.on('message', ({ data }) => {
  let msg;
  try { msg = JSON.parse(typeof data === 'string' ? data : ''); } catch { return; }
  switch (msg.type) {
    case AgentEventType.ExtSkillQueryResult:
      if (msg.code === 0) {
        console.log('可用技能清单:', msg.output);
        // 拿到清单后触发某个技能
        doInvoke();
      } else {
        console.error('query 失败:', msg.code, msg.message);
      }
      break;
    case AgentEventType.ExtSkillInvokeAck:
      console.log('invoke 已受理, code =', msg.code, ', eventId =', msg.eventId);
      break;
    case AgentEventType.ExtSkillInvokeResult:
      if (msg.code === 0) {
        console.log('技能执行结果:', msg.output);
      } else {
        console.error('invoke 执行失败:', msg.code, msg.message);
      }
      break;
  }
});

await sdk.connect();

// 创建 ExtSkill 会话
const session = sdk.createExtSkillSession({ agentId });
console.log('requestId =', session.requestId);

// 查询可用技能清单
const queryEventId = session.query();
console.log('query eventId =', queryEventId);

// 触发技能执行
function doInvoke() {
  const invokeEventId = session.invoke('skill_weather', {
    city: 'shanghai',
    days: 3,
  });
  console.log('invoke eventId =', invokeEventId);
}

带错误处理的完整示例

ts
import { createAgentSdk, AgentEventType } from 'linksoul-agentsdk';
import type { ExtSkillQueryResult, ExtSkillInvokeAck } from 'linksoul-agentsdk';

const appId = '<appId>';
const agentId = '<agentId>';
const upstreamUrl = 'wss://open.agibot.com/api/V1/open-portal/app/wss/agent-sdk';

const sdk = createAgentSdk(appId, {
  authProvider: async () => {
    const res = await fetch('/api/ws-auth', {
      method: 'POST',
      headers: { 'content-type': 'application/json' },
      body: JSON.stringify({ url: upstreamUrl, appId }),
    });
    if (!res.ok) throw new Error(`auth failed: ${res.status}`);
    return { url: (await res.json()).url };
  },
  reconnect: true,
});

sdk.on('connected', () => {
  console.log('SDK 已连接,可以发送请求');
});

sdk.on('disconnected', ({ willReconnect }) => {
  console.log('SDK 断开, willReconnect =', willReconnect);
});

sdk.on('message', ({ data }) => {
  let msg: Record<string, unknown>;
  try { msg = JSON.parse(typeof data === 'string' ? data : ''); } catch { return; }

  switch (msg.type) {
    case AgentEventType.ExtSkillQueryResult: {
      const result = msg as unknown as ExtSkillQueryResult;
      if (result.code === 0) {
        console.log('技能清单:', result.output);
      } else {
        console.error(`query 失败 [code=${result.code}]:`, result.message);
      }
      break;
    }
    case AgentEventType.ExtSkillInvokeAck: {
      const ack = msg as unknown as ExtSkillInvokeAck;
      if (ack.code !== 0) {
        console.error(`invoke 受理失败 [code=${ack.code}]:`, ack.message);
      }
      break;
    }
    case AgentEventType.ExtSkillInvokeResult: {
      const result = msg as unknown as ExtSkillQueryResult; // 形状相同
      if (result.code === 0) {
        console.log('执行输出:', result.output);
      } else {
        console.error(`invoke 执行失败 [code=${result.code}]:`, result.message);
      }
      break;
    }
  }
});

await sdk.connect();

const session = sdk.createExtSkillSession({ agentId });

// 安全调用:确保连接后再发送
try {
  session.query();
} catch (err) {
  console.error('发送失败(可能未连接):', err);
}

常见改写要点

想做改哪里
只拉清单不触发只调 session.query(),在 message 里处理 ExtSkillQueryResult 即可
触发不同技能换 invoke 的第一个参数 extSkillId
传结构化入参invoke('skill_id', { key: value });wire 上以 input 对象原样下发
只关心最终结果ExtSkillInvokeAck 只记日志;真正输出在 ExtSkillInvokeResult 的 output(code === 0 时)
感知 SDK 断连sdk.on('disconnected') / sdk.on('connected');未连接时 send 抛错,不会静默丢失
多智能体并行每个 agentId 建一个 ExtSkillSession(各自独立 requestId);复用同一 AgentSdk 连接
区分同一智能体的多次会话手动传入 requestId:sdk.createExtSkillSession({ agentId, requestId: 'my_session_1' })