- .kiro/specs/ → docs/specs/(41 个历史需求 spec 迁移,移除 .config.kiro) - CLAUDE.md 三层拆分:根文件精简 + apps/backend/CLAUDE.md + .claude/commands/ - 新增 /spec-close、/pre-change 两个工作流命令 - DDL 基线刷新(从测试库重新导出 11 个文件,dws 35→38 表,biz 18→21 表) - BD_Manual → BD_manual 命名统一(48 个文件) - 修复 3 处文档与数据库不一致(auth.users.status 默认值、scheduled_tasks 字段、RLS 视图数) - 新增 BD_manual_public_rbac_tables.md(public schema 8 张 RBAC/工作流表) - 合并 biz.trigger_jobs 文档(10→12 字段,归档独立文档) - docs/database/README.md 索引更新 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
23 KiB
需求文档 — NS2:AI Prompt 细化
简介
NS2 将 P5-A 阶段交付的 6 个 AI 应用(应用 1/3/4/5/6/7)的 build_prompt() 占位骨架升级为完整实现。核心工作包括:创建共享数据获取层(data_fetchers/)从 FDW 视图获取真实业务数据、完善 6 个应用的 Prompt 拼接函数使 AI 能基于真实数据生成分析、以及实现应用 1 的页面上下文文本化(根据 contextType 自动获取并格式化页面数据)。
依赖
- P5-A(AI 集成管道)— 百炼封装、缓存 API、SSE 框架、应用 2/8 完整实现、应用 1/3/4/5/6/7 触发机制和调用骨架
- NS1(小程序后端 API)— 后端数据结构确定、FDW 映射建立
- RNS1.4(CHAT 对齐与联调收尾)— chat 模块路径迁移、
contextType/contextId参数机制
来源文档
docs/prd/Neo_Specs/NS2-ai-prompt-refinement.md— NS2 完整 PRDdocs/prd/specs/P5-miniapp-ai-integration.md— P5 AI 集成 spec(Prompt JSON 结构定义)docs/miniprogram-dev/API-contract.md— 接口契约(数据结构参考)docs/prd/Neo_Specs/NS1-xcx-backend-api.md— NS1 后端 API spec
术语表
- Data_Fetcher:共享数据获取模块,位于
apps/backend/app/ai/data_fetchers/,负责从数据库查询并格式化 AI 所需数据 - Build_Prompt:各 AI 应用中的 Prompt 拼接函数,将数据获取层返回的数据组装为百炼 API 所需的消息列表
- FDW_View:PostgreSQL Foreign Data Wrapper 视图,前缀
fdw_etl.v_*,用于从业务库只读访问 ETL 库数据 - Page_Context_Builder:页面上下文文本化模块,根据
contextType从数据库获取页面数据并格式化为 AI 可读的结构化中文文本 - Bailian_Client:百炼 API 统一封装层,位于
apps/backend/app/ai/bailian_client.py - AI_Cache:AI 缓存表
biz.ai_cache,存储各应用的 AI 分析结果供前端读取和跨应用引用 - items_sum:DWD-DOC 强制使用的消费金额口径,=
table_charge_money+goods_money+assistant_pd_money+assistant_cx_money+electricity_money - RLS:Row Level Security,通过
SET LOCAL app.current_site_id实现多门店数据隔离 - contextType:前端跳转 chat 页面时传入的页面类型标识(如
task-detail、board-finance),后端据此路由到对应的文本化函数 - contextId:前端跳转 chat 页面时传入的页面实体 ID(如 taskId、memberId)
- biz_params:应用 1 system prompt 中注入的用户身份参数,百炼平台侧据此执行数据查询隔离
需求
需求 1:客户消费数据获取模块
用户故事: 作为 AI 应用(应用 3/6/7),我需要获取客户近期消费数据的完整结构化信息,以便基于真实数据生成维客线索分析、备注分析和运营策略。
验收标准
- THE Data_Fetcher SHALL 提供
fetch_member_consumption_data(site_id, member_id, months)异步函数,从 FDW_View 获取客户近 N 个月(默认 3 个月)的消费数据,返回包含以下字段的字典:consumption_records(消费记录列表)、member_cards(会员卡明细列表)、card_balance_total(储值卡余额合计)、stored_value_balance_total(储值余额合计)、expected_visit_date(预计到店日期)、days_since_last_visit(距上次到店天数) - THE Data_Fetcher SHALL 从
fdw_etl.v_dwd_settlement_head和fdw_etl.v_dwd_table_fee_log获取台桌结账记录,从fdw_etl.v_dwd_store_goods_sale获取商城订单记录,仅包含正向交易(settle_type IN (1, 3)) - THE Data_Fetcher SHALL 为每条消费记录返回以下金额拆分字段:
settle_date、settle_type、items_sum、table_charge_money、assistant_pd_money、assistant_cx_money、goods_money、room_name、duration_minutes、assistant_names(服务助教列表) - THE Data_Fetcher SHALL 使用
items_sum作为消费金额口径,禁止使用consume_money - THE Data_Fetcher SHALL 从
fdw_etl.v_dim_member_card_account获取会员卡明细,每张卡包含card_type(卡类型)、balance(余额)、gift_balance(赠送余额) - THE Data_Fetcher SHALL 从
fdw_etl.v_dws_member_visit_detail的到店间隔数据推算expected_visit_date,并计算days_since_last_visit - THE Data_Fetcher SHALL 通过
member_idJOINfdw_etl.v_dim_member(scd2_is_current=1)获取会员昵称和手机号,禁止使用结算单冗余的member_phone/member_name字段 - THE Data_Fetcher SHALL 在执行 FDW 查询前通过
get_etl_readonly_connection(site_id)获取连接,并执行SET LOCAL app.current_site_id设置 RLS 隔离 - THE Data_Fetcher SHALL 将单次查询返回的消费记录数限制为最多 100 条,按
settle_date倒序排列 - IF FDW 查询超时(超过 5 秒)或连接失败,THEN THE Data_Fetcher SHALL 抛出明确的异常,包含失败的视图名称和错误类型,由调用方决定降级策略
需求 2:助教数据获取模块
用户故事: 作为 AI 应用(应用 4/5),我需要获取助教基本信息和助教-客户服务历史,以便基于真实数据生成关系分析和话术参考。
验收标准
- THE Data_Fetcher SHALL 提供
fetch_assistant_info(site_id, assistant_id)异步函数,从fdw_etl.v_dim_assistant和fdw_etl.v_dws_assistant_salary_calc获取助教基本信息,返回包含以下字段的字典:nickname(花名)、level(级别)、hire_date(入职日期)、tenure_months(工龄月数)、monthly_customers(本月服务客户数)、performance_tier(绩效档位) - THE Data_Fetcher SHALL 提供
fetch_service_history(site_id, assistant_id, member_id, months)异步函数,从fdw_etl.v_dwd_assistant_service_log获取助教服务该客户的历史记录(默认近 3 个月),每条记录包含:service_date、duration_minutes、items_sum、room_name、is_pd(是否陪打) - THE Data_Fetcher SHALL 使用
dwd_assistant_service_log_ex.is_trash排除废单记录,禁止使用已废弃的dwd_assistant_trash_event表 - THE Data_Fetcher SHALL 从
fdw_etl.v_dws_member_assistant_relation_index获取助教-客户关系指数,从fdw_etl.v_dws_member_assistant_intimacy获取亲密度数据 - THE Data_Fetcher SHALL 在执行 FDW 查询前通过
get_etl_readonly_connection(site_id)获取连接,并执行SET LOCAL app.current_site_id设置 RLS 隔离 - IF FDW 查询超时或连接失败,THEN THE Data_Fetcher SHALL 抛出明确的异常,包含失败的视图名称和错误类型
需求 3:应用 3 Prompt 拼接完善(客户数据维客线索分析)
用户故事: 作为系统,我需要在客户新增消费时,将真实的消费数据、会员卡信息和历史线索拼接为完整的 Prompt JSON,以便 AI 能基于真实数据提取维客线索。
验收标准
- WHEN 应用 3 的
build_prompt()被调用时,THE Build_Prompt SHALL 调用fetch_member_consumption_data()获取客户近 3 个月消费数据,替换当前的 TODO 占位符 - THE Build_Prompt SHALL 将获取到的数据组装为 P5 spec 定义的 Prompt JSON 结构,包含
current_time(精确到秒)、member_nickname、main_data(含consumption_records、member_cards、card_balance_total、stored_value_balance_total、expected_visit_date、days_since_last_visit)、reference(含app6_clues和app8_history) - THE Build_Prompt SHALL 在
reference.app8_history中包含最近 2 套应用 8 历史结果,每套附带generated_at时间戳 - IF 客户无消费记录,THEN THE Build_Prompt SHALL 将
consumption_records设为空数组,其余字段使用默认值(余额为 0、days_since_last_visit为 null),Prompt 中标注"该客户暂无消费记录" - THE Build_Prompt SHALL 将每条消费记录中的金额字段逐项拆分(
table_charge_money、assistant_pd_money、assistant_cx_money、goods_money),禁止使用consume_money
需求 4:应用 4 Prompt 拼接完善(关系分析/任务建议)
用户故事: 作为系统,我需要在助教参与新结算或被分配召回任务时,将助教信息、服务历史和客户数据拼接为完整的 Prompt JSON,以便 AI 能生成关系分析和任务建议。
验收标准
- WHEN 应用 4 的
build_prompt()被调用时,THE Build_Prompt SHALL 调用fetch_assistant_info()获取助教基本信息,调用fetch_service_history()获取助教-客户服务历史,调用fetch_member_consumption_data()获取客户消费数据,替换当前的 TODO 占位符 - THE Build_Prompt SHALL 将获取到的数据组装为 P5 spec 定义的 Prompt JSON 结构,包含
current_time、assistant_info、service_history、task_assignment_basis、customer_data(含system_data和notes)、reference(含app8_current和app8_history) - THE Build_Prompt SHALL 从
biz.notes获取所有助教对该客户的全部备注,每条备注包含recorded_by(创建者)、content(内容)、created_at(创建时间) - THE Build_Prompt SHALL 对备注内容进行截断处理,单条备注最大 500 字符,超出部分截断并附加"…(已截断)"标记
- IF 助教无服务该客户的历史记录,THEN THE Build_Prompt SHALL 将
service_history设为空数组,Prompt 中标注"该助教暂无服务该客户的记录" - THE Build_Prompt SHALL 在
reference中包含应用 8 最新结果(app8_current)和最近 2 套历史(app8_history),每套附带generated_at时间戳
需求 5:应用 5 Prompt 拼接完善(话术参考)
用户故事: 作为系统,我需要在应用 4 完成后,将应用 4 的分析结果连同助教信息和服务历史拼接为完整的 Prompt JSON,以便 AI 能生成针对性的沟通话术。
验收标准
- WHEN 应用 5 的
build_prompt()被调用时,THE Build_Prompt SHALL 复用应用 4 的数据获取逻辑(fetch_assistant_info()、fetch_service_history()、fetch_member_consumption_data()),替换当前的 TODO 占位符 - THE Build_Prompt SHALL 将获取到的数据组装为 P5 spec 定义的 Prompt JSON 结构,包含
current_time、assistant_info、service_history、task_assignment_basis、customer_data(含system_data和notes)、task_suggestion(应用 4 的完整返回结果)、reference(含app8_history) - THE Build_Prompt SHALL 从
context["app4_result"]获取应用 4 的返回结果,作为task_suggestion字段传入 Prompt - IF
context["app4_result"]为空或缺失,THEN THE Build_Prompt SHALL 将task_suggestion设为空对象,Prompt 中标注"暂无任务建议"
需求 6:应用 6 Prompt 拼接完善(备注分析)
用户故事: 作为系统,我需要在助教提交备注后,将备注内容、客户消费数据和历史备注拼接为完整的 Prompt JSON,以便 AI 能分析备注价值并提取维客线索。
验收标准
- WHEN 应用 6 的
build_prompt()被调用时,THE Build_Prompt SHALL 调用fetch_member_consumption_data()获取客户消费数据,替换当前的 TODO 占位符 - THE Build_Prompt SHALL 将获取到的数据组装为 P5 spec 定义的 Prompt JSON 结构,包含
current_time、current_note(含content、recorded_by、created_at)、reference(含member_nickname、consumption_data、all_notes、app3_clues、app8_history) - THE Build_Prompt SHALL 从
biz.notes获取所有助教对该客户的全部备注作为all_notes,每条备注包含recorded_by、content、created_at - THE Build_Prompt SHALL 对
all_notes中每条备注内容进行截断处理,单条备注最大 500 字符 - THE Build_Prompt SHALL 在
reference中包含应用 3 最新线索(app3_clues)和最近 2 套应用 8 历史(app8_history),每套附带generated_at时间戳 - IF 客户无历史备注,THEN THE Build_Prompt SHALL 将
all_notes设为空数组
需求 7:应用 7 Prompt 拼接完善(客户分析)
用户故事: 作为系统,我需要在消费事件链中应用 8 完成后,将客户全量客观数据和主观备注拼接为完整的 Prompt JSON,以便 AI 能生成全局运营策略。
验收标准
- WHEN 应用 7 的
build_prompt()被调用时,THE Build_Prompt SHALL 调用fetch_member_consumption_data()获取客户消费数据,替换当前的 TODO 占位符 - THE Build_Prompt SHALL 将获取到的数据组装为 P5 spec 定义的 Prompt JSON 结构,包含
current_time、member_id、member_nickname、objective_data(同应用 3 的main_data结构)、subjective_data(含notes)、reference(含app8_current和app8_history) - THE Build_Prompt SHALL 从
biz.notes获取该客户的全部备注作为subjective_data.notes,每条备注包含recorded_by(创建者)、content、created_at - THE Build_Prompt SHALL 对来自备注的主观信息在 Prompt 中标注"【来源:{recorded_by},请甄别信息真实性】"
- THE Build_Prompt SHALL 在
reference中包含应用 8 最新结果(app8_current)和最近 2 套历史(app8_history),每套附带generated_at时间戳 - IF 客户无备注记录,THEN THE Build_Prompt SHALL 将
subjective_data.notes设为空数组,Prompt 中标注"该客户暂无主观备注信息"
需求 8:页面上下文文本化框架
用户故事: 作为助教,我希望从任意页面进入 AI 对话时,AI 能自动了解当前页面的上下文信息(客户数据、任务信息、看板数据等),以便提供与当前场景相关的回答。
P5 对齐说明:P5 spec 定义应用 1 首条 Prompt 包含
page_context(页面上下文摘要)和screen_content(用户当前屏幕可见内容)两个独立字段。NS2 采用后端自动查询方案,通过build_page_text()生成合并的页面上下文文本,等效覆盖两个字段的信息需求。详见 NS2 PRD 3.7 设计决策。
验收标准
- THE Page_Context_Builder SHALL 提供
build_page_text(source_page, context_id, site_id, filters)异步函数,根据source_page(即contextType)从数据库获取对应页面数据并格式化为结构化中文文本 - THE Page_Context_Builder SHALL 支持以下 10 种页面类型的文本化:
task-detail、customer-detail、coach-detail、board-finance、board-customer、board-coach、performance、my-profile、task-list、customer-service-records - WHEN
source_page为task-detail时,THE Page_Context_Builder SHALL 从biz.coach_tasks、会员信息、备注和ai_cache获取数据,输出包含任务信息、客户信息、备注摘要和 AI 分析的结构化文本 - WHEN
source_page为customer-detail时,THE Page_Context_Builder SHALL 从会员信息、消费记录和维客线索获取数据,输出包含客户全信息、消费记录摘要和维客线索的结构化文本 - WHEN
source_page为coach-detail时,THE Page_Context_Builder SHALL 从助教信息、任务统计和备注获取数据,输出包含助教信息、任务统计和备注摘要的结构化文本 - WHEN
source_page为看板类页面(board-finance、board-customer、board-coach)时,THE Page_Context_Builder SHALL 接受可选的筛选参数(timeDimension、dimension、areaFilter等),从对应 DWS 汇总视图获取数据,输出数据摘要文本 - WHEN
source_page为performance时,THE Page_Context_Builder SHALL 从fdw_etl.v_dws_assistant_salary_calc获取绩效数据,输出绩效数据摘要文本 - THE Page_Context_Builder SHALL 将每个页面上下文的输出文本控制在 2000 字符以内,避免 token 浪费
- THE Page_Context_Builder SHALL 输出结构化中文描述文本(非 JSON),使用分段标题和缩进格式便于 AI 理解
- IF 看板类页面未传入筛选参数,THEN THE Page_Context_Builder SHALL 使用默认值(如
board-finance默认"本月"、board-customer默认按消费金额排序) - IF 数据获取失败(FDW 超时、连接失败等),THEN THE Page_Context_Builder SHALL 返回"页面上下文获取失败,请直接描述您的问题"文本,不阻断对话流程
- THE Page_Context_Builder SHALL 不传入
member_phone等已断档的敏感字段,会员信息通过member_idJOINv_dim_member获取
需求 9:应用 1 页面上下文集成
用户故事: 作为助教,我希望从不同页面入口进入 AI 对话时,AI 的首条回复能体现对当前页面上下文的理解,而不是一个通用的空白对话。
验收标准
- WHEN 用户从任意页面进入 chat 页面时,THE Build_Prompt SHALL 在
app1_chat.py的_build_page_context()中调用Page_Context_Builder.build_page_text(),根据contextType和contextId获取页面上下文文本 - THE Build_Prompt SHALL 将
build_page_text()返回的文本作为 system prompt 中的page_context字段注入,替换当前的空透传实现 - WHEN
contextType为详情类页面(task-detail、customer-detail、coach-detail)时,THE Build_Prompt SHALL 使用contextId作为实体 ID 传入build_page_text() - WHEN
contextType为看板类页面(board-finance、board-customer、board-coach)时,THE Build_Prompt SHALL 将前端传入的筛选参数(timeDimension、dimension等)作为filters传入build_page_text() - IF
contextType为空或未识别的类型,THEN THE Build_Prompt SHALL 跳过页面上下文注入,AI 以通用对话模式响应 - THE Build_Prompt SHALL 确保
biz_params.user_prompt_params(User_ID、Role、Nickname)在页面上下文注入后仍正确存在于 system prompt 中
需求 10:前端看板筛选参数传递
用户故事: 作为助教或管理者,我希望从看板页面进入 AI 对话时,AI 能了解当前看板的筛选条件(时间维度、排序维度等),以便基于当前视图提供分析。
验收标准
- WHEN 用户从
board-finance页面跳转到 chat 页面时,THE Miniprogram SHALL 将当前的timeDimension(时间维度)和areaFilter(区域筛选)作为额外参数传入 chat 页面 - WHEN 用户从
board-customer页面跳转到 chat 页面时,THE Miniprogram SHALL 将当前的dimension(排序维度)和typeFilter(类型筛选)作为额外参数传入 chat 页面 - WHEN 用户从
board-coach页面跳转到 chat 页面时,THE Miniprogram SHALL 将当前的dimension、projectFilter(技能筛选)和timeDimension作为额外参数传入 chat 页面 - THE Miniprogram SHALL 将看板筛选参数编码为 chat 页面 URL 的查询参数,后端从请求中提取并传入
build_page_text()的filters参数
需求 11:数据获取层全局约束
用户故事: 作为后端开发者,我需要确保所有数据获取函数遵循统一的数据库连接、金额口径和安全规范,以保证数据一致性和多门店隔离。
验收标准
- THE Data_Fetcher SHALL 对所有 FDW 查询使用
get_etl_readonly_connection(site_id)获取只读连接,对业务库查询使用get_connection()获取连接 - THE Data_Fetcher SHALL 在每次 FDW 查询前执行
SET LOCAL app.current_site_id = {site_id},确保 RLS 多门店数据隔离生效 - THE Data_Fetcher SHALL 在所有涉及金额的查询和计算中使用
items_sum口径,禁止使用consume_money - THE Data_Fetcher SHALL 在所有涉及助教费用的查询中将费用拆分为
assistant_pd_money(陪打)和assistant_cx_money(超休),禁止使用合并的service_fee - THE Data_Fetcher SHALL 在所有涉及会员信息的查询中通过
member_idJOINv_dim_member(scd2_is_current=1)获取昵称和手机号,禁止使用结算单冗余字段 - THE Data_Fetcher SHALL 支持多个数据获取函数并发执行(通过
asyncio.gather),以减少 Prompt 构建的总耗时 - THE Data_Fetcher SHALL 为所有 FDW 查询设置 5 秒超时,超时后抛出
TimeoutError并记录慢查询日志
需求 12:空数据与错误降级处理
用户故事: 作为系统,我需要在数据获取部分失败或数据为空时,仍能生成有效的 Prompt 并调用 AI,以保证 AI 功能的可用性。
验收标准
- IF 某个数据获取函数失败(FDW 超时、连接失败等),THEN THE Build_Prompt SHALL 将该部分数据设为默认空值(空数组或空对象),在 Prompt 中标注"该部分数据获取失败",继续生成 Prompt 并调用百炼 API
- IF 客户无消费记录、无备注、无服务历史,THEN THE Build_Prompt SHALL 在 Prompt 中使用明确的空状态提示词(如"该客户暂无消费记录,请基于已有信息分析"),而非传入空数据不做说明
- IF AI_Cache 中无 reference 历史数据(如新客户首次结算),THEN THE Build_Prompt SHALL 将
reference设为空对象,在 Prompt 中标注"暂无历史线索" - THE Build_Prompt SHALL 确保在任何数据获取失败的情况下,Prompt 的整体 JSON 结构仍然合法,不出现 null 值导致百炼 API 解析失败
- IF 应用 1 的页面上下文获取失败,THEN THE Build_Prompt SHALL 使用"页面上下文获取失败,请直接描述您的问题"作为
page_context,不阻断对话
需求 13:biz_params 端到端正确性
用户故事: 作为系统管理员,我需要确保应用 1 的用户身份信息从前端到百炼平台的完整传递链路正确无误,以保证数据查询隔离生效。
验收标准
- THE Backend SHALL 从 JWT token 中提取
user_id、role、nickname,传入app1_chat.py的_build_system_prompt(),确保用户身份信息不可由前端伪造 - THE Build_Prompt SHALL 在 system prompt 的
biz_params.user_prompt_params中注入User_ID(字符串)、Role("助教"或"管理者")、Nickname(昵称),供百炼平台侧执行数据查询隔离 - WHILE 用户身份为助教时,THE Bailian_Client SHALL 确保百炼平台仅允许查询与该助教相关的数据,禁止查询其他助教业绩、工资、客户关系等敏感数据,禁止查询门店级财务数据
- WHILE 用户身份为管理者时,THE Bailian_Client SHALL 确保百炼平台允许查询该门店下所有数据
- THE Backend SHALL 确保所有传入百炼平台的查询均包含
site_id过滤,防止跨门店数据泄露
需求 14:Prompt Token 预算控制
用户故事: 作为系统,我需要控制每个应用首条 Prompt 的数据量,避免超出百炼 API 的 token 限制或产生不必要的 token 消耗。
验收标准
- THE Build_Prompt SHALL 将应用 3/4/5/6/7 的首条 Prompt JSON 总字符数控制在 8000 字符以内(约 4000 token)
- THE Build_Prompt SHALL 将应用 1 的 system prompt(含页面上下文)总字符数控制在 4000 字符以内(约 2000 token)
- WHEN 消费记录数量超过 100 条时,THE Data_Fetcher SHALL 仅返回最近 100 条记录,并在 Prompt 中标注"仅展示最近 100 条,共 {total} 条"
- WHEN 备注总数超过 50 条时,THE Build_Prompt SHALL 仅传入最近 50 条备注(按
created_at倒序),并在 Prompt 中标注"仅展示最近 50 条备注" - THE Build_Prompt SHALL 对所有传入 Prompt 的文本字段进行长度检查,单个文本字段不超过 1000 字符