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 逐一处理
This commit is contained in:
137
apps/backend/app/ai/references.py
Normal file
137
apps/backend/app/ai/references.py
Normal file
@@ -0,0 +1,137 @@
|
||||
"""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:
|
||||
"""为 App1(chat)assistant 消息构建单个 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)
|
||||
Reference in New Issue
Block a user