From 14788cad6a703853b2e05ee756890ed96ba0076f Mon Sep 17 00:00:00 2001 From: minorQAQ <202200120184@mail.sdu.edu.cn> Date: Wed, 27 May 2026 17:39:55 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9Erag=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=EF=BC=8C=E6=94=AF=E6=8C=81=E7=94=A8=E6=88=B7=E4=B8=8A=E4=BC=A0?= =?UTF-8?q?=E3=80=81=E8=81=8A=E5=A4=A9=E6=8F=90=E4=BE=9B=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E5=A4=B9=E6=88=96=E6=96=87=E4=BB=B6=E5=9C=B0=E5=9D=80=E4=B8=8A?= =?UTF-8?q?=E4=BC=A0rag=E7=9F=A5=E8=AF=86=E5=BA=93=EF=BC=8C=E5=88=A0?= =?UTF-8?q?=E9=99=A4=E3=80=81=E6=9F=A5=E8=AF=A2=E4=B8=AA=E4=BA=BA=E7=9F=A5?= =?UTF-8?q?=E8=AF=86=E5=BA=93=E5=8F=8A=E6=9A=B4=E9=9C=B2source=E7=BB=99age?= =?UTF-8?q?nt=E4=BB=A5=E8=8E=B7=E5=8F=96=E6=89=80=E6=9C=89=E7=9F=A5?= =?UTF-8?q?=E8=AF=86=E6=9D=A5=E6=BA=90=EF=BC=9B=E6=96=B0=E5=A2=9E=E8=8E=B7?= =?UTF-8?q?=E5=8F=96=E6=96=87=E4=BB=B6tree=20.=20/f=E5=B7=A5=E5=85=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 12 +- requirements.txt | 12 +- .../__pycache__/chatbot_agent.cpython-312.pyc | Bin 4691 -> 9574 bytes src/agent/__pycache__/llm.cpython-312.pyc | Bin 21730 -> 22678 bytes src/agent/__pycache__/llm.cpython-313.pyc | Bin 0 -> 23722 bytes src/agent/chatbot_agent.py | 190 +++++-- src/agent/history/data_base/chat_history.db | Bin 20480 -> 24576 bytes .../20260519_144316/ui_messages.json | 22 - .../20260527_130428/ui_messages.json | 22 + .../20260527_130748/ui_messages.json | 56 ++ src/agent/llm.py | 35 +- .../data_level0.bin | Bin 0 -> 423600 bytes .../header.bin | Bin 0 -> 100 bytes .../length.bin | Bin 0 -> 400 bytes .../link_lists.bin | 0 src/agent/memory/chroma_rag_db/chroma.sqlite3 | Bin 0 -> 430080 bytes src/agent/tools/__init__.py | 3 +- .../__pycache__/__init__.cpython-312.pyc | Bin 555 -> 577 bytes .../tools/__pycache__/tool.cpython-312.pyc | Bin 12477 -> 15997 bytes .../__pycache__/tool_rag.cpython-312.pyc | Bin 16224 -> 24391 bytes src/agent/tools/tool.py | 167 ++++-- src/agent/tools/tool_rag.py | 509 +++++++++++++----- .../__pycache__/agent_utils.cpython-312.pyc | Bin 12879 -> 25494 bytes .../__pycache__/image_utils.cpython-312.pyc | Bin 6808 -> 6808 bytes src/agent/utils/agent_utils.py | 287 +++++++++- src/web/__pycache__/server.cpython-312.pyc | Bin 20280 -> 21766 bytes src/web/__pycache__/server.cpython-313.pyc | Bin 0 -> 22362 bytes .../__pycache__/ui_session.cpython-312.pyc | Bin 22205 -> 24449 bytes .../__pycache__/ui_session.cpython-313.pyc | Bin 0 -> 23915 bytes src/web/app.css | 53 ++ src/web/app.js | 155 +++--- src/web/image/文档.svg | 1 + src/web/image/附件.svg | 1 + src/web/index.html | 12 +- src/web/server.py | 39 +- src/web/ui_session.py | 66 ++- 36 files changed, 1238 insertions(+), 404 deletions(-) create mode 100644 src/agent/__pycache__/llm.cpython-313.pyc delete mode 100644 src/agent/history/multi_inputs/20260519_144316/ui_messages.json create mode 100644 src/agent/history/multi_inputs/20260527_130428/ui_messages.json create mode 100644 src/agent/history/multi_inputs/20260527_130748/ui_messages.json create mode 100644 src/agent/memory/chroma_rag_db/93b83aa9-b3da-41a4-b34c-90fcd21bda19/data_level0.bin create mode 100644 src/agent/memory/chroma_rag_db/93b83aa9-b3da-41a4-b34c-90fcd21bda19/header.bin create mode 100644 src/agent/memory/chroma_rag_db/93b83aa9-b3da-41a4-b34c-90fcd21bda19/length.bin create mode 100644 src/agent/memory/chroma_rag_db/93b83aa9-b3da-41a4-b34c-90fcd21bda19/link_lists.bin create mode 100644 src/agent/memory/chroma_rag_db/chroma.sqlite3 create mode 100644 src/web/__pycache__/server.cpython-313.pyc create mode 100644 src/web/__pycache__/ui_session.cpython-313.pyc create mode 100644 src/web/image/文档.svg create mode 100644 src/web/image/附件.svg diff --git a/.env b/.env index 3db4a82..96a0cc4 100644 --- a/.env +++ b/.env @@ -14,14 +14,14 @@ TTS_BASE_URL = "http://192.168.137.96:8902" RAG_BASE_URL = "http://192.168.137.96:8903" #6000pro: -LLM_API_KEY='semi-ai.tech' -LLM_BASE_URL="http://semi-ai.tech:40056/v1" -LLM_MODEL = "Intel/Qwen3.6-35B-A3B-int4-mixed-AutoRound" +# LLM_API_KEY='semi-ai.tech' +# LLM_BASE_URL="http://semi-ai.tech:40056/v1" +# LLM_MODEL = "Intel/Qwen3.6-35B-A3B-int4-mixed-AutoRound" #A100: -# LLM_API_KEY = 'sk-kGhWAQTqLk9ECsmIDb758a456bD34e92B9771dF2E36432Fb' -# LLM_BASE_URL = "https://app.semi-ai.tech:60004/v1" -# LLM_MODEL = "Qwen3.5-122B-A10B" +LLM_API_KEY = 'sk-kGhWAQTqLk9ECsmIDb758a456bD34e92B9771dF2E36432Fb' +LLM_BASE_URL = "https://app.semi-ai.tech:60004/v1" +LLM_MODEL = "Qwen3.5-122B-A10B" # LLM_TIMEOUT = 300 # LLM_REQUESTS_PER_SECOND = 10.0 # LLM_MAX_BUCKET_SIZE = 10 diff --git a/requirements.txt b/requirements.txt index cd2918c..d6f1aad 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,10 +1,10 @@ # Core LangChain & LangGraph -langchain>=0.1.0 -langchain-core>=0.1.0 -langchain-community>=0.1.0 -langchain-openai>=0.2.0 -langgraph>=1.0.0 -openai>=1.0.0 +langchain>=1.2.0 +langchain-core>=1.2.0 +langchain-community>=0.4.1 +langchain-openai>=1.1.12 +langgraph>=1.1.6 +openai>=2.25.0 # Web framework & server fastapi>=0.115.0 diff --git a/src/agent/__pycache__/chatbot_agent.cpython-312.pyc b/src/agent/__pycache__/chatbot_agent.cpython-312.pyc index 75ed37c879214af7bf87fa1e58201ae9dd394a7f..90def3a7bf9f9906239804cfc3b0cf58aa99cb47 100644 GIT binary patch literal 9574 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 delta 3845 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 X?k$G9V&A{Jo9Yz(&ST*(leG~vCMrlw5? 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 SqV+#9WbDyve^RA{|9=4?#ELcm literal 0 HcmV?d00001 diff --git a/src/agent/chatbot_agent.py b/src/agent/chatbot_agent.py index a24329e..db96ed0 100644 --- a/src/agent/chatbot_agent.py +++ b/src/agent/chatbot_agent.py @@ -1,15 +1,18 @@ -# agent/chatbot_agent.py -from typing import Any +from typing import Any, Literal, Sequence, Annotated, TypedDict +import json -from langchain_core.messages import HumanMessage, AIMessage -from langchain.agents import create_agent +from langgraph.graph import StateGraph, START, END +from langgraph.prebuilt import ToolNode +from langgraph.graph.message import add_messages +from langchain_core.messages import BaseMessage, HumanMessage, AIMessage, ToolMessage -from agent.llm import Qwen36_35B_A3B_FP8_LLM +from agent.utils.image_utils import image_bytes_to_openai_image_url_part +from agent.llm import llm from agent.tools import get_tools from agent.utils.agent_utils import ( assistant_text, get_history, - summarizer, # 使用新的上下文感知构建器 + summarizer, get_session_meta, update_session_meta, ) @@ -20,26 +23,152 @@ from web.ui_session import ( ) from agent.memory.system_prompt import MAIN_SYSTEM_PROMPT -# ---------- 1. 工具 ---------- +# ---------- 1. 工具与模型 ---------- tools = get_tools() +model_with_tools = llm.bind_tools(tools) -# ---------- 2. 创建 ReAct agent(LangGraph)---------- -agent_graph = create_agent( - model=Qwen36_35B_A3B_FP8_LLM, - tools=tools, - name="main" -) +# ---------- 2. 定义状态 ---------- +class AgentState(TypedDict): + messages: Annotated[Sequence[BaseMessage], add_messages] -# ---------- 3. 辅助函数:保存消息到数据库 ---------- +# ---------- 3. 节点定义 ---------- +def call_model(state: AgentState) -> dict: + """调用 LLM 生成回复或工具调用""" + response = model_with_tools.invoke(state["messages"]) + + # ----- 打印 token 使用情况 ----- + usage = response.usage_metadata if hasattr(response, 'usage_metadata') else {} + if usage: + input_tokens = usage.get('input_tokens', '?') + output_tokens = usage.get('output_tokens', '?') + total_tokens = usage.get('total_tokens', '?') + print(f"[LLM] Token使用: input={input_tokens}, output={output_tokens}, total={total_tokens}") + + # ----- 打印工具调用信息 ----- + if hasattr(response, 'tool_calls') and response.tool_calls: + for tc in response.tool_calls: + tool_name = tc.get('name', 'unknown') + args = tc.get('args', {}) + # LangChain 中 tool_call 的 args 可能是 JSON 字符串,尝试解析 + if isinstance(args, str): + try: + args = json.loads(args) + except Exception: + pass + print(f"[LLM] 工具调用: {tool_name}, 参数: {args}") + + return {"messages": [response]} + +# 工具执行节点(LangGraph 内置,自动解包 content_and_artifact 到 ToolMessage.artifact) +tool_node = ToolNode(tools) + +def process_tool_artifact(state: AgentState) -> dict: + last_msg = state["messages"][-1] + + # 日志打印 + if isinstance(last_msg, ToolMessage): + content_preview = str(last_msg.content) + if len(content_preview) > 200: + content_preview = content_preview[:200] + '...' + print(f"[Tool] 工具返回 (id={last_msg.tool_call_id}): {content_preview}") + + # 只处理 ToolMessage 且有 artifact 的情况 + if not isinstance(last_msg, ToolMessage) or not last_msg.artifact: + return {} + + artifact = last_msg.artifact + + # ---------- 统一提取所有 image_bytes ---------- + img_bytes_list: list[bytes] = [] + + if isinstance(artifact, dict) and "image_bytes" in artifact: + # 单张图片:artifact = {"image_bytes": b"..."} + img_bytes_list.append(artifact["image_bytes"]) + + elif isinstance(artifact, list): + for item in artifact: + if isinstance(item, dict) and "image_bytes" in item: + img_bytes_list.append(item["image_bytes"]) + elif isinstance(item, bytes): + img_bytes_list.append(item) + + # 兼容 artifact 直接就是 bytes 的情况 + elif isinstance(artifact, bytes): + img_bytes_list.append(artifact) + + if not img_bytes_list: + return {} + + # ---------- 将图片转换为多模态 content ---------- + content_parts = [] + failed_count = 0 + + for img_bytes in img_bytes_list: + data_url = image_bytes_to_openai_image_url_part(img_bytes) + if data_url is None: + failed_count += 1 + continue + content_parts.append(data_url) + + # 所有图片都处理失败时的降级方案 + if not content_parts: + human_msg = HumanMessage( + content="工具返回了截图,但图片处理失败,请向用户说明。", + additional_kwargs={"synthetic": True} + ) + return {"messages": [human_msg]} + + # 追加引导文本(放在最后,也可以放在最前,取决于你的需求) + content_parts.append({ + "type": "text", + "text": "上面是工具返回的截图。" + }) + + # 如果有部分图片失败,可以在 text 中提示(可选) + if failed_count > 0: + content_parts[-1]["text"] += f"(其中 {failed_count} 张图片处理失败,请忽略)" + + human_msg = HumanMessage( + content=content_parts, + additional_kwargs={"synthetic": True} + ) + return {"messages": [human_msg]} + +# ---------- 4. 条件路由 ---------- +def should_continue(state: AgentState) -> Literal["tools", END]: + last_msg = state["messages"][-1] + if hasattr(last_msg, "tool_calls") and last_msg.tool_calls: + return "tools" + return END + +# ---------- 5. 构建 Graph ---------- +workflow = StateGraph(AgentState) + +workflow.add_node("agent", call_model) +workflow.add_node("tools", tool_node) +workflow.add_node("process_tool_artifact", process_tool_artifact) + +workflow.add_edge(START, "agent") +workflow.add_conditional_edges("agent", should_continue, { + "tools": "tools", + END: END, +}) +workflow.add_edge("tools", "process_tool_artifact") +workflow.add_edge("process_tool_artifact", "agent") + +agent_graph = workflow.compile() + +# ---------- 6. 辅助函数:保存消息(过滤合成消息) ---------- def save_messages_to_history(session_id: str, user_msg: HumanMessage, assistant_msg: AIMessage) -> None: - """将用户消息和助手消息追加到 SQLite 历史记录中""" if not session_id: return history = get_history(session_id) - history.add_message(user_msg) + # 只保存非合成消息 + if not user_msg.additional_kwargs.get("synthetic"): + history.add_message(user_msg) history.add_message(assistant_msg) -# ---------- 4. 核心执行函数 ---------- +# ---------- 7. 核心执行函数 ---------- def execute_agent( chat_history: list[dict[str, Any]], session_id: str = "test", @@ -54,20 +183,18 @@ def execute_agent( if session_id: rewrite_user_message_files_to_session_dir(chat_history[-1], session_id) - # 直接使用 chat_history 中已经正确格式化的 content(由 _build_user_chat_content 生成) user_content = chat_history[-1]["content"] - # 动态上下文感知构建消息 + # 上下文感知构建 messages, context_exceeded = summarizer( session_id=session_id, - agent_name="main", # 固定 agent 名称,可扩展 + agent_name="main", system_prompt=MAIN_SYSTEM_PROMPT, user_content=user_content, - llm=Qwen36_35B_A3B_FP8_LLM, + llm=llm, ) if context_exceeded: - # 上下文已满,返回提示信息 chat_history.append({ "role": "assistant", "content": "上下文已满,请新建会话继续对话。", @@ -76,7 +203,6 @@ def execute_agent( sync_manifest_from_chat_history(session_id, chat_history, "") return chat_history - # 调用 graph 的配置 graph_config = { "configurable": {"session_id": session_id}, "metadata": { @@ -86,15 +212,15 @@ def execute_agent( } try: - # 调用 LangGraph agent + # 调用自定义 Graph result = agent_graph.invoke({"messages": messages}, config=graph_config) - # 从最终消息中提取文本回复 + # 提取最后一条 AI 消息 all_messages = result["messages"] assistant_msg = all_messages[-1] if all_messages else AIMessage(content="") text_content = assistant_text(assistant_msg.content) - # 提取 token 使用量并更新到 meta + # token 统计更新 usage = assistant_msg.usage_metadata if hasattr(assistant_msg, 'usage_metadata') else {} total_tokens = usage.get('total_tokens', 0) if isinstance(usage, dict) else 0 if total_tokens > 0: @@ -107,9 +233,9 @@ def execute_agent( last_total_tokens=total_tokens, ) - # 处理 TTS 音频数据 + # TTS 处理(保持原逻辑) output_type_from_config = graph_config.get("metadata", {}).get("output_type", "text") - audio_data = Qwen36_35B_A3B_FP8_LLM.last_audio_kwargs or {} + audio_data = llm.last_audio_kwargs or {} if audio_data.get("output_type") != output_type_from_config: audio_data = {} @@ -119,7 +245,6 @@ def execute_agent( "audio": audio_data.get("audio"), } - # 根据输出类型构建前端 content if output_type_from_config == "audio" and assistant_meta.get("audio"): assistant_content = [] elif output_type_from_config == "both" and assistant_meta.get("audio"): @@ -127,22 +252,20 @@ def execute_agent( else: assistant_content = text_content if text_content else "(空回复)" - # 追加到返回的 chat_history chat_history.append({ "role": "assistant", "content": assistant_content, "meta": assistant_meta, }) - # 持久化历史消息 + # 持久化历史消息(注意过滤掉合成消息) save_messages_to_history( session_id, HumanMessage(content=user_content), AIMessage(content=text_content), ) - # 清空音频缓存 - Qwen36_35B_A3B_FP8_LLM.last_audio_kwargs = None + llm.last_audio_kwargs = None except Exception as e: import traceback @@ -151,7 +274,6 @@ def execute_agent( err_text = "抱歉,我遇到了一些问题,请稍后再试。" chat_history.append({"role": "assistant", "content": err_text}) - # 同步文件清单 if session_id: sync_manifest_from_chat_history(session_id, chat_history, "") diff --git a/src/agent/history/data_base/chat_history.db b/src/agent/history/data_base/chat_history.db index fbf8476bbd29ca4c83b1170d00685b51f73560df..d96b9db958eb0aff443b661dbaf7ee2cd5a9d556 100644 GIT binary patch literal 24576 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 LCf|Pny%hcpI9;0d delta 584 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 JSHu83{a=*LTn+#L literal 0 HcmV?d00001 diff --git a/src/agent/tools/__init__.py b/src/agent/tools/__init__.py index e36bd43..d4f507b 100644 --- a/src/agent/tools/__init__.py +++ b/src/agent/tools/__init__.py @@ -24,9 +24,10 @@ from .tool_rag import RAGTool _all_tools = [ Websearchclass(), DateQueryClass(), - ScreenCaptureDescribeClass(), + ScreenCapture(), BrowserControlClass(), RAGTool(), + DirectoryListing(), ] def get_tools(): diff --git a/src/agent/tools/__pycache__/__init__.cpython-312.pyc b/src/agent/tools/__pycache__/__init__.cpython-312.pyc index e3861756e25327652e78b45af553fadcc3781a32..a6bb0833a9e73366a68904a785fe693c6d681a44 100644 GIT binary patch delta 196 zcmZ3@a*##+G%qg~0}$M-6wBJd%)sy%#DM{JDC2X^MD=hnCWch@)liXiwkVDi<`#x1 z&WRm67!@ZwGKw>DO^#$VGvy6VE=ooW8J delta 174 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 b1cmqU20}5$&RrkT8J{!Sah75-N+fCEf9%d%L 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 literal 16224 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 diff --git a/src/agent/tools/tool.py b/src/agent/tools/tool.py index ff2e9f5..2eb4114 100644 --- a/src/agent/tools/tool.py +++ b/src/agent/tools/tool.py @@ -1,16 +1,19 @@ from __future__ import annotations +import os import io -from typing import Optional, Literal +from typing import Optional, Literal, Tuple import sys import time import webbrowser import pyautogui +import asyncio +from pathlib import Path from langchain.tools import BaseTool from pydantic import BaseModel, Field, create_model -from agent.llm import Qwen36_35B_A3B_FP8_LLM +from agent.llm import llm from utils.env_utils import BROWSER_MAP @@ -56,8 +59,8 @@ class DateQueryClass(BaseTool): return self._run() -class ScreenCaptureDescribeClass(BaseTool): - """截取当前屏幕并用与对话上传图片相同的视觉链路解析为文字,供 Agent 推理。""" +class ScreenCapture(BaseTool): + """截取当前屏幕,生成文字描述并同时提供原始截图供模型直接查看。""" args_schema: type[BaseModel] = create_model( "ScreenCaptureInput", @@ -69,44 +72,42 @@ class ScreenCaptureDescribeClass(BaseTool): ), ), ) - name: str = "screen_capture_describe" + name: str = "screen_capture" description: str = ( - "截取当前主屏幕画面,并用视觉模型转为文字描述。" + "截取当前主屏幕画面。除了返回文字描述外,也会提供原始截图给模型进行视觉分析。" "注意:这个工具仅用于获取当前屏幕的实时画面,其余任何情况严禁调用。" ) + response_format: Literal["content", "content_and_artifact"] = "content_and_artifact" - def _run(self, focus_query: Optional[str] = None) -> str: - # 1. 确保 pyautogui 可用 - try: - import pyautogui - except ImportError as e: - return ( - f"未安装 pyautogui,无法截屏: {e}。" - "请在项目环境中执行: pip install pyautogui" - ) - - # 2. 截屏并转为 PNG 字节流 + def _run(self, focus_query: Optional[str] = None) -> Tuple[str, dict]: + # 1. 截屏 try: pil_img = pyautogui.screenshot() buf = io.BytesIO() pil_img.save(buf, format="PNG") raw_png = buf.getvalue() except Exception as e: - return f"截屏失败: {e}" + return f"截屏失败: {e}", {} - # 3. 构建描述指令 query = (focus_query or "").strip() - - # 4. 调用与对话图片上传完全相同的视觉模型进行描述 - try: - # Qwen36_35B_A3B_FP8_LLM 为全局 ChatQwen 实例,已在模块中定义 - text = Qwen36_35B_A3B_FP8_LLM.describe_image_bytes(raw_png, prompt=query) - except Exception as e: - return f"屏幕图像视觉解析失败: {e}" - if not (text or "").strip(): - return "(已截屏,但模型未返回有效描述。)" - return f"【当前屏幕视觉描述】\n{text.strip()}" + if query: # focus_query 存在且非空 + # 生成视觉描述,返回文字内容,不返回图片字节 + try: + description = llm.describe_image_bytes(raw_png, prompt=query) + except Exception as e: + description = f"屏幕图像视觉解析失败: {e}" + if not description.strip(): + description = "(未返回有效描述)" + content_text = f"【当前屏幕视觉描述】\n{description.strip()}" + return content_text, {} + else: + # 不生成描述,直接返回图片字节 + artifact = {"image_bytes": raw_png} + return "", artifact + + async def _arun(self, focus_query: Optional[str] = None) -> Tuple[str, dict]: + return await asyncio.to_thread(self._run, focus_query) class BrowserControlClass(BaseTool): """统一浏览器控制工具:支持打开/关闭浏览器、刷新/关闭标签页、打开指定 URL""" @@ -133,21 +134,19 @@ class BrowserControlClass(BaseTool): "- action='open', url='current': 刷新当前标签页\n" "- action='open', url='https://...': 在新标签页打开指定 URL\n" "- action='close', url=None: 关闭整个浏览器窗口\n" - "- action='close', url='current': 关闭当前标签页\n" - "- action='close', url='https://...': 关闭当前活动标签页" + "- action='close', url='任意非空字符(例如current)': 关闭当前标签页\n" + "操作完成后会返回当前屏幕截图,帮助判断操作是否成功。" "仅用于进行浏览器操作时调用,其余任何情况严禁调用。" ) + response_format: Literal["content", "content_and_artifact"] = "content_and_artifact" - # ---------- 辅助方法 ---------- + # ---------- 辅助方法(与原版相同,无改动) ---------- def _get_browser_info(self, browser_name: Optional[str]): - """获取浏览器控制器和窗口标题关键字""" if browser_name is None: return webbrowser.get(), None - name_lower = browser_name.lower().strip() if name_lower not in BROWSER_MAP: raise ValueError(f"不支持的浏览器: {browser_name},支持: {', '.join(BROWSER_MAP.keys())}") - reg_name, title_keyword = BROWSER_MAP[name_lower] try: controller = webbrowser.get(reg_name) @@ -156,7 +155,6 @@ class BrowserControlClass(BaseTool): return controller, title_keyword def _activate_window(self, title_keyword: str) -> bool: - """激活浏览器窗口(仅 Windows)""" if sys.platform != "win32": return False try: @@ -173,24 +171,18 @@ class BrowserControlClass(BaseTool): return False def _send_keys(self, *keys): - """发送快捷键,根据平台调整""" pyautogui.hotkey(*keys) - # ---------- 核心操作 ---------- - def _open_action(self, url: Optional[str], browser: Optional[str]): - """执行打开类操作""" + # ---------- 核心操作(返回操作结果描述) ---------- + def _open_action(self, url: Optional[str], browser: Optional[str]) -> str: controller, title_keyword = self._get_browser_info(browser) - if url is None: - # 仅打开浏览器首页(激活或启动) controller.open("about:blank") if title_keyword: self._activate_window(title_keyword) browser_info = f" (使用 {browser})" if browser else "" return f"已打开浏览器首页{browser_info}" - elif url.lower() == "current": - # 刷新当前页 if title_keyword: if not self._activate_window(title_keyword): return f"未找到 {browser or '默认'} 浏览器窗口,无法刷新" @@ -200,20 +192,15 @@ class BrowserControlClass(BaseTool): else: self._send_keys("f5") return "已刷新当前标签页" - else: - # 打开指定 URL controller.open(url) if title_keyword: self._activate_window(title_keyword) return f"已尝试打开 URL: {url}" - def _close_action(self, url: Optional[str], browser: Optional[str]): - """执行关闭类操作""" + def _close_action(self, url: Optional[str], browser: Optional[str]) -> str: _, title_keyword = self._get_browser_info(browser) - if url is None: - # 关闭整个浏览器 if title_keyword: if not self._activate_window(title_keyword): return f"未找到 {browser or '默认'} 浏览器窗口,无法关闭" @@ -223,9 +210,7 @@ class BrowserControlClass(BaseTool): else: self._send_keys("alt", "f4") return "已尝试关闭浏览器窗口" - - elif url.lower() == "current": - # 关闭当前标签页 + else: if title_keyword: if not self._activate_window(title_keyword): return f"未找到 {browser or '默认'} 浏览器窗口,无法关闭标签页" @@ -236,16 +221,82 @@ class BrowserControlClass(BaseTool): self._send_keys("ctrl", "w") return "已尝试关闭当前标签页" - def _run(self, action: str = "open", url: Optional[str] = None, browser: Optional[str] = None) -> str: + # ---------- 同步主逻辑,返回 (描述, artifact) ---------- + def _run(self, action: str = "open", url: Optional[str] = None, browser: Optional[str] = None) -> Tuple[str, dict]: try: if action == "open": - return self._open_action(url, browser) + msg = self._open_action(url, browser) elif action == "close": - return self._close_action(url, browser) + msg = self._close_action(url, browser) else: - return f"不支持的操作类型: {action}" + msg = f"不支持的操作类型: {action}" except Exception as e: - return f"执行浏览器操作时出错: {e}" + msg = f"执行浏览器操作时出错: {e}" + + # 无论操作是否成功,都尝试截屏 + try: + pil_img = pyautogui.screenshot() + buf = io.BytesIO() + pil_img.save(buf, format="PNG") + raw_png = buf.getvalue() + return msg, {"image_bytes": raw_png} + except Exception as e: + return f"{msg}\n(截屏失败: {e})", {} + + # ---------- 异步入口 ---------- + async def _arun(self, action: str = "open", url: Optional[str] = None, browser: Optional[str] = None) -> Tuple[str, dict]: + return await asyncio.to_thread(self._run, action, url, browser) + +class DirectoryListing(BaseTool): + """列出指定目录下的文件和文件夹,帮助 Agent 了解本地文件结构。""" + + args_schema: type[BaseModel] = create_model( + "DirectoryListingInput", + path=( + Optional[str], + Field( + default=None, + description="要查看的目录路径,默认为当前工作目录。" + ), + ), + show_hidden=( + bool, + Field( + default=False, + description="是否显示隐藏文件(以点开头的文件/文件夹),默认不显示。" + ), + ), + ) + name: str = "directory_listing" + description: str = ( + "列出指定目录下的所有文件和子目录。可用于探索文件系统、查找文件位置。" + "仅当需要获取目录内容或确认文件结构时调用,其余情况严禁调用。" + ) + + def _run(self, path: Optional[str] = None, show_hidden: bool = False) -> str: + try: + target = Path(path).resolve() if path else Path.cwd() + if not target.exists(): + return f"路径不存在: {target}" + if not target.is_dir(): + return f"路径不是目录: {target}" + + items = [] + with os.scandir(target) as entries: + for entry in entries: + if not show_hidden and entry.name.startswith('.'): + continue + item_type = "DIR" if entry.is_dir() else "FILE" + items.append(f"[{item_type}] {entry.name}") + + if not items: + return f"目录 {target} 为空" + return f"目录 {target} 的内容:\n" + "\n".join(sorted(items)) + except Exception as e: + return f"列出目录时出错: {str(e)}" + + async def _arun(self, path: Optional[str] = None, show_hidden: bool = False) -> str: + return await asyncio.to_thread(self._run, path, show_hidden) if __name__ == "__main__": # 这里可以进行一些简单的测试,例如创建工具实例并调用方法 diff --git a/src/agent/tools/tool_rag.py b/src/agent/tools/tool_rag.py index dce1e2a..a395a31 100644 --- a/src/agent/tools/tool_rag.py +++ b/src/agent/tools/tool_rag.py @@ -1,11 +1,12 @@ # agent/tools/tool_rag.py from __future__ import annotations +import asyncio +import re import hashlib -import json import os from datetime import datetime -from typing import List, Optional, Literal, Union +from typing import List, Optional, Literal, Union, Tuple import chromadb import requests @@ -14,24 +15,113 @@ from pydantic import BaseModel, Field, create_model from langchain_text_splitters import RecursiveCharacterTextSplitter from utils.env_utils import RAG_BASE_URL, RAG_CHUNK_SIZE, RAG_CHUNK_OVERLAP +from utils.agent_utils import Documents_process +from ..llm import llm -# Chroma 持久化路径:agent/memory/chroma_rag_db +# ---------- 前置函数---------- +ENDING_PUNCTUATION = set(".!?。!?…\"'”’);:;:…") + + +def has_ending_punctuation(text: str) -> bool: + stripped = text.rstrip() + if not stripped: + return True + return stripped[-1] in ENDING_PUNCTUATION + + +def pre_process(text_content: str, list_image_byte: List[bytes]) -> Tuple[str, List[bytes]]: + lines = text_content.split('\n') + new_lines = [] + i = 0 + while i < len(lines): + line = lines[i] + stripped = line.strip() + # 情形1:当前行是 ... + if stripped.startswith('') and stripped.endswith(''): + if i + 1 < len(lines): + next_line = lines[i + 1] + next_stripped = next_line.strip() + # 如果下一行不是 且没有结束标点,合并 + if (next_stripped and + not has_ending_punctuation(next_stripped) and + not next_stripped.startswith('')): + merged = f'{next_stripped}' + new_lines.append(merged) + i += 2 + continue + new_lines.append(line) + i += 1 + # 情形2:当前行是普通文本,没有结束标点 + elif (stripped and + not has_ending_punctuation(stripped) and + not stripped.startswith('')): + if i + 1 < len(lines): + next_line = lines[i + 1] + next_stripped = next_line.strip() + # 如果下一行是 ...,合并 + if next_stripped.startswith('') and next_stripped.endswith(''): + merged = f'{stripped}' + new_lines.append(merged) + i += 2 + continue + new_lines.append(line) + i += 1 + else: + new_lines.append(line) + i += 1 + + merged_text = '\n'.join(new_lines) + + # 替换 内容:插入图片描述 + pattern = re.compile(r'(.*?)', re.DOTALL) + matches = list(pattern.finditer(merged_text)) + result_parts = [] + last_end = 0 + for idx, match in enumerate(matches): + result_parts.append(merged_text[last_end:match.start()]) + inner = match.group(1).strip() + if idx < len(list_image_byte): + try: + description = llm.describe_image_bytes( + list_image_byte[idx], + prompt="""你是一名专业的图像理解助手,现在需要你为即将加入向量库的图片做文字分析以便于用户更迅速、更高效、较准确地查询到此图片。 + 首先请用客观、准确的中文描述用户提供的图片,覆盖主要物体、场景、人物动作、界面元素、图表数据与清晰可读的文字; + 涉及到你认识的具体品牌、时间、地点、人物、影视剧、物品等特定内容时请直接命名说明;看不清的内容请如实说明,不要臆测。 + 其次请为此图片设想出3到5个可能会问及的客观问题,放在末尾处。""" + ) + except Exception: + description = "图片描述生成失败" + else: + description = "" + if inner: + new_inner = f"{inner}\n{description}" if description else inner + else: + new_inner = description + result_parts.append(f"{new_inner}") + last_end = match.end() + result_parts.append(merged_text[last_end:]) + final_text = ''.join(result_parts) + return final_text, list_image_byte + + +# ---------- 存储路径、客户端 ---------- CHROMA_PERSIST_DIR = os.path.join(os.path.dirname(__file__), '..', 'memory', 'chroma_rag_db') +# 统一的图片存储目录 +IMAGE_STORE_DIR = os.path.join(CHROMA_PERSIST_DIR, "images") -# 全局 Chroma 客户端和统一集合 _chroma_client = None _global_collection = None - GLOBAL_COLLECTION_NAME = "rag_global" + def get_chroma_client(): global _chroma_client if _chroma_client is None: _chroma_client = chromadb.PersistentClient(path=CHROMA_PERSIST_DIR) return _chroma_client + def get_global_collection(): - """获取或创建全局知识库 Collection""" global _global_collection if _global_collection is None: client = get_chroma_client() @@ -46,11 +136,8 @@ def get_global_collection(): ) return _global_collection -# ---------- Embedding 接口封装 ---------- + def embed_texts(queries: List[str], documents: List[str] = None) -> tuple: - """ - 调用 /embed,返回 (query_embeddings, doc_embeddings) - """ payload = { "task_description": "Given a web search query, retrieve relevant passages that answer the query", "queries": queries, @@ -64,8 +151,8 @@ def embed_texts(queries: List[str], documents: List[str] = None) -> tuple: except Exception as e: raise RuntimeError(f"Embedding 请求失败: {e}") + def rerank_documents(query: str, documents: List[str]) -> List[float]: - """调用 /rerank,返回排序后的分数列表""" payload = { "task_description": "Given a web search query, retrieve relevant passages that answer the query", "query": query, @@ -78,93 +165,49 @@ def rerank_documents(query: str, documents: List[str]) -> List[float]: except Exception as e: raise RuntimeError(f"Reranker 请求失败: {e}") -# ---------- 文本分块 ---------- -def split_into_chunks(text: str) -> List[str]: - splitter = RecursiveCharacterTextSplitter( - chunk_size=RAG_CHUNK_SIZE, - chunk_overlap=RAG_CHUNK_OVERLAP, - separators=["\n\n", "\n", " ", ""] - ) - return splitter.split_text(text) - -# ---------- 文件解析 ---------- -def parse_file_to_chunks(file_path: str) -> List[dict]: - """ - 解析文件,返回每个 chunk 的字典,包含: - - content: 文本内容 - - source: 详细来源描述 - """ - ext = os.path.splitext(file_path)[1].lower() - chunks = [] - - if ext == ".txt" or ext == ".md": - with open(file_path, "r", encoding="utf-8") as f: - text = f.read() - texts = split_into_chunks(text) - for i, t in enumerate(texts): - chunks.append({ - "content": t, - "source": f"{os.path.basename(file_path)} (chunk {i+1}/{len(texts)})" - }) - - elif ext == ".json": - with open(file_path, "r", encoding="utf-8") as f: - data = json.load(f) - text = json.dumps(data, ensure_ascii=False, indent=2) - texts = split_into_chunks(text) - for i, t in enumerate(texts): - chunks.append({ - "content": t, - "source": f"{os.path.basename(file_path)} (JSON, chunk {i+1}/{len(texts)})" - }) - - elif ext == ".csv": - import pandas as pd - df = pd.read_csv(file_path) - text = df.to_string(index=False) - texts = split_into_chunks(text) - for i, t in enumerate(texts): - chunks.append({ - "content": t, - "source": f"{os.path.basename(file_path)} (CSV, chunk {i+1}/{len(texts)})" - }) - - elif ext == ".docx": - from docx import Document - doc = Document(file_path) - full_text = "\n".join([p.text for p in doc.paragraphs]) - texts = split_into_chunks(full_text) - for i, t in enumerate(texts): - chunks.append({ - "content": t, - "source": f"{os.path.basename(file_path)} (DOCX, chunk {i+1}/{len(texts)})" - }) - - elif ext == ".pdf": - import pdfplumber - with pdfplumber.open(file_path) as pdf: - for page_num, page in enumerate(pdf.pages, start=1): - page_text = page.extract_text() or "" - if page_text.strip(): - page_chunks = split_into_chunks(page_text) - for i, t in enumerate(page_chunks): - chunks.append({ - "content": t, - "source": f"{os.path.basename(file_path)} (Page {page_num}, chunk {i+1})" - }) +def split_into_chunks(input_data) -> List[dict]: + if isinstance(input_data, str): + text = input_data + image_bytes_list = [] + elif isinstance(input_data, tuple) and len(input_data) == 2: + text, image_bytes_list = input_data else: - raise ValueError(f"不支持的文件格式: {ext}") + text = "" + image_bytes_list = [] - return chunks + pattern = re.compile(r'([\s\S]*?|[\s\S]*?
)') + parts = pattern.split(text) + all_chunks = [] + image_index = 0 + for part in parts: + if not part: + continue + if (part.startswith('') and part.endswith('')) or \ + (part.startswith('') and part.endswith('
')): + chunk_info = {"content": part, "is_protected": True} + if part.startswith('') and image_index < len(image_bytes_list): + chunk_info["image_bytes"] = image_bytes_list[image_index] + image_index += 1 + all_chunks.append(chunk_info) + else: + splitter = RecursiveCharacterTextSplitter( + chunk_size=RAG_CHUNK_SIZE, + chunk_overlap=RAG_CHUNK_OVERLAP, + separators=["\n\n", "\n", "。", "!", "?", ";", ",", ".", "!", "?", ";", ",", " ", ""] + ) + split_parts = splitter.split_text(part) + all_chunks.extend([{"content": p, "is_protected": False} for p in split_parts]) + return all_chunks -# ---------- RAG 工具本体 ---------- + +# ---------- RAGTool ---------- class RAGTool(BaseTool): name: str = "rag_tool" description: str = ( - "通用知识库工具,支持增删查操作。\n" + "个人知识库管理工具,支持增删查及列出所有来源操作。\n" "参数说明:\n" - "- action_type: 操作类型,必填。可选:'query'(查询), 'add_document'(添加文档), 'clear'(清除)。\n" + "- action_type: 操作类型,必填。可选:'query'(查询), 'add_document'(添加文档), 'clear'(清除), 'list_sources'(列出所有来源)。\n" "- content: 查询内容(用于query) 或 直接文本内容(用于add_document,与source二选一)。\n" "- source: 文件路径(用于add_document,与content二选一)。\n" "- filter: 过滤条件(用于query或clear)。\n" @@ -172,24 +215,29 @@ class RAGTool(BaseTool): "- 查询:action_type='query', content='用户问题'\n" "- 添加文本:action_type='add_document', content='知识文本'\n" "- 添加文件:action_type='add_document', source='/path/to/file.pdf'\n" - "- 清除:action_type='clear', filter='某个来源'" + "- 清除:action_type='clear', filter='某个来源'\n" + "- 列出所有来源:action_type='list_sources'" ) args_schema: type[BaseModel] = create_model( "RAGInput", - action_type=(Literal["query", "add_document", "clear"], Field(..., description="操作类型")), + action_type=(Literal["query", "add_document", "clear", "list_sources"], Field(..., description="操作类型")), content=(Optional[str], Field(None, description="查询内容或直接添加的文本内容")), - source=(Optional[str], Field(None, description="文件路径,用于添加文档")), + source=(Optional[Union[str, List[str]]], Field(None, description="文件路径(字符串或列表),用于添加文档")), filter=(Optional[Union[str, List[str]]], Field(None, description="过滤条件,用于查询或清除")) ) + # ========== 关键改动:声明返回内容和 artifact ========== + response_format: Literal["content", "content_and_artifact"] = "content_and_artifact" + def _run( self, action_type: str, content: Optional[str] = None, - source: Optional[str] = None, + source: Optional[Union[str, List[str]]] = None, filter: Optional[Union[str, List[str]]] = None - ) -> str: + ) -> Tuple[str, Union[dict, list]]: + """统一返回 (text, artifact) 元组""" try: if action_type == "query": return self._handle_query(content, filter) @@ -197,27 +245,47 @@ class RAGTool(BaseTool): return self._handle_add(content, source) elif action_type == "clear": return self._handle_clear(filter) + elif action_type == "list_sources": + return self._handle_list_sources() else: - return f"未知操作: {action_type}" + return f"未知操作: {action_type}", {} except Exception as e: - return f"RAG 工具执行出错: {str(e)}" + return f"RAG 工具执行出错: {str(e)}", {} - # ---------- 查询 ---------- - def _handle_query(self, query: str, filter: Optional[Union[str, List[str]]]) -> str: + async def _arun(self, **kwargs) -> Tuple[str, Union[dict, list]]: + return await asyncio.to_thread(self._run, **kwargs) + + def _get_batch_chunks(self, coll, batch_id: str) -> dict: + res = coll.get(where={"batch_id": batch_id}) + batch_chunks = {} + if res and res["metadatas"] and res["documents"]: + for meta, doc in zip(res["metadatas"], res["documents"]): + chunk_idx = meta.get("chunk_index") + if chunk_idx is not None: + batch_chunks[chunk_idx] = { + "content": doc, + "is_image": meta.get("is_image", False), + "is_table": meta.get("is_table", False), + "is_protected": meta.get("is_protected", False), + "image_path": meta.get("image_path", "") + } + return batch_chunks + + # ---------- 查询(返回 artifact)---------- + def _handle_query(self, query: str, filter: Optional[Union[str, List[str]]]) -> Tuple[str, list]: if not query: - return "查询内容不能为空。" + return "查询内容不能为空。", [] # 1. 获取查询向量 q_embs, _ = embed_texts(queries=[query]) query_vector = q_embs[0] - # 2. 从全局 Collection 中检索 + # 2. 检索 coll = get_global_collection() where = None if filter: if isinstance(filter, str): where = {"source": {"$contains": filter}} - # 列表暂简化为只支持单个字符串,但保留接口 elif isinstance(filter, list) and len(filter) > 0: where = {"source": {"$contains": filter[0]}} @@ -228,7 +296,7 @@ class RAGTool(BaseTool): ) if not res["ids"] or not res["ids"][0]: - return "未在知识库中找到相关内容。" + return "未在知识库中找到相关内容。", [] ids = res["ids"][0] docs = res["documents"][0] @@ -243,7 +311,7 @@ class RAGTool(BaseTool): unique_results = all_results[:10] - # 3. Reranker 精排 + # 3. Reranker docs_for_rerank = [r["document"] for r in unique_results] scores = rerank_documents(query, docs_for_rerank) @@ -254,93 +322,240 @@ class RAGTool(BaseTool): )[:3] if not sorted_items: - return "Reranker 未返回有效结果。" + return "Reranker 未返回有效结果。", [] outputs = [] + all_image_bytes = [] # 收集图片字节 + for item, score in sorted_items: meta = item["metadata"] - source_info = meta.get("source", "未知来源") - outputs.append(f"【来源:{source_info}(相关度 {score:.3f})】\n{item['document']}") + is_image = meta.get("is_image", False) + is_table = meta.get("is_table", False) + batch_id = meta.get("batch_id") + chunk_index = meta.get("chunk_index") - return "\n\n".join(outputs) + batch_chunks = self._get_batch_chunks(coll, batch_id) if batch_id else {} + + # 拼接上下文(前、当前、后) + context_parts = [] + for idx_offset in [-1, 0, 1]: + current_idx = chunk_index + idx_offset + if current_idx in batch_chunks: + chunk = batch_chunks[current_idx] + context_parts.append(chunk["content"]) + # 如果是图片块,从文件读取字节 + if chunk["is_image"] and chunk.get("image_path"): + img_path = chunk["image_path"] + if os.path.isfile(img_path): + try: + with open(img_path, "rb") as f: + all_image_bytes.append(f.read()) + except Exception: + pass + + context_text = "\n\n".join(context_parts) + + # 格式化文本输出 + if is_image: + outputs.append( + f"【图片数据 - 来源:{meta.get('source', '未知来源')}(相关度 {score:.3f})】\n[图片数据已获取]") + elif is_table: + outputs.append( + f"【表格数据 - 来源:{meta.get('source', '未知来源')}(相关度 {score:.3f})】\n{context_text}") + else: + outputs.append( + f"【文本数据 - 来源:{meta.get('source', '未知来源')}(相关度 {score:.3f})】\n{context_text}") + + final_text = "\n\n".join(outputs) + + # 去重图片字节,并按每个图片单独打包成一个 dict,放入 artifact 列表 + unique_image_bytes = list(dict.fromkeys(all_image_bytes)) # 保持顺序去重 + artifact = [{"image_bytes": img_bytes} for img_bytes in unique_image_bytes] + + return final_text, artifact # ---------- 添加文档 ---------- - def _handle_add(self, content: Optional[str], source: Optional[str]) -> str: + def _handle_add(self, content: Optional[str], source: Optional[Union[str, List[str]]]) -> Tuple[str, dict]: if not content and not source: - return "必须提供 content 或 source。" + return "必须提供 content 或 source。", {} if content and source: - return "content 和 source 只能提供一个。" + return "content 和 source 只能提供一个。", {} - # 1. 准备 chunks 列表 - chunks_meta = [] # 元素为 (text, source_desc) + # 统一 batch_id,整个添加操作共享 timestamp = datetime.now().isoformat() + batch_id = hashlib.md5(timestamp.encode()).hexdigest() + # 收集所有文档的 chunk 信息 + all_chunks_meta = [] # (text, src_desc, is_protected, image_path) + file_errors = [] + + # 处理直接文本输入 if content: - texts = split_into_chunks(content) - chunks_meta = [(t, "直接输入") for t in texts] + processed_text, processed_images = pre_process(content, []) + chunk_infos = split_into_chunks((processed_text, processed_images)) + base_dir = IMAGE_STORE_DIR + base_name = f"direct_{batch_id[:8]}" + source_desc_prefix = "直接输入" + + image_idx = 0 + for idx, chunk_info in enumerate(chunk_infos): + text = chunk_info["content"] + img_bytes = chunk_info.get("image_bytes") + is_protected = chunk_info.get("is_protected", False) + src_desc = f"{source_desc_prefix} (chunk {idx + 1}/{len(chunk_infos)})" + + image_path = "" + if text.startswith('') and text.endswith('') and img_bytes is not None: + os.makedirs(base_dir, exist_ok=True) + image_path = os.path.join(base_dir, f"{base_name}_image{image_idx}.jpg") + try: + with open(image_path, "wb") as f: + f.write(img_bytes) + except Exception as e: + return f"图片保存失败: {e}", {} + image_idx += 1 + + all_chunks_meta.append((text, src_desc, is_protected, image_path)) + + # 处理文件输入(字符串或列表) elif source: - if not os.path.isfile(source): - return f"文件不存在: {source}" - chunks_meta = [(c["content"], c["source"]) for c in parse_file_to_chunks(source)] + # 统一为列表处理 + source_files = [source] if isinstance(source, str) else source + for file_path in source_files: + if not os.path.isfile(file_path): + file_errors.append(f"文件不存在: {file_path}") + continue - if not chunks_meta: - return "文档内容为空,未添加任何条目。" + try: + raw_text, image_list = Documents_process.process(file_path) + except Exception as e: + file_errors.append(f"处理文件失败 {file_path}: {e}") + continue - # 2. 批量获取文档向量 - doc_texts = [cm[0] for cm in chunks_meta] + processed_text, processed_images = pre_process(raw_text, image_list) + chunk_infos = split_into_chunks((processed_text, processed_images)) + if not chunk_infos: + file_errors.append(f"文件内容为空: {file_path}") + continue + + base_dir = os.path.dirname(file_path) + base_name = os.path.splitext(os.path.basename(file_path))[0] + source_desc_prefix = os.path.basename(file_path) + + image_idx = 0 + for idx, chunk_info in enumerate(chunk_infos): + text = chunk_info["content"] + img_bytes = chunk_info.get("image_bytes") + is_protected = chunk_info.get("is_protected", False) + src_desc = f"{source_desc_prefix} (chunk {idx + 1}/{len(chunk_infos)})" + + image_path = "" + if text.startswith('') and text.endswith('') and img_bytes is not None: + os.makedirs(IMAGE_STORE_DIR, exist_ok=True) + image_path = os.path.join(IMAGE_STORE_DIR, f"{base_name}_image{image_idx}.jpg") + try: + with open(image_path, "wb") as f: + f.write(img_bytes) + except Exception as e: + file_errors.append(f"图片保存失败 ({file_path}): {e}") + # 即便图片保存失败,文本块仍可入库,继续执行 + image_idx += 1 + + all_chunks_meta.append((text, src_desc, is_protected, image_path)) + + if not all_chunks_meta: + error_msg = "没有可用内容添加到知识库。" + if file_errors: + error_msg += " 错误详情: " + "; ".join(file_errors) + return error_msg, {} + + # 统一写入 Chroma:分配连续 chunk_index + total_chunks = len(all_chunks_meta) + doc_texts = [m[0] for m in all_chunks_meta] _, doc_embs = embed_texts(queries=["dummy"], documents=doc_texts) - # 3. 写入全局 Collection coll = get_global_collection() - ids = [] - embeddings = [] - metadatas = [] - documents = [] + ids, embeddings, metadatas, documents = [], [], [], [] - for idx, (text, src_desc) in enumerate(chunks_meta): - uid = hashlib.md5(f"{timestamp}_{text[:80]}".encode()).hexdigest() + for idx, (text, src_desc, is_protected, image_path) in enumerate(all_chunks_meta): + uid = hashlib.md5(f"{timestamp}_{text[:80]}_{idx}".encode()).hexdigest() ids.append(uid) embeddings.append(doc_embs[idx]) - metadatas.append({ + + metadata = { "source": src_desc, "timestamp": timestamp, - }) + "batch_id": batch_id, + "chunk_index": idx, # 全局连续索引 + "total_chunks": total_chunks, + "is_image": text.startswith('') and text.endswith(''), + "is_table": text.startswith('') and text.endswith('
'), + "is_protected": is_protected + } + if image_path: + metadata["image_path"] = image_path + + metadatas.append(metadata) documents.append(text) - coll.add( - ids=ids, - embeddings=embeddings, - metadatas=metadatas, - documents=documents - ) + coll.add(ids=ids, embeddings=embeddings, metadatas=metadatas, documents=documents) - return f"成功添加 {len(ids)} 条知识块到全局知识库。" + result_msg = f"成功添加 {total_chunks} 条知识块到全局知识库。" + if file_errors: + result_msg += " 部分文件处理出错: " + "; ".join(file_errors) + + return result_msg, {} # ---------- 清除 ---------- - def _handle_clear(self, filter: Optional[Union[str, List[str]]]) -> str: + def _handle_clear(self, filter: Optional[Union[str, List[str]]]) -> Tuple[str, dict]: if not filter: - return "清除操作必须提供 filter 条件。" + return "清除操作必须提供 filter 条件。", {} coll = get_global_collection() where = None if isinstance(filter, str): where = {"source": {"$contains": filter}} elif isinstance(filter, list) and len(filter) > 0: - # 简化为使用第一个条件 where = {"source": {"$contains": filter[0]}} if not where: - return "无法构建有效的过滤条件。" + return "无法构建有效的过滤条件。", {} try: res = coll.get(where=where) if res["ids"]: + # 清理关联的图片文件 + for meta in res.get("metadatas", []): + img_path = meta.get("image_path", "") + if img_path and os.path.isfile(img_path): + try: + os.remove(img_path) + except Exception: + pass coll.delete(ids=res["ids"]) - return f"已从全局知识库中删除 {len(res['ids'])} 条记录。" + return f"已从全局知识库中删除 {len(res['ids'])} 条记录。", {} else: - return "未找到匹配的记录。" + return "未找到匹配的记录。", {} except Exception as e: - return f"清除时出错: {e}" + return f"清除时出错: {e}", {} - async def _arun(self, **kwargs): - return self._run(**kwargs) \ No newline at end of file + # ---------- 列出所有来源 ---------- + def _handle_list_sources(self) -> Tuple[str, dict]: + """获取知识库中所有不重复的 source 信息""" + coll = get_global_collection() + try: + all_data = coll.get() # 获取全部文档的元数据 + except Exception as e: + return f"获取知识库数据失败: {e}", {} + + if not all_data or not all_data["metadatas"]: + return "知识库中暂无数据。", {} + + sources = set() + for meta in all_data["metadatas"]: + src = meta.get("source", "未知来源") + sources.add(src) + + sorted_sources = sorted(sources) + text = "知识库中的来源列表:\n" + "\n".join(f"- {s}" for s in sorted_sources) + return text, {} \ No newline at end of file diff --git a/src/agent/utils/__pycache__/agent_utils.cpython-312.pyc b/src/agent/utils/__pycache__/agent_utils.cpython-312.pyc index cb781617bf50262e77c3746b8f3f13120c67dc9b..74782b59c2d508f400ae94dc00c6bea6b5db3a1d 100644 GIT binary patch delta 13980 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 delta 2539 zcma)7U2IfE6rOwcr@P&sZo6%1yZzm^OPBsY3ss?DLujy?N;D8G8{1{OGuv+2d$-QL z#kQ%G7#={0LWbaj(cl|cA58WIBPR0TPohREM#)v9B*F_2nxOGf&zYr#1vK1bznS^Y znK|donRE8^7s|KP|CP^IOyCP$^GvE8C;e5b%XYG@Z^4#vY3`U?A(T*irdadDJeoJ= z)qF9Z32R!+dru%$eio))=1$pZ2}qmeIv}~0 zl#?!$0@=Dsmazy8g0LJ$FI7@rS^@I4)WzHsWnSiBo@zK8wpN0qjjmZ?Ko$WbRlr6c zV5@;$M{DlU4TEmzh<$IYoz{ZEI_Xguj;#mPj#Y;mfNz9f6DtA(&XogLXG)cBcxf2) zyFeCBsk9m9>t~f{tH?Nh&VA`Tt~$%W*|`fMRNRs~tG$q9ryMt&vd}lDo$o~ zoh8k5R*%}a6DZz*d=Y0;nM9IB6^?oaKCDI_MUiKmnemCVVP^R(?*V3;sPK0y?YXD? z=amg%Ab2wZp2J%JELVz|$%(Aacm$SFhdh$w3+8QNeW0pVo*Te(4Q3eNOI%}Sg7<^Q zfJg@B0_~{#IKmSMn{zh}vWl(zvG7;K~b@^J;Hr)a`tg)VFd zr9G(qG{9ltat$Iw{v zPPc$5ArAPVXSXK--Xi9z%tTgx^JJy3P-PCfDOIVuct<)wM%EDWiZV~;6?~bKc5%@a zT(BL4r*phwUP-D7*-h+ZR*l+sag2{|LBMN`+AIf8=;KU=#)ptnWn_F8%nj^CEAf=a zGYKOfPMqm9p4KUw;k!X&237k24wGMeWv4ctm=W80&U7yYTh5G~4gcutJgQvw1W#-~ z*7uVqwB!kW*UDrqM zFXCyV2qOsl0isTh|4n=h!Gk~%7(x;N3kZV< z!wCBjVhDZWKx;v;zP7-T3Y8N$l;TgdAFQt-C*40Ze;K>IaVOY zD&aB_(l!^jT0{Ffkd$4-z?q4F7iHOH_b3d?D%KgJ`J`EHImb(q6>529ao(w2{;Wtw zhMj3VP8Sy=4ayF2CDKtT(_U;$Yg&S*=NRWlLF1iV^V;{E-T3aH+%ctADQ5{ItEcsG z$bfD#sNB^Hix%b6o)mv|hLr=Ns;jqj7B|^xI%!%qXo0*|jCKu#q&5F)@yAUzWAb-F z^&gV^q)StJ;6;_ga#wT%KMf;?@*}(v0o$VGga!dFa2qn%6SzZsKJuB$L*m2HjsVIvG%qg~0}$-@5ZK7wD+K^I)&&m$ delta 20 acmbPXI>VIvG%qg~0}vRj int: i += 1 return turns -def _last_turn_end_index(messages: List[BaseMessage]) -> int: - for i in range(len(messages) - 1, -1, -1): - if messages[i].type in ("ai", "assistant"): - return i - return -1 - # ---------- 压缩函数 ---------- def _lightweight_compress(llm, messages_to_compress: List[BaseMessage]) -> str: """轻量压缩:保留关键事实、实体、决策,删除冗余""" @@ -281,3 +288,271 @@ def assistant_text(value) -> str: return str(value).strip() return str(value).strip() + +class Documents_process: + """文件分类与内容提取工具集""" + + @staticmethod + def doc_classify(file_path: str) -> str: + """ + 根据扩展名和 MIME 类型返回内部处理类型标签。 + 可能的返回值:'plain_text'、'docx'、'pdf'、'image'、'tabular'。 + """ + ext = pathlib.Path(file_path).suffix.lower() + # 先检查文档映射表 + if ext in DOC_EXT_MAP: + return DOC_EXT_MAP[ext] + + # 再检查是否为图片 + if ext in IMAGE_FILE_EXTENSIONS: + return "image" + + # 最后使用 MIME 类型判断 + mime, _ = mimetypes.guess_type(file_path) + if mime: + if mime.startswith("text/"): + return "plain_text" + if mime.startswith("image/"): + return "image" + + raise ValueError(f"不支持的文件类型: {file_path}") + + # ---------- 各类文件的具体处理方法 ---------- + @staticmethod + def process_plain_text(file_path: str) -> tuple[str, list]: + with open(file_path, "r", encoding="utf-8") as f: + return f.read(), [] + + @staticmethod + def process_docx(file_path: str) -> tuple[str, list]: + """按文档顺序提取文本、图片和表格,并将短标题与图表合并。""" + try: + from docx import Document + from docx.oxml.ns import qn + from docx.opc.constants import RELATIONSHIP_TYPE as RT + except ImportError: + raise ImportError("需要安装 python-docx: pip install python-docx") + + doc = Document(file_path) + content_parts = [] + image_bytes_list = [] + image_counter = 0 + + # ---------- 辅助函数:按文档顺序迭代段落和表格 ---------- + def iter_block_items(parent): + """生成文档中的段落和表格,保持原始顺序""" + from docx.oxml.ns import qn as ns_qn + if hasattr(parent, 'element'): + parent_elm = parent.element.body + else: + parent_elm = parent + + # 建立元素到段落的映射,便于 O(1) 查找 + para_map = {p._element: p for p in doc.paragraphs} + # 表格同样建立映射 + table_map = {t._element: t for t in doc.tables} + + for child in parent_elm.iterchildren(): + if child.tag == ns_qn('w:p'): + para = para_map.get(child) + if para is not None: + yield para + elif child.tag == ns_qn('w:tbl'): + table = table_map.get(child) + if table is not None: + yield table + + def extract_image_from_run(run, para): + """从 run 中提取图片,返回 N 字符串和图片二进制列表""" + nonlocal image_counter + drawings = run._element.findall('.//' + qn('w:drawing')) + imgs = [] + for drawing in drawings: + blips = drawing.findall('.//' + qn('a:blip')) + for blip in blips: + embed = blip.get(qn('r:embed')) + if embed: + image_counter += 1 + related_part = para.part.related_parts[embed] + image_bytes_list.append(related_part.blob) + imgs.append(f"{image_counter}") + return imgs + + pending_title = "" # 缓存可能存在的图表短标题 + + for item in iter_block_items(doc): + if hasattr(item, 'text'): # 段落 + para = item + para_parts = [] + # 提取图片 + for run in para.runs: + para_parts.extend(extract_image_from_run(run, para)) + # 提取文字 + text = para.text.strip() + if text: + para_parts.insert(0, text) + para_str = "".join(para_parts) + + if not para_str: + continue + + # 判断是否是短标题(如“表1 一个表格”、“图1 一张图片”) + if len(text) < 30 and (text.startswith("表") or text.startswith("图") or text.startswith("Table") or text.startswith("Fig")): + # 暂存为候选标题,等待后续表格/图片 + pending_title = para_str + continue + + # 普通段落:先输出可能遗留的标题(没有后续图表匹配的),再输出本段 + if pending_title: + content_parts.append(pending_title + "\n") + pending_title = "" + content_parts.append(para_str + "\n") + + else: # 表格 + table = item + table_str_parts = [f"===== Table ??? =====\n"] # 标题稍后合并 + for row in table.rows: + row_data = [cell.text.strip() for cell in row.cells] + table_str_parts.append("| " + " | ".join(row_data) + " |\n") + table_content = "".join(table_str_parts) + + # 如果有暂存标题,合并到一起 + if pending_title: + # 替换表格占位标题 + final_table = f"{pending_title}\n{table_content[table_content.index('\n')+1:]}
" + content_parts.append(final_table + "\n") + pending_title = "" + else: + # 没有标题,保留原始表格内容 + content_parts.append(f"{table_content}
\n") + + # 文件末尾的遗留标题 + if pending_title: + content_parts.append(pending_title + "\n") + + return "".join(content_parts), image_bytes_list + + @staticmethod + def process_pdf(file_path: str) -> tuple[str, list]: + try: + from pypdf import PdfReader + except ImportError: + raise ImportError("需要安装 pypdf:pip install pypdf") + + reader = PdfReader(file_path) + text_parts = [] + image_bytes_list = [] + image_counter = 0 # 全局图片计数器 + + for page in reader.pages: + page_content = [] + # 提取文字 + page_text = page.extract_text() + if page_text: + page_content.append(page_text) + + # 提取图片,并在文本中追加占位符 + for _ in page.images: + image_counter += 1 + page_content.append(f"{image_counter}") + + # 收集图片二进制数据 + for image_file_object in page.images: + image_bytes_list.append(image_file_object.data) + + # 合并当前页的内容 + if page_content: + text_parts.append("".join(page_content)) + + return "\n".join(text_parts), image_bytes_list + + @staticmethod + def process_tabular(file_path: str) -> tuple[str, list]: + """ + 处理 CSV 和 Excel(.xlsx) 文件。 + 将所有 sheet 的内容转化为 SheetName\n内容
格式的字符串。 + 图片列表固定为空。 + """ + try: + import csv + from openpyxl import load_workbook + except ImportError: + raise ImportError("需要安装 csv和openpyxl:pip install csv openpyxl") + + result_parts = [] + ext = pathlib.Path(file_path).suffix.lower() + + if ext == '.csv': + # CSV 只有一个“sheet” + with open(file_path, 'r', encoding='utf-8-sig', newline='') as f: + reader = csv.reader(f) + rows = list(reader) + table_str = '\n'.join([' | '.join(row) for row in rows]) + result_parts.append(f"Sheet1\n{table_str}
") + + elif ext == '.xlsx': + wb = load_workbook(file_path, read_only=True, data_only=True) + for sheet_name in wb.sheetnames: + ws = wb[sheet_name] + rows = [] + for row in ws.iter_rows(values_only=True): + rows.append(' | '.join([str(cell) if cell is not None else '' for cell in row])) + table_str = '\n'.join(rows) + result_parts.append(f"{sheet_name}\n{table_str}
") + wb.close() + else: + raise ValueError(f"Unsupported tabular file extension: {ext}") + + return "\n".join(result_parts), [] + + @staticmethod + def process_image(file_path: str) -> tuple[str, list]: + with open(file_path, "rb") as f: + return "", [f.read()] + + # ---------- 统一处理入口 ---------- + @staticmethod + def process(file_path: str) -> tuple[str, list]: + file_type = Documents_process.doc_classify(file_path) + + if file_type == "plain_text": + return Documents_process.process_plain_text(file_path) + elif file_type == "docx": + return Documents_process.process_docx(file_path) + elif file_type == "pdf": + return Documents_process.process_pdf(file_path) + elif file_type == "image": + return Documents_process.process_image(file_path) + elif file_type == "tabular": + return Documents_process.process_tabular(file_path) + else: + raise ValueError(f"未处理的文件类型: {file_type}") + +if __name__ == "__main__": + docx_path = r"C:\Users\86166\Desktop\Agent_Learning_minor\信息科学与工程学院-赵永顺-《兼爱》——墨翟一生的“白月光”.docx" + pdf_path = r"C:\Users\86166\Desktop\Agent_Learning_minor\信息科学与工程学院-赵永顺-《兼爱》——墨翟一生的“白月光”.pdf" + xlsx_path = r"C:\Users\86166\Desktop\Agent_Learning_minor\电科二班2025德育评价.xlsx" + py_path = r"C:\Users\86166\Desktop\Agent_Learning_minor\speech_server.py" + + output_path = "test_results.txt" + with open(output_path, "w", encoding="utf-8") as f: + test_files = [ + ("py", py_path), + ("docx", docx_path), + ("xlsx", xlsx_path), + ("pdf", pdf_path), + ] + for label, path in test_files: + f.write(f"\n===== 测试 {label} 文件:{path} =====\n") + try: + text, images = Documents_process.process(path) + f.write(f"提取文本长度:{len(text)}\n") + f.write(f"提取图片数量:{len(images)}\n") + f.write("文本预览:\n") + f.write(text) + except Exception as e: + f.write(f"❌ 处理失败: {e}\n") + print(f"测试结果已写入 {output_path}") + + + \ No newline at end of file diff --git a/src/web/__pycache__/server.cpython-312.pyc b/src/web/__pycache__/server.cpython-312.pyc index 6fd3311645e8c15bcaa0f8a6673bf8d1eb5ba189..9592cae6e1a4dc2c39d59ee04ed1d5b163b5885e 100644 GIT binary patch delta 6313 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 diff --git a/src/web/__pycache__/server.cpython-313.pyc b/src/web/__pycache__/server.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..357586275463b49ba49436882d39d3494279cf59 GIT binary patch literal 22362 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 wmn(J|ca^Ium!#H<>AdX&cO~bgIDU z8)g(t>?BJ7Xz}PKip2?t`-PU2d+-EpU-az@7?7D>+iDbgu**2tuE8(7$*L9{ItM2g z*h6sbB9~yDut{7J7INw(o$=%7iYD%t0Oiku8^5={{2#wN^FHhwo|}Gi1Z=HjI{9-U zd;XcpA$xJZfSX0d{WCnG3eM&2?cVQ-s^KR?;MC227nm5Ms-2E*zl$86=9kw0g5{Rr ztqN~j@rEM?zYn5{&kZs^@mD80`00uc?zuXC2JBm)5yCkbvI&Q~e*<76ICXyr3DEF0 z3r2E-nv$@3Nl?8cq_z&g(PgP@Ck-KqVWlakG@Z=lPius7swR|H5--Tl>!G#0X0_t( zz&)*S1S3%UKv4H!KnZrb*qQ8CmXG>gu7d;Dhu~cHq&jvJL#B?wl%2R7HF{rl(v7xv&}o{Z@lPDJW;YPgZgQPYF(xDr+Mmi<0!!n%D9j}i;09;-Jxs>{WF0j<1>BZ*E^+$&|DRF19*nyN$Ur2%Fs&(ul~A$`^kulH|(sUlVv zcL7?QiyFjvA+4b)?n}u2(Ok^xf+nDERe-7DF;fuC_Wn%}!!uvjz21~~LC_iE1-btf z&^kL8t(8GjWk|gwz$_tX!Qr(c>>N&d*d4gg`1FkmZ20C!%uT%>7pNDw-RE#6Bhl6U zS?C&R1&AeP_zj&0VsLy2^6wDpq5uzdw?n!=55pK+lZ%Pv5V}7z6$B`{B(u^7{s9-? zB7uXpA6Q~eRpfzKnQwH-tpO&_K|4HaW2M}GgBvUpufpS-Y*JPHSAIm?$foEk1kXXZ zj5vtUYqEhN3&ur&=+EU*O>TUN84(6OE{+1x$q)s8un00V@v|P))pb z><33MO~#px_~>~>HM?45p? zZ!Wu?gJp!`YVbzZ;XF*e3^#~WJ?Q0L!q-uJRiM+U+r68^wI?cvQ=IU-K4fx6L0JG* zu+Pn**yeEiz@fs+FY%~1MzdoO)*kWao2+2%^m1y<%@A@yXpoL8t-PQh zY4Up@TomE{8H=TX$+zDHbpk8m$3nc7;%yb)FjR^J%8}Tgs1z0ih}=gzv)DKOEa+nZ z2tTj{zm!EnA4zZ=iMZl04g^R(yirKEvD3@P>LbQND;6BP%dj?8wqGcR^IH z>FIU)yIpI!KS42AcPOtM$OM9grmsrmH1h`?MVEXX{zi?%KRl&_d77ADW6%( zp9~GDK?E-jsuvF^BigJHMNnHfp!%J5!TIbhA#LM;>VkGbAbUwjTQ#7%E|=4W>xMkK zVs;Ij?B7ZU>>XcIm2}4SHhKlEzs}KVbnaCXrO*X3OUJr`@)ZNp2qg_uh9G5#FouY6 z!Ejk*K`#HqmsvGM8?RDwS^`HBC90nz7w+xXE6oQ^+IWDh^~g85uqO-rI*fgxTIl9g*YJe ztcH?jUe`hj2WjD~1ivj78!iaZg+jyGc_SN-J~As)&j^wbPv9Br38wg-V2bYvrZkixYZl7^kpUGwq!5$r zSqXky8YBE5%!=%55}MA(Ow%l;pgMk53_^cR*Cc7QpL8N;mLz9QvFtz^jtAy%7dnYa vF@Mnbcs^ELgH`LW>Y8}fxgk0eGp(4Jg_+iPCQM}`W>#Tl=2c7)W#fMXM5dWX literal 0 HcmV?d00001 diff --git a/src/web/__pycache__/ui_session.cpython-312.pyc b/src/web/__pycache__/ui_session.cpython-312.pyc index e70b30c82b6ea37ab048b23086130e8a98393575..ae1e597c7c3b3857cbd2bbec945f05354ba4c9b1 100644 GIT binary patch delta 5985 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 W=OHqWRNdJ@RuK1{BztUU-TwhBYVF_v delta 4275 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 literal 0 HcmV?d00001 diff --git a/src/web/app.css b/src/web/app.css index c6a04e5..d6250d3 100644 --- a/src/web/app.css +++ b/src/web/app.css @@ -964,6 +964,59 @@ body { min-height: 0; } +.file-btn { + position: relative; +} + +.file-btn--icon-only { + width: 2.1rem; + min-width: 2.1rem; + height: 2.1rem; + padding: 0; + display: inline-flex; + align-items: center; + justify-content: center; + border-radius: 999px; +} + +.file-btn-icon { + width: 0.9rem; + height: 0.9rem; + display: block; +} + +.document-badge { + display: inline-flex; + align-items: center; + gap: 0.45rem; + max-width: 100%; + margin: 0.3rem 0; + padding: 0.1rem 0; + border: none; + background: transparent; + box-shadow: none; +} + +.document-badge--user { + margin-left: auto; +} + +.document-badge-icon { + width: 0.95rem; + height: 0.95rem; + flex-shrink: 0; + border: none; + margin: 0; +} + +.document-badge-name { + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 0.88rem; +} + .chip { font-size: 0.75rem; padding: 0.22rem 0.5rem; diff --git a/src/web/app.js b/src/web/app.js index 060e9cf..a17e553 100644 --- a/src/web/app.js +++ b/src/web/app.js @@ -5,8 +5,7 @@ const chatMessages = $("chatMessages"); const chatPlaceholder = $("chatPlaceholder"); const textInput = $("textInput"); - const imageInput = $("imageInput"); - const audioInput = $("audioInput"); + const fileInput = $("fileInput"); const micBtn = $("micBtn"); const autoInputCheck = $("autoInputCheck"); const attachmentPreview = $("attachmentPreview"); @@ -17,7 +16,6 @@ const collapseBtn = $("collapseBtn"); const expandBtn = $("expandBtn"); const expandRail = $("expandRail"); - const sidebar = $("sidebar"); const appRoot = $("app"); const mainPanel = $("mainPanel"); const headerToggleBtn = $("headerToggleBtn"); @@ -39,10 +37,7 @@ /** @type {string[]} */ let sessions = []; /** @type {File[]} */ - let pendingImages = []; - /** @type {File | null} */ - let pendingAudio = null; - let sidebarCollapsed = false; + let pendingFiles = []; let sending = false; let mediaRecorder = null; let recorderStream = null; @@ -128,21 +123,32 @@ .replace(/"/g, """); } - function buildOptimisticUserContent(text, imageBlobUrls, audioPart) { + function buildOptimisticUserContent(text, fileParts) { const parts = []; - (imageBlobUrls || []).forEach((url) => { - parts.push({ type: "image", url }); + (fileParts || []).forEach((filePart) => { + if (filePart && filePart.kind === "image" && filePart.url) { + parts.push({ type: "image", url: filePart.url, name: filePart.name || "图片" }); + } else if (filePart && filePart.kind === "audio" && filePart.url) { + parts.push({ type: "audio", url: filePart.url, name: filePart.name || "音频" }); + } else if (filePart && filePart.name) { + parts.push({ type: "file", name: filePart.name }); + } }); const t = (text || "").trim(); if (t) parts.push({ type: "text", text: t }); - if (audioPart && audioPart.url) { - parts.push({ type: "audio", url: audioPart.url, name: audioPart.name || "音频" }); - } if (parts.length === 0) return null; if (parts.length === 1 && parts[0].type === "text") return parts[0].text; return parts; } + function inferUploadKind(file) { + const name = String(file?.name || "").toLowerCase(); + const type = String(file?.type || "").toLowerCase(); + if (type.startsWith("image/") || /\.(png|jpe?g|gif|webp|bmp|svg)$/.test(name)) return "image"; + if (type.startsWith("audio/") || /\.(wav|mp3|m4a|flac|ogg|webm)$/.test(name)) return "audio"; + return "file"; + } + function getTextFromContent(content) { if (typeof content === "string") return content; if (!Array.isArray(content)) return ""; @@ -295,7 +301,7 @@ audioContext = new (window.AudioContext || window.webkitAudioContext)(); - if (audioContext.state === 'suspended') { + if (audioContext.state === "suspended") { await audioContext.resume(); } @@ -406,7 +412,7 @@ recorder.addEventListener("stop", () => { try { const blob = new Blob(recordedChunks, { type: recordingMimeType || recorder.mimeType || "audio/webm" }); - pendingAudio = buildRecordedAudioFile(blob, recordingMimeType || recorder.mimeType); + pendingFiles = pendingFiles.concat(buildRecordedAudioFile(blob, recordingMimeType || recorder.mimeType)); renderAttachmentChips(); resolve(); } catch (err) { @@ -627,10 +633,6 @@ return wrap; } - function parseAssistantContent(full) { - return renderMarkdownToFragment(full); - } - function fillAssistantBubbleImmediate(bubble, text) { const t = text == null ? "" : String(text); bubble.classList.add("msg-bubble--rich", "markdown-body"); @@ -895,6 +897,24 @@ return wrap; } + function renderDocumentBadge(part, options = {}) { + const wrap = document.createElement("div"); + wrap.className = "document-badge" + (options.role === "user" ? " document-badge--user" : ""); + + const icon = document.createElement("img"); + icon.className = "document-badge-icon"; + icon.src = "/image/文档.svg"; + icon.alt = "文档"; + + const name = document.createElement("span"); + name.className = "document-badge-name"; + name.textContent = part.name || "附件"; + + wrap.appendChild(icon); + wrap.appendChild(name); + return wrap; + } + function renderContentPart(part, options = {}) { if (typeof part === "string") { const div = document.createElement("div"); @@ -923,13 +943,8 @@ if (part.type === "audio" && part.url) { return renderAudioBubble(part, options); } - if (part.type === "file" && part.url) { - const a = document.createElement("a"); - a.href = part.url; - a.textContent = part.name || "附件"; - a.target = "_blank"; - a.rel = "noopener"; - return a; + if (part.type === "file") { + return renderDocumentBadge(part, options); } const div = document.createElement("div"); div.className = "msg-text"; @@ -994,27 +1009,16 @@ function renderAttachmentChips() { attachmentPreview.innerHTML = ""; - pendingImages.forEach((f, i) => { + pendingFiles.forEach((f, i) => { const chip = document.createElement("span"); chip.className = "chip"; chip.innerHTML = `${escapeHtml(f.name)} `; chip.querySelector("button").addEventListener("click", () => { - pendingImages = pendingImages.filter((_, j) => j !== i); + pendingFiles = pendingFiles.filter((_, j) => j !== i); renderAttachmentChips(); }); attachmentPreview.appendChild(chip); }); - if (pendingAudio) { - const chip = document.createElement("span"); - chip.className = "chip"; - chip.innerHTML = `${escapeHtml(pendingAudio.name)} `; - chip.querySelector("button").addEventListener("click", () => { - pendingAudio = null; - audioInput.value = ""; - renderAttachmentChips(); - }); - attachmentPreview.appendChild(chip); - } } async function bootstrap() { @@ -1039,10 +1043,8 @@ const data = await api("/api/sessions/new", { method: "POST" }); sessionId = data.sessionId; sessions = data.sessions || []; - pendingImages = []; - pendingAudio = null; - imageInput.value = ""; - audioInput.value = ""; + pendingFiles = []; + fileInput.value = ""; renderSessions(); renderAttachmentChips(); renderMessages(data.messages); @@ -1058,10 +1060,8 @@ const data = await api("/api/sessions/delete", { method: "POST", body: fd }); sessionId = data.sessionId; sessions = (data.sessions || []).filter((id) => id !== deletingSessionId); - pendingImages = []; - pendingAudio = null; - imageInput.value = ""; - audioInput.value = ""; + pendingFiles = []; + fileInput.value = ""; renderAttachmentChips(); renderSessions(); renderMessages(data.messages || []); @@ -1079,46 +1079,40 @@ async function sendChat() { if (!sessionId || sending) return; const trimmed = textInput.value.trim(); - if (!trimmed && pendingImages.length === 0 && !pendingAudio) return; + if (!trimmed && pendingFiles.length === 0) return; sending = true; sendBtn.disabled = true; const savedTextRaw = textInput.value; - const savedImages = pendingImages.slice(); - const savedAudio = pendingAudio; + const savedFiles = pendingFiles.slice(); const priorMessages = JSON.parse(JSON.stringify(lastRenderedMessages)); const blobUrls = []; - const hadOptimisticMedia = savedImages.length > 0 || !!savedAudio; + const hadOptimisticMedia = savedFiles.length > 0; if (hadOptimisticMedia) { - const imageBlobUrls = savedImages.map((f) => { - const u = URL.createObjectURL(f); - blobUrls.push(u); - return u; + const fileParts = savedFiles.map((f) => { + const kind = inferUploadKind(f); + if (kind === "image" || kind === "audio") { + const u = URL.createObjectURL(f); + blobUrls.push(u); + return { kind, url: u, name: f.name }; + } + return { kind, name: f.name }; }); - let audioPart = null; - if (savedAudio) { - const u = URL.createObjectURL(savedAudio); - blobUrls.push(u); - audioPart = { url: u, name: savedAudio.name }; - } - const optimisticContent = buildOptimisticUserContent(trimmed, imageBlobUrls, audioPart); + const optimisticContent = buildOptimisticUserContent(trimmed, fileParts); if (optimisticContent != null) { renderMessages([...priorMessages, { role: "user", content: optimisticContent }]); } textInput.value = ""; - pendingImages = []; - pendingAudio = null; - imageInput.value = ""; - audioInput.value = ""; + pendingFiles = []; + fileInput.value = ""; renderAttachmentChips(); } const fd = new FormData(); fd.append("session_id", sessionId); fd.append("text", trimmed); - savedImages.forEach((f) => fd.append("images", f, f.name)); - if (savedAudio) fd.append("audio", savedAudio, savedAudio.name); + savedFiles.forEach((f) => fd.append("files", f, f.name)); let typingEl = null; try { const startData = await api("/api/chat/start", { method: "POST", body: fd }); @@ -1127,10 +1121,8 @@ sessions = startData.sessions || []; if (!hadOptimisticMedia) { textInput.value = ""; - pendingImages = []; - pendingAudio = null; - imageInput.value = ""; - audioInput.value = ""; + pendingFiles = []; + fileInput.value = ""; renderAttachmentChips(); } renderSessions(); @@ -1164,8 +1156,7 @@ revokeBlobUrlStack(blobUrls); if (hadOptimisticMedia) { textInput.value = savedTextRaw; - pendingImages = savedImages.slice(); - pendingAudio = savedAudio; + pendingFiles = savedFiles.slice(); renderAttachmentChips(); renderMessages(priorMessages); } @@ -1177,18 +1168,10 @@ } } - imageInput.addEventListener("change", () => { - const files = Array.from(imageInput.files || []); - pendingImages = pendingImages.concat(files); - imageInput.value = ""; - renderAttachmentChips(); - textInput.focus(); - }); - - audioInput.addEventListener("change", () => { - const f = audioInput.files && audioInput.files[0]; - pendingAudio = f || null; - audioInput.value = ""; + fileInput.addEventListener("change", () => { + const files = Array.from(fileInput.files || []); + pendingFiles = pendingFiles.concat(files); + fileInput.value = ""; renderAttachmentChips(); textInput.focus(); }); diff --git a/src/web/image/文档.svg b/src/web/image/文档.svg new file mode 100644 index 0000000..2b4e61c --- /dev/null +++ b/src/web/image/文档.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/web/image/附件.svg b/src/web/image/附件.svg new file mode 100644 index 0000000..17b8ef1 --- /dev/null +++ b/src/web/image/附件.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/web/index.html b/src/web/index.html index 9a8d649..cd18cf1 100644 --- a/src/web/index.html +++ b/src/web/index.html @@ -81,7 +81,7 @@

开始对话

-

输入文字,或上传图片 / 音频 后点击发送。

+

输入文字,或上传附件 后点击发送。

支持用户音频输入、助手文本/语音/双模输出、会话本地媒体持久化。

@@ -101,13 +101,9 @@
- -