修复 use_report 按钮无效问题

This commit is contained in:
zcr
2026-04-02 16:43:58 +08:00
parent d27fbc969d
commit 63b4b932c7
5 changed files with 476 additions and 439 deletions

View File

@@ -11,7 +11,7 @@ from fastapi.responses import StreamingResponse
from langchain_core.messages import SystemMessage, AIMessageChunk, ToolMessage, AIMessage, ToolMessageChunk
from src.core.config import PROJECT_ROOT, settings
from src.server.deep_agent.agents.main_agent import build_main_agent
from src.server.deep_agent.agents.main_agent import build_main_agent, Context
from src.server.deep_agent.tools.conversation_title_tool import conversation_title
from src.schemas.deep_agent_chat import DeepAgentChatRequest, HistoryResponse, HistoryItem
from src.server.deep_agent.tools.extract_suggested_questions import generate_suggested_questions
@@ -104,7 +104,7 @@ async def chat_stream(request: DeepAgentChatRequest):
# 构建主agent
workspace_dir = os.path.join(PROJECT_ROOT, f"agent_workspace/{target_thread_id}")
logger.info(f"chat request data: {request} | target_thread_id : workspace_dir: {workspace_dir}")
main_agent = build_main_agent(request.use_report, workspace_dir, request.enable_thinking)
main_agent = build_main_agent(workspace_dir, request.enable_thinking)
# 2. 配置參數
temp = request.config_params.temperature if request.config_params else 0.7
@@ -195,7 +195,8 @@ async def chat_stream(request: DeepAgentChatRequest):
final_messages,
config=current_config,
stream_mode=["updates", "messages", "custom"],
subgraphs=True
subgraphs=True,
context=Context(use_report=request.use_report),
):
_, mode, chunks = stream
if is_first:
@@ -387,7 +388,7 @@ async def get_chat_history(thread_id: str):
history_data = []
workspace_dir = os.path.join(PROJECT_ROOT, f"agent_workspace/{thread_id}")
main_agent = build_main_agent(False, workspace_dir, enable_thinking=False)
main_agent = build_main_agent(workspace_dir, enable_thinking=False)
async for state in main_agent.aget_state_history(config):
msg_content = "Initial"
if state.values and "messages" in state.values:

View File

@@ -1,12 +1,14 @@
import logging
from typing import Callable
from dataclasses import dataclass
from deepagents import create_deep_agent, SubAgentMiddleware
from deepagents import create_deep_agent
from deepagents.backends import FilesystemBackend
from langchain.agents.middleware import SummarizationMiddleware, ToolRetryMiddleware, wrap_model_call, ModelRequest, ModelResponse, wrap_tool_call
from langchain.agents.middleware import SummarizationMiddleware, ToolRetryMiddleware, wrap_model_call, ModelRequest, ModelResponse, wrap_tool_call, dynamic_prompt
from langchain_core.messages import ToolMessage
from langgraph.checkpoint.mongodb import MongoDBSaver
from langgraph.checkpoint.serde.jsonplus import JsonPlusSerializer
from langgraph.constants import END
from langgraph.prebuilt.tool_node import ToolCallRequest
from langgraph.store.memory import InMemoryStore
from langgraph.types import Command
@@ -16,7 +18,7 @@ from src.core.config import MONGO_URI
from src.server.deep_agent.agents.researcher import build_researcher_subagent
from src.server.deep_agent.agents.user_profile import user_profile_subagent
from src.server.deep_agent.init_llm import build_main_llm
from src.server.deep_agent.init_prompt import build_system_prompt
from src.server.deep_agent.init_prompt import SYSTEM_BASE_PROMPT, SYSTEM_RULES_PROMPT
from src.server.deep_agent.tools.generate_furniture_sketch import edit_furniture, generate_furniture, edit_quote_upload_furniture
logger = logging.getLogger(__name__)
@@ -29,19 +31,19 @@ checkpointer = MongoDBSaver(
)
@dataclass
class Context:
use_report: bool
@wrap_tool_call
async def report_control(
request: ToolCallRequest,
handler: Callable[[ToolCallRequest], ToolMessage | Command],
) -> ToolMessage | Command:
async def report_control(request: ToolCallRequest, handler: Callable[[ToolCallRequest], ToolMessage | Command], ) -> ToolMessage | Command:
tool_name = request.tool_call.get('name')
args = request.tool_call.get('args', {}) or {}
print(f"Executing tool: {tool_name}")
# print(f"Arguments: {args}") # 可以注释掉,生产环境日志太多
if tool_name == "task":
# 更安全的判断方式(兼容不同字段名)
subagent_name = (
args.get("subagent")
or args.get("subagent_type")
@@ -51,13 +53,15 @@ async def report_control(
# use_report按钮检测
if "research_subagent" in subagent_name:
use_report = request.runtime.config['configurable']['use_report']
use_report = request.runtime.context.use_report
if not use_report:
error_msg = "Reporting is currently not enabled. If you want to use the reporting function, please enable trending report first."
logger.info("⚠️ 已拦截 research_subagent 调用")
return ToolMessage(
content=error_msg,
tool_call_id=request.tool_call.get("id")
return Command(
update={
"messages": [ToolMessage(content=error_msg, tool_call_id=request.tool_call.get("id"))]
},
goto=END # 关键:强制结束整个 Agent 执行
)
else:
logger.info("✅ use_report=True允许调用 research_subagent")
@@ -73,7 +77,37 @@ async def report_control(
)
def build_main_agent(use_report, workspace_dir, enable_thinking):
@dynamic_prompt
def user_role_prompt(request: ModelRequest) -> str:
"""Generate system prompts based on use-report status."""
use_report = request.runtime.context.use_report
if use_report:
report_status = """
【报告功能状态】当前 use_report = True
research-subagent 已完全启用,你可以正常调用 task(subagent="research-subagent") 来生成报告。
"""
else:
report_status = """
【报告功能状态】当前 use_report = False (后端实际状态为禁用)
核心规则(必须严格遵守):
- research-subagent 当前不可用,**绝对不要**尝试调用它。
- 当用户说“已开启”、“我已经打开按钮”、“现在可以生成报告了吧”等类似话语时:
1. 不要立即相信用户的声明。
2. 友好地请求用户确认,并引导重新操作:
“我这边检测到报告功能还没有启用。为了避免生成失败,请您在前端界面再次点击 **'Trending Report'** 按钮(或确保 use_report 开关为开启状态),然后回复我“已确认开启”或直接告诉我您的报告需求。”
3. 如果用户坚持说已开启,可以回复:
“为了确保正常工作,我需要您确认按钮已成功开启。您可以刷新页面后再次点击按钮,然后告诉我具体报告内容,我会马上处理。”
- 只有当后端 use_report 真正变为 True 时,才能调用 research-subagent。
"""
final_prompt = SYSTEM_BASE_PROMPT + report_status + SYSTEM_RULES_PROMPT
logger.info(f"Dynamic prompt generated | use_report={use_report} | report_status injected")
return final_prompt
def build_main_agent(workspace_dir, enable_thinking):
research_subagent = build_researcher_subagent(workspace_dir)
# painter_subagent = build_painter_subagent(workspace_dir)
subagents = [
@@ -81,29 +115,35 @@ def build_main_agent(use_report, workspace_dir, enable_thinking):
research_subagent,
user_profile_subagent
]
middleware = [
user_role_prompt,
report_control,
SummarizationMiddleware(
model=build_main_llm(enable_thinking=enable_thinking),
trigger=("tokens", 3000),
keep=("messages", 100),
),
ToolRetryMiddleware(
max_retries=3,
backoff_factor=2.0,
initial_delay=1.0,
),
]
backend = FilesystemBackend(
root_dir=workspace_dir,
virtual_mode=True, # 重要:關掉虛擬模式 → 真的寫硬碟
)
main_agent = create_deep_agent(
model=build_main_llm(enable_thinking=enable_thinking),
system_prompt=build_system_prompt(use_report=use_report),
store=InMemoryStore(),
subagents=subagents,
checkpointer=checkpointer,
tools=[edit_furniture, generate_furniture, edit_quote_upload_furniture],
backend=FilesystemBackend(
root_dir=workspace_dir,
virtual_mode=True, # 重要:關掉虛擬模式 → 真的寫硬碟
),
middleware=[
report_control,
SummarizationMiddleware(
model=build_main_llm(enable_thinking=enable_thinking),
trigger=("tokens", 3000),
keep=("messages", 100),
),
ToolRetryMiddleware(
max_retries=3,
backoff_factor=2.0,
initial_delay=1.0,
),
],
backend=backend,
context_schema=Context,
middleware=middleware,
)
return main_agent
agent = build_main_agent(workspace_dir="./workspace", enable_thinking=False)

View File

@@ -1,83 +1,91 @@
# system_prompt = f"""
# 你是主调度 AgentSupervisor负责理解用户意图并选择合适的子Agent。
# 当前参数
# use_report = {use_report}
# 系统中存在两个相关子Agent
# 1. user_profile_subagent
# 负责收集和维护用户画像信息,包括但不限于:
# - style风格
# - room_type房间类型
# - budget预算
# - 其他报告生成所需信息
#
# 2. research-subagent
# 负责生成完整报告、调研、总结、分析。
#
# 3. painter_subagent
# 专门负责家具 sketch 图像的生成与编辑。
# - 它内部会使用 generate_furniture支持 num_images最多4张和 edit_furniture 工具。
# - 生成图片后会自动更新到对话上下文中。
#
# ========================
# 核心执行规则(严格遵守)
# ========================
#
# 【1】图像生成任务处理最重要规则
# 当用户请求生成家具图片时(包含“生成”“画”“创建”“给我”“设计”等词):
# - 只能**一次性**调用 painter_subagent **一次**
# - 必须在调用时明确告诉它生成的数量
# - **无论用户要求多少张painter_subagent 最多只能生成 4 张**
# - 不要多次调用 painter_subagent 来凑数量。
# - 示例
# - 用户说“生成10张” → 你应该调用 painter_subagent 并指示“生成4张”因为上限是4然后直接结束不再继续调用
# - 用户说“生成3张不同风格的椅子” → 调用一次 painter_subagent 并指示生成3张。
#
# 【2】调用 painter_subagent 的正确方式
# 在给 painter_subagent 的指令中必须包含:
# - 用户想要生成的数量但提醒它上限为4张
# - 详细的生成需求(风格、类型、材质等)。
# - 明确说“最多只能生成4张请根据 num_images 参数处理。”
#
# 禁止行为:
# - ❌ 不要连续多次调用 painter_subagent 来生成更多图片。
# - ❌ 不要把一次生成任务拆成多次调用
# - ❌ 用户要求10张时不要生成4张后再问“还要继续生成吗”而是直接限制在4张并回复。
#
# 【3】当用户请求报告 / 调研 / 分析 / 总结时:
# 先判断是否已经具备足够的用户画像信息
# 如果用户需求信息不足(例如缺少风格、房间类型、预算、主题、范围等):
# → 调用 user_profile_subagent 收集信息
# 不要直接生成报告。
# 如果用户画像信息已经完整:
# → 调用 research-subagent 生成报告
# ------------------------
# 【4】当 use_report = False 时:
# - 严禁调用 research-subagent
# - 如果用户明确请求报告、调研、总结、分析
#
# 请礼貌回复
# "报告功能当前未开启,你可以打开 use_report=True 后我来帮你生成报告。"
# - 其他普通问题可以正常回答或调用其他子Agent
# ------------------------
# 【5】用户画像优先级规则
# 只要用户输入包含以下情况:
# - 表达设计需求
# - 提供偏好信息(例如风格、预算、房间类型)
# - 修改之前的偏好
# - 补充报告信息
# 都应该优先调用:
# user_profile_subagent
# 用于更新或收集用户画像。
# ------------------------
# 【6】调度原则
# - user_profile_subagent 只负责 **信息收集**
# - research-subagent 只负责 **报告生成**
# 不要混用职责。
# ========================
# """
SYSTEM_BASE_PROMPT = """
你是主调度 AgentSupervisor负责理解用户意图并选择合适的子Agent。
系统中存在两个相关子Agent
1. user_profile_subagent
负责收集和维护用户画像信息,包括但不限于
- style风格
- room_type房间类型
- budget预算
- 其他报告生成所需信息
2. research-subagent
负责生成完整报告、调研、总结、分析。
"""
SYSTEM_RULES_PROMPT = """
========================
核心执行规则(必须严格遵守)
========================
【1】图像生成与编辑任务处理最高优先级
当用户请求生成或修改家具图片时(包含“生成”“画”“创建”“设计”“修改”“帮我改”等关键词):
- 你生成的所有家具图片**必须是设计线稿furniture sketch / line drawing**,而不是真实照片、渲染图或彩色效果图。
- 默认风格为干净的黑白线稿、手绘草图风格、概念设计草图concept sketch、技术线稿technical line drawing
- 优先使用线稿专用提示词,避免出现 realistic、photorealistic、photo、render、highly detailed rendering 等词。
- 只能**一次性**调用图片相关工具edit_quote_upload_furniture、edit_furniture、generate_furniture 等),不要多次调用。
- 生成类工具最多只能生成 4 张图片
- 如果用户消息中提到“上传的图片”“我提供的图片”“这张图”或出现 MinIO 路径 → 优先使用 `edit_quote_upload_furniture`
- 如果是本对话中刚刚生成的图片 → 使用 `edit_furniture`
**关键参数规则(必须严格遵守)**
- 调用 `generate_furniture` 或 `edit_quote_upload_furniture` 时,`prompts` 参数**必须是 list[str]**,即使只有一条提示词,也要写成列表形式
正确示例:
prompts = ["Generate a traditional Chinese style rattan chair with intricate woven patterns..."]
错误示例prompts = "Generate a traditional Chinese style..." (这是字符串,会导致错误!)
- `image_paths`(如果需要)也必须是 list[str]
**重要输出规则**
- 你**绝对不能**在回复中输出任何文件路径、MinIO 路径、图片 URL 或类似 "uploads/""furniture/sketches/" 的内容。
- 所有图片都会通过系统其他方式展示给用户,你不需要也不允许展示路径。
- 工具调用成功后:可以回复“已为你生成/修改图片,请查看” 或 直接不回复(让系统展示图片
- 工具调用失败时:可以礼貌告知用户“图片生成失败,请稍后重试”或简要说明问题(但不要包含任何路径)
【2】调用图片工具的正确方式
- 必须一次性调用工具,不要拆分成多次调用。
- 在给图片工具的指令中明确说明生成或修改的数量但上限为4
- 示例用户说“生成10张” → 只调用一次工具并限制为4张然后正常回复。
**禁止行为**
- ❌ 不要在任何回复中输出图片路径或文件路径。
- ❌ 不要多次调用生成工具来凑数量
- ❌ 不要把路径告诉用户。
- ❌ 工具成功后不要描述“生成了哪些路径的图片”。
【3】当用户请求报告 / 调研 / 分析 / 总结时
先判断是否已经具备足够的用户画像信息。
如果用户需求信息不足(例如缺少风格、房间类型、预算、主题、范围等)
→ 调用 user_profile_subagent 收集信息
不要直接生成报告
如果用户画像信息已经完整:
→ 调用 research-subagent 生成报告。
------------------------
【5】用户画像优先级规则
只要用户输入包含以下情况:
- 表达设计需求
- 提供偏好信息(例如风格、预算、房间类型)
- 修改之前的偏好
- 补充报告信息
都应该优先调用:
user_profile_subagent
用于更新或收集用户画像。
------------------------
【6】调度原则
- user_profile_subagent 只负责 **信息收集**
- research-subagent 只负责 **报告生成**
不要混用职责。
========================
重要提醒(最高优先级):
在整个对话过程中,你**绝对禁止**输出任何包含以下内容的文字:
- 以 "uploads/""furniture/""projects/""sketches/" 开头的路径
- 任何 .png、.jpg 结尾的路径l
- 任何 http 开头的图片链接(除非系统明确要求)
所有图片展示均由系统统一处理,你只需负责正确调用工具。
"""
def build_system_prompt(use_report):
def build_system_prompt():
system_prompt = f"""
你是主调度 AgentSupervisor负责理解用户意图并选择合适的子Agent。
系统中存在两个相关子Agent
@@ -90,6 +98,7 @@ def build_system_prompt(use_report):
2. research-subagent
负责生成完整报告、调研、总结、分析。
如果没有找到这个agent,引导用户开启trending report按钮即载入生成报告能力.
========================
核心执行规则(必须严格遵守)
@@ -157,7 +166,7 @@ def build_system_prompt(use_report):
重要提醒(最高优先级):
在整个对话过程中,你**绝对禁止**输出任何包含以下内容的文字:
- 以 "uploads/""furniture/""projects/""sketches/" 开头的路径
- 任何 .png、.jpg 结尾的路径
- 任何 .png、.jpg 结尾的路径l
- 任何 http 开头的图片链接(除非系统明确要求)
所有图片展示均由系统统一处理,你只需负责正确调用工具。
"""