Files

6.8 KiB

Agent Platform — Project Root Instructions

Project Context

Enterprise AI agent platform powering three production scenarios:

  1. Presales — proposal documents (Word/Excel) in ~30 min from days
  2. Security — incident reports from hours to minutes
  3. Knowledge QA — citation-backed answers, ≥90 % reference coverage

Tech Stack (all versions locked in uv.lock)

  • Orchestration : langgraph ≥ 0.3.0 StateGraph + interrupt/resume for human-in-the-loop
  • LLM cloud : anthropic ≥ 0.50.0 → claude-sonnet-4-6-20260217
  • LLM local : Qwen2.5-14B / 7B via OpenAI-compatible HTTP endpoint
  • Vector DB : pymilvus ≥ 2.4.0 + BGE-M3 embeddings + BGE-reranker
  • API : fastapi ≥ 0.115.0 + uvicorn[standard]
  • ORM : sqlalchemy[asyncio] ≥ 2.0.30 + asyncpg ≥ 0.29.0
  • Migrations : alembic ≥ 1.13.0
  • Observability : opentelemetry-sdk ≥ 1.27.0 + prometheus exporter + grafana dashboards
  • Auth : python-jose[cryptography] + passlib[bcrypt]
  • Encryption : cryptography ≥ 42.0.0 (AES-256-GCM, audit payloads)
  • Docs : python-docx ≥ 1.1.2

Repository Layout

agent-platform/
├── core/
│   ├── engine/        # LangGraph: state, 5 nodes, graph builder
│   ├── profile/       # SOP YAML schema (Pydantic), loader, registry
│   ├── tools/         # BaseTool, ToolResult, ToolExecutor, registry
│   ├── llm/           # ClaudeClient, PromptBuilder
│   ├── verify/        # EvidenceScorer
│   ├── audit/         # AuditLogger (3-tier), Sanitizer, Encryptor
│   ├── observability/ # OTel setup, decorators, CostTracker, dashboards/
│   └── router/        # ModelRouter
├── api/
│   ├── routes/        # health, chat, approve, trace
│   ├── contracts/     # Pydantic req/resp models, permissions matrix
│   └── middleware/    # tracing, auth (JWT), rbac, rate_limit
├── profiles/
│   ├── presales/prompts/
│   ├── security_analysis/prompts/
│   └── knowledge_qa/prompts/
├── db/
│   ├── base.py
│   ├── models.py
│   ├── repositories/
│   └── migrations/versions/
├── tests/
│   ├── unit/
│   ├── integration/
│   ├── contracts/          # real Claude API; @pytest.mark.claude_api
│   └── fixtures/
├── scripts/
│   ├── setup_env.sh
│   ├── health_check.sh
│   ├── seed_data.py
│   └── gen_audit_key.py
├── docker/
│   ├── Dockerfile.api
│   ├── otel-collector-config.yml
│   └── prometheus.yml
├── .env.example
├── tests/.env.test         # committed; no real secrets
├── pyproject.toml
├── uv.lock                 # MUST be committed; MUST pass uv sync --frozen
├── alembic.ini
├── Makefile
└── docker-compose.yml

Critical Architecture Rules (NEVER Violate)

R1 — Tool Whitelist Enforcement

ToolExecutor.execute_tool() checks profile.allowed_tools BEFORE calling any tool. Violation → raise ToolNotAllowedError(tool_name, profile_name) + write AuditEvent(event_type="whitelist_violation"). Tool implementations NEVER perform whitelist checks themselves.

R2 — Exception Boundary

Layer Raises? On failure
ToolExecutor / Act node YES — policy ToolNotAllowedError
BaseTool.execute() NEVER ToolResult(success=False)
LangGraph nodes NEVER escape set state.error → END

R3 — State Serialization

AgentState is a BaseModel (fully JSON-serializable at all times). Large objects (documents, embeddings, raw HTML) → stored in DB / object-store; only UUID / string references live inside state. Validate with AgentState.validate_serializable(state) after every node that mutates state.

R4 — Approval Gates

Any tool whose ToolDeclaration.requires_approval is True MUST call interrupt(ApprovalPayload(...)) before execution. Resume via Command(resume=ApprovalDecision(...)). There are NO exceptions to this rule.

R5 — Audit Completeness

Every tool execution — success, failure, AND skipped — MUST produce an AuditEvent written by AuditLogger. A missing audit entry is a P0 production bug. audit_event rows are append-only; enforced by DB trigger trg_audit_event_no_mutation.

R6 — Sensitive Data

Sanitizer.mask() MUST be applied to all user inputs before they appear in logs, audit payloads, or API responses. Patterns:

  • Phone 1[3-9]\d{9} → 138****8888
  • ID card 18-digit → ****19700101****
  • password / key fields → [REDACTED]

R7 — Async Discipline

No synchronous blocking inside any async def. CPU-bound operations → asyncio.to_thread(). Every external I/O call carries an explicit timeout= parameter. No asyncio.create_task() fire-and-forget without error collection.

R8 — Decimal for Money

All cost / fee / score calculations use Decimal with explicit precision and ROUND_HALF_UP. DB columns: Numeric(12,6) for USD, Numeric(12,4) for CNY, Numeric(5,4) for evidence scores. NEVER store or compute these values as float.

Settings Access Pattern

# CORRECT — lazy singleton, test-safe
from core.config import get_settings
s = get_settings()

# WRONG — instantiated at import time, breaks test monkeypatching
from core.config import settings          # this symbol does not exist

Gate Definitions

Every task MUST pass its gate before being declared complete.

Static Gate (no Docker required)

uv sync --frozen
uv run ruff check . --select E,F,I --quiet
uv run mypy core/ api/ --ignore-missing-imports --no-error-summary
uv run pytest tests/unit/ -x -q --tb=short

Enhanced Gate (Docker required)

docker compose up -d --wait
uv run alembic upgrade head
uv run pytest tests/integration/ -x -q --tb=short
bash scripts/health_check.sh

Testing Standards

  • Unit tests : zero external I/O; mock DB, HTTP, Claude, Milvus, Redis
  • Integration : real services via testcontainers or running Docker stack
  • Contract : real Claude API; @pytest.mark.claude_api; skipped automatically if ANTHROPIC_API_KEY is unset
  • Coverage gate: --cov-fail-under=80 across core/ and api/
  • Secret injection: via tests/conftest.py settings_override fixture; NEVER os.environ assignment in test files directly

Commit Convention

feat(engine): implement interrupt/resume approval in act_node
fix(verify): correct EWMA weight formula in EvidenceScorer
test(api): add integration test for /approve expired-token path
chore(deps): add alembic, cryptography to pyproject.toml
refactor(tools): extract _resolve_params to standalone module