feat: add official Codex skill and harden task execution
Install and update artemis-use with task diagnostics and plugin guidance. Preserve cross-device file receipts, review canonical reports, guard stage transitions and reduce small history summaries. Improve exploration lifecycle, request recovery and browser live connections.
This commit is contained in:
1 parent
2328a4df0c
commit
e1ea099592
128 files changed
+4944
-700
No files matched your search
@@ -33,6 +33,7 @@ from artemis.graph.state import State
|
||||
from artemis.graph.visibility import strict_state
|
||||
from artemis.mcp.action_specs import OPERATOR_SHELL_ORDER, operator_shell_tool
|
||||
from artemis.llm.usage import usage_from_message
|
||||
from artemis.llm.structured import content_to_text
|
||||
from artemis.runtime.task_control import TaskControl, has_pending_instruction
|
||||
from artemis.services.llm import acomplete, get_llm, invoke_llm_with_timeout_message
|
||||
from artemis.tools.command_tool import (
|
||||
@@ -571,9 +572,9 @@ class OperatorNode:
|
||||
fallback_limit = settings.ARTEMIS_TOOL_FALLBACK_MAX_NO_PROGRESS_ATTEMPTS
|
||||
active_llm = base_llm
|
||||
using_tool_fallback = False
|
||||
exploration_plan_before = (
|
||||
self._read_task_plan_text() if getattr(self.ctx, "exploration_id", None) else None
|
||||
)
|
||||
state.execution_report = None
|
||||
report_requested = False
|
||||
other_tool_failed = False
|
||||
|
||||
for iteration in range(max_iterations):
|
||||
control = getattr(self.ctx, "task_control", None)
|
||||
@@ -583,7 +584,25 @@ class OperatorNode:
|
||||
):
|
||||
# The device may have changed while another task held its lease.
|
||||
return None, None, None, False
|
||||
if iteration == max_iterations - 1:
|
||||
if (
|
||||
not report_requested
|
||||
and not other_tool_failed
|
||||
and self._exploration_ready_for_report(state)
|
||||
):
|
||||
current_messages.append(
|
||||
SystemMessage(
|
||||
content=(
|
||||
"探索计划已完成。现在交付最终答复,不要为了结束再操作设备。"
|
||||
"沿用本次上下文,按用户要求给出完整报告正文,而不是仅引用笔记、"
|
||||
"承诺稍后提供或重复计划。需要时可读取已保存报告再原样交付,"
|
||||
"不重复调查或重写已有完整报告。明确区分实际发现、未测范围与"
|
||||
"调查完成度,不能将执行完成等同于测试通过。"
|
||||
"最后以不含工具调用的正文回复;系统会保存并交给独立验收。"
|
||||
)
|
||||
)
|
||||
)
|
||||
report_requested = True
|
||||
if iteration == max_iterations - 1 and not report_requested:
|
||||
logger.warning(
|
||||
"Operator has reached the maximum number of tool calls. Adding reminder."
|
||||
)
|
||||
@@ -614,13 +633,14 @@ class OperatorNode:
|
||||
if interrupted:
|
||||
return None, None, None, False
|
||||
assert response is not None
|
||||
if getattr(self.ctx, "exploration_id", None):
|
||||
from artemis.exploration.runtime import publish_response
|
||||
|
||||
publish_response(self.ctx, response)
|
||||
if isinstance(control, TaskControl) and has_pending_instruction(control.directory):
|
||||
# The model answered a prompt that predates the user's new direction.
|
||||
return None, None, None, False
|
||||
if getattr(self.ctx, "exploration_id", None):
|
||||
from artemis.exploration.runtime import needs_exploration_turn
|
||||
|
||||
if needs_exploration_turn(self.ctx):
|
||||
return None, None, None, False
|
||||
|
||||
usage = usage_from_message(response)
|
||||
# The ledger's compaction thresholds read the operator's own
|
||||
@@ -680,12 +700,29 @@ class OperatorNode:
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to parse function_call fallback: {e}")
|
||||
|
||||
if not response.tool_calls and not getattr(response, "invalid_tool_calls", None):
|
||||
report_text = content_to_text(response.content).strip()
|
||||
if report_requested and report_text and self._exploration_ready_for_report(state):
|
||||
state.execution_report = report_text
|
||||
current_messages.append(response)
|
||||
logger.info("Exploration final report prepared; returning for final review.")
|
||||
break
|
||||
|
||||
if getattr(self.ctx, "exploration_id", None):
|
||||
from artemis.exploration.runtime import publish_response
|
||||
|
||||
publish_response(self.ctx, response)
|
||||
|
||||
if not response.tool_calls:
|
||||
no_progress_attempts += 1
|
||||
if no_progress_attempts >= (
|
||||
fallback_limit if using_tool_fallback else no_progress_limit
|
||||
):
|
||||
if using_tool_fallback:
|
||||
if report_requested:
|
||||
raise OperatorNoProgressError(
|
||||
"模型未生成可用的最终报告正文,已停止空转。"
|
||||
)
|
||||
raise OperatorNoProgressError(
|
||||
f"首选模型连续 {no_progress_limit} 次、兜底模型连续 "
|
||||
f"{no_progress_attempts} 次未提供可执行的工具调用;"
|
||||
@@ -697,16 +734,23 @@ class OperatorNode:
|
||||
logger.warning("Operator 首选模型未调用工具,当前步骤切换到兜底模型")
|
||||
current_messages.append(
|
||||
SystemMessage(
|
||||
content="本步骤已切换兜底模型。请使用工具完成操作或明确报告阻碍。"
|
||||
content=(
|
||||
"已切换兜底模型。请直接交付完整报告正文,不要再次操作设备。"
|
||||
if report_requested
|
||||
else "本步骤已切换兜底模型。请使用工具完成操作或明确报告阻碍。"
|
||||
)
|
||||
)
|
||||
)
|
||||
continue
|
||||
logger.warning("LLM returned no tool call; requesting a concrete tool call.")
|
||||
logger.warning("LLM returned no usable result; requesting bounded recovery.")
|
||||
current_messages.append(response)
|
||||
current_messages.append(
|
||||
SystemMessage(
|
||||
content=(
|
||||
"No tool call was returned, so no device action occurred. "
|
||||
"尚未收到可用的最终报告正文。请用文字交付实际结果;"
|
||||
"缺少证据时如实说明,不要为了结束再操作设备。"
|
||||
if report_requested
|
||||
else "No tool call was returned, so no device action occurred. "
|
||||
"Use the available tool interface now, not a prose promise or JSON "
|
||||
"inside text. Use an index from the current element list or "
|
||||
"normalized coordinates with target_description. If blocked, "
|
||||
@@ -1040,32 +1084,6 @@ class OperatorNode:
|
||||
for tm in tool_outputs:
|
||||
current_messages.append(tm)
|
||||
|
||||
if (
|
||||
getattr(self.ctx, "exploration_id", None)
|
||||
and other_calls
|
||||
and not has_terminal_calls
|
||||
and not other_tool_failed
|
||||
):
|
||||
from artemis.exploration.runtime import needs_exploration_turn
|
||||
|
||||
plan_after = self._read_task_plan_text()
|
||||
snapshot = parse_plan(plan_after or "")
|
||||
if (
|
||||
plan_after != exploration_plan_before
|
||||
and snapshot.has_top_level
|
||||
and snapshot.all_top_level_done
|
||||
and (
|
||||
not snapshot.continuous_top_level
|
||||
or getattr(state, "user_stop_requested", False)
|
||||
)
|
||||
and not needs_exploration_turn(self.ctx)
|
||||
):
|
||||
# Completing the plan is useful progress. Return to the
|
||||
# convergence gate and final review instead of demanding
|
||||
# another device action just to end the operator turn.
|
||||
logger.info("Exploration plan completed; returning for final review.")
|
||||
break
|
||||
|
||||
if action_calls and not validation_errors:
|
||||
break
|
||||
|
||||
@@ -1244,6 +1262,7 @@ class OperatorNode:
|
||||
|
||||
return {
|
||||
"structured_decisions": structured_decisions,
|
||||
"execution_report": state.execution_report,
|
||||
"operator_raw_thinking": raw_thinking,
|
||||
"operator_native_thinking": native_thinking,
|
||||
"indexed_points": state.indexed_points,
|
||||
@@ -1253,6 +1272,19 @@ class OperatorNode:
|
||||
"operator_tool_limit_exceeded": tool_limit_exceeded,
|
||||
}
|
||||
|
||||
def _exploration_ready_for_report(self, state: State) -> bool:
|
||||
if not getattr(self.ctx, "exploration_id", None):
|
||||
return False
|
||||
from artemis.exploration.runtime import needs_exploration_turn
|
||||
|
||||
snapshot = parse_plan(self._read_task_plan_text() or "")
|
||||
return bool(
|
||||
snapshot.has_top_level
|
||||
and snapshot.all_top_level_done
|
||||
and (not snapshot.continuous_top_level or getattr(state, "user_stop_requested", False))
|
||||
and not needs_exploration_turn(self.ctx)
|
||||
)
|
||||
|
||||
def _plan_ledger_gate_enabled(self) -> bool:
|
||||
try:
|
||||
from artemis.config import load_agent_config
|
||||
|
||||
Reference in new issue
Block a user