技能插件开发
概述
在SERVICEME中,技能插件是指Copilot可选的能力,除了系统自带插件以外,还可以通过开发的方式对技能插件进行扩展,为Copilot赋予更多的能力。
系统内置技能插件
编号 | 插件 | 插件类型 | 用途 |
---|---|---|---|
SearchKnowledgebase | 搜索知识库 | API插件 | 提供私域知识的 检索功能 |
ImageGeneration | 生成图片 | API插件 | 根据文本描述生成图片 |
Browsing | 浏览网络 | API插件 | 提供网络浏览以搜索实时信息 |
Charts | 图表 | API插件 | 根据所提供的数据生成对应的图表,支持柱状图、折线图、饼图 |
DocumentReader | 阅读文档 | API插件 | 在单文档或者选定文档的情况下,提供文档的摘要信息 |
QuestionContact | 问题联系人 | API插件 | 根据问题所属的领域,返回对应领域的联系人 |
类型说明
类型 | 说明 |
---|---|
API插件 | 技能插件表现为一个后端API,由Copilot负责根据上下文填充API的输入参数,并对API的返回值进行解析 |
API技能插件
认证方式支持
目前仅支持固定Header认证或者无需认证的接口调用。
类型 | 说明 |
---|---|
固定Header | 通过请求头指定固定的密钥进行接口认证,类似于OpenAI api_key的形式 |
插件开发
为了便于理解这个过程,我们以一个通过时区编码查询时区的API 插件作为例子。
第一步:先提供一个能够根据时区编码返回时区信息的API。
API示例
- .NET (Web API)
- Java (Spring Boot)
- Python (Fast API)
- Javascript (Next.js)
using Microsoft.AspNetCore.Mvc;
using System;
namespace WorldTimeApi.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class TimeController : ControllerBase
{
[HttpGet("current/zone")]
public IActionResult GetTimeByZone([FromQuery] string timeZone)
{
if (string.IsNullOrEmpty(timeZone))
{
return BadRequest("Missing required parameter: timeZone");
}
try
{
// 查找时区信息
var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(timeZone);
// 获取指定时区的当前时间
var currentTime = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, timeZoneInfo);
// 返回结果
return Ok(new
{
TimeZone = timeZone,
CurrentTime = currentTime.ToString("yyyy-MM-ddTHH:mm:ss")
});
}
catch (TimeZoneNotFoundException)
{
return NotFound("Time zone not found");
}
catch (InvalidTimeZoneException)
{
return BadRequest("Invalid time zone");
}
catch (Exception ex)
{
return StatusCode(500, $"Internal server error: {ex.Message}");
}
}
}
}
package com.example.worldtime;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
@RestController
public class TimeController {
@GetMapping("/api/Time/current/zone")
public Object getTimeByZone(@RequestParam String timeZone) {
try {
ZoneId zoneId = ZoneId.of(timeZone);
ZonedDateTime currentTime = ZonedDateTime.ofInstant(Instant.now(), zoneId);
return new TimeResponse(timeZone, currentTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
} catch (Exception e) {
return "Invalid time zone: " + e.getMessage();
}
}
static class TimeResponse {
public String timeZone;
public String currentTime;
public TimeResponse(String timeZone, String currentTime) {
this.timeZone = timeZone;
this.currentTime = currentTime;
}
}
}
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from datetime import datetime
import pytz
app = FastAPI()
class TimeResponse(BaseModel):
timeZone: str
currentTime: str
@app.get("/api/Time/current/zone", response_model=TimeResponse)
async def get_time_by_zone(timeZone: str):
if not timeZone:
raise HTTPException(status_code=400, detail="Missing required parameter: timeZone")
try:
tz = pytz.timezone(timeZone)
current_time = datetime.now(tz)
return TimeResponse(
timeZone=timeZone,
currentTime=current_time.strftime('%Y-%m-%dT%H:%M:%S')
)
except pytz.UnknownTimeZoneError:
raise HTTPException(status_code=404, detail="Time zone not found")
except Exception as e:
raise HTTPException(status_code=500, detail=f"Internal server error: {str(e)}")
import moment from 'moment-timezone';
export default function handler(req, res) {
const { timeZone } = req.query;
if (!timeZone) {
return res.status(400).json({ error: 'Missing required parameter: timeZone' });
}
try {
const currentTime = moment().tz(timeZone).format('YYYY-MM-DDTHH:mm:ss');
res.status(200).json({
timeZone: timeZone,
currentTime: currentTime,
});
} catch (e) {
res.status(404).json({ error: 'Time zone not found' });
}
}
第二步:将这个插件注册到系统
添加技能插件,填写以下信息
字段说明
- Code,插件的编码,插件在系统中的唯一标识,尽量采用有意义的描述。
- Name,插件的显示名,Copilot在思考与执行过程中会显示它所采用的插件名,注意多语言的完整性。
- Description,插件的描述,用于说明插件的作用,将在Copilot添加技能插件的界面显示。
- Request Header,用于输入一些调用插件必要的Header参数,例如API_KEY
- 【重要】OpenAPI / Swagger Schema ( JSON ),API主体的描述,这里的信息决定了Copilot是否能够很好的理解这个技能应该在什么样的情况下调用,以及每个参数应该如何被填充。
OpenAPI Schema (JSON) 示例
Get time zone
{
"Openapi": "3.0.0",
"Info": {
"Title": "World Time",
"Description": "Getting world time",
"Version": "v1.0.0"
},
"Servers": [
{
"Url": "https://www.timeapi.io"
}
],
"Paths": {
"/api/Time/current/zone": {
"Get": {
"OperationId": "zone",
"Description": "Get world time by timezone name",
"Deprecated": false,
"Parameters": [
{
"Name": "timeZone",
"In": "query",
"Required": true,
"Description": "Full IANA time zone names.",
"Schema": {
"Type": "string"
}
}
]
}
}
}
}
注意
- Info/Title,此处填写的插件标题,帮助Copilot理解这个插件的作用。
- Paths/[api path]/Get/OperationId,可以在Copilot Prompt中使用反引号`%OperationId%`明确特定场景下调用的API,具体可参考下面第四步的示例。
- Paths/[api path]/Get/Description,帮忙Copilot理解,影响Copilot在什么情况下进行特定API的调用。
- Paths/[api path]/Get/Parameters,注意与实际的API保持一致,比如这里是query,api就应该从query中获取参数。如果这里是其他类型(如json body),则api也要保持一致。参数名称的说明有助于提高填充的成功率。
- Servers/Url,是API的BaseURL,必须填写正确才能成功调用
- 【可选】插件配置完成后,可以通过DEBUG功能进行调试
第三步:为Copilot添加技能插件
在Copilot设置界面,通过[技能插件]选项卡进行插件添加。
保存设置后,与专家对话进行测试。
第四步:【可选】在Copilot的Prompt处,添加必要的说明,引导Copilot更好的执行这个技能。
示例
引导浏览器插件在特定站点下进行搜索
### Skill 3: Web Searching
- You can access web site by `Browsering` toolkit and looking for HR infomation from following sites: (site:*.linkedin.com; site:*.zhipin.com)
- Using result to fulfill users request and list up reference link at end of your response.
注:大多数情况下,Copilot会根据OpenAPI Schema中信息自行判断执行时机以及参数填充。