mcpField
A
agent-workspace

agent-workspace

github.com/serac-labs设计与 UI7757 次安装9 周前更新
代码仓库

关于 agent-workspace

面向 ServiceNow 履行团队的 Agent Workspace 配置技能,涵盖工作区布局、上下文侧面板、Agent Assist 和 UI 操作,并提供基于 ES5 的示例及 MCP 工具工作流。

主要功能

  1. 01配置工作区列表和表单
  2. 02设置上下文侧面板
  3. 03配置 Agent Assist
  4. 04创建工作区 UI 操作
  5. 05使用 MCP 工具查询和部署组件

适用场景

  1. 01搭建 IT 服务台工作区
  2. 02在记录旁展示事件上下文
  3. 03为 Agent 查找相似事件

MCP 相关

技能介绍了使用 snow_query_table、snow_artifact_manage 和 snow_execute_script 等 MCP 工具来检查、部署和测试工作区元素的工作流。

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/serac-labs/serac --skill agent-workspace

安装后在 Claude Code 中输入/agent-workspace确认已加载。

详细介绍

ServiceNow Agent 工作区

Agent 工作区提供现代、可配置的界面,提升履单人员的工作效率。

工作区架构

Workspace (sys_aw_workspace)
    ├── Lists (sys_aw_list)
    ├── Forms (sys_aw_form)
    ├── Related Lists
    ├── UI Actions
    └── Contextual Side Panel
        ├── Agent Assist
        ├── Related Records
        └── Activity Stream

关键表

表用途
sys_aw_workspace工作区定义
sys_aw_list列表配置
sys_aw_form表单配置
sys_aw_related_list相关列表配置
sys_aw_action工作区 UI 操作

工作区配置(ES5)

创建工作区
// Create workspace (ES5 ONLY!)
var workspace = new GlideRecord("sys_aw_workspace")
workspace.initialize()

// Basic info
workspace.setValue("name", "IT Service Desk Workspace")
workspace.setValue("title", "IT Service Desk")
workspace.setValue("description", "Workspace for IT service desk agents")

// Primary table
workspace.setValue("primary_table", "incident")

// URL path
workspace.setValue("url", "it-service-desk")

// Icon and branding
workspace.setValue("icon", "support")
workspace.setValue("color", "#0056B3")

// Default list
workspace.setValue("default_list", getListConfig("incident_active"))

// Enable features
workspace.setValue("agent_assist_enabled", true)
workspace.setValue("contextual_side_panel_enabled", true)
workspace.setValue("activity_stream_enabled", true)

workspace.insert()
工作区列表配置
// Create list configuration (ES5 ONLY!)
var list = new GlideRecord("sys_aw_list")
list.initialize()

list.setValue("name", "My Active Incidents")
list.setValue("table", "incident")
list.setValue("workspace", workspaceSysId)

// Filter
list.setValue("filter", "active=true^assigned_to=javascript:gs.getUserID()")

// Columns
list.setValue("columns", "number,short_description,priority,state,caller_id,opened_at")

// Sort
list.setValue("order_by", "priority")
list.setValue("order_by_desc", false)

// Row actions
list.setValue("show_row_actions", true)

// Grouping (optional)
list.setValue("group_by", "priority")

list.insert()
工作区表单配置
// Create form configuration (ES5 ONLY!)
var form = new GlideRecord("sys_aw_form")
form.initialize()

form.setValue("name", "Incident Form")
form.setValue("table", "incident")
form.setValue("workspace", workspaceSysId)

// Form sections
var sections = [
  {
    name: "Details",
    columns: 2,
    fields: ["number", "state", "caller_id", "opened_at", "short_description", "priority"],
  },
  {
    name: "Assignment",
    columns: 2,
    fields: ["assignment_group", "assigned_to", "escalation"],
  },
  {
    name: "Resolution",
    columns: 1,
    fields: ["resolution_code", "close_notes"],
    condition: "state=6^ORstate=7", // Only show for resolved/closed
  },
]

form.setValue("sections", JSON.stringify(sections))

// Related lists
form.setValue("related_lists", "incident.task_sla,incident.sys_attachment")

// Enable Agent Assist
form.setValue("agent_assist_enabled", true)

form.insert()

上下文侧边面板(ES5)

配置侧边面板
// Side panel configuration (ES5 ONLY!)
var panel = new GlideRecord("sys_aw_contextual_side_panel")
panel.initialize()

panel.setValue("workspace", workspaceSysId)
panel.setValue("table", "incident")
panel.setValue("name", "Incident Context")

// Tabs
var tabs = [
  {
    id: "agent_assist",
    label: "Agent Assist",
    icon: "lightbulb-outline",
    component: "agent-assist",
  },
  {
    id: "caller_info",
    label: "Caller Info",
    icon: "user",
    component: "custom-caller-info",
  },
  {
    id: "related",
    label: "Related Records",
    icon: "link",
    component: "related-records",
  },
  {
    id: "activity",
    label: "Activity",
    icon: "history",
    component: "activity-stream",
  },
]

panel.setValue("tabs", JSON.stringify(tabs))
panel.setValue("default_tab", "agent_assist")

panel.insert()
自定义面板组件(ES5)
// Widget for side panel (ES5 ONLY!)
// Server Script
;(function () {
  // Get current record from context
  var recordSysId = input.sys_id
  var tableName = input.table

  if (tableName === "incident" && recordSysId) {
    var gr = new GlideRecord("incident")
    if (gr.get(recordSysId)) {
      // Get caller information
      data.caller = {
        name: gr.caller_id.getDisplayValue(),
        email: gr.caller_id.email.getDisplayValue(),
        phone: gr.caller_id.phone.getDisplayValue(),
        location: gr.caller_id.location.getDisplayValue(),
        vip: gr.caller_id.vip.getDisplayValue() === "true",
      }

      // Get caller's open incidents
      data.openIncidents = []
      var incidents = new GlideRecord("incident")
      incidents.addQuery("caller_id", gr.getValue("caller_id"))
      incidents.addQuery("active", true)
      incidents.addQuery("sys_id", "!=", recordSysId)
      incidents.orderByDesc("opened_at")
      incidents.setLimit(5)
      incidents.query()

      while (incidents.next()) {
        data.openIncidents.push({
          sys_id: incidents.getUniqueValue(),
          number: incidents.getValue("number"),
          short_description: incidents.getValue("short_description"),
          state: incidents.state.getDisplayValue(),
        })
      }
    }
  }
})()

Agent 辅助(ES5)

配置 Agent 辅助
// Agent Assist configuration (ES5 ONLY!)
var config = new GlideRecord("sys_aw_agent_assist_config")
config.initialize()

config.setValue("workspace", workspaceSysId)
config.setValue("table", "incident")
config.setValue("name", "Incident Agent Assist")
config.setValue("active", true)

// Recommendations sources
config.setValue("show_knowledge", true)
config.setValue("show_similar_incidents", true)
config.setValue("show_solutions", true)
config.setValue("show_macros", true)

// Knowledge search configuration
config.setValue("knowledge_bases", kbSysIds) // Comma-separated
config.setValue("knowledge_search_fields", "short_description,description")

config.insert()
相似记录脚本(ES5)
// Find similar incidents for Agent Assist (ES5 ONLY!)
var SimilarIncidentFinder = Class.create()
SimilarIncidentFinder.prototype = {
  initialize: function () {},

  /**
   * Find similar resolved incidents
   */
  findSimilar: function (incidentSysId) {
    var current = new GlideRecord("incident")
    if (!current.get(incidentSysId)) {
      return []
    }

    var similar = []
    var keywords = this._extractKeywords(current.getValue("short_description"))

    // Search resolved incidents
    var gr = new GlideRecord("incident")
    gr.addQuery("state", "IN", "6,7") // Resolved or Closed
    gr.addQuery("sys_id", "!=", incidentSysId)

    // Match by category
    if (current.category) {
      gr.addQuery("category", current.getValue("category"))
    }

    // Match by CI
    if (current.cmdb_ci) {
      gr.addOrCondition("cmdb_ci", current.getValue("cmdb_ci"))
    }

    // Keyword matching
    for (var i = 0; i < keywords.length && i < 3; i++) {
      gr.addOrCondition("short_description", "CONTAINS", keywords[i])
    }

    gr.setLimit(10)
    gr.orderByDesc("resolved_at")
    gr.query()

    while (gr.next()) {
      var score = this._calculateSimilarity(current, gr)
      if (score > 0.3) {
        similar.push({
          sys_id: gr.getUniqueValue(),
          number: gr.getValue("number"),
          short_description: gr.getValue("short_description"),
          resolution_code: gr.resolution_code.getDisplayValue(),
          close_notes: gr.getValue("close_notes"),
          score: Math.round(score * 100),
        })
      }
    }

    // Sort by similarity score
    similar.sort(function (a, b) {
      return b.score - a.score
    })

    return similar.slice(0, 5)
  },

  _extractKeywords: function (text) {
    var stopWords = ["the", "is", "at", "which", "on", "a", "an", "and", "or", "not", "to", "for"]
    var words = text.toLowerCase().split(/\s+/)
    var keywords = []

    for (var i = 0; i < words.length; i++) {
      var word = words[i].replace(/[^a-z0-9]/g, "")
      if (word.length > 3 && stopWords.indexOf(word) === -1) {
        keywords.push(word)
      }
    }

    return keywords
  },

  _calculateSimilarity: function (source, target) {
    var score = 0

    // Category match
    if (source.getValue("category") === target.getValue("category")) {
      score += 0.3
    }

    // Subcategory match
    if (source.getValue("subcategory") === target.getValue("subcategory")) {
      score += 0.2
    }

    // CI match
    if (source.getValue("cmdb_ci") === target.getValue("cmdb_ci")) {
      score += 0.3
    }

    // Keyword overlap
    var sourceKeywords = this._extractKeywords(source.getValue("short_description"))
    var targetKeywords = this._extractKeywords(target.getValue("short_description"))
    var overlap = 0

    for (var i = 0; i < sourceKeywords.length; i++) {
      if (targetKeywords.indexOf(sourceKeywords[i]) !== -1) {
        overlap++
      }
    }

    if (sourceKeywords.length > 0) {
      score += 0.2 * (overlap / sourceKeywords.length)
    }

    return score
  },

  type: "SimilarIncidentFinder",
}

工作区 UI 操作(ES5)

创建工作区操作
// Create workspace-specific UI action (ES5 ONLY!)
var action = new GlideRecord("sys_aw_action")
action.initialize()

action.setValue("name", "Quick Resolve")
action.setValue("label", "Quick Resolve")
action.setValue("workspace", workspaceSysId)
action.setValue("table", "incident")

// Action type
action.setValue("action_type", "form") // form, list, both
action.setValue("order", 100)

// Condition
action.setValue("condition", "current.active == true && current.state != 6")

// Client action (opens modal)
action.setValue(
  "client_script",
  "function onClick() {\n" +
    "    spModal.open({\n" +
    '        title: "Quick Resolve",\n' +
    '        widget: "quick-resolve-modal",\n' +
    "        widgetInput: { table: g_form.getTableName(), sys_id: g_form.getUniqueValue() }\n" +
    "    }).then(function(result) {\n" +
    "        if (result) {\n" +
    '            g_form.setValue("state", 6);\n' +
    '            g_form.setValue("resolution_code", result.code);\n' +
    '            g_form.setValue("close_notes", result.notes);\n' +
    "            g_form.save();\n" +
    "        }\n" +
    "    });\n" +
    "}",
)

// Icon and style
action.setValue("icon", "check-circle")
action.setValue("button_class", "btn-success")

action.insert()

MCP 工具集成

可用工具
工具用途
snow_artifact_manage查找工作区小组件(action: "find")
snow_query_table查询工作区表
snow_artifact_manage部署工作区小组件(action: "create")
snow_execute_script测试工作区脚本
示例工作流
// 1. Find workspaces
await snow_query_table({
  table: "sys_aw_workspace",
  query: "active=true",
  fields: "name,title,primary_table,url",
})

// 2. Get list configurations
await snow_query_table({
  table: "sys_aw_list",
  query: "workspace.name=IT Service Desk Workspace",
  fields: "name,table,filter,columns",
})

// 3. Test similar incident finder
await snow_execute_script({
  script: `
        var finder = new SimilarIncidentFinder();
        var similar = finder.findSimilar('incident_sys_id');
        gs.info('Found: ' + similar.length);
    `,
})

最佳实践

  1. 基于角色 - 针对特定角色进行设计
  2. 高效列表 - 优化筛选器和列
  3. 上下文面板 - 便于访问相关信息
  4. Agent 辅助 - 启用知识库/相似记录
  5. 操作 - 简化常见任务
  6. 性能 - 延迟加载组件
  7. 适配移动设备 - 测试响应式布局
  8. 仅限 ES5 - 不使用现代 JavaScript 语法

评论

支持 Markdown