feat(agent): redesign project_cli with module/method/input protocol

- Replace command/subcommand/args with module/method/input envelope
- Calendar handler uses discriminated union (mode) for read operations
- Strict Pydantic models with extra='forbid' for all calendar methods
- Worker max_iters=7, router prompt simplified (removed project_cli_defaults)
- Skill index cards + per-action files for progressive disclosure
- Frontend/AG-UI aligned to module/method dispatch
- Protocol docs updated to module/method/input contract

WIP: action cards need envelope fix, 2 tests need update, memory
handler needs Pydantic models.
This commit is contained in:
qzl
2026-04-24 13:24:13 +08:00
parent ab526af2c4
commit d060962a5f
62 changed files with 4802 additions and 805 deletions
@@ -39,7 +39,9 @@ from schemas.agent.forwarded_props import (
parse_forwarded_props_runtime_mode,
)
from schemas.agent.runtime_models import (
ErrorInfo,
RouterAgentOutput,
RunStatus,
WorkerAgentOutputLite,
)
from schemas.agent.skill_config import ProjectCliCommand, SkillName
@@ -74,6 +76,8 @@ class AgentScopeRunner:
self._active_agent: JsonReActAgent | None = None
self._active_agent_lock = asyncio.Lock()
_WORKER_MAX_ITERS = 7
async def execute(
self,
*,
@@ -442,6 +446,11 @@ class AgentScopeRunner:
if self._active_agent is agent:
self._active_agent = None
worker_payload = worker_output_model.model_validate(response_msg.metadata or {})
worker_payload = self._enforce_tool_evidence_contract(
worker_output=worker_payload,
requires_tool_evidence=requires_tool_evidence,
has_successful_tool_result=emitter.has_successful_tool_result,
)
response_metadata = self._llm_pricing_service.build_usage_metadata(
model=stage_config.model_code,
usage_summary=tracking_model.usage_summary(),
@@ -458,6 +467,28 @@ class AgentScopeRunner:
finally:
reset_tool_credential(credential_token)
@staticmethod
def _enforce_tool_evidence_contract(
*,
worker_output: WorkerAgentOutputLite,
requires_tool_evidence: bool,
has_successful_tool_result: bool,
) -> WorkerAgentOutputLite:
if not requires_tool_evidence or has_successful_tool_result:
return worker_output
return worker_output.model_copy(
update={
"status": RunStatus.FAILED,
"answer": "无法确认结果:所需工具调用未成功完成。",
"suggested_actions": [],
"error": ErrorInfo(
code="TOOL_EVIDENCE_MISSING",
message="requires_tool_evidence=true but no tool call completed successfully in this run",
retryable=False,
),
}
)
def _build_worker_input_messages(
self,
*,
@@ -501,6 +532,7 @@ class AgentScopeRunner:
model: TrackingChatModel,
emitter: PipelineStageEmitter | None = None,
force_tool_on_first_reasoning: bool = False,
max_iters: int = _WORKER_MAX_ITERS,
) -> JsonReActAgent:
return JsonReActAgent(
name=agent_name,
@@ -511,6 +543,7 @@ class AgentScopeRunner:
memory=InMemoryMemory(),
emitter=emitter,
force_tool_on_first_reasoning=force_tool_on_first_reasoning,
max_iters=max_iters,
)
async def _emit_step_event(