refactor(skill-review): use create_agent with skill_evolution disabled to allow autonomous skill creation
This commit is contained in:
1 parent
b5d80025e9
commit
3955bd72b5
1 file changed
+57
-29
+57
-29
@@ -123,15 +123,26 @@ _REVIEW_SYSTEM_PROMPT = textwrap.dedent("""\
|
||||
|
||||
|
||||
async def _run_review(thread_id: str, messages: list[BaseMessage], app_config: "AppConfig") -> None:
|
||||
"""Core review logic: run a minimal lead agent with full middleware support.
|
||||
"""Core review logic: create_agent with full middleware chain, no 'silent creation' constraint.
|
||||
|
||||
Reuses make_lead_agent so that ToolErrorHandlingMiddleware, LLMErrorHandlingMiddleware,
|
||||
and the rest of the middleware chain handle all error cases uniformly — no custom
|
||||
handle_tool_errors hack needed.
|
||||
Uses create_agent (the deerflow agent factory) directly so we can:
|
||||
- Reuse the full middleware chain (ToolErrorHandlingMiddleware, etc.)
|
||||
- Reuse the full tool set
|
||||
- But pass app_config with skill_evolution.enabled=False to strip the
|
||||
"Never create skills silently without user confirmation" constraint
|
||||
from the system prompt.
|
||||
Review instructions are injected via PromptPrefixMiddleware through
|
||||
additional_kwargs["prompt_prefix"] on the HumanMessage.
|
||||
"""
|
||||
from langchain_core.runnables import RunnableConfig
|
||||
from langchain.agents import create_agent
|
||||
from langchain_core.messages import HumanMessage
|
||||
|
||||
from deerflow.agents.lead_agent.agent import make_lead_agent
|
||||
from deerflow.agents.lead_agent.prompt import apply_prompt_template
|
||||
from deerflow.agents.middlewares.prompt_prefix_middleware import PromptPrefixMiddleware
|
||||
from deerflow.agents.middlewares.tool_error_handling_middleware import build_lead_runtime_middlewares
|
||||
from deerflow.agents.thread_state import ThreadState
|
||||
from deerflow.models import create_chat_model
|
||||
from deerflow.tools.tools import get_available_tools
|
||||
|
||||
evolution_cfg = app_config.skill_evolution
|
||||
timeout = evolution_cfg.review_worker.timeout_seconds
|
||||
@@ -139,36 +150,53 @@ async def _run_review(thread_id: str, messages: list[BaseMessage], app_config: "
|
||||
model_name = evolution_cfg.review_worker.model_name
|
||||
|
||||
conversation_text = _format_messages_for_review(messages, max_messages)
|
||||
user_message = (
|
||||
_REVIEW_SYSTEM_PROMPT
|
||||
+ "\n\nReview this conversation and apply any skill changes you deem appropriate.\n\n"
|
||||
f"<conversation>\n{conversation_text}\n</conversation>"
|
||||
|
||||
# Disable skill_evolution so the system prompt does NOT include
|
||||
# "Never create skills silently without user confirmation".
|
||||
# Also prevents SkillReviewMiddleware from registering (no recursive reviews).
|
||||
review_app_config = app_config.model_copy(deep=True)
|
||||
review_app_config.skill_evolution.enabled = False
|
||||
|
||||
try:
|
||||
model = create_chat_model(name=model_name, app_config=review_app_config)
|
||||
except Exception:
|
||||
logger.warning("skill_review_worker: could not load model %r, aborting review", model_name)
|
||||
return
|
||||
|
||||
tools = get_available_tools(app_config=review_app_config, subagent_enabled=False)
|
||||
middlewares = build_lead_runtime_middlewares(app_config=review_app_config)
|
||||
middlewares.append(PromptPrefixMiddleware())
|
||||
|
||||
system_prompt = apply_prompt_template(
|
||||
subagent_enabled=False,
|
||||
max_concurrent_subagents=0,
|
||||
app_config=review_app_config,
|
||||
memory_injection_enabled=False,
|
||||
)
|
||||
|
||||
# Use a synthetic thread id so this review run is isolated from the source thread.
|
||||
review_thread_id = f"__skill-review__{thread_id}"
|
||||
agent = create_agent(
|
||||
model=model,
|
||||
tools=tools,
|
||||
middleware=middlewares,
|
||||
system_prompt=system_prompt,
|
||||
state_schema=ThreadState,
|
||||
)
|
||||
|
||||
configurable: dict = {
|
||||
"thread_id": review_thread_id,
|
||||
"is_bootstrap": True, # skip MemoryMiddleware injection
|
||||
"is_scheduled_run": True, # skip TitleMiddleware
|
||||
"subagent_enabled": False, # no task-delegation tool
|
||||
"app_config": app_config,
|
||||
}
|
||||
if model_name:
|
||||
configurable["model_name"] = model_name
|
||||
|
||||
run_config = RunnableConfig(configurable=configurable)
|
||||
agent = make_lead_agent(run_config)
|
||||
# Review instructions arrive as prompt_prefix: PromptPrefixMiddleware prepends
|
||||
# them before every model call without persisting them to state.
|
||||
first_message = HumanMessage(
|
||||
content=(
|
||||
"Review this conversation and apply any skill changes you deem appropriate.\n\n"
|
||||
f"<conversation>\n{conversation_text}\n</conversation>"
|
||||
),
|
||||
additional_kwargs={"prompt_prefix": _REVIEW_SYSTEM_PROMPT},
|
||||
)
|
||||
|
||||
try:
|
||||
result = await asyncio.wait_for(
|
||||
asyncio.get_event_loop().run_in_executor(
|
||||
None,
|
||||
lambda: agent.invoke(
|
||||
{"messages": [("user", user_message)]},
|
||||
config=run_config,
|
||||
),
|
||||
lambda: agent.invoke({"messages": [first_message]}),
|
||||
),
|
||||
timeout=timeout,
|
||||
)
|
||||
@@ -178,7 +206,7 @@ async def _run_review(thread_id: str, messages: list[BaseMessage], app_config: "
|
||||
for msg in reversed(final_msgs):
|
||||
content = getattr(msg, "content", None)
|
||||
if content and isinstance(content, str) and content.strip():
|
||||
final_text = content.strip()[:300]
|
||||
final_text = content.strip()[:1000]
|
||||
break
|
||||
logger.info(
|
||||
"skill_review_worker: review completed for thread %s — agent said: %s",
|
||||
|
||||
Reference in new issue
Block a user