From 993b41b7b151d07941b6a474564f80729b9a2a46 Mon Sep 17 00:00:00 2001 From: Wang Qi Date: Fri, 7 Aug 2026 16:04:19 +0800 Subject: [PATCH] Append attachments content to last message (#17993) --- api/db/services/dialog_service.py | 6 +++++- rag/advanced_rag/agentic_rag.py | 15 ++------------- rag/advanced_rag/harness/orchestrator/agentic.py | 2 -- .../harness/orchestrator/decompose.py | 2 -- .../rag/advanced_rag/test_agentic_rag.py | 9 ++------- 5 files changed, 9 insertions(+), 25 deletions(-) diff --git a/api/db/services/dialog_service.py b/api/db/services/dialog_service.py index b57196aea9..c6d1fba61d 100644 --- a/api/db/services/dialog_service.py +++ b/api/db/services/dialog_service.py @@ -828,7 +828,7 @@ async def async_chat(dialog, messages, stream=True, **kwargs): kwargs.setdefault("knowledge", "") gen_conf = dialog.llm_setting - system_content = prompt_config["system"].format(**kwargs) + text_attachments_content + system_content = prompt_config["system"].format(**kwargs) # If knowledge was retrieved but the template has no {knowledge} # placeholder, auto-append it so the LLM still sees the context. if knowledges and "{knowledge}" not in prompt_config.get("system", ""): @@ -838,6 +838,8 @@ async def async_chat(dialog, messages, stream=True, **kwargs): if knowledges and (prompt_config.get("quote", True) and kwargs.get("quote", True)): prompt4citation = citation_prompt() msg.extend([{"role": m["role"], "content": re.sub(r"##\d+\$\$", "", m["content"])} for m in messages if m["role"] != "system"]) + if text_attachments_content and msg: + msg[-1]["content"] += text_attachments_content used_token_count, msg = message_fit_in(msg, int(max_tokens * 0.95)) if llm_model_config["model_type"] == "chat" and image_attachments: convert_last_user_msg_to_multimodal(msg, image_attachments, factory) @@ -1882,6 +1884,8 @@ async def rag_agent(dialog, messages, stream=True, **kwargs): factory = chat_mdl.model_config.get("llm_factory", "") if chat_mdl.model_config else "" text_attachments_content, image_attachments, image_files = get_files_content(messages[-1], model_type) agent_messages = deepcopy(messages) + if text_attachments_content and agent_messages: + agent_messages[-1]["content"] += text_attachments_content if model_type == "chat" and image_attachments: convert_last_user_msg_to_multimodal(agent_messages, image_attachments, factory) use_web_search = _should_use_web_search(prompt_config, kwargs.get("internet")) diff --git a/rag/advanced_rag/agentic_rag.py b/rag/advanced_rag/agentic_rag.py index b076807918..2fff48f5c0 100644 --- a/rag/advanced_rag/agentic_rag.py +++ b/rag/advanced_rag/agentic_rag.py @@ -574,20 +574,9 @@ class RAGTools: if self.tool_started_sink is not None: self.tool_started_sink() - if self.text_attachments_content: - self.kbinfos = { - "chunks": [ - { - "id": "chat_attachment", - "chunk_id": "chat_attachment", - "doc_id": "chat_attachment", - "docnm_kwd": "Chat attachment", - "content_with_weight": self.text_attachments_content, - } - ], - "doc_aggs": [{"doc_id": "chat_attachment", "doc_name": "Chat attachment", "count": 1}], - } messages = [{"role": "user", "content": question}] if question else [] + if self.text_attachments_content and messages: + messages[-1]["content"] += self.text_attachments_content final = "" async for kind, delta in _split_think_stream(run_agentic_rag(self, messages)): if kind == "answer": diff --git a/rag/advanced_rag/harness/orchestrator/agentic.py b/rag/advanced_rag/harness/orchestrator/agentic.py index af0ef5a1f0..18970a1219 100644 --- a/rag/advanced_rag/harness/orchestrator/agentic.py +++ b/rag/advanced_rag/harness/orchestrator/agentic.py @@ -143,8 +143,6 @@ async def agentic_research(state: dict, tools) -> dict: if action == "ANSWER_PARTIAL": return _finalize(ctx, tools, partial=True) if action == "ABSTAIN": - if getattr(tools, "text_attachments_content", ""): - return {"verdict": verdict.__dict__, "kbinfos": tools.kbinfos} tools.kbinfos["chunks"] = [] return {"verdict": verdict.__dict__, "abstain": True} if action == "REPLAN": diff --git a/rag/advanced_rag/harness/orchestrator/decompose.py b/rag/advanced_rag/harness/orchestrator/decompose.py index 315b9ae77e..925dfb6d9b 100644 --- a/rag/advanced_rag/harness/orchestrator/decompose.py +++ b/rag/advanced_rag/harness/orchestrator/decompose.py @@ -186,8 +186,6 @@ async def decompose_and_search(state: dict, tools) -> dict: if action in ("ANSWER", "ANSWER_PARTIAL"): return _finalize(ctx, tools, partial=action == "ANSWER_PARTIAL", loop=completed_cycles) if action == "ABSTAIN": - if getattr(tools, "text_attachments_content", ""): - return {"verdict": verdict.__dict__, "kbinfos": tools.kbinfos} tools.kbinfos["chunks"] = [] return {"verdict": verdict.__dict__, "abstain": True, "loop": completed_cycles} if action == "FALLBACK_LLM": diff --git a/test/unit_test/rag/advanced_rag/test_agentic_rag.py b/test/unit_test/rag/advanced_rag/test_agentic_rag.py index 9e826d942b..7714a81453 100644 --- a/test/unit_test/rag/advanced_rag/test_agentic_rag.py +++ b/test/unit_test/rag/advanced_rag/test_agentic_rag.py @@ -1,5 +1,3 @@ -from copy import deepcopy - import pytest from rag.advanced_rag.agentic_rag import RAGTools @@ -13,11 +11,10 @@ class FakeChatModel: @pytest.mark.asyncio -async def test_rag_tool_adds_text_attachment_as_evidence(monkeypatch): +async def test_rag_tool_adds_text_attachment_to_user_question(monkeypatch): captured = {} async def fake_run_agentic_rag(tools, messages): - captured["kbinfos"] = deepcopy(tools.kbinfos) captured["messages"] = messages yield "answer" @@ -26,6 +23,4 @@ async def test_rag_tool_adds_text_attachment_as_evidence(monkeypatch): tools = RAGTools([], FakeChatModel(), text_attachments_content="attached facts") assert await tools.rag("What is attached?") == "answer" - assert captured["messages"] == [{"role": "user", "content": "What is attached?"}] - assert captured["kbinfos"]["chunks"][0]["docnm_kwd"] == "Chat attachment" - assert captured["kbinfos"]["chunks"][0]["content_with_weight"] == "attached facts" + assert captured["messages"] == [{"role": "user", "content": "What is attached?attached facts"}]