Files
XingHuo/tests/_ddl.py
T
GaoYiYuan_0626 301c78edc3 feat: T-04 Core RO Tool 节点——chat Tool 接入+归属校验+agent_tool_call 落库
- app/tool/core_tools.py: 三只读 Tool(query_customer_profile/holdings/recent_trades, Core RO 仅 SELECT) + TOOL_REGISTRY 白名单(requires_customer); JSON 安全化(Decimal 两位/时间 isoformat)
- app/service/tool_service.py: match_intent 关键词意图(仅 customer/advisor; risk/analyst 空转归 C1/C2) + assert_tool_access 归属断言(customer 本人/advisor assigned/risk_officer 全量/其余拒, 口径对齐 deps.assert_customer_access) + run_tool 编排(白名单→校验→执行→agent_tool_call 落库, 落库失败降级 warning)
- agent_service: 图 START→tool→llm→guard; Tool 结果注入 LLM 上下文(SystemMessage); 降级回复带 Tool 摘要; chat() 可选 session 上下文(缺省空转, T-07 兼容)
- session_repository: insert_tool_call(message_id 一期 NULL, session_id+trace_id 可还原)
- 归属拒绝口径: Tool 层不抛 403 改 blocked 留痕(AUTH_403_* 同码), 对话内呈现——API 层 deny 铁律不变
- tests: test_chat_tools 20 例(三态+落库字段+降级+图注入) + test_chat 4 例(全链路/JWT 外 debug 通道), _ddl 补 agent_tool_call/core_holding, 297 绿
- 真库冒烟: uvicorn+真 MySQL/Redis chat 触发持仓查询 success 落痕 47ms, 现场已清理
2026-09-07 08:43:36 +08:00

127 lines
5.9 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.
"""sqlite 测试 DDL 单一事实源(B8 · B4 评审 P3-12 收敛 / B5 评审 P3-4 时区)。
以 VARCHAR/TEXT/INTEGER/REAL 近似 MySQL 的 ENUM/SMALLINT/JSON/DECIMAL,
枚举防线不在测试 DB 层(由 service 白名单保证,见 test_profile_l3 口径注释)。
时间默认值统一 `(datetime('now','localtime'))`:sqlite CURRENT_TIMESTAMP 为 UTC,
本地 23:00~24:00 跑测试时与 MySQL localtime 口径错位一个日界(B5 评审 P3-4)。
列集为各测试所需列的并集;新表/新列只改此处,禁止再回测试文件手写。
"""
from sqlalchemy import create_engine, text
from sqlalchemy.pool import StaticPool
_TS = "TIMESTAMP DEFAULT (datetime('now','localtime'))"
SQLITE_TABLES: dict[str, str] = {
# ---- Core 模拟库(只读侧) ----
"core_customer": """
CREATE TABLE core_customer (
customer_id VARCHAR(64) PRIMARY KEY, display_name VARCHAR(128), age INTEGER,
occupation VARCHAR(64), open_date DATE, is_active TINYINT DEFAULT 1)
""",
"core_customer_risk": """
CREATE TABLE core_customer_risk (
customer_id VARCHAR(64), risk_code VARCHAR(8), evaluated_at TIMESTAMP)
""",
"core_customer_advisor": """
CREATE TABLE core_customer_advisor (
advisor_id VARCHAR(64), customer_id VARCHAR(64), rel_status VARCHAR(16))
""",
"core_product": """
CREATE TABLE core_product (
product_id VARCHAR(64) PRIMARY KEY, product_name VARCHAR(128),
min_risk_code VARCHAR(8), product_type VARCHAR(32))
""",
"core_holding": """
CREATE TABLE core_holding (
customer_id VARCHAR(64), product_id VARCHAR(64),
market_value DECIMAL, quantity DECIMAL)
""",
"core_trade": """
CREATE TABLE core_trade (
trade_id VARCHAR(64) PRIMARY KEY, customer_id VARCHAR(64), product_id VARCHAR(64),
trade_type VARCHAR(16), amount DECIMAL, trade_status VARCHAR(16),
traded_at TIMESTAMP)
""",
# ---- agent 库 ----
"audit_log": f"""
CREATE TABLE audit_log (
id INTEGER PRIMARY KEY AUTOINCREMENT, trace_id VARCHAR(64), event_type VARCHAR(64),
agent_type VARCHAR(16), actor_id VARCHAR(64), customer_id VARCHAR(64),
rule_id VARCHAR(64), input_summary TEXT, decision VARCHAR(64), risk_score INTEGER,
handler_id VARCHAR(64), handler_result VARCHAR(64), handler_comment VARCHAR(512),
created_at {_TS})
""",
"input_guard_log": f"""
CREATE TABLE input_guard_log (
id INTEGER PRIMARY KEY AUTOINCREMENT, trace_id VARCHAR(64), session_id VARCHAR(64),
agent_type VARCHAR(16), actor_id VARCHAR(64), guard_type VARCHAR(24),
raw_excerpt VARCHAR(1024), action VARCHAR(16),
created_at {_TS})
""",
"agent_session": f"""
CREATE TABLE agent_session (
id INTEGER PRIMARY KEY AUTOINCREMENT, session_id VARCHAR(64) UNIQUE,
trace_id VARCHAR(64), agent_type VARCHAR(16), actor_id VARCHAR(64),
actor_role VARCHAR(32), customer_id VARCHAR(64), advisor_id VARCHAR(64),
title VARCHAR(256), status VARCHAR(16) DEFAULT 'active',
created_at {_TS}, closed_at TIMESTAMP)
""",
"agent_message": f"""
CREATE TABLE agent_message (
id INTEGER PRIMARY KEY AUTOINCREMENT, session_id VARCHAR(64),
trace_id VARCHAR(64), seq_no INTEGER, role VARCHAR(16),
content TEXT, has_disclaimer TINYINT DEFAULT 0, token_est INTEGER,
created_at {_TS})
""",
"agent_tool_call": f"""
CREATE TABLE agent_tool_call (
id INTEGER PRIMARY KEY AUTOINCREMENT, session_id VARCHAR(64),
trace_id VARCHAR(64), message_id INTEGER, tool_name VARCHAR(128),
tool_input TEXT, tool_output TEXT, status VARCHAR(16),
error_code VARCHAR(64), latency_ms INTEGER,
created_at {_TS})
""",
"risk_alert": f"""
CREATE TABLE risk_alert (
alert_id VARCHAR(64) PRIMARY KEY, trace_id VARCHAR(64), customer_id VARCHAR(64),
trade_id VARCHAR(64), alert_type VARCHAR(16), triggered_rules TEXT,
risk_score INTEGER, status VARCHAR(24) DEFAULT 'pending_review', payload TEXT,
handler_id VARCHAR(64), handler_result VARCHAR(64), handler_comment VARCHAR(512),
created_at {_TS}, handled_at TIMESTAMP)
""",
"risk_suitability_log": f"""
CREATE TABLE risk_suitability_log (
id INTEGER PRIMARY KEY AUTOINCREMENT, trace_id VARCHAR(64), customer_id VARCHAR(64),
product_id VARCHAR(64), customer_risk_level VARCHAR(8), product_risk_level VARCHAR(8),
is_matched TINYINT, is_blocked TINYINT, block_reason VARCHAR(512),
request_ref VARCHAR(64), profile_l1_version VARCHAR(32),
created_at {_TS})
""",
"customer_profile_l3": f"""
CREATE TABLE customer_profile_l3 (
customer_id VARCHAR(64) PRIMARY KEY, monitor_tier VARCHAR(16) NOT NULL,
risk_score INTEGER, score_dimensions TEXT, monitor_tags TEXT,
last_alert_id VARCHAR(64), computed_at TIMESTAMP NOT NULL,
updated_at {_TS})
""",
"risk_aml_list": f"""
CREATE TABLE risk_aml_list (
id INTEGER PRIMARY KEY AUTOINCREMENT, list_id VARCHAR(64), list_type VARCHAR(16),
full_name VARCHAR(128), id_no VARCHAR(32), bank_card_no VARCHAR(32),
match_threshold REAL, source VARCHAR(64), list_version VARCHAR(16),
effective_date DATE, is_active TINYINT DEFAULT 1, created_at {_TS})
""",
}
def create_sqlite_engine():
"""内存 sqlite + 全部表(StaticPool 单连接共享,同现有测试模式)。"""
engine = create_engine(
"sqlite://", poolclass=StaticPool, connect_args={"check_same_thread": False}
)
with engine.begin() as conn:
for ddl in SQLITE_TABLES.values():
conn.execute(text(ddl))
return engine