mcpField
D
debugging-surveys

debugging-surveys

github.com/posthog开发95157 次安装今天更新
代码仓库

关于 debugging-surveys

面向 PostHog 支持工作的调查问卷诊断技能,帮助排查应用内问卷未显示或响应数据异常。重点分析资格检查、SDK 差异及响应处理流程。

主要功能

  1. 01比较各 SDK 的问卷支持情况
  2. 02追踪 Web 问卷资格检查
  3. 03诊断响应采集与合并
  4. 04解读分支逻辑和可选问题
  5. 05利用事件与追踪排查工单

适用场景

  1. 01查明问卷为何未显示
  2. 02解释移动 SDK 间的行为差异
  3. 03调查响应数量和答案缺失

MCP 相关

可通过 PostHog MCP 工具或调查问卷 API 检查问卷配置和响应;无需检出 PostHog 代码库。

安装

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

$npx skills add https://github.com/posthog/ai-plugin --skill debugging-surveys

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

附带文件references/diagnostic-queries.mdreferences/reading-responses.md

详细介绍

调试问卷

PostHog Surveys 是一款无代码的应用内表单构建工具。客户在 PostHog UI 中创建问卷;随后,应用运行的相应 SDK 必须对其进行评估和渲染。 大多数“问卷未显示”工单都是资格问题,而非渲染错误——SDK 正确地判定用户不符合资格,接下来要找出_哪个_条件未通过,以及_为什么_。

访问要求

使用 PostHog MCP 工具或问卷 API 检查问卷配置和回复。执行这些诊断步骤不需要检出 PostHog 仓库。

跨 SDK 功能对等情况(请先检查)

有一大类工单是“客户期望使用的平台不支持的功能”。请先确认问卷的 lib / 客户的平台,再查阅此表。已根据 SDK 源代码验证——如果距离上次验证已有数月,请重新核实;这些差异会随着时间推移逐渐消除。

功能Web(posthog-js)iOSAndroidFlutterReact Native
渲染DOM + shadow root原生 SwiftUI(SurveysWindow)无内置 UI — 仅 delegateDart widgets(SurveyBottomSheet)RN 组件(SurveyModal)
基于事件的触发条件是(从 1.137.0 起,2024-06-05)是是是(原生端)是
URL / 屏幕定位是已解码,但不会进行评估(// TODO)已解码,但不会进行评估不会进行评估(原生端缺失)过滤器中明确排除
功能标记 / 队列定位是是是是(原生端)是
seenSurveyWaitPeriodInDays是是是是(原生端)是(≥ 4.81.5)
surveyPopupDelaySeconds是未实现未实现未实现未实现(TODO)
enable_partial_responses是(≥ 1.240.0)否否否否
skipSubmitButton(自动提交)是(≥ 1.244.0)否否否否

最后两行依据的是编辑器自身的帮助文本(“目前不适用于移动 SDK” / “目前不支持移动 SDK”),而非对各 SDK 源代码的逐项审查。

值得牢记的后果:

  • surveyPopupDelaySeconds 仅适用于 Web。 如果移动端工单将延迟归咎于它,这就是个误导信息。
  • 部分响应和自动提交也仅适用于 Web。 在移动端,问卷始终会在结束时存储一份响应,点击评分也不会自动提交。不要把 Web 端的诊断结论套用到移动端工单上。
  • URL 定向实际上仅适用于 Web。 移动端 SDK 会解析该字段,但从不执行它;React Native 则会完全过滤掉这类问卷。带有 URL 条件的移动端问卷,其行为会是“无 URL 条件”(mobile/flutter)或“永不显示”(RN)。
  • Android 不提供问卷 UI。 应用(或 Flutter 插件)必须提供一个 PostHogSurveysDelegate。“问卷从未在 Android 上渲染”通常是因为缺少委托,而非 PostHog 的错误。
  • **Flutter 是混合架构:**触发和资格判断在原生 iOS/Android 层运行;渲染则由 Dart 负责(SurveyService.showSurvey → showModalBottomSheet)。UI 并非“直接调用原生”实现。因此,Flutter 渲染问题出在 Dart;Flutter 资格判断问题出在原生层。
  • 从 4.81.5 开始,React Native 会强制执行等待期。 较旧版本会跳过检查,因此遇到这些版本时,不要将问题归咎于等待期。

如需深入了解各版本的功能审计,请参阅 survey-sdk-audit skill(如果可用)。

问卷实际显示的方式(Web 资格判断流程)

Web SDK 最复杂,也最常出现在工单中。根据 packages/browser/src/extensions/surveys.tsx(checkSurveyEligibility)建立心智模型——检查会按顺序运行,遇到的第一个失败项将决定结果:

  1. isSurveyRunning — 有 start_date,且没有 end_date。
  2. 问卷 type 为应用内类型(Popover / Widget / API)。
  3. 已启用 linked_flag_key(如果已设置)。
  4. 已启用 targeting_flag_key(如果已设置)——客户定义的属性定向。
  5. _internalFlagCheckSatisfied — 自动生成的内部定向标记。
  6. hasWaitPeriodPassed — seenSurveyWaitPeriodInDays 与 localStorage.lastSeenSurveyDate 的比较。
  7. getSurveySeen — 每份问卷各自的已查看标记。

然后在 getActiveMatchingSurveys 中检查 URL/设备/选择器匹配、事件/操作触发,以及再次检查标记。

两个不太明显、却会影响实际工单的事实:

  • 服务端会返回所有未归档的问卷(SurveyViewSet、products/surveys/backend/api/survey.py)。它不会根据内部定向标记预先筛选。因此不能断定“后端排除了它们”——实际执行筛选的是 SDK。
  • 等待期有两套相互独立的实现。 canActivateRepeatedly(在 schedule: 'always' 时为 true)会短路 _internalFlagCheckSatisfied(第 5 步),因此 always 会绕过内部标记,包括其 $last_seen_survey_date 规则。但 hasWaitPeriodPassed(第 6 步)会直接读取 localStorage.lastSeenSurveyDate,不会被 canActivateRepeatedly 绕过。因此,设置了 seenSurveyWaitPeriodInDays: 30 的 schedule: 'always' 问卷仍会通过 localStorage 路径强制执行 30 天等待期。每当显示_任何_问卷时,都会更新 lastSeenSurveyDate,无论问卷是否完成。同样的短路也会去掉内部标记中的 $survey_responded/<id> is_not_set 规则,因此**schedule: 'always' 会允许同一个人重复响应**——在将响应数视为受访者数之前,先检查 survey sent 事件中 uniq(distinct_id) 与 count() 的对比。

响应实际存储的方式

问卷显示和响应存储属于两个独立的流程。“数据错误”工单涉及的是后一个流程,而其 UI 文案确实容易造成误解——应根据代码判断,而不是根据标签推断。

“响应收集”对应 enable_partial_responses

单选框位于 frontend/src/scenes/surveys/SurveyResponsesCollection.tsx。“任何问题:至少回答一个问题时……”对应 true;“完成问卷:回答完所有问题后保存响应”对应 false。

  • true -> 每次点击 onNextButtonClick 都会触发 sendSurveyEvent,因此每回答一个问题就会产生一条 survey sent 事件;这些事件共享一个 $survey_submission_id,且 $survey_completed 的值从 false 变为 true。
  • false -> 在流程结束时产生一条 survey sent 事件。

此设置控制的是数据采集,而不是响应可见性。结果中包含每一条 survey sent 事件,以及所有标记为 $survey_partially_completed = true 的 survey dismissed 或 survey abandoned 事件。frontend/src/scenes/surveys/utils.ts 中的 buildMergedSubmissionsSubquery 会按提交 ID 合并每个问题的最新答案;没有 ID 的事件会单独保留。**原始事件数可能多于响应数。**已完成的事件优先于更早的部分完成事件。否则,如果最新的响应事件是关闭事件,结果标签会显示“已关闭”;其他未完成的提交则显示“已放弃”。“已放弃”并不能证明发生了页面卸载事件或不活动超时;之后完成问卷会更新同一行。

不同创建路径的默认值并不一致:NEW_SURVEY 设置为 true(frontend/src/scenes/surveys/constants.tsx),但 Django 模型的默认值是 False(products/surveys/backend/models.py)。请读取实际值,不要根据创建日期推断。

“回答完所有问题”并非字面意思

isSurveyCompleted 的判断条件是 getNextSurveyStep(...) === End——**受访者到达了自己的分支路径末尾。**代码不会检查每个问题是否都有值,而且完整响应中的两种情况会让单元格为空:因分支逻辑跳过的问题根本没有键(onNextButtonClick 会在采集前筛选出 visitedIndices,所以这些问题缺失,而不是值为 "" 或 null);点击跳过的 optional: true 问题会存储为 null(submitDisabled 的判断条件是 isNull(rating) && !question.optional)。

因此,在带有可选尾部问题的分支问卷中,“只回答了第一个问题”是完整响应的预期表现。空白 ≠ 未回答。在认定是 bug 之前,请检查每个问题的 branching 和 optional。

分支逻辑就在 API 中——读取它,无需询问

分支逻辑位于 questions[].branching 中,管理 API 和 SDK 载荷都会原样返回,包含四种形式:next_question、end(确认消息)、specific_question + index,以及 response_based + responseValues。有两个容易踩的坑:对于评分问题,responseValues 的键是_区间_(negative/neutral/positive,或 NPS 的 detractors/passives/promoters),而不是具体值;如果某个区间未出现在 responseValues 中,系统会静默地回退到 currentQuestionIndex + 1。因此,在 5 级评分中,{negative: 3, neutral: 3} 是一套完整且有效的配置,看起来却像漏配了值。各评分等级的区间边界见 references/reading-responses.md——不要猜测;2 级评分的区间是反向的。

事件词汇表

survey shown(在 showSurvey() 内触发,时机是弹窗变为可见时,也就是在 surveyPopupDelaySeconds 之后;因此 shown→sent 的延迟就是弹窗实际显示的时长)、survey sent、survey dismissed(主动关闭)、survey abandoned(handlePageUnload → sendSurveyAbandonedEvent)。只有 dismissed 和 abandoned 事件会包含 $survey_partially_completed,sent 事件不会。所有这些事件都带有 sessionRecordingUrl,因此对于有争议的提交,通常可以直接观看录屏,而不必猜测。URL 非空只能证明捕获了会话 ID,并不能证明存在录屏:回放默认关闭,而且云端默认保留期限为 30 天,远短于这些查询使用的 180 天窗口。将链接作为证据提供之前,请先打开查看。

使用 getSurveyResponse 读取答案,切勿猜测键名

getSurveyResponse(<index>, '<questionId>') 是产品本身用来读取答案的 HogQL 辅助函数 (posthog/hogql/functions/survey.py,用于 products/surveys/backend/responses/)。优先使用它,而不是手动编写属性键:响应键有三种格式, 该辅助函数会合并当前的 UUID 键格式($survey_response_<questionId>)和旧版的索引键格式, 因此返回的结果与客户自己的结果表一致。对于多选题,将 true 作为第三个参数传入。手动编写的 properties.$survey_response_<uuid> 会漏掉使用旧版键格式的答案;在 countIf 中,这会悄无声息地将 真实答案计为“未回答”,并抬高未完成率。

无论使用哪种方式,索引和 ID 都必须来自调查 JSON 中的 questions[]。 猜测哪个 UUID 对应哪个问题, 是得出错误结论最快的途径:它会将一道题的答案读成另一道题的答案,而由此得出的比率看起来像是严重缺陷。 如果没有调查 JSON,请展开包含问题文本和答案的 $survey_questions。格式和模板:references/reading-responses.md, references/diagnostic-queries.md。

在相信这些数字之前,先检查映射是否合理: 如果标记为单选题的列中包含自由文本,说明映射有误。

调试工作流

  1. 解析工单。 提取:组织/项目 ID、实例(US 还是 EU——URL 不同)、调查 ID、lib(平台)、准确描述的症状,以及客户已经尝试过的操作。如果工单已搁置较久,或之前有支持回复,配置可能在对话期间被编辑过——应将早先的说法视为过时,并重新拉取当前状态。

  2. 区分“没有”与“更少”。 客户说“没有回复”时,实际可能是“回复更少”。拉取疑似变更前后的 survey shown 与 survey sent 计数(参见 references/diagnostic-queries.md)。如果_回复率_(sent/shown)保持稳定,问题出在上游的资格判定(展示给更少的人),而不是渲染或提交。这一项检查就能让大多数调查转向正确方向。

2b. 对于数据质量工单,区分“不完整”与“完整但稀疏”。 首先检查空白单元格是否对应受访者从未被问到的问题。分支逻辑加上可选问题,会让完整回复看起来像是半途放弃;这是调查被误报为故障的最常见原因。参见回复的实际存储方式。

  1. 检查平台一致性。 确认 lib 并查阅平台支持情况表。在调查这些功能之前,先排除该平台不支持的功能。

  2. 拉取调查定义。 GET /api/projects/<id>/surveys/<sid>/。检查 conditions(事件、url、seenSurveyWaitPeriodInDays、repeatedActivation)、appearance.surveyPopupDelaySeconds、appearance.position、schedule、linked_flag、targeting_flag、internal_targeting_flag.filters、responses_limit、iteration_*、enable_partial_responses——然后深入查看每个问题:id、type、scale、optional、skipSubmitButton、branching。每个问题的字段本身就能解答大多数“数据错误”工单,而且很容易被漏看,因为只浅看顶层键不会显示这些信息。

  3. 对于任何“停止展示”的工单,拉取目标标记的活动日志。 群组替换和发布变更在当前配置中不可见,但会显示在这里:GET /api/projects/<id>/activity_log/?scope=FeatureFlag&item_id=<flag_id>&limit=20。还可以使用 ?scope=Survey&item_id=<sid> 查看调查本身是否被编辑过。

  4. 通过事件确认。 使用 $feature_flag_called 查看门控标记对受影响用户实际返回了什么,以及是否设置了 $groups(见下文的群组聚合原因)。使用 survey shown 查看实际覆盖范围,并与统计界面进行对比。

  5. 对照已知原因目录进行诊断,用一个有针对性的查询确认,然后撰写回复。

在认定是 SDK 错误之前

这类工单容易引发一种特定的误判:症状看似不可能,于是归咎于 SDK,并建议客户暂停调查、提交错误报告并重建问题。这种建议代价高昂,而且难以撤回——重组会破坏所有已收集回复的可比性,调整问题顺序还可能引发下文所述的 UUID 问题。在写下“这是一个错误”之前,先通过以下四项检查:

  1. 阅读调查 JSON,包括每个问题的 branching、optional、skipSubmitButton 和 scale。大多数看似“不可能”的行为,其实是配置所致。
  2. 在 Preview 中复现。
  3. 阅读你所怀疑的处理程序对应的 SDK 源码。 搜索该符号;不要只根据某项设置的名称推断。enable_partial_responses 和“已回答所有问题”实际代表的范围都比字面意思窄。
  4. 在相信异常比例之前,重新检查自己的查询。 “89% 的回复不完整”往往是回复键映射错误,而不是真正的缺陷。

如果通过全部四项检查仍能复现,那就是错误:指出文件和处理程序,并向上游提交。

已知原因目录

大致按最常见的原因排序。

“调查显示给的用户比预期少”
  • surveyPopupDelaySeconds + URL 重新检查(仅限 Web)。 事件触发且符合条件后,SDK 会等待 N 秒,然后在渲染前(handlePopoverSurvey)根据 当前 URL 重新检查 doesSurveyUrlMatch。如果用户在等待期间跳转,调查会被静默丢弃——不会触发 survey shown。在延迟时间不短、导航频繁的应用中很常见。解决方法:将延迟时间缩短至 0–2 秒。
  • seenSurveyWaitPeriodInDays + 客户的其他调查。 向用户显示任何调查都会更新 lastSeenSurveyDate;在等待窗口内,这份调查会因此被屏蔽。是否完成调查无关紧要。请检查 未显示 的群组最近是否看到过其他调查,并与对照组进行比较(已显示 的用户是否有所不同?)。解决方法:缩短等待时间,或暂停有冲突的调查。
  • 群组构成发生变化。 如果调查面向某个群组,且有人编辑了源动态群组(例如添加了行为筛选条件),之后创建的所有静态快照都会继承更窄的定义。触达人数会减少,但调查本身没有任何变化。可在标记活动日志中查找(群组切换),并通过 static_cohort_people 确认群组规模。
“基于事件的调查从未触发”
  • 会话开始时的时序竞争。 在 /api/surveys 返回且捕获钩子注册之前,事件就已被捕获。特征:事件在会话开始后很早就触发。这种情况无法在客户端避免;可通过改用稍晚触发的事件来缓解。
  • 按群组聚合的 linked_flag 缺少群组上下文。 如果 linked_flag(或定向标记)设置了 aggregation_group_type_index,它评估的是 群组,而非个人。如果事件触发前没有设置 posthog.group(<index>, <key>),标记会返回 false,调查也就不会显示。特征:$feature_flag_called 返回 false,且 $groups 为空;触发事件中也缺少 $feature/<key> 属性。解决方法:在 SDK 中设置群组上下文,或将调查改用个人级标记。
  • 客户将调查关联到了错误的标记。 他们创建了一个按电子邮件/属性定向的标记,但调查的 linked_flag/targeting_flag 指向了 另一个 标记。务必通过 API 确认实际的 linked_flag.key / targeting_flag.key——不要轻信客户的描述。
  • 实时标记中使用了行为群组。 包含 performed_event/行为筛选条件的群组无法通过实时标记字节码进行评估("Unsupported behavioral filter for realtime bytecode",posthog/api/cohort.py)。该群组会显示 bytecode_error。调查/标记无法直接使用它——客户必须创建该群组的 静态 副本,并将其用作定向对象。
“回复不完整——只有第一个问题有值”

通常这不是错误——请参阅回复实际如何存储。请先逐一排查以下四项;只有在此之后,才能确认完成条件确实未生效。

  • 分支逻辑跳过了空白问题。 请自行根据 questions[].branching 梳理流程。客户描述的是他们的意图,但保存的内容可能并非如此;而且,如果 responseValues 中缺少某个评分档位,流程会进入下一个索引。
  • 空白问题设置了 optional: true,且受访者直接跳过了它们。一个评分答案加上两个被跳过的可选开放文本问题,仍是一份只包含一个值的 完整 提交。
  • 已启用部分回复,而你正在原始 SQL 中读取中间事件;UI 会按 $survey_submission_id 将这些事件合并。
  • 回复键是猜出来的。 请根据 questions[].id 重新运行。
“受访者表示自己并非有意提交”
  • skipSubmitButton,在编辑器中显示为“选择后自动提交”。 启用后不会渲染提交按钮(BottomSection.tsx),一次点击既会记录答案,也会进入下一题——在同一次处理程序调用中执行 setRating(response),然后执行 handleSubmit(response) → onNextButtonClick,没有防抖,也没有确认步骤。仅适用于评分题和不含开放选项的单选题(canQuestionSkipSubmitButton);仅限 Web。它在所有问卷模板(constants.tsx)和快速创建中默认启用,因此客户很少知道自己启用了它。
  • 它会与弹窗位置设置叠加。 appearance.position: middle_center、较大的 maxWidth 和非零的 surveyPopupDelaySeconds,意味着触发后几秒弹窗才出现,此时光标仍在移动,接下来的点击就会落在答案上。连续设置为自动提交的问题,会让用户无需阅读就点完问卷的大部分题目。
  • **通过显示→发送的延迟来诊断,**然后查看回放(survey sent 事件上的 sessionRecordingUrl)进行确认。多题问卷中,完成时间集中在 10 秒以内是这一问题的典型特征;真实受访者会花更长时间,并留下文字。解决方法是取消勾选该设置,而不是移动问题,因为无论问题位于何处,这种行为都会随之发生。
“UI 中的响应数为 0,但原始事件存在”
  • Max AI 破坏了问卷定义。 Max 的 edit_survey 工具(products/surveys/backend/max_tools.py)有两种故障模式:(a) 重新排序或编辑时,它会通过 QUESTION_TYPE_MAP 重建每个问题(nps→10 级量表、csat→5 级量表等),因此选错语义类型会悄悄更改问题的量表;(b) id 字段需要使用从 1 开始的标签("1"、"2")——传入真实 UUID 时会匹配失败,并生成一个全新的 UUID,因此以 $survey_response_<old_uuid> 为键的响应将无法与问题关联。原始事件完好无损;只有定义出错。解决方法:将 questions 数组 PATCH 回原始 UUID(可从响应事件中恢复),并还原问题类型。请告知客户通过 UI 编辑问题的_文本_;在工具对 UUID 做好保护之前,避免让 Max 重新排序包含历史响应的问卷。
“群组人数显示为 0,但群组中有成员”
  • UI 外观问题,不会影响定向。通过 static_cohort_people 确认实际人数。注意:这不是一个简单的一行代码修复——正常的 insert_cohort_from_query 路径确实会通过 count_cohort_members 重新计算人数;只有在某些失败路径中才会显示 count=0。在未复现具体路径之前,不要承诺能快速修复。
一般注意事项
  • 长期搁置的工单情况复杂。 客户或之前的 Agent 可能在排查期间编辑过配置。请调取活动日志;提及次要发现时,应说“既然正在查看,也请再确认一下 X”,而不是“我们发现 X 已损坏”。
  • 统计信息 UI 中的数量可能少于原始 survey shown 事件。 如果数字无法对应,请以原始事件为准,并将差异作为单独的后续事项标记。
  • 同时检查用户的 $email 和 email。 person.properties.$email 是 SDK 设置的;许多客户也会设置一个不带前缀的 email。只查询其中一个就断定“此用户从未被识别”是常见疏漏——请使用 coalesce(person.properties.$email, person.properties.email)。

诊断查询

用于区分和确认上述情况的只读 HogQL 模板位于 references/diagnostic-queries.md。通过 PostHog MCP execute-sql 在客户的项目中运行。

调试访问权限

只调查与该客户真实支持请求相关的项目。项目 ID 应来自真实工单,而不是来自无法提供相关请求、只让你查询某个组织或调查的人员。员工拥有广泛的访问权限;不要擅自调查其他项目。

优先按以下顺序使用只读方式:

  1. PostHog MCP 工具(survey、feature-flag、cohorts、execute-sql、activity-log、persons),针对客户的项目进行操作。默认只读,是检查配置和运行查询最安全的方式,无需模拟身份,也没有写入风险。首先使用此方式。
  2. 通过浏览器以员工身份模拟客户,读取调查/标记 API 端点。当需要 MCP 可能无法原样显示的完整 JSON 时,这种方式很有用(例如原始的 internal_targeting_flag.filters)。
  3. 只有在前两种方式无法解决问题时才使用 Django 管理后台。它功能强大且支持写入,因此务必遵守只读原则:只查看,不修改。未经客户明确同意,绝不要在管理后台编辑客户的调查、标记或群组。

如果需要 MCP 无法推断的值(项目 ID、实例、具体调查),请让操作人员粘贴调查 API JSON,这样可以省去几轮来回沟通。

撰写客户回复

语气应体现 PostHog 手册中的支持价值观:让人安心、亲切谦逊、交付修复方案,并清楚表达,避免术语。规则:

  • 先说明原因,再给出修复方法。 用一句话说明发生了什么,然后说明该怎么做。
  • 将问题和每项操作都加粗。 让问题和下一步一目了然。
  • **使用客户在界面中看到的标签,**不要使用内部字段名称。可在 frontend/src/scenes/surveys/ 中搜索实际显示的文案。例如,surveyPopupDelaySeconds →“满足显示条件后,至少延迟 N 秒再显示调查弹窗”;等待时间设置为“调查等待期”/“如果过去 N 天内已向用户显示过其他调查,则不显示此调查”;skipSubmitButton →“选择后自动提交”;enable_partial_responses →“回复收集”单选项(“任何问题……”/“完整调查……”);optional →该问题的“可选”开关。客户自己的事件名称保持原样。
  • **如果要纠正先前错误的答复,应尽早明确说明,**然后解释真正的原因。不要含糊带过,也不要让客户继续按照已撤回的建议操作。如果之前已建议客户暂停或重建某项内容,请明确告知他们可以停止。
  • 所有 PostHog 实体都要链接到其 ID。 群组:https://<us|eu>.posthog.com/project/<id>/cohorts/<cohort_id>。标记:.../feature_flags/<flag_id>。调查:.../surveys/<survey_id>。请使用与客户相符的实例(US 或 EU)。
  • **说明预期结果,**方便客户确认修复是否生效(“之后你应该会看到约 X”)。
  • 判断客户的技术水平。 如果他们能运行 SQL 和调用 API,可以提供补丁方案;如果不能,则提出由我们代为修复(并先确认任何具有破坏性的细节)。
  • 不要提议“通话”或预约会议。 PostHog 支持以异步沟通为先。结尾使用“如需后续帮助,我们随时为你提供支持。”
  • 绝不要泄露内部信息——包括 MCP 工具名称、代码路径、行号、Django 管理后台、员工身份模拟或其他客户的信息。只谈客户熟悉的产品概念。
  • 发送前,使用 humanizer 技能润色最终草稿(如果有,例如 humanizer)。删除破折号、铺垫语(“关键在于:”“有三点需要了解”)、刻意凑成三点的内容,以及过于工整的并列列表结构。回复应读起来自然,就像真人写的一样。

回复模板:

Hi <name>,

<one line: tracked it down + the cause in plain terms.>

**The problem:** <what's happening, with the evidence you pulled.>

**<Fastest fix / two ways to fix it>:**
1. **<action>** — <why / how.>
2. **<action>** — <why / how.>

**Also worth knowing while you're in there:** <secondary finding, softened.>

We're always here if you need a follow-up.

评论

支持 Markdown