代码修改
This commit is contained in:
1 parent
79d87ad661
commit
3eae4a0f45
3 files changed
+84
-4
No files matched your search
+9
-1
@@ -26,6 +26,9 @@ from deerflow.models import create_chat_model
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# 写作配置 Q&A:意图澄清最多几轮(一条用户消息 = 一轮),到顶强制提交配置卡。
|
||||||
|
WRITING_SETUP_MAX_ROUNDS = 3
|
||||||
|
|
||||||
|
|
||||||
def _get_runtime_config(config: RunnableConfig) -> dict:
|
def _get_runtime_config(config: RunnableConfig) -> dict:
|
||||||
"""Merge legacy configurable options with LangGraph runtime context."""
|
"""Merge legacy configurable options with LangGraph runtime context."""
|
||||||
@@ -454,12 +457,17 @@ def _make_lead_agent(config: RunnableConfig, *, app_config: AppConfig):
|
|||||||
# 写作配置 Q&A:最小化 agent —— **只挂 setup_writing 一个工具** + 一份职责单一的独立提示。
|
# 写作配置 Q&A:最小化 agent —— **只挂 setup_writing 一个工具** + 一份职责单一的独立提示。
|
||||||
# 关键:绝不给全套工具、绝不复用 lead 超级体提示,否则模型会把「新能源汽车市场分析」之类
|
# 关键:绝不给全套工具、绝不复用 lead 超级体提示,否则模型会把「新能源汽车市场分析」之类
|
||||||
# 长得像任务的输入直接做掉,而不是转成写作配置提交。
|
# 长得像任务的输入直接做掉,而不是转成写作配置提交。
|
||||||
|
# 末尾追加 WritingSetupRoundCapMiddleware:澄清最多 3 轮,到顶强制提交配置卡。
|
||||||
from deerflow.agents.lead_agent.prompt import build_writing_setup_prompt
|
from deerflow.agents.lead_agent.prompt import build_writing_setup_prompt
|
||||||
|
from deerflow.agents.middlewares.writing_setup_round_cap_middleware import WritingSetupRoundCapMiddleware
|
||||||
|
|
||||||
return create_agent(
|
return create_agent(
|
||||||
model=create_chat_model(name=model_name, thinking_enabled=thinking_enabled, app_config=resolved_app_config),
|
model=create_chat_model(name=model_name, thinking_enabled=thinking_enabled, app_config=resolved_app_config),
|
||||||
tools=[setup_writing],
|
tools=[setup_writing],
|
||||||
middleware=_build_middlewares(config, model_name=model_name, app_config=resolved_app_config),
|
middleware=[
|
||||||
|
*_build_middlewares(config, model_name=model_name, app_config=resolved_app_config),
|
||||||
|
WritingSetupRoundCapMiddleware(max_rounds=WRITING_SETUP_MAX_ROUNDS),
|
||||||
|
],
|
||||||
system_prompt=build_writing_setup_prompt(writing_setup_article_types),
|
system_prompt=build_writing_setup_prompt(writing_setup_article_types),
|
||||||
state_schema=ThreadState,
|
state_schema=ThreadState,
|
||||||
)
|
)
|
||||||
|
|||||||
+3
-3
@@ -927,9 +927,9 @@ def build_writing_setup_prompt(article_types: list[dict] | None = None) -> str:
|
|||||||
- 你能做的动作只有两个:① 用自然语言向用户提问或确认;② 调用 `setup_writing` 工具。**除此之外你没有任何其它工具**,也不要假装调用别的工具、不要做检索、不要做研究。
|
- 你能做的动作只有两个:① 用自然语言向用户提问或确认;② 调用 `setup_writing` 工具。**除此之外你没有任何其它工具**,也不要假装调用别的工具、不要做检索、不要做研究。
|
||||||
- 不要在回复里输出文章段落、要点清单或大纲(用户主动粘贴的大纲除外——原样放进 `outline` 参数)。
|
- 不要在回复里输出文章段落、要点清单或大纲(用户主动粘贴的大纲除外——原样放进 `outline` 参数)。
|
||||||
|
|
||||||
【工作流程】
|
【工作流程】(与用户交互确认,但最多 3 轮内必须提交)
|
||||||
1. 读用户输入。如果「写什么」已经明确,关键信息(文章类型、字数、读者)即使缺失也可以用合理默认补全——**信息基本够了就直接进入第 3 步,不要为了凑齐而反复追问**。
|
1. 读用户输入,先用一两句话回应并和用户**确认写作意图**;就缺失或模糊的关键项(文章类型、目标字数、目标读者)提问澄清,一次最多问 1-2 个最关键的问题。
|
||||||
2. 只有当「写什么」本身都不清楚时,才用一句话问 1-2 个最关键的问题(最多一轮)。
|
2. 与用户的澄清来回**最多 2 次**。一旦关键信息明确、或已经问过 2 次,就**立即进入第 3 步提交**,缺失字段用合理默认值,绝不无休止追问。
|
||||||
3. 调用 `setup_writing` 提交配置:
|
3. 调用 `setup_writing` 提交配置:
|
||||||
- `user_intent`:精炼成一句话的写作目标(必填)。
|
- `user_intent`:精炼成一句话的写作目标(必填)。
|
||||||
- `article_type`:见下方可选类型;不确定就留空。
|
- `article_type`:见下方可选类型;不确定就留空。
|
||||||
|
|||||||
+72
@@ -0,0 +1,72 @@
|
|||||||
|
"""写作配置 Q&A 的「轮数上限」中间件。
|
||||||
|
|
||||||
|
意图阶段允许与用户来回澄清确认,但**最多 N 轮后必须出结果**:到上限时,在模型请求里
|
||||||
|
注入强制指令 + 用 ``tool_choice`` 锁定 ``setup_writing``,逼模型立刻提交配置卡,不再提问。
|
||||||
|
|
||||||
|
「一轮」= 一条用户消息(HumanMessage)。达到 ``max_rounds`` 条用户消息时强制提交。
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections.abc import Awaitable, Callable
|
||||||
|
from typing import override
|
||||||
|
|
||||||
|
from langchain.agents import AgentState
|
||||||
|
from langchain.agents.middleware import AgentMiddleware
|
||||||
|
from langchain.agents.middleware.types import ModelCallResult, ModelRequest, ModelResponse
|
||||||
|
from langchain_core.messages import HumanMessage, SystemMessage
|
||||||
|
|
||||||
|
|
||||||
|
class WritingSetupRoundCapMiddleware(AgentMiddleware[AgentState]):
|
||||||
|
"""达到澄清轮数上限时,强制模型调用 ``setup_writing`` 提交。
|
||||||
|
|
||||||
|
软约束(注入 SystemMessage)+ 硬约束(``tool_choice`` 锁定工具)双保险:即使个别模型
|
||||||
|
无视提示,``tool_choice`` 也会逼它产出一次 ``setup_writing`` 调用。
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, max_rounds: int = 3, tool_name: str = "setup_writing") -> None:
|
||||||
|
super().__init__()
|
||||||
|
self._max_rounds = max(1, max_rounds)
|
||||||
|
self._tool_name = tool_name
|
||||||
|
|
||||||
|
def _should_force(self, messages: list) -> bool:
|
||||||
|
human_count = sum(1 for m in messages if isinstance(m, HumanMessage))
|
||||||
|
return human_count >= self._max_rounds
|
||||||
|
|
||||||
|
def _reminder(self) -> SystemMessage:
|
||||||
|
return SystemMessage(
|
||||||
|
content=(
|
||||||
|
f"【系统强制】意图澄清已达 {self._max_rounds} 轮上限。"
|
||||||
|
"现在必须立即调用 setup_writing 提交写作配置,禁止再向用户提任何问题。"
|
||||||
|
"缺失或不确定的字段一律用合理默认值:"
|
||||||
|
"word_count_target 默认 800、target_audience 默认「普通读者」、"
|
||||||
|
"writing_mode 默认 loose、material_source 默认 knowledge_base、"
|
||||||
|
"article_type 不确定就留空。"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def _patch(self, request: ModelRequest) -> ModelRequest:
|
||||||
|
return request.override(
|
||||||
|
messages=[*request.messages, self._reminder()],
|
||||||
|
tool_choice=self._tool_name,
|
||||||
|
)
|
||||||
|
|
||||||
|
@override
|
||||||
|
def wrap_model_call(
|
||||||
|
self,
|
||||||
|
request: ModelRequest,
|
||||||
|
handler: Callable[[ModelRequest], ModelResponse],
|
||||||
|
) -> ModelCallResult:
|
||||||
|
if self._should_force(request.messages):
|
||||||
|
request = self._patch(request)
|
||||||
|
return handler(request)
|
||||||
|
|
||||||
|
@override
|
||||||
|
async def awrap_model_call(
|
||||||
|
self,
|
||||||
|
request: ModelRequest,
|
||||||
|
handler: Callable[[ModelRequest], Awaitable[ModelResponse]],
|
||||||
|
) -> ModelCallResult:
|
||||||
|
if self._should_force(request.messages):
|
||||||
|
request = self._patch(request)
|
||||||
|
return await handler(request)
|
||||||
Reference in new issue
Block a user