feat: 前后端分离架构 — FastAPI SSE后端 + Vue 3前端

将单体 Streamlit 应用拆分为三层架构:
- api_server.py: FastAPI SSE 流式后端 (端口 8000)
- frontend/: Vue 3 + Vite + Pinia 聊天前端 (端口 5173)
- agent/graph.py: 新增 node_start 回调支持
- 更新启动脚本为三服务模式

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-21 20:04:27 +08:00
parent 2befd44430
commit 74f3f03d2c
29 changed files with 3668 additions and 72 deletions
+40 -22
View File
@@ -31,8 +31,8 @@ from agent.nodes import (
)
from backend.logger import get_logger
load_dotenv()
MAX_RETRY = int(os.getenv("MAX_RETRY", "3"))
load_dotenv(override=True)
MAX_RETRY = int(os.getenv("MAX_RETRY", "5"))
_graph_log = get_logger("agent")
@@ -147,33 +147,51 @@ def route_after_correct(state: AgentState) -> Literal["validate", "finalize"]:
# 图构建
# ============================================================
def build_graph() -> StateGraph:
def build_graph(on_node_start=None) -> StateGraph:
"""构建 LangGraph 状态图。
Args:
on_node_start: 可选回调,在每个节点开始执行时调用。
签名: on_node_start(node_name: str) -> None
用于 SSE 流式推送 node_start 事件。
"""
workflow = StateGraph(AgentState)
def _wrap(name, fn):
"""包装节点函数,在开始执行时触发 on_node_start 回调。"""
if on_node_start is None:
return fn
@functools.wraps(fn)
def wrapped(state, *args, **kwargs):
on_node_start(name)
return fn(state, *args, **kwargs)
return wrapped
# 现有节点
workflow.add_node("load_session", load_session_node)
workflow.add_node("process_input", process_input)
workflow.add_node("manage_context", manage_context)
workflow.add_node("save_session", save_session_node)
workflow.add_node("retrieve", retrieve)
workflow.add_node("generate", generate)
workflow.add_node("modify_jrxml", modify_jrxml)
workflow.add_node("validate", validate)
workflow.add_node("explain_error", explain_error)
workflow.add_node("correct_jrxml", correct_jrxml)
workflow.add_node("finalize", finalize)
workflow.add_node("load_session", _wrap("load_session", load_session_node))
workflow.add_node("process_input", _wrap("process_input", process_input))
workflow.add_node("manage_context", _wrap("manage_context", manage_context))
workflow.add_node("save_session", _wrap("save_session", save_session_node))
workflow.add_node("retrieve", _wrap("retrieve", retrieve))
workflow.add_node("generate", _wrap("generate", generate))
workflow.add_node("modify_jrxml", _wrap("modify_jrxml", modify_jrxml))
workflow.add_node("validate", _wrap("validate", validate))
workflow.add_node("explain_error", _wrap("explain_error", explain_error))
workflow.add_node("correct_jrxml", _wrap("correct_jrxml", correct_jrxml))
workflow.add_node("finalize", _wrap("finalize", finalize))
# 新增节点:意图识别
workflow.add_node("save_state_snapshot", save_state_snapshot)
workflow.add_node("classify_intent", classify_intent)
workflow.add_node("handle_consult", handle_consult)
workflow.add_node("handle_undo", handle_undo)
workflow.add_node("handle_reset", handle_reset)
workflow.add_node("save_state_snapshot", _wrap("save_state_snapshot", save_state_snapshot))
workflow.add_node("classify_intent", _wrap("classify_intent", classify_intent))
workflow.add_node("handle_consult", _wrap("handle_consult", handle_consult))
workflow.add_node("handle_undo", _wrap("handle_undo", handle_undo))
workflow.add_node("handle_reset", _wrap("handle_reset", handle_reset))
# 新增节点:分层精确生成(阶段一~三)
workflow.add_node("generate_skeleton", generate_skeleton)
workflow.add_node("refine_layout", refine_layout)
workflow.add_node("map_fields", map_fields)
workflow.add_node("generate_skeleton", _wrap("generate_skeleton", generate_skeleton))
workflow.add_node("refine_layout", _wrap("refine_layout", refine_layout))
workflow.add_node("map_fields", _wrap("map_fields", map_fields))
# ---- 入口和前置流程 ----
workflow.set_entry_point("load_session")