前提条件
说明:
已创建智能体。
已为智能体部署服务访问地址。
智能体定义字典
上下文变量 ContextVariable
定义结构:
/*** 上下文变量对象* 每一种上下文对象对应一种具体数据结构*/interface ContextVariable{// 上下文类型,全局唯一,决定 value 的数据结构name: string// 上下文解析器参数,Agent 发布时定义,Client 可根据此策略调整上下文收集细节,如文本截取长度等resolverOptions: any;}
用户自定义 prompt:
name:
userInputPrompt
描述:用户对话时手动输入的原始内容。
解析参数:无
采集结果:
interface UserInputPromptContextVariable{name: "userInputPrompt";value: string;}
用户信息:
name:
userInfo
描述:当前用户相关信息。
解析参数:无
采集结果:
interface UserInfoContextVariable{name: "userInfo";value: {id: string; // 用户 IDname: string; // 用户英文名nickname: string; // 用户昵称}}
工作空间版本控制信息:
name:
vcs
描述:当前工作空间对应的版本控制信息。
解析参数:无
采集结果:
interface VCSContextVariable{name: "vcs";value: {type: string; // vcs 类型,如 gitbranchName: string; // 分支名upstreamBranchName: string; // 远端分支名remoteUrl: string; // 远端仓库地址remotes: string[]; // 远端仓库列表}}
知识库:
name:
knowledgeBase
描述:知识库列表。
解析参数:
interface KnowledgeBaseResolverOptions{knowledgeBaseIds?: string[]; // 目标知识库 id 列表recallByAgent?: boolean; // 是否由 Agent 执行召回,为 false 时,由客户端执行召回动作,此时上报的上下文中应包含 references 属性内容}
采集结果:
interface KnowledgeBaseContextVariable{name: "knowledgeBase";value: {knowledgeBaseId: string; // 知识库 idreferences?: {score: number; // 检索得分chunk: string; // 句向量对应的文本块metadata: {fileName: string; // 文件名称,例如:/指南/开发指南.mdsourceType: 'doc' | 'code'; // 文件类型 doc or codesource: string; // 文件来源,例如:/home/xxx/xxx.py 或者 github.com/xxx/xxx/xxx/xxx.pystartPos?: number; // chunk 开始行号(可为空)endPos?: number; // chunk 结束行号(可为空)};}[]}[];}
激活的编辑器内容:
name:
activeEditor
描述:激活的编辑器内容。
解析参数:
interface ActiveEditorResolverOptions{contentBeforeSelectionLines?: number; // 选区前文本行数,为空时不获取,为0时仅获取当前行选区前的内容contentAfterSelectionLines?: number; // 选区后文本行数,为空时不获取,为0时仅获取当前行选区后的内容}
采集结果:
interface ActiveEditorContextVariable{name: "activeEditor";value: {filePath: string; // 文件路径language: string; // 内容语言:java/javascript/...content: string; // 文件内容cursorOffset: number; // 光标位置,从0开始startLine: number; // 起始行,从1开始startColumn: number; // 起始列,从1开始endLine: number; // 结束行,从1开始endColumn: number; // 结束列,从1开始selectedRanges: {content: string; // 选中文件内容startLine: number; // 起始行,从1开始startColumn: number; // 起始列,从1开始endLine: number; // 结束行,从1开始endColumn: number; // 结束列,从1开始contentBeforeSelection?: string; // 选区前文本contentAfterSelection?: string; // 选区后文本}[];visibleRange: {content: string; // 可视区文件内容startLine: number; // 起始行,从1开始startColumn: number; // 起始列,从1开始endLine: number; // 结束行,从1开始endColumn: number; // 结束列,从1开始}};}
全局提示词:
name:
globalPrompts
描述:全局提示词,作为限定规则置于用户问题之前。
解析参数:无
采集结果:
interface GlobalPromptsContextVariable{name: "globalPrompts";value: {role: string; // 角色content: string; // 全局提示词内容}[];}
包管理文件:
name:
packageFiles
描述:项目包管理文件列表。
解析参数:
interface PackageFilesResolverOptions{maximumNumber: number; // 最大文件个数}
采集结果:
interface PackageFilesContextVariable{name: "packageFiles";value: [{filepath: string; // 文件本地路径content: string; // 文件内容}];}
交互动作 Action
支持确认按钮和树状结构两种交互动作的智能体下发。
interface ConfirmAction{action: "confirm";state: any;data: {title: string; // 按钮文本message: string; // 描述信息command?: string; // 可选,有值时以此 command 发起标准 Agent 对话请求,不再发送 Action 反馈请求prompt?: string; // 可选,有值时以此 prompt 发起标准 Agent 对话请求,不再发送 Action 反馈请求}}
interface TreeAction{action: "tree";state: any;data: {treeId: string;// 树 ID,agent 下唯一rootPId: string; // 根节点的父 ID// 树节点列表nodes : {id: string; // 节点 IDpId: string; // 父节点 IDtype: string; // 节点类型name: string; // 节点名称description: string;// 描述labels : string[];// 标签列表}[];// 树节点支持的操作列表nodeActions: {nodeType: string; // 树节点类型nodeAction : "expand" | "click"; // 树节点操作类型,展开、点击command?: string; // 可选,有值时以此 command 发起标准 Agent 对话请求,不再发送 Action 反馈请求prompt?: string; // 可选,有值时以此 prompt 发起标准 Agent 对话请求,不再发送 Action 反馈请求promptByNodeProperty?: string; // 可选,有值时以此 node 的某个属性发起标准 Agent 对话请求,不再发送 Action 反馈请求}[];}}
示例
智能体服务的实现不限语言,下面以 javascript 为例,实现智能体接收请求、处理参数、请求对话模型、响应数据的过程。
const http = require('http');// 创建HTTP服务器const server = http.createServer(async (req, res) => {const {agent, // agent 名称command, // command 名称agentDefinition, // agent 原始定义contextVariables // 上下文变量列表} = await parseBody(req);// 从请求中获取bodyconsole.log('agent:', agent);console.log('command:', command);// 获取上下文信息const userInfo = contextVariables?.find(ctx => ctx.name === 'userInfo') || {};// 设置响应头,指定内容类型为 text/event-stream,这是 SSE 所需的res.setHeader('Content-Type', 'text/event-stream');res.setHeader('Cache-Control', 'no-cache');res.setHeader('Connection', 'keep-alive');// 生成消息ID,在一次响应中需要保持唯一const messageId = Date.now();// 根据 command 执行不同业务if (command == 'default') {// 默认指令推送欢迎信息文本res.write(createMessageChunk(messageId, `你好,${userInfo.value?.nickname} !\\n`));res.write(createMessageChunk(messageId, `欢迎访问 Agent hello !`));// 推送一个交互按钮 actionres.write(createActionChunk('confirm',{},{title: '获取版本号',command: 'version',prompt: ''}));// 发送响应数据结束标识res.write(`data: [Done]\\n`);// 结束响应res.end();} else if (command == 'version') {// 版本指令返回版本信息res.write(createMessageChunk(messageId, `当前版本是 v1.0.0 \\n`));// 发送响应数据结束标识res.write(`data: [Done]\\n`);// 结束响应res.end();} else if (command == 'xxx') {// 如果是 xxx 指令,则发起对话请求// 获取配置的消息模版const messageTemplates = getMessageTemplates(command, agentDefinition);// 根据消息模板和上下文变量列表,拼装 prompt 消息列表const messages = [{role : 'system',content : 'xxxxx'},{role : 'user',content : 'xxxxx'}];const baseRequestConfig = {headers: cropHeadersreq.headers), // 复用原请求头中的认证信息responseType: 'stream',timeout: 180 * 1000,};const payload = {...req.body, // 附加原请求中的其他参数contextVariables: null, // 移除messageTemplates: null, // 移除agentDefinition: null, // 移除modelConfiguration: null, // 移除messages, // 处理后的消息列表stream: true, // 流式};// 请求平台的模型对话接口const fetcher = axios.create({baseURL: CPILOT_SERVER_ENDPOINT, // 平台 API 地址timeout: 3000 * 1000,});const { stream, headers } = await fetcher.post('/chat/completions', payload, baseRequestConfig);// 直接将请求返回的 res 对接 pipe 给客户端响应输出流中res.writeHead(200);stream.pipe(res);}});// 创建一个文本消息 chunkfunction createMessageChunk(id, content) {const msg = JSON.stringify({id: id,model: 'happy',object: '',created: Date.now() / 1000,choices: [{ index: 0, delta: { role: 'assistant', content: content }, logprobs: null, finish_reason: '' }],usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 }});return `data: ${msg}\\n\\n`;}// 创建一个 action chunkfunction createActionChunk(action, state, data) {const msg = JSON.stringify({action,state,data});return `event: copilot_action\\ndata: ${msg}\\n\\n`;}// 解析请求体async function parseBody(req) {return new Promise((resolve, reject) => {let body = '';req.on('data', chunk => {body += chunk.toString();});req.on('end', () => {if (req.headers['content-type'] === 'application/json') {try {const data = JSON.parse(body);resolve(data); // 这里可以访问到解析后的 JSON 数据} catch (error) {reject(error);}} else {reject(new Error('content-type error'));}});});}// 从 agent 定义中获取配置的消息模板function getMessageTemplates(command, agent) {const messageTemplates = [];agent?.commands?.find(commandDefinition => {if (commandDefinition.name === command) {messageTemplates.push(...commandDefinition?.messageTemplates);}});return messageTemplates;}// 复制原请求头function cropHeaders(headers) {const croppedHeaders = JSON.parse(JSON.stringify(headers));delete croppedHeaders['Host'];delete croppedHeaders['Host'.toLowerCase()];delete croppedHeaders['Content-Length'];delete croppedHeaders['Content-Length'.toLowerCase()];return croppedHeaders;}// 监听端口const PORT = 18080;server.listen(PORT, () => {console.log(`Server is running at http://localhost:${PORT}`);});