feat: restore risk assessment workspace and improve page scrolling

This commit is contained in:
NathanAlex7
2026-09-07 02:28:37 +08:00
parent db5d6763f1
commit f2439ee5a3
65 changed files with 13612 additions and 2649 deletions
+81 -12
View File
@@ -19,12 +19,17 @@ class AgentManager:
self._base_dir: Path | None = None
self._tools: list = []
self._llm = None
self._raw_logs_dir: Path | None = None
def initialize(self, base_dir: Path) -> None:
"""Initialize LLM (DeepSeek) and tools. Called once at startup."""
self._base_dir = base_dir
self._tools = get_all_tools(base_dir)
# 初始化原始报文日志目录 (base_dir 已是 backend/ 目录)
self._raw_logs_dir = base_dir / "raw_logs"
self._raw_logs_dir.mkdir(exist_ok=True, parents=True)
# Use langchain-deepseek for native DeepSeek support
from langchain_deepseek import ChatDeepSeek
@@ -70,7 +75,7 @@ class AgentManager:
return messages
async def astream(
self, message: str, history: list[dict[str, Any]]
self, message: str, history: list[dict[str, Any]], session_id: str = "default"
) -> AsyncGenerator[dict[str, Any], None]:
"""Stream agent response with token-level and node-level events.
@@ -80,6 +85,8 @@ class AgentManager:
{"type": "tool_start", "tool": "...", "input": "..."}
{"type": "tool_end", "tool": "...", "output": "..."}
{"type": "done", "content": "..."}
Also persists complete message flow to {session}.msgs.jsonl for trajectory.
"""
# RAG retrieval: inject memory context if enabled
rag_mode = get_rag_mode()
@@ -101,6 +108,31 @@ class AgentManager:
)
rag_context = f"[记忆检索结果]\n{snippets}"
# 为当前 session 创建原始报文回调
from graph.raw_trace import RawTraceCallback
raw_callback = RawTraceCallback(session_id, self._raw_logs_dir)
# 消息流日志文件(记录完整的 human/assistant/tool 消息序列)
import json
import time
msgs_log_file = self._raw_logs_dir / f"{session_id}.msgs.jsonl"
# 记录 human 消息
def _append_msg(role: str, content: str, **kwargs):
"""追加一条消息到 msgs.jsonl"""
msg_record = {
"role": role,
"content": content,
"timestamp": time.time(),
**kwargs
}
with open(msgs_log_file, "a", encoding="utf-8") as f:
f.write(json.dumps(msg_record, ensure_ascii=False) + "\n")
_append_msg("human", message)
# 构建 agent 时传入 callback
agent = self._build_agent()
# Build messages with optional RAG context appended to history
@@ -113,10 +145,13 @@ class AgentManager:
full_response = ""
tools_just_finished = False
current_assistant_content = "" # 当前 assistant 消息累积
current_tool_calls = [] # 当前 assistant 的 tool_calls
async for event in agent.astream(
{"messages": messages},
stream_mode=["messages", "updates"],
config={"callbacks": [raw_callback]},
):
# event is a tuple of (stream_mode, data) when using multiple modes
if isinstance(event, tuple):
@@ -136,31 +171,65 @@ class AgentManager:
yield {"type": "new_response"}
tools_just_finished = False
full_response += msg.content
current_assistant_content += msg.content
yield {"type": "token", "content": msg.content}
elif mode == "updates":
if isinstance(data, dict):
for node_name, node_data in data.items():
if node_name == "tools" and "messages" in node_data:
for tool_msg in node_data["messages"]:
if hasattr(tool_msg, "name"):
yield {
"type": "tool_end",
"tool": tool_msg.name,
"output": str(tool_msg.content)[:2000],
}
# After all tool results, mark that tools finished
tools_just_finished = True
elif node_name == "model" and "messages" in node_data:
if node_name == "model" and "messages" in node_data:
# 模型节点:记录 assistant 消息和 tool_calls
for agent_msg in node_data["messages"]:
if hasattr(agent_msg, "tool_calls") and agent_msg.tool_calls:
# 记录 assistant 消息(带 tool_calls)
tool_calls_data = []
for tc in agent_msg.tool_calls:
tool_call_info = {
"name": tc["name"],
"args": tc.get("args", {})
}
tool_calls_data.append(tool_call_info)
current_tool_calls.append(tool_call_info)
yield {
"type": "tool_start",
"tool": tc["name"],
"input": str(tc.get("args", ""))[:1000],
}
# 记录带 tool_calls 的 assistant 消息
_append_msg(
"assistant",
agent_msg.content if hasattr(agent_msg, "content") else "",
tool_calls=tool_calls_data
)
elif node_name == "tools" and "messages" in node_data:
# 工具节点:记录每个 ToolMessage 的返回内容
for tool_msg in node_data["messages"]:
if hasattr(tool_msg, "name"):
tool_output = str(tool_msg.content)
# 记录 tool 消息(完整 content)
_append_msg(
"tool",
tool_output,
name=tool_msg.name,
tool_call_id=getattr(tool_msg, "tool_call_id", None)
)
yield {
"type": "tool_end",
"tool": tool_msg.name,
"output": tool_output[:2000], # yield 时截断,但完整内容已记录
}
# After all tool results, mark that tools finished
tools_just_finished = True
# 记录最终的 assistant 消息(纯回复,无 tool_calls)
if current_assistant_content and not current_tool_calls:
_append_msg("assistant", current_assistant_content)
elif current_assistant_content and current_tool_calls:
# 如果有 tool_calls,记录最后一轮的纯文本回复
_append_msg("assistant", current_assistant_content)
yield {"type": "done", "content": full_response}
async def ainvoke(self, message: str, session_id: str) -> str: