fix: writing mode uses chat search then pipeline write only
Keep deep_research_report as a write-only deliverable from conversation materials, block post-success rewrite loops with a summary nudge, fill thin/empty pools from model knowledge, and open the sandbox only on real report.md content. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
9afb9750e5
commit
49c01cb72b
20 files changed
+1449
-381
No files matched your search
+79
-61
@@ -1,5 +1,5 @@
|
||||
import hashlib
|
||||
import logging
|
||||
import hashlib
|
||||
import logging
|
||||
|
||||
from langchain.agents import create_agent
|
||||
from langchain.agents.middleware import AgentMiddleware
|
||||
@@ -38,45 +38,45 @@ logger = logging.getLogger(__name__)
|
||||
# 写作配置 Q&A 现在「先生成大纲并与用户多轮确认 → 再提交配置」,需要更多来回,故上限放宽。
|
||||
# 仍是兜底安全阀:到顶才用 tool_choice 强制提交,正常流程由用户确认大纲后自然提交。
|
||||
WRITING_SETUP_MAX_ROUNDS = 8
|
||||
REPORT_STRUCTURE_SETUP_MAX_ROUNDS = 8
|
||||
|
||||
_ES_QUERY_ROUTING_OPEN_TAG = '<skill_routing skill="es_query">'
|
||||
|
||||
|
||||
def _log_es_query_routing_registration(
|
||||
*,
|
||||
system_prompt: str,
|
||||
app_config: AppConfig,
|
||||
agent_id: str | None,
|
||||
available_skills: set[str] | None,
|
||||
) -> None:
|
||||
"""Log whether the exact configured ES routing block reached this Q&A prompt."""
|
||||
routing = app_config.skills.es_query_routing
|
||||
configured_prompt = (routing.prompt or "").strip()
|
||||
expected_block = (
|
||||
f"{_ES_QUERY_ROUTING_OPEN_TAG}\n{configured_prompt}\n</skill_routing>"
|
||||
if configured_prompt
|
||||
else ""
|
||||
)
|
||||
eligible = available_skills is None or "es_query" in available_skills
|
||||
registered = bool(expected_block) and expected_block in system_prompt
|
||||
fingerprint = (
|
||||
hashlib.sha256(configured_prompt.encode("utf-8")).hexdigest()[:12]
|
||||
if configured_prompt
|
||||
else "-"
|
||||
)
|
||||
|
||||
log = logger.info if registered or not routing.enabled or not eligible else logger.warning
|
||||
log(
|
||||
"ES query routing prompt registration: agent_id=%s enabled=%s eligible=%s "
|
||||
"registered=%s prompt_chars=%d prompt_sha256=%s",
|
||||
agent_id or "default",
|
||||
routing.enabled,
|
||||
eligible,
|
||||
registered,
|
||||
len(configured_prompt),
|
||||
fingerprint,
|
||||
)
|
||||
REPORT_STRUCTURE_SETUP_MAX_ROUNDS = 8
|
||||
|
||||
_ES_QUERY_ROUTING_OPEN_TAG = '<skill_routing skill="es_query">'
|
||||
|
||||
|
||||
def _log_es_query_routing_registration(
|
||||
*,
|
||||
system_prompt: str,
|
||||
app_config: AppConfig,
|
||||
agent_id: str | None,
|
||||
available_skills: set[str] | None,
|
||||
) -> None:
|
||||
"""Log whether the exact configured ES routing block reached this Q&A prompt."""
|
||||
routing = app_config.skills.es_query_routing
|
||||
configured_prompt = (routing.prompt or "").strip()
|
||||
expected_block = (
|
||||
f"{_ES_QUERY_ROUTING_OPEN_TAG}\n{configured_prompt}\n</skill_routing>"
|
||||
if configured_prompt
|
||||
else ""
|
||||
)
|
||||
eligible = available_skills is None or "es_query" in available_skills
|
||||
registered = bool(expected_block) and expected_block in system_prompt
|
||||
fingerprint = (
|
||||
hashlib.sha256(configured_prompt.encode("utf-8")).hexdigest()[:12]
|
||||
if configured_prompt
|
||||
else "-"
|
||||
)
|
||||
|
||||
log = logger.info if registered or not routing.enabled or not eligible else logger.warning
|
||||
log(
|
||||
"ES query routing prompt registration: agent_id=%s enabled=%s eligible=%s "
|
||||
"registered=%s prompt_chars=%d prompt_sha256=%s",
|
||||
agent_id or "default",
|
||||
routing.enabled,
|
||||
eligible,
|
||||
registered,
|
||||
len(configured_prompt),
|
||||
fingerprint,
|
||||
)
|
||||
|
||||
|
||||
def _get_runtime_config(config: RunnableConfig) -> dict:
|
||||
@@ -426,6 +426,18 @@ def _build_middlewares(
|
||||
if agent_name == FORCED_RESEARCH_AGENT_ID:
|
||||
middlewares.append(ForcedResearchMiddleware())
|
||||
|
||||
# 写作模式首轮:允许先聊/检索;只有模型开始写 md 时才改走 deep_research_report。
|
||||
# 不设 tool_choice——thinking 模式的兼容接口会 400。
|
||||
writing_mode = bool(cfg.get("writing_mode", False))
|
||||
writing_artifact_path = cfg.get("writing_artifact_path") if writing_mode else None
|
||||
if writing_mode:
|
||||
from deerflow.tools.builtins.deep_research_report_tool import is_writing_mode_report_path
|
||||
|
||||
if not is_writing_mode_report_path(writing_artifact_path if isinstance(writing_artifact_path, str) else None):
|
||||
from deerflow.agents.middlewares.writing_mode_report_middleware import WritingModeReportMiddleware
|
||||
|
||||
middlewares.append(WritingModeReportMiddleware())
|
||||
|
||||
# LoopDetectionMiddleware — detect and break repetitive tool call loops
|
||||
middlewares.append(LoopDetectionMiddleware())
|
||||
|
||||
@@ -517,6 +529,12 @@ def _make_lead_agent(config: RunnableConfig, *, app_config: AppConfig):
|
||||
writing_artifact_path = cfg.get("writing_artifact_path") if writing_mode else None
|
||||
if not isinstance(writing_artifact_path, str) or not writing_artifact_path.strip():
|
||||
writing_artifact_path = None
|
||||
# 只有写作模式产物 report.md 才走续写;其它 leftover .md 不得跳过管线。
|
||||
if writing_artifact_path is not None:
|
||||
from deerflow.tools.builtins.deep_research_report_tool import is_writing_mode_report_path
|
||||
|
||||
if not is_writing_mode_report_path(writing_artifact_path):
|
||||
writing_artifact_path = None
|
||||
agent_id = validate_agent_id(cfg.get("agent_id") or cfg.get("agent_name"))
|
||||
requested_excluded_tools = cfg.get("excluded_tools") or None
|
||||
|
||||
@@ -691,10 +709,10 @@ def _make_lead_agent(config: RunnableConfig, *, app_config: AppConfig):
|
||||
from deerflow.tools.builtins.notebook_search_tool import notebook_search_tool
|
||||
|
||||
default_tools = [notebook_search_tool] + default_tools
|
||||
# 写作模式首轮(尚无 md 产物):挂深度研究报告工具,报告由管线生成而非
|
||||
# 模型手工 write_file。续写轮(已有 writing_artifact_path)不挂,保持
|
||||
# 原有「直接编辑该文件」行为。不受 tool_groups/excluded_tools 过滤——
|
||||
# 它是写作模式合约的核心交付手段。
|
||||
# 写作模式首轮(尚无 report.md):挂深度研究报告工具,报告由管线生成而非
|
||||
# 模型手工 write_file。续写轮(已有 report.md)不挂,保持原有「直接编辑
|
||||
# 该文件」行为。不受 tool_groups/excluded_tools 过滤——它是写作模式合约
|
||||
# 的核心交付手段。
|
||||
if writing_mode and not writing_artifact_path:
|
||||
from deerflow.tools.builtins.deep_research_report_tool import deep_research_report_tool
|
||||
|
||||
@@ -726,8 +744,8 @@ def _make_lead_agent(config: RunnableConfig, *, app_config: AppConfig):
|
||||
writing_mode=writing_mode,
|
||||
writing_artifact_path=writing_artifact_path,
|
||||
)
|
||||
else:
|
||||
system_prompt = apply_prompt_template(
|
||||
else:
|
||||
system_prompt = apply_prompt_template(
|
||||
subagent_enabled=subagent_enabled,
|
||||
max_concurrent_subagents=max_concurrent_subagents,
|
||||
agent_id=agent_id,
|
||||
@@ -737,20 +755,20 @@ def _make_lead_agent(config: RunnableConfig, *, app_config: AppConfig):
|
||||
available_skills=prompt_available_skills,
|
||||
app_config=resolved_app_config,
|
||||
memory_injection_enabled=memory_injection_enabled,
|
||||
rag_mode_enabled=rag_mode_enabled,
|
||||
is_notebook_mode=is_notebook_mode,
|
||||
notebook_source_ids=notebook_source_ids,
|
||||
is_scheduled_run=bool(cfg.get("is_scheduled_run", False)),
|
||||
)
|
||||
|
||||
_log_es_query_routing_registration(
|
||||
system_prompt=system_prompt,
|
||||
app_config=resolved_app_config,
|
||||
agent_id=agent_id,
|
||||
available_skills=prompt_available_skills,
|
||||
)
|
||||
|
||||
return create_agent(
|
||||
rag_mode_enabled=rag_mode_enabled,
|
||||
is_notebook_mode=is_notebook_mode,
|
||||
notebook_source_ids=notebook_source_ids,
|
||||
is_scheduled_run=bool(cfg.get("is_scheduled_run", False)),
|
||||
)
|
||||
|
||||
_log_es_query_routing_registration(
|
||||
system_prompt=system_prompt,
|
||||
app_config=resolved_app_config,
|
||||
agent_id=agent_id,
|
||||
available_skills=prompt_available_skills,
|
||||
)
|
||||
|
||||
return create_agent(
|
||||
model=create_chat_model(name=model_name, thinking_enabled=thinking_enabled, reasoning_effort=reasoning_effort, force_disable_thinking=thinking_force_disabled, app_config=resolved_app_config),
|
||||
tools=default_tools,
|
||||
middleware=_build_middlewares(config, model_name=model_name, agent_name=agent_id, app_config=resolved_app_config),
|
||||
|
||||
Reference in new issue
Block a user