Files
2026-03-01 07:01:34 +08:00

8.7 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

Enterprise AI agent platform powering three production scenarios:

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

Tech Stack: LangGraph orchestration, Claude Sonnet 4.6, Qwen2.5 local models, Milvus vector DB, FastAPI, PostgreSQL, Redis

Common Commands

Development Setup

# Install dependencies (must pass with --frozen)
uv sync --frozen

# Start all services (Docker required)
make up

# Stop services
make down

Testing

# Run all tests (excludes Claude API tests)
make test

# Run unit tests with coverage (must be ≥80%)
make test-unit

# Run integration tests (requires Docker)
make test-integration

# Run specific test file
uv run pytest tests/unit/test_scorer.py -v

# Run single test
uv run pytest tests/unit/test_scorer.py::test_evidence_scorer_below_threshold -v

Code Quality

# Lint (ruff + mypy)
make lint

# Format code
make format

# 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

Database

# Run migrations
make migrate

# Seed test data
make seed

# Create new migration
uv run alembic revision --autogenerate -m "description"

Health & Monitoring

# Check service health
make health

# View API logs
make logs

Architecture Overview

Core Layers (P5-P8)

The platform follows a strict layered architecture:

P8 — Orchestration Layer (core/engine/)

  • LangGraph StateGraph with 5 nodes: ask, plan, act, verify, report
  • Interrupt/resume for human-in-the-loop approval gates
  • PostgreSQL checkpointer for state persistence

P7 — Profile & Tool Layer (core/profile/, core/tools/)

  • SOP YAML profiles define allowed tools, prompts, and thresholds per scenario
  • Tool whitelist enforcement happens in ToolExecutor, NOT in individual tools
  • All tools return ToolResult(success=bool, data=dict, error=str, meta=dict)

P6 — LLM & Router Layer (core/llm/, core/router/)

  • ClaudeClient wraps Anthropic SDK with retry, timeout, token tracking
  • ModelRouter routes between cloud (Claude) and local (Qwen) models
  • Structured output via tool calling with tool_choice={"type": "any"}

P5 — Audit & Observability (core/audit/, core/observability/)

  • 3-tier audit: L1 PostgreSQL (append-only), L2 OpenTelemetry spans, L3 AES-256-GCM encrypted payloads
  • PII sanitization via Sanitizer.mask() before logging
  • Cost tracking per request with Decimal precision

API Layer (api/)

  • FastAPI with async handlers, Pydantic request/response models
  • Middleware stack: Tracing → Logging → RateLimiting → CORS
  • JWT auth via require_auth dependency (not global middleware)
  • RBAC permission matrix: admin, manager, analyst, viewer roles
  • SSE streaming for /api/v1/chat endpoint

Database Layer (db/)

  • SQLAlchemy 2.0 async ORM with asyncpg driver
  • Alembic migrations in db/migrations/versions/
  • Repository pattern in db/repositories/

Critical Architecture Rules

R1 — Tool Whitelist Enforcement

ToolExecutor.execute_tool() checks profile.allowed_tools BEFORE calling any tool. Tool implementations NEVER perform whitelist checks themselves.

R2 — Exception Boundary

  • BaseTool.execute(): NEVER raises exceptions, always returns ToolResult(success=False)
  • LangGraph nodes: NEVER let exceptions escape; set state.error and route to END
  • ToolExecutor: MAY raise ToolNotAllowedError for policy violations

R3 — State Serialization

AgentState is a Pydantic BaseModel and must be JSON-serializable at all times. Large objects (documents, embeddings) are stored externally; state holds only UUIDs/references.

R4 — Approval Gates

Any tool with requires_approval=True MUST call interrupt(ApprovalPayload(...)) before execution. Resume via Command(resume=ApprovalDecision(...)).

R5 — Audit Completeness

Every tool execution (success, failure, OR skipped) MUST produce an AuditEvent. Missing audit entries are P0 bugs. The audit_event table is append-only (enforced by DB trigger).

R6 — Sensitive Data

Sanitizer.mask() MUST be applied to all user inputs before they appear in logs, audit payloads, or API responses. Patterns: phone 138****8888, ID card ****19700101****, password [REDACTED].

R7 — Async Discipline

No synchronous blocking inside async def. CPU-bound operations use asyncio.to_thread(). Every external I/O call has explicit timeout= parameter.

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 use float.

Settings Access Pattern

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

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

Testing Standards

Test Isolation

  • unit/: Zero external I/O; mock DB, HTTP, Claude, Milvus, Redis
  • integration/: Real services via testcontainers or Docker stack
  • contracts/: Real Claude API; @pytest.mark.claude_api; skipped if ANTHROPIC_API_KEY unset

Secret Injection

Use settings_override fixture in tests/conftest.py. NEVER assign os.environ[...] directly in test files.

Coverage Requirements

  • Overall: ≥80% across core/ and api/ (enforced by --cov-fail-under=80)
  • core/engine/: ≥90%
  • core/verify/: ≥95% (formula is critical path)

Test Naming Convention

test_<unit_under_test>_<scenario>_<expected_outcome>

Examples:
  test_ask_node_missing_required_fields_returns_clarification
  test_tool_executor_unlisted_tool_raises_and_audits
  test_evidence_scorer_below_reject_threshold_returns_insufficient

Evidence Scoring Formula

DO NOT modify without updating tests and Profile schema defaults:

score = (
    Decimal("0.4") * retrieval_score
  + Decimal("0.3") * completeness_score
  + Decimal("0.3") * historical_accuracy
)

# Thresholds (overridable per Profile):
# sufficient    : score >= 0.85  → normal output
# limited       : score >= 0.60  → output with ⚠️ warning
# insufficient  : score <  0.60  → reject, explain gaps

Claude API Contract

  • Use anthropic SDK version locked in uv.lock
  • Structured output: tool_choice={"type": "any"} to force tool calling
  • Parse tool results: content_block.input (dict) directly — NEVER regex-parse
  • Prompt Caching: add cache_control={"type": "ephemeral"} to system message blocks
  • Token tracking: record usage.input_tokens, usage.output_tokens, usage.cache_read_input_tokens after EVERY API call
  • Timeout: pass httpx.Timeout(connect=10, read=60, write=10, pool=5) to AsyncAnthropic constructor

FastAPI Patterns

Error Response Format

ALL error responses must use:

class ErrorResponse(BaseModel):
    code: str          # machine-readable, e.g. "TOOL_NOT_ALLOWED"
    message: str       # human-readable Chinese message
    trace_id: str
    details: dict = Field(default_factory=dict)

HTTP Status Codes

  • 200: Success
  • 202: Async task accepted
  • 400: Client validation error
  • 401: Missing/invalid JWT
  • 403: Insufficient permission
  • 404: Resource not found
  • 409: State conflict (e.g. already decided)
  • 422: Pydantic validation failure
  • 500: Unhandled server error
  • 503: Dependency unhealthy

AgentEngine Lifecycle

Initialized ONCE in api/main.py lifespan startup, stored at app.state.engine. NEVER instantiate AgentEngine inside route handlers.

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

Important Files

  • AGENTS.md: Project root instructions (read this first)
  • core/AGENTS.md: Core engine submodule instructions
  • api/AGENTS.md: API layer submodule instructions
  • tests/AGENTS.md: Testing standards and patterns
  • pyproject.toml: Python dependencies and tool configuration
  • uv.lock: Locked dependency versions (MUST be committed)
  • Makefile: Common development commands
  • docker-compose.yml: Local development services
  • alembic.ini: Database migration configuration