Appearance
onUserPromptSubmitted 在用户消息提交后、AI 处理前触发,可以注入额外上下文、扩展用户的快捷指令、过滤敏感词汇、追加用户配置信息。返回 null 不修改,返回 additionalContext 追加上下文,返回 modifiedPrompt 替换原始内容。
GitHub Copilot SDK onUserPromptSubmitted Hook:增强和过滤用户输入
基本结构
typescript
const session = await createSession({
hooks: {
onUserPromptSubmitted: async (context) => {
const { prompt, sessionId, userId } = context
// 处理逻辑
return null // 不修改
}
}
})自动注入上下文
最常见的用途——在每次用户提问时自动补充项目信息:
typescript
onUserPromptSubmitted: async ({ prompt, userId }) => {
const userPrefs = await getUserPreferences(userId) // 可以是异步的
return {
additionalContext: `
用户偏好:
- 代码语言:${userPrefs.preferredLanguage}
- 框架:${userPrefs.frameworks.join(', ')}
- 回答语言:中文
当前工作目录:${process.cwd()}
`.trim()
}
}扩展快捷指令
实现自定义的 Prompt 快捷方式:
typescript
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
}过滤敏感内容
防止用户意外输入敏感信息:
typescript
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 了解请求的来源和目的:
typescript
onUserPromptSubmitted: async ({ prompt, sessionId }) => {
return {
additionalContext: `
[请求元数据]
- 时间:${new Date().toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' })}
- 会话:${sessionId}
- 环境:${process.env.NODE_ENV}
`.trim()
}
}限制 Prompt 长度
避免用户发送过长的内容导致 token 超限:
typescript
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 展示什么由你决定。建议如实展示修改(透明度),或至少通过提示告知用户有上下文被追加。