Files
group_xinghuo_jinrong/scripts/dev/build_defense_ppt.py
T
zhanghongyu_0626 2296a4a002 feat(api): Add convert_meta_api for retrieving latest NAV date and enhance created_at field handling
- Introduced `convert_meta_api` endpoint to fetch the latest NAV date for conversion processes, restricted to users with the "risk_officer" role.
- Updated `created_at` field in `AgentMessage` to use UTC timezone for consistency in timestamp handling.
- Added `get_max_product_nav_date` method in `CoreReadOnlyRepository` to support the new API functionality.
- Enhanced Milvus template loading in `MilvusTemplateVectorStore` to ensure collections are loaded when they exist.

This update improves the API's capability to handle conversion metadata and ensures accurate timestamp management across the application.
2026-09-13 12:40:44 +08:00

242 lines
9.5 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.
"""Generate defense deck from docs/答辩/答辩知识点清单.md structure."""
from __future__ import annotations
from pathlib import Path
from pptx import Presentation
from pptx.dml.color import RGBColor
from pptx.enum.text import MSO_ANCHOR, PP_ALIGN
from pptx.util import Inches, Pt
ROOT = Path(__file__).resolve().parents[2]
OUT = ROOT / "docs" / "答辩" / "JinRong-答辩.pptx"
INK = RGBColor(0x13, 0x2B, 0x3A)
TEXT = RGBColor(0x17, 0x21, 0x2B)
MUTED = RGBColor(0x71, 0x80, 0x8A)
GOLD = RGBColor(0xC9, 0xA6, 0x6B)
WHITE = RGBColor(0xFF, 0xFF, 0xFF)
BG_LIGHT = RGBColor(0xF6, 0xF5, 0xF1)
def _set_slide_bg(slide, rgb: RGBColor) -> None:
fill = slide.background.fill
fill.solid()
fill.fore_color.rgb = rgb
def _add_title_slide(prs: Presentation, title: str, subtitle: str, notes: str) -> None:
slide = prs.slides.add_slide(prs.slide_layouts[6])
_set_slide_bg(slide, INK)
box = slide.shapes.add_textbox(Inches(0.7), Inches(2.0), Inches(11.5), Inches(1.2))
tf = box.text_frame
tf.text = title
p = tf.paragraphs[0]
p.font.size = Pt(40)
p.font.bold = True
p.font.color.rgb = WHITE
sub = slide.shapes.add_textbox(Inches(0.7), Inches(3.3), Inches(11.5), Inches(1.0))
stf = sub.text_frame
stf.text = subtitle
sp = stf.paragraphs[0]
sp.font.size = Pt(20)
sp.font.color.rgb = GOLD
slide.notes_slide.notes_text_frame.text = notes
def _add_section_slide(prs: Presentation, title: str, bullets: list[str], notes: str) -> None:
slide = prs.slides.add_slide(prs.slide_layouts[6])
_set_slide_bg(slide, BG_LIGHT)
bar = slide.shapes.add_shape(1, Inches(0), Inches(0), Inches(13.33), Inches(0.12))
bar.fill.solid()
bar.fill.fore_color.rgb = GOLD
bar.line.fill.background()
tbox = slide.shapes.add_textbox(Inches(0.7), Inches(0.45), Inches(12), Inches(0.8))
ttf = tbox.text_frame
ttf.text = title
tp = ttf.paragraphs[0]
tp.font.size = Pt(32)
tp.font.bold = True
tp.font.color.rgb = INK
body = slide.shapes.add_textbox(Inches(0.85), Inches(1.35), Inches(11.8), Inches(5.5))
btf = body.text_frame
btf.word_wrap = True
btf.vertical_anchor = MSO_ANCHOR.TOP
for i, line in enumerate(bullets):
p = btf.paragraphs[0] if i == 0 else btf.add_paragraph()
p.text = line
p.level = 0
p.font.size = Pt(18 if len(line) < 80 else 16)
p.font.color.rgb = TEXT
p.space_after = Pt(10)
slide.notes_slide.notes_text_frame.text = notes
def main() -> None:
prs = Presentation()
prs.slide_width = Inches(13.333)
prs.slide_height = Inches(7.5)
slides: list[tuple[str, list[str], str, bool]] = [
(
"金融四 Agent 智能代销平台",
[],
"",
True,
),
]
# Title handled separately
_add_title_slide(
prs,
"金融四 Agent 智能代销平台",
"答辩演示 · XX 科技综合财富 · merger 基线",
"开场:代销四角色 + L0 与合规底座;Core 为模拟库;四 Agent 不互调 LLM。",
)
deck: list[tuple[str, list[str], str]] = [
(
"我们要做什么(网站三句话)",
[
"用于:持牌财富代销场景下的 AI 工作台(Web + FastAPI),不是营销落地页",
"业务:XX 科技综合财富平台 · 前端视觉「静奢智能」",
"用户:客户看资产与对话 · 分析员问数 · 风控处置 · 理财师看归属(顾问 Agent 未完整接入)",
],
"对应用户模板:用途 / 业务名 / 用户能做什么。",
),
(
"总架构(30 秒)",
[
"四角色共用 L0 事实 + 合规底座;跨线靠画像表、预警表、代销 REST",
"分层:浏览器 → FastAPI → Service → Tool/Repository → MySQL 双库 / Redis / Milvus / Core",
"对话:通用 Chat/SSE · 客服 14 节点 · 问数独立(interpret 按需解读)",
"铁律:L0 不可覆盖 · 仅 R-02 可阻断交易 · 不自动冻户 · 审计只 INSERT",
],
"MEMORY §0 + 答辩清单 §一。",
),
(
"数据怎么流",
[
"L0 Core 模拟:Agent 只读;仅模拟交易网关 INSERT core_trade",
"jinrong_agent 与 jinrong_core 物理隔离",
"Redis 6380:会话窗口、限流、问数缓存世代;MySQL 永久落盘",
"Milvus:产品规则 RAG(Ollama bge-m3 1024 维,不出内网)",
],
"§二 数据分层表口述。",
),
(
"共用底座 Wave 0",
[
"JWT + RBAC + X-Agent-Type 准入 · dev token 与 gateway 双栈(AL-09)",
"输入防护:限流 / 注入 / 超长 · 被拒 fail-fast 不建会话",
"审计:trace_id 贯通 http_access + audit_log + agent_tool_call",
"Tool:customer_id 会话注入,LLM 不能指定 · 归属校验 + 落库",
],
"F-01~F-04 演示工程化。",
),
(
"客服 Agent(主 Demo 之一)",
[
"14 节点 LangGraph:Core Tool + fin_* RAG + 1B sanitize 出口",
"C-04/C-05:阈值提醒、净值查询 · C-11 适当性 vs 营销 reject",
"Demo:CUST-9527 持仓 + 阈值 · 游客 RAG",
"SSE:Tool 同步完成后再推文本,避免半句不一致",
],
"步骤 1 客户助手 + 持仓页。",
),
(
"数据分析 Agent(主 Demo 之二)",
[
"问数与 Chat 分离:默认 interpret=false 出表 · 按钮触发 /interpret",
"五层 sql_guard + 模板命中 Tag + 结果缓存世代(D-06)",
"D-10:解读数字与表逐字比对,失败降级只出表",
"Demo:分析员问数 → 模板/缓存 Tag → 解读按钮",
],
"DEMO-SOP-问数.md。",
),
(
"风控 Agent(主 Demo 之三)",
[
"事件线:模拟交易 → R-02 适当性 → 规则 R-01~R-05 → 聚合预警 → 人工处置",
"对话线:6 只只读 Tool,无处置 Tool(状态不变)",
"前端:预警台账 / 适当性 / AML / 模拟交易 + 风控 Chat",
"Demo:prepare_all.ps1 · A-1/A-3 · 台账筛选处置",
],
"演示SOP-风控模块.md。",
),
(
"代销平台 API + 前端 web/",
[
"v0.1 canonical REST:客户/产品/顾问/合规 · 重复能力以平台 API 为准",
"React 19 + Vite + Ant Design · HashRouter · /api 代理 8000",
"四角色 Dashboard + 产品行情(nav/history 图表)",
"DevReadyBanner:前后端版本不一致先告警",
],
"FRONTEND-HANDOFF + 登录四 Demo 账号。",
),
(
"诚实边界(答辩加分)",
[
"理财师 Agent:无 advisor_service 级编排 · A-01~A-05 未做 · 勿作主 Demo",
"问数:D-09 多轮 / D-12 钻取仍 open",
"看板:无钻取 · 进页全量拉平台 API",
"测试:827 pytest · 22 Vitest(口播基线以当日 pytest 为准)",
],
"§4.2 §4.3 §4.6 诚实缺口。",
),
(
"技术选型(为什么这样)",
[
"LangGraph 显式图 · MySQL 双库隔离 · Milvus Lite 本机 RAG",
"DeepSeek 生成 · 无 Key 明确降级 · Redis RESP2 @6380",
"FastAPI 一体栈 · React+AntD 四角色 Demo 快",
"不用 Streamlit · 不用 Agent 互调 LLM",
],
"§五 选型表。",
),
(
"工程化亮点",
[
"契约优先:代销 API v0.1 · AL-09 合并接缝",
"双护栏:输入防护 + 客服 sanitize + 问数 guardrail",
"RAG 溯源 source_refs · 问数 battery 本地跑分不入库",
"docs/memory + docs/course 交互课 + 本套 SOP",
],
"§六 加分项。",
),
(
"Demo 动线(12~15 分钟标准)",
[
"0 开场 30 秒铁律 + 架构句",
"1 客户:登录 → 持仓/阈值 → 客户助手",
"2 分析员:问数 → Tag → 解读",
"3 风控:模拟交易 → 台账 → 处置",
"4 可选:行情页 · /api/ready 探针",
],
"DEMO-SOP-答辩全流程.md 路线 B。",
),
(
"谢谢 · Q&A",
[
"配套文档:docs/答辩/答辩知识点清单.md",
"Demo:docs/答辩/DEMO-SOP-答辩全流程.md",
"交互课:docs/course/jinrong-overview/index.html",
"仓库:JinRong · 分支 merger",
],
"预留 Q&A:适当性、审计、问数 guard、顾问线规划。",
),
]
for title, bullets, notes in deck:
_add_section_slide(prs, title, bullets, notes)
OUT.parent.mkdir(parents=True, exist_ok=True)
prs.save(str(OUT))
print(f"Wrote {OUT} ({len(prs.slides)} slides)")
if __name__ == "__main__":
main()