Files
group_fqcd_jr/docs/superpowers/plans/2026-09-09-agent-platform-implementation.md
T

13 KiB
Raw Blame History

Agent Platform Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: 将 v3.1 Agent 底座和 05 接口规范落成可恢复、可测试、与 HTTP/SSE 解耦的 MVP 实现。

Architecture: API 层只负责 JWT、请求映射和 View;AgentRunApplicationService 负责受理、幂等和 Outbox;Worker 通过 AgentExecutor 调用 AgentFactory/BaseAgent.execute()。MySQL 是运行状态、最终消息和审计权威源,Redis 只做缓存和通知,SSE 从运行查询投影输出结果级恢复流。

Tech Stack: Python 3.11+、FastAPI、Pydantic v2、SQLAlchemy 2、Alembic、MySQL 8、Redis、pytest、pytest-asyncio、httpx;模型、Milvus、Neo4j 通过 Service Protocol/Port 注入。

Spec: docs/05-接口文档.md、docs/01-通用Agent平台开发设计.md、docs/02-数据库建表设计.md、docs/03-平台端到端流程文档.md、AGENTS.md

Global Constraints

  • 数据库允许新增表和字段,禁止重命名、删除或改变已有表及已有字段定义。
  • MVC+S 固定为 Controller -> Service -> Repository -> Model;Agent 属于 Service 层。
  • Agent 必须由 AgentFactory 创建并继承 BaseAgent,不得绕过公共鉴权、记忆、模型、工具、合规、审计和事件流程。
  • agent_run 是运行主键;trace_id 只用于追踪;不得复用 trace_id 代替 run_id。
  • 最终助手消息、审计、幂等完成状态、agent_run=succeeded 和领域 Outbox 必须在同一事务提交。
  • 记忆提取事件必须在 complete_run() 同一事务写入 Outbox,请求线程不得提交后直接调用记忆提取。
  • BaseAgent 只返回传输无关的 RunProgressEvent;SSE 由 View 适配,MVP 只实现结果级恢复。
  • 场内基金模拟交易是当前业务范围;场外运营独立建表、独立接口。

Task 1: 项目骨架与公共契约

Files:

  • Create: pyproject.toml
  • Create: app/core/contracts.py
  • Create: app/core/errors.py
  • Create: app/core/context.py
  • Create: tests/unit/core/test_contracts.py
  • Create: tests/unit/core/test_errors.py

Interfaces:

  • Produces AgentRequest, AgentRequestMetadata, RequestContext, AgentDefinition, ResolvedAgentConfig, RunProgressEvent, AgentResult, DomainEvent and AgentError classes matching 01 contract IDs.

  • Also produces immutable transport-neutral RunAccepted, RunCancellation and RunView projection dataclasses used by the application and View layers.

  • Step 1: Write failing validation tests for forbidden metadata keys, blank messages, invalid idempotency keys, confidence outside 0..1, and extra request fields.

  • Step 2: Run python -m pytest tests/unit/core/test_contracts.py -q; expect validation failures before implementation.

  • Step 3: Implement Pydantic/dataclass contracts with ConfigDict(extra="forbid"); keep HTTP DTOs separate from internal contracts.

  • Step 4: Add stable error codes and public messages; errors must not contain stack traces or provider responses.

  • Step 5: Run python -m pytest tests/unit/core -q and python -m ruff check app tests.

Task 2: 数据库迁移与 Repository

Files:

  • Create: alembic/versions/20260909_agent_platform_v31.py
  • Create: app/model/agent_run.py
  • Create: app/model/platform_entities.py
  • Create: app/repository/agent_run_repository.py
  • Create: app/repository/idempotency_repository.py
  • Create: app/repository/outbox_repository.py
  • Create: tests/integration/repository/test_agent_run_repository.py

Interfaces:

  • AgentRunRepository.create_queued(...) -> AgentRun

  • AgentRunRepository.claim(run_id, worker_id, lease_seconds) -> AgentRun | None

  • AgentRunRepository.request_cancel(run_id, user_id) -> AgentRun

  • IdempotencyRepository.claim(scope, request_hash) -> ClaimResult

  • OutboxRepository.append(event: DomainEvent) -> None

  • ClaimResult is a typed result with decision, record_id, existing_run_id and request_hash; AgentRun is the SQLAlchemy model for agent_run.

  • Step 1: Write migration tests asserting creation of agent_run, ten platform increment tables, foreign keys, status check, unique run_id, unique idempotency_id, and lease indexes.

  • Step 2: Run alembic upgrade head against an empty MySQL test schema and inspect with SQLAlchemy Inspector.

  • Step 3: Implement agent_run migration exactly as 02 §8.4; do not alter baseline tables.

  • Step 4: Implement repositories with conditional updates for leases, cancellation and terminal states.

  • Step 5: Test duplicate claims, expired lease takeover, concurrent completion and forbidden terminal-state transitions.

  • Step 6: Run alembic downgrade -1 in a disposable schema and verify no baseline table or column changed.

Task 3: JWT、统一信封和 API DTO

Files:

  • Create: app/api/dependencies/auth.py
  • Create: app/api/schemas/envelopes.py
  • Create: app/api/schemas/agent_runs.py
  • Create: app/api/mappers/agent_run_mapper.py
  • Create: tests/api/test_auth.py
  • Create: tests/api/test_envelopes.py

Interfaces:

  • build_request_context(request) -> RequestContext

  • AgentRunCreateRequest

  • success_envelope(data, trace_id)

  • error_envelope(code, message, trace_id, field_errors)

  • Step 1: Test missing token, invalid signature, wrong issuer/audience, expiry and revoked jti return 401.

  • Step 2: Test roles, customer assignment and data scope are reloaded server-side and cannot be supplied by request body or metadata.

  • Step 3: Implement JWT dependency and trace middleware; always return X-Trace-ID.

  • Step 4: Implement JSON DTO validation, cursor validation, decimal-string serialization and BIGINT string serialization.

  • Step 5: Run python -m pytest tests/api -q.

Task 4: Agent 运行受理、幂等和 Outbox

Files:

  • Create: app/service/agent_run_application_service.py
  • Create: app/service/run_dispatch_port.py
  • Create: app/api/controllers/agent_runs.py
  • Create: tests/service/test_agent_run_application_service.py
  • Create: tests/api/test_agent_runs_create.py

Interfaces:

  • AgentRunApplicationService.accept(payload, context) -> RunAccepted

  • AgentRunApplicationService.request_cancel(run_id, context, idempotency_key) -> RunCancellation

  • RunDispatchPort.enqueue(run_id) -> None

  • RunAccepted and RunCancellation are the immutable result dataclasses from Task 1; controllers never return ORM objects.

  • Step 1: Write failing transaction tests for user message + idempotency + agent_run + agent.run_requested atomicity.

  • Step 2: Test same key/same hash returns original run; same key/different hash returns 409 IDEMPOTENCY_CONFLICT.

  • Step 3: Implement accept(); do not execute model or Agent in the HTTP request.

  • Step 4: Implement cancellation with cancel_requested conditional update and agent.run_cancel_requested Outbox.

  • Step 5: Add controller routes and verify response is 202 with run_id, trace_id, status_url, events_url.

  • Step 6: Run python -m pytest tests/service/test_agent_run_application_service.py tests/api/test_agent_runs_create.py -q.

Task 5: AgentFactory、BaseAgent.execute 与 Worker

Files:

  • Create: app/service/agent/base.py
  • Create: app/service/agent/factory.py
  • Create: app/service/agent/executor.py
  • Create: app/worker/agent_run_worker.py
  • Create: tests/contract/test_registered_agents.py
  • Create: tests/worker/test_agent_run_worker.py

Interfaces:

  • AgentFactory.create(agent_type, context) -> BaseAgent

  • BaseAgent.execute(request, context) -> AsyncIterator[RunProgressEvent]

  • AgentExecutor.execute_run(run_id) -> None

  • AgentRunWorker.claim_and_execute() -> None

  • Step 1: Write contract tests proving every registered Agent subclasses BaseAgent, declares valid definition, and cannot override execute or public governance methods.

  • Step 2: Test Worker lease claim, retry after expiry, same run_id reuse and cancellation checkpoint.

  • Step 3: Implement BaseAgent seven-step flow with injected Services; no HTTP/SSE imports.

  • Step 4: Implement Worker exception translation to AgentError; retry recoverable failures and terminalize non-recoverable failures.

  • Step 5: Run python -m pytest tests/contract tests/worker -q.

Task 6: complete_run、事务 Outbox 和结果查询

Files:

  • Create: app/service/agent_persistence_service.py
  • Create: app/service/run_query_service.py
  • Create: app/api/controllers/run_queries.py
  • Create: tests/service/test_agent_persistence_service.py
  • Create: tests/api/test_run_queries.py

Interfaces:

  • AgentPersistenceService.complete_run(..., memory_extraction_requested: bool) -> int

  • RunQueryService.get(run_id, context) -> RunView

  • RunView is the persisted HTTP-neutral projection from Task 1; its result is populated only from database rows.

  • Step 1: Write a transaction test that fails if assistant message, audit, idempotency completion, agent_run=succeeded, or Outbox is missing after rollback.

  • Step 2: Test memory.extraction_requested is present in the same transaction when requested and absent otherwise.

  • Step 3: Implement domain event construction with event_id, aggregate_type, occurred_at, and trace_id.

  • Step 4: Implement result projection from persisted message/run rows; never read Worker memory for GET responses.

  • Step 5: Run python -m pytest tests/service/test_agent_persistence_service.py tests/api/test_run_queries.py -q.

Task 7: 结果级恢复 SSE 与会话接口

Files:

  • Create: app/view/sse_view.py
  • Create: app/api/controllers/run_events.py
  • Create: app/api/controllers/conversations.py
  • Create: app/api/schemas/conversations.py
  • Create: tests/api/test_run_events.py
  • Create: tests/api/test_conversations.py

Interfaces:

  • SseView.stream(run_id, context) -> StreamingResponse

  • GET /api/v1/agent-runs/{run_id}/events

  • POST/GET /api/v1/conversations... routes listed in 05 §7

  • Step 1: Test SSE headers, event order, 15-second comment heartbeat, terminal done/error, and no tools/delta/done before persistence success.

  • Step 2: Test reconnect after disconnect rebuilds complete result from agent_run and conversation_message; do not implement event replay.

  • Step 3: Implement View adapter from RunProgressEvent to 01 §12 events; BaseAgent remains HTTP-free.

  • Step 4: Implement session ownership, message cursor pagination, closure, feedback and public handover request mapping.

  • Step 5: Run python -m pytest tests/api/test_run_events.py tests/api/test_conversations.py -q.

Task 8: 平台管理面与配置发布

Files:

  • Create: app/api/controllers/admin_config.py
  • Create: app/api/controllers/admin_models.py
  • Create: app/api/controllers/admin_prompts.py
  • Create: app/api/controllers/admin_intents.py
  • Create: app/api/controllers/admin_audit.py
  • Create: app/service/config_release_service.py
  • Create: tests/api/test_admin_config.py

Interfaces:

  • Routes and permissions exactly match 05 §9 and §19 entries A001-A033.

  • ConfigReleaseService.validate/review/activate/rollback are separate methods with maker-checker enforcement.

  • Step 1: Test creator cannot review own release, invalid release cannot activate, and If-Match conflict returns 409.

  • Step 2: Test active template/intent uniqueness and fallback endpoint foreign-key/order validation.

  • Step 3: Implement draft/review/activate/rollback service transactions and audit records.

  • Step 4: Implement route DTOs without exposing secrets, internal URLs or old fallback_endpoint_ids.

  • Step 5: Run python -m pytest tests/api/test_admin_config.py -q.

Task 9: OpenAPI、契约测试和故障演练

Files:

  • Create: openapi/agent-platform-v1.yaml

  • Create: tests/contract/test_openapi_contract.py

  • Create: tests/security/test_data_scope.py

  • Create: tests/recovery/test_agent_run_recovery.py

  • Modify: docs/05-接口文档.md only when the HTTP contract intentionally changes

  • Step 1: Generate OpenAPI from the same DTO/router source and compare paths, methods, required headers and response envelopes with 05 §19.

  • Step 2: Test cross-customer access returns 404, JWT failures return 401, and Agent cannot execute write tools.

  • Step 3: Test Worker crash, lease takeover, Redis loss, Outbox retry/dead-letter and client disconnect.

  • Step 4: Run python -m pytest -q, python -m ruff check app tests, and python -m mypy app.

  • Step 5: Run empty-schema migration and upgrade-schema migration in disposable MySQL databases; verify 49-table count and immutable baseline definitions.