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
@@ -7,6 +7,7 @@ from ag_ui.core import RunAgentInput
import core.agentscope.runtime.runner as runner_module
from core.agentscope.runtime.runner import AgentScopeRunner
from schemas.agent.runtime_models import (
RunStatus,
RouterAgentOutput,
WorkerAgentOutputLite,
)
@@ -60,6 +61,31 @@ def test_build_worker_input_messages_only_contains_router_contract() -> None:
assert "[RouterAgentOutput]" in str(input_messages[0].content)
def test_build_agent_sets_worker_max_iters(
monkeypatch: pytest.MonkeyPatch,
) -> None:
captured: dict[str, object] = {}
class _FakeJsonReActAgent:
def __init__(self, **kwargs: object) -> None:
captured.update(kwargs)
monkeypatch.setattr(runner_module, "JsonReActAgent", _FakeJsonReActAgent)
runner = AgentScopeRunner()
model = runner_module.TrackingChatModel(object())
agent = runner._build_agent(
agent_name="worker",
system_prompt="test",
toolkit=object(),
model=model,
)
assert isinstance(agent, _FakeJsonReActAgent)
assert captured["max_iters"] == 7
def test_build_router_messages_injects_user_input_when_context_last_not_user() -> None:
runner = AgentScopeRunner()
run_input = _run_input()
@@ -119,6 +145,45 @@ def test_build_router_messages_appends_user_input_to_context_tail() -> None:
assert messages[0].content == "上一轮回复"
def test_enforce_tool_evidence_contract_keeps_success_when_tool_succeeds() -> None:
runner = AgentScopeRunner()
worker_output = runner._enforce_tool_evidence_contract(
worker_output=WorkerAgentOutputLite(
status=RunStatus.SUCCESS,
answer="今天没有日程",
suggested_actions=["查明天"],
),
requires_tool_evidence=True,
has_successful_tool_result=True,
)
assert worker_output.status == RunStatus.SUCCESS
assert worker_output.answer == "今天没有日程"
assert worker_output.suggested_actions == ["查明天"]
assert worker_output.error is None
def test_enforce_tool_evidence_contract_forces_failure_without_successful_tool() -> None:
runner = AgentScopeRunner()
worker_output = runner._enforce_tool_evidence_contract(
worker_output=WorkerAgentOutputLite(
status=RunStatus.SUCCESS,
answer="今天没有日程",
suggested_actions=["查明天"],
),
requires_tool_evidence=True,
has_successful_tool_result=False,
)
assert worker_output.status == RunStatus.FAILED
assert worker_output.answer == "无法确认结果:所需工具调用未成功完成。"
assert worker_output.suggested_actions == []
assert worker_output.error is not None
assert worker_output.error.code == "TOOL_EVIDENCE_MISSING"
def test_build_model_omits_none_generate_kwargs(
monkeypatch: pytest.MonkeyPatch,
) -> None: