Merge branch 'data-analysis-agent-work' into merger
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
"""演示:用真实 LLM + 真实 MySQL 跑数据分析 Agent 的典型问答。"""
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
|
||||
|
||||
from app.service.analyst_agent import AnalystAgent
|
||||
from app.utils.auth import AuthContext
|
||||
|
||||
|
||||
def run(question, roles, subject):
|
||||
agent = AnalystAgent()
|
||||
ctx = AuthContext(subject_id=subject, token_type="staff", roles=roles, staff_type=roles[0])
|
||||
resp = agent.run(question, ctx)
|
||||
print(f"\n=== 问题:{question}(角色 {roles[0]})===")
|
||||
print(f"状态:{resp.status} 缓存命中:{resp.meta.cache_hit}")
|
||||
print(f"解读:{resp.answer}")
|
||||
print(f"表格:{json.dumps(resp.table.model_dump(), ensure_ascii=False, default=str)}")
|
||||
print(f"SQL:{resp.sql}")
|
||||
if resp.error_code:
|
||||
print(f"错误码:{resp.error_code} 建议:{resp.suggestions}")
|
||||
return resp
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# 先拿到一个顾问和他的非名下客户
|
||||
agent = AnalystAgent()
|
||||
advisor = agent.repo.execute_readonly(
|
||||
"SELECT staff_id FROM core_staff WHERE staff_type='advisor' AND is_active=1 LIMIT 1"
|
||||
)["rows"][0][0]
|
||||
scope = agent.repo.resolve_advisor_scope(advisor)
|
||||
out_customer = next(
|
||||
c for c in agent.repo.execute_readonly("SELECT customer_id FROM core_customer LIMIT 100")["rows"]
|
||||
if c[0] not in scope
|
||||
)[0]
|
||||
|
||||
run("按产品类型统计总持仓规模", ["analyst"], "STAFF-20001")
|
||||
run(f"查 {out_customer} 的持仓", ["advisor"], advisor)
|
||||
@@ -0,0 +1,30 @@
|
||||
"""诊断单个问题的 SQL 生成与执行(用法:python scripts/dev/diag_question.py "问题" 角色)。"""
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
|
||||
|
||||
from app.service.analyst_agent import AnalystAgent
|
||||
from app.utils.auth import AuthContext
|
||||
|
||||
|
||||
def main():
|
||||
question = sys.argv[1] if len(sys.argv) > 1 else "近30天申购金额总额"
|
||||
role = sys.argv[2] if len(sys.argv) > 2 else "ops"
|
||||
agent = AnalystAgent()
|
||||
ctx = AuthContext(subject_id=f"STAFF-{role}", token_type="staff", roles=[role], staff_type=role)
|
||||
domain = {"analyst": "full", "advisor": "assigned", "risk_officer": "risk", "ops": "aggregate"}[role]
|
||||
scope = []
|
||||
if domain == "assigned":
|
||||
scope = agent.repo.resolve_advisor_scope(ctx.subject_id)
|
||||
sql, _ = agent._generate_sql(question, domain, scope)
|
||||
print("SQL:", sql)
|
||||
try:
|
||||
res = agent.repo.execute_readonly(sql)
|
||||
print("OK cols=", res["columns"], "rows[:3]=", res["rows"][:3])
|
||||
except Exception as exc: # noqa: BLE001
|
||||
print("EXEC ERROR:", type(exc).__name__, exc)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,55 @@
|
||||
"""导出数据分析 Agent 可读表的列结构(用于 NL2SQL 的 schema 元数据注入)。"""
|
||||
import pathlib
|
||||
|
||||
import pymysql
|
||||
|
||||
|
||||
def load_env(path=".env"):
|
||||
env = {}
|
||||
for line in pathlib.Path(path).read_text(encoding="utf-8").splitlines():
|
||||
line = line.strip()
|
||||
if not line or line.startswith("#") or "=" not in line:
|
||||
continue
|
||||
k, v = line.split("=", 1)
|
||||
env[k.strip()] = v.strip()
|
||||
return env
|
||||
|
||||
|
||||
TABLES = [
|
||||
("jinrong_core", "core_customer"),
|
||||
("jinrong_core", "core_customer_risk"),
|
||||
("jinrong_core", "core_customer_advisor"),
|
||||
("jinrong_core", "core_holding"),
|
||||
("jinrong_core", "core_trade"),
|
||||
("jinrong_core", "core_cash_flow"),
|
||||
("jinrong_core", "core_product"),
|
||||
("jinrong_core", "core_product_nav"),
|
||||
("jinrong_core", "core_staff"),
|
||||
("jinrong_core", "core_risk_grade"),
|
||||
("jinrong_agent", "risk_alert"),
|
||||
]
|
||||
|
||||
|
||||
def main():
|
||||
env = load_env()
|
||||
conn = pymysql.connect(
|
||||
host=env.get("MYSQL_HOST", "127.0.0.1"),
|
||||
port=int(env.get("MYSQL_PORT", "3306")),
|
||||
user=env.get("MYSQL_USER", "root"),
|
||||
password=env.get("MYSQL_PASSWORD", ""),
|
||||
charset="utf8mb4",
|
||||
)
|
||||
with conn.cursor() as cur:
|
||||
for db, tbl in TABLES:
|
||||
cur.execute(
|
||||
"SELECT column_name, data_type FROM information_schema.columns "
|
||||
"WHERE table_schema=%s AND table_name=%s ORDER BY ordinal_position",
|
||||
(db, tbl),
|
||||
)
|
||||
cols = [f"{c[0]}:{c[1]}" for c in cur.fetchall()]
|
||||
print(f"{tbl} ({db}) -> {', '.join(cols)}")
|
||||
conn.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,24 @@
|
||||
"""查看种子数据关键事实(用于集成测试与演示)。"""
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
|
||||
|
||||
from app.service.analytics_repo import AnalyticsRepo
|
||||
|
||||
|
||||
def main():
|
||||
repo = AnalyticsRepo()
|
||||
q = lambda sql: repo.execute_readonly(sql) # noqa: E731
|
||||
print("advisor ids:", [r[0] for r in q("SELECT staff_id FROM core_staff WHERE staff_type='advisor' AND is_active=1 LIMIT 5")["rows"]])
|
||||
print("risk_officer ids:", [r[0] for r in q("SELECT staff_id FROM core_staff WHERE staff_type='risk_officer' AND is_active=1 LIMIT 5")["rows"]])
|
||||
print("ops ids:", [r[0] for r in q("SELECT staff_id FROM core_staff WHERE staff_type='ops' AND is_active=1 LIMIT 5")["rows"]])
|
||||
print("analyst ids:", [r[0] for r in q("SELECT staff_id FROM core_staff WHERE staff_type='analyst' AND is_active=1 LIMIT 5")["rows"]])
|
||||
print("risk codes:", q("SELECT risk_code, COUNT(*) c FROM core_customer_risk GROUP BY risk_code ORDER BY risk_code")["rows"])
|
||||
print("product types:", q("SELECT product_type, COUNT(*) c FROM core_product GROUP BY product_type")["rows"])
|
||||
print("alert status:", q("SELECT status, COUNT(*) c FROM jinrong_agent.risk_alert GROUP BY status")["rows"])
|
||||
print("customers sample:", [r[0] for r in q("SELECT customer_id FROM core_customer LIMIT 8")["rows"]])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user