Files

167 lines
5.8 KiB
Python

"""
AI Review 测试页面
启动方式: streamlit run ai_review/test_app.py
"""
import streamlit as st
import requests
API = "http://127.0.0.1:8007"
st.set_page_config(page_title="AI Review 测试", layout="wide")
st.title("AI Review 审核测试")
# ── 左侧:审核区 ──
left, right = st.columns([2, 1])
with left:
st.subheader("内容审核")
content = st.text_area("输入待审核文本", height=150,
value=st.session_state.get("input_text", ""),
key="content")
if st.button("提交审核", type="primary", use_container_width=True):
if not content.strip():
st.warning("请输入内容")
else:
resp = requests.post(f"{API}/review", json={"content": content})
if resp.ok:
data = resp.json()
st.session_state["last_result"] = data
st.session_state["last_content"] = content
else:
st.error(f"请求失败: {resp.text}")
# 显示审核结果
if "last_result" in st.session_state:
result = st.session_state["last_result"]
invalid = result.get("invalid") in (True, "true")
st.divider()
if invalid:
st.error(f"审核结果: 违规")
else:
st.success(f"审核结果: 通过")
with st.expander("原始响应"):
st.json(result)
# 人工纠偏
st.divider()
st.subheader("人工纠偏")
if invalid:
st.caption("AI 判违规但人工认为正常?点击下方按钮纠正。")
if st.button("人工纠正为正常", type="secondary"):
correct = st.session_state["last_content"]
resp = requests.post(
f"{API}/rag/feedback",
json={"content": correct, "correct_type": "safe"},
)
if resp.ok:
fb = resp.json()
if fb["added"]:
st.toast("已写入 RAG 库")
else:
st.toast("已存在,跳过")
else:
st.caption("AI 放行了但人工发现违规?点击下方按钮纠正。")
if st.button("人工纠正为违规", type="secondary"):
correct = st.session_state["last_content"]
resp = requests.post(
f"{API}/rag/feedback",
json={"content": correct, "correct_type": "violation"},
)
if resp.ok:
fb = resp.json()
if fb["added"]:
st.toast("已写入 RAG 库")
else:
st.toast("已存在,跳过")
# ── 右侧:快捷用例 ──
with right:
st.subheader("快捷用例")
test_cases = {
"违规-色情服务": "加我微信xxxxx,裸聊视频一对一服务,学生妹上门",
"违规-赌博": "真人百家乐在线投注,包赢不赔,注册送彩金",
"违规-假证": "专业办理身份证驾驶证,高仿毕业证,货到付款",
"违规-药品": "迷奸水听话水货到付款,保密发货",
"安全-日常": "今天天气真好,适合出门散步",
"安全-技术": "Python的装饰器本质上是一个高阶函数",
"安全-娱乐": "请问最近有什么好看的电影推荐吗",
"安全-学习": "机器学习入门需要先掌握线性代数和概率论",
}
for label, text in test_cases.items():
if st.button(label, use_container_width=True):
st.session_state["input_text"] = text
st.rerun()
st.divider()
st.subheader("服务状态")
try:
resp = requests.get(f"{API}/health")
if resp.ok:
st.success("服务在线")
else:
st.error("服务异常")
except Exception:
st.error("无法连接服务")
# ── 底部:RAG 知识库 ──
st.divider()
st.subheader("RAG 知识库")
if st.button("刷新知识库", type="secondary"):
st.rerun()
try:
resp = requests.get(f"{API}/rag/list")
if resp.ok:
data = resp.json()
entries = data.get("entries", [])
total = data.get("total", 0)
if total == 0:
st.info("知识库为空")
else:
violation_count = sum(1 for e in entries if e["type"] == "violation")
safe_count = sum(1 for e in entries if e["type"] == "safe")
col1, col2, col3 = st.columns(3)
col1.metric("总条目", total)
col2.metric("违规样本", violation_count)
col3.metric("安全样本", safe_count)
rows = []
for e in entries:
src = "预设" if e["category"] != "human_feedback" else "人工反馈"
tag = "违规" if e["type"] == "violation" else "安全"
rows.append({
"标签": tag,
"来源": src,
"分类": e["category"],
"ID": e["id"],
"内容": e["text"][:80] + ("..." if len(e["text"]) > 80 else ""),
})
st.dataframe(
rows,
use_container_width=True,
column_config={
"标签": st.column_config.Column(width="small"),
"来源": st.column_config.Column(width="small"),
"分类": st.column_config.Column(width="medium"),
"ID": st.column_config.Column(width="small"),
"内容": st.column_config.Column(width="large"),
},
)
else:
st.error("无法获取知识库数据")
except Exception as e:
st.error(f"请求失败: {e}")