Files
Neo-ZQYY/apps/backend/app/ai/references.py
Neo caf179a5da feat: 2026-04-15~05-02 累积变更基线 — AI 重构 + Runtime Context + DWS 修复
涵盖(每条对应已存的审计记录):
- AI 模块拆分:apps/backend/app/ai/apps -> prompts/(8 个 APP + app2a 派生)
  audit: 2026-04-20__ai-module-complete.md
- admin-web AI 管理套件:AIDashboard / AIOperations / AIRunLogs / AITriggers / TriggerManager
  audit: 2026-04-21__admin-web-ai-management-suite.md
- App2 财务洞察 prompt v3 -> v5.1 + 小程序 AI 接入(chat / board-finance)
  audit: 2026-04-22__app2_prompt_v5_1_and_miniprogram_ai_insight.md
- App2 prewarm 全过滤器 + AI 触发器 cron reschedule
  audit: 2026-04-21__app2-finance-prewarm-all-filters.md
  migration: 20260420_ai_trigger_jobs_and_app2_prewarm.sql / 20260421_app2_prewarm_cron_reschedule.sql
- AppType 联合类型对齐 + adminAiAppTypes.test.ts
  audit: 2026-04-30__admin_web_ai_app_type_alignment.md
- DashScope tokens_used 提取修复
  audit: 2026-04-30__backend_dashscope_tokens_used_extraction.md
- App3 线索完整详情 prompt
  audit: 2026-05-01__backend_app3_full_detail_prompt.md
- Runtime Context 沙箱(5-1~5-2 主线):
  - 后端 schema/service + admin_runtime_context / xcx_runtime_clock 两个 router
  - admin-web RuntimeContext.tsx + miniprogram runtime-clock.ts
  - migration: 20260501__runtime_context_sandbox.sql
  - tools/db/verify_admin_web_sandbox.py + verify_sandbox_end_to_end.py
  - database/changes: 7 份 sandbox_* 验证报告
- 飞球 DWS 修复:finance_area_daily 区域汇总 + task_engine 调整
  + RLS 视图业务日上界(migration 20260502 + scripts/ops/gen_rls_business_date_migration.py)

合规:
- .gitignore 启用 tmp/ 排除
- 不入仓:apps/etl/connectors/feiqiu/.env(API_TOKEN secret,本地修改保留)

待验证清单:
- docs/audit/changes/2026-05-04__cumulative_baseline_pending_verification.md
  每个主题的功能完整性 / 上线验证几乎都未收口,按优先级 P0~P3 逐一处理
2026-05-04 02:30:19 +08:00

138 lines
4.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""AI references 工具模块。
为 AI 输出ai_cache.result_json / ai_messages.reference_card
注入数据来源引用元数据,便于前端渲染可点击引用卡片。
- App2~8通过 dispatcher._write_cache 统一注入到 result['_references']
- App1通过 xcx_chat 在 assistant 消息写入时调用 build_app1_reference 生成单卡片
"""
from __future__ import annotations
from typing import Any
def build_app_references(app_type: str, context: dict) -> list[dict]:
"""为 App2~8 构建 references 列表,供前端消息卡片渲染。
引用结构:
{
"type": "member" | "task" | "assistant" | "finance",
"id": int | str,
"label": "卡片上的文字",
"link": "/pages/xxx/xxx?param=val"(小程序页面路径),
"source_page": 小程序页面 contextType
}
Args:
app_type: 应用名称
context: 传给 build_prompt 的上下文(含 site_id / member_id 等)
Returns:
refs 数组。无有效上下文时返回空数组。
"""
refs: list[dict] = []
site_id = context.get("site_id")
member_id = context.get("member_id")
assistant_id = context.get("assistant_id")
time_dimension = context.get("time_dimension")
if member_id is not None:
refs.append({
"type": "member",
"id": member_id,
"label": f"客户 #{member_id}",
"link": f"/pages/customer-detail/customer-detail?customerId={member_id}",
"source_page": "customer-detail",
})
if assistant_id is not None:
refs.append({
"type": "assistant",
"id": assistant_id,
"label": f"助教 #{assistant_id}",
"link": f"/pages/coach-detail/coach-detail?coachId={assistant_id}",
"source_page": "coach-detail",
})
if app_type == "app2_finance" and time_dimension:
refs.append({
"type": "finance",
"id": time_dimension,
"label": f"财务看板:{_label_for_dimension(time_dimension)}",
"link": f"/pages/board-finance/board-finance?timeDimension={time_dimension}",
"source_page": "board-finance",
})
# 保留 site_id 作为兜底上下文(不单独成卡,但用于前端场景判断)
if site_id is not None and refs:
for r in refs:
r.setdefault("site_id", site_id)
return refs
def attach_references(app_type: str, result: dict | None, context: dict) -> dict | None:
"""向 AI 输出 result 追加 _references 字段(非破坏性)。
- result 为 None 时原样返回(调用失败不注入)
- result 为 dict 时追加 _references 字段;如果 result 已含 _references保留原值
"""
if result is None or not isinstance(result, dict):
return result
if "_references" in result:
return result
refs = build_app_references(app_type, context)
if refs:
result["_references"] = refs
return result
def build_app1_reference_card(source_page: str | None, context_id: int | str | None) -> dict | None:
"""为 App1chatassistant 消息构建单个 reference_card。
兼容前端 chat.wxml 已有的 {type, title, summary, data, dataList} 渲染结构,
额外携带 link 字段供前端点击跳转详情页。
当用户在特定页面customer-detail / coach-detail / task-detail发起对话时
自动附加对应跳转卡片。普通浮窗对话source_page='general')返回 None。
与 chat_service.build_reference_card 不同:本函数不查 DB仅按 source_page 构造链接。
"""
if not source_page or not context_id:
return None
mapping: dict[str, tuple[str, str, str]] = {
"customer-detail": ("customer", "客户", "customerId"),
"coach-detail": ("assistant", "助教", "coachId"),
"task-detail": ("task", "任务", "taskId"),
}
entry = mapping.get(source_page)
if entry is None:
return None
ref_type, label_prefix, param = entry
return {
"type": ref_type,
"title": f"{label_prefix} #{context_id}",
"summary": f"点击查看{label_prefix}详情",
"data": {},
"link": f"/pages/{source_page}/{source_page}?{param}={context_id}",
"source_page": source_page,
}
def _label_for_dimension(dimension: str) -> str:
"""8 个财务维度 → 中文标签。"""
mapping = {
"this_month": "本月",
"last_month": "上月",
"this_week": "本周",
"last_week": "上周",
"this_quarter": "本季度",
"last_quarter": "上季度",
"last_3_months": "近三个月",
"last_6_months": "近六个月",
}
return mapping.get(dimension, dimension)