feat: 添加结构化日志系统,更新LLM配置与全部文档
新增: - backend/logger.py — 集中日志模块 (JSON格式 + trace_id + 独立llm.log) - @log_node / @_log_route 装饰器覆盖17个节点和8个路由 改进: - backend/llm.py — _LLMLoggingWrapper 自动记录LLM输入输出 - backend/llm.py — API Key优先读ANTHROPIC_API_KEY,模型名改为MiniMax-M2.7 - backend/llm.py — get_llm() 新增caller参数标识调用来源 - backend/validation.py — 新增验证结果/连接失败日志 - backend/session.py — 新增会话创建/删除日志 - app.py — 新增用户交互日志 (输入/执行/异常/会话操作) - app.py — 提前导入torchvision抑制transformers懒加载报错 - .env.example — 新增LOG_DIR/LOG_LEVEL/ANTHROPIC_API_KEY等配置项 - .gitignore — 新增logs/和db/忽略规则 文档: - ROADMAP.md — 新增阶段四: 可观测性 - README.md — 补充日志架构/LLM配置/项目结构 - CLAUDE.md — 同步最新配置/日志/MAX_RETRY(3) - CODE_GUIDE.md — 新增第15章日志系统,更新架构图/LLM/配置
This commit is contained in:
@@ -9,6 +9,15 @@
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
os.environ.setdefault("TRANSFORMERS_VERBOSITY", "error")
|
||||
|
||||
try:
|
||||
import torchvision
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
import streamlit as st
|
||||
@@ -23,6 +32,9 @@ from backend.session import (
|
||||
delete_session,
|
||||
list_all_sessions,
|
||||
)
|
||||
from backend.logger import get_logger, set_trace_id, generate_trace_id
|
||||
|
||||
_app_log = get_logger("app")
|
||||
|
||||
st.set_page_config(
|
||||
page_title="JRXML 代理",
|
||||
@@ -32,7 +44,7 @@ st.set_page_config(
|
||||
)
|
||||
|
||||
# 阻止 Streamlit 裸 'c' 键清除缓存,保留 Ctrl+C 复制行为
|
||||
st.components.v1.html("""
|
||||
st.html("""
|
||||
<script>
|
||||
(function() {
|
||||
const parent = window.parent.document;
|
||||
@@ -48,7 +60,7 @@ st.components.v1.html("""
|
||||
}, true);
|
||||
})();
|
||||
</script>
|
||||
""", height=0)
|
||||
""")
|
||||
|
||||
# ---- 节点名称 → 中文标签 ----
|
||||
NODE_LABELS = {
|
||||
@@ -130,7 +142,22 @@ current_session_id = st.session_state.agent_state.get("session_id", "")
|
||||
|
||||
def run_agent(user_input: str):
|
||||
"""运行代理图:流式渲染节点进度 + LLM 文本。"""
|
||||
trace_id = generate_trace_id()
|
||||
set_trace_id(trace_id)
|
||||
agent_state = st.session_state.agent_state
|
||||
session_id = agent_state.get("session_id", "")
|
||||
|
||||
_app_log.info(
|
||||
"代理执行开始",
|
||||
extra={
|
||||
"session_id": session_id,
|
||||
"trace_id": trace_id,
|
||||
"user_input_preview": user_input[:200],
|
||||
"user_input_length": len(user_input),
|
||||
"has_jrxml": bool(agent_state.get("current_jrxml", "").strip()),
|
||||
"intent": agent_state.get("intent", ""),
|
||||
},
|
||||
)
|
||||
|
||||
if agent_state.get("current_jrxml") and agent_state.get("status") == "pass":
|
||||
agent_state["user_modification_request"] = user_input
|
||||
@@ -222,6 +249,10 @@ def run_agent(user_input: str):
|
||||
|
||||
except Exception as e:
|
||||
progress_placeholder.empty()
|
||||
_app_log.error(
|
||||
f"代理执行异常: {e}",
|
||||
extra={"session_id": session_id, "error": str(e)},
|
||||
)
|
||||
st.error(f"工作流异常: {e}")
|
||||
return
|
||||
|
||||
@@ -296,6 +327,17 @@ def run_agent(user_input: str):
|
||||
else:
|
||||
st.error("未产生结果,请重试。")
|
||||
|
||||
_app_log.info(
|
||||
"代理执行完成",
|
||||
extra={
|
||||
"session_id": session_id,
|
||||
"intent": final_state.get("intent", ""),
|
||||
"status": final_state.get("status", ""),
|
||||
"jrxml_length": len(final_state.get("current_jrxml", "")),
|
||||
"retry_count": final_state.get("retry_count", 0),
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
# ---- 侧边栏 ----
|
||||
with st.sidebar:
|
||||
@@ -330,6 +372,10 @@ with st.sidebar:
|
||||
new_sid = session_options[selected]
|
||||
data = load_session(new_sid)
|
||||
if data and data.get("agent_state"):
|
||||
_app_log.info(
|
||||
"切换会话",
|
||||
extra={"from_session": current_session_id, "to_session": new_sid},
|
||||
)
|
||||
st.session_state.agent_state = data["agent_state"]
|
||||
st.session_state.messages = []
|
||||
st.rerun()
|
||||
@@ -338,6 +384,10 @@ with st.sidebar:
|
||||
with col1:
|
||||
if st.button("➕ 新建", use_container_width=True):
|
||||
new_data = create_session(name="", agent_state=create_initial_state())
|
||||
_app_log.info(
|
||||
"新建会话",
|
||||
extra={"session_id": new_data["session_id"]},
|
||||
)
|
||||
st.session_state.agent_state = create_initial_state()
|
||||
st.session_state.agent_state["session_id"] = new_data["session_id"]
|
||||
st.session_state.agent_state["session_name"] = new_data["session_name"]
|
||||
@@ -347,6 +397,10 @@ with st.sidebar:
|
||||
with col2:
|
||||
if st.button("🗑 删除", use_container_width=True):
|
||||
if current_session_id:
|
||||
_app_log.info(
|
||||
"删除会话",
|
||||
extra={"session_id": current_session_id},
|
||||
)
|
||||
delete_session(current_session_id)
|
||||
st.session_state.agent_state = create_initial_state()
|
||||
new_data = create_session(name="", agent_state=st.session_state.agent_state)
|
||||
@@ -541,15 +595,28 @@ for msg in st.session_state.messages:
|
||||
if prompt := st.chat_input("描述您的报表需求..."):
|
||||
# 拼接上传文件的文本
|
||||
uploaded_texts = []
|
||||
uploaded_files_info = []
|
||||
if st.session_state.get("uploaded_files"):
|
||||
for f in st.session_state.uploaded_files:
|
||||
uploaded_texts.append(f"[上传文件: {f['name']}]\n{f['text']}")
|
||||
uploaded_files_info.append({"name": f["name"], "type": f["type"], "length": len(f["text"])})
|
||||
if uploaded_texts:
|
||||
full_prompt = "\n\n".join(uploaded_texts) + "\n\n---\n用户需求:\n" + prompt
|
||||
st.session_state.uploaded_files = [] # 用后即清
|
||||
else:
|
||||
full_prompt = prompt
|
||||
|
||||
_app_log.info(
|
||||
"收到用户输入",
|
||||
extra={
|
||||
"session_id": current_session_id,
|
||||
"prompt_preview": prompt[:200],
|
||||
"prompt_length": len(prompt),
|
||||
"has_uploaded_files": bool(uploaded_files_info),
|
||||
"uploaded_files": uploaded_files_info,
|
||||
},
|
||||
)
|
||||
|
||||
st.session_state.messages.append({"role": "user", "content": prompt})
|
||||
with st.chat_message("user"):
|
||||
st.markdown(prompt)
|
||||
|
||||
Reference in New Issue
Block a user