onUserPromptSubmitted 在用户消息提交后、AI 处理前触发,可以注入额外上下文、扩展用户的快捷指令、过滤敏感词汇、追加用户配置信息。返回 null 不修改,返回 additionalContext 追加上下文,返回 modifiedPrompt 替换原始内容。

GitHub Copilot SDK onUserPromptSubmitted Hook:增强和过滤用户输入

基本结构

const session = await createSession({
  hooks: {
    onUserPromptSubmitted: async (context) => {
      const { prompt, sessionId, userId } = context
      
      // 处理逻辑
      return null  // 不修改
    }
  }
})

自动注入上下文

最常见的用途——在每次用户提问时自动补充项目信息:

onUserPromptSubmitted: async ({ prompt, userId }) => {
  const userPrefs = await getUserPreferences(userId)  // 可以是异步的
  
  return {
    additionalContext: `
用户偏好:
- 代码语言:${userPrefs.preferredLanguage}
- 框架:${userPrefs.frameworks.join(', ')}
- 回答语言:中文
当前工作目录:${process.cwd()}
    `.trim()
  }
}

扩展快捷指令

实现自定义的 Prompt 快捷方式:

const shortcuts: Record<string, string> = {
  '/review': '请对我接下来描述的代码进行全面审查,关注安全性、性能和可维护性',
  '/fix': '请修复以下 Bug,只改最小范围的代码,不做额外重构',
  '/test': '请为以下代码生成单元测试,使用 Vitest,覆盖正常路径和边界情况',
  '/doc': '请为以下代码生成 JSDoc 注释,重点说明参数和返回值的含义'
}

onUserPromptSubmitted: async ({ prompt }) => {
  const firstWord = prompt.trim().split(' ')[0]
  const expansion = shortcuts[firstWord]
  
  if (expansion) {
    return {
      // 替换快捷指令为完整说明
      modifiedPrompt: prompt.replace(firstWord, expansion)
    }
  }
  
  return null
}

过滤敏感内容

防止用户意外输入敏感信息:

onUserPromptSubmitted: async ({ prompt }) => {
  // 检测可能的 API Key 格式
  const sensitivePatterns = [
    /sk-[a-zA-Z0-9]{48}/,     // OpenAI API Key
    /ghp_[a-zA-Z0-9]{36}/,    // GitHub Token
    /eyJ[a-zA-Z0-9]/          // JWT Token
  ]
  
  const hasSensitive = sensitivePatterns.some(pattern => pattern.test(prompt))
  if (hasSensitive) {
    // 告知 AI 检测到敏感内容,并提醒用户
    return {
      modifiedPrompt: '[系统提示:检测到疑似凭证信息,已过滤] ' + 
                      prompt.replace(/sk-[a-zA-Z0-9]+/g, '[REDACTED]')
                            .replace(/ghp_[a-zA-Z0-9]+/g, '[REDACTED]')
    }
  }
  
  return null
}

添加结构化元数据

让 AI 了解请求的来源和目的:

onUserPromptSubmitted: async ({ prompt, sessionId }) => {
  return {
    additionalContext: `
[请求元数据]
- 时间:${new Date().toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' })}
- 会话:${sessionId}
- 环境:${process.env.NODE_ENV}
    `.trim()
  }
}

限制 Prompt 长度

避免用户发送过长的内容导致 token 超限:

const MAX_PROMPT_LENGTH = 4000

onUserPromptSubmitted: async ({ prompt }) => {
  if (prompt.length > MAX_PROMPT_LENGTH) {
    return {
      modifiedPrompt: prompt.slice(0, MAX_PROMPT_LENGTH) + 
                      `\n\n[注意:输入已截断到 ${MAX_PROMPT_LENGTH} 字符]`
    }
  }
  return null
}

最佳实践

保留用户原意:修改 Prompt 时只追加和补充,不改变用户的核心意图。替换时要确保替换后的内容语义等价。

避免过度注入:每次 Prompt 都追加大量上下文会消耗大量 token,只注入当前任务真正需要的信息。

透明化修改:如果修改了用户的 Prompt(特别是敏感内容过滤),建议在 UI 层告知用户做了什么处理。

常见问题

Q: additionalContext 和 modifiedPrompt 可以同时返回吗?

A: 可以。additionalContext 作为额外的系统消息追加,modifiedPrompt 替换用户原始消息,两者可以同时使用。

Q: 这个 Hook 能看到 Prompt 的历史吗?

A: 目前 context 只包含当前这条用户消息,不包含对话历史。如果需要基于历史做决策,需要在应用层自行追踪。

Q: 修改 Prompt 后,用户在 UI 上看到的是原始内容还是修改后的?

A: 这取决于你的 UI 实现。AI 会基于修改后的内容回答,但 UI 展示什么由你决定。建议如实展示修改(透明度),或至少通过提示告知用户有上下文被追加。