commit to gitea
This commit is contained in:
@@ -0,0 +1,250 @@
|
||||
# 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
|
||||
```bash
|
||||
# Install dependencies (must pass with --frozen)
|
||||
uv sync --frozen
|
||||
|
||||
# Start all services (Docker required)
|
||||
make up
|
||||
|
||||
# Stop services
|
||||
make down
|
||||
```
|
||||
|
||||
### Testing
|
||||
```bash
|
||||
# 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
|
||||
```bash
|
||||
# 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
|
||||
```bash
|
||||
# Run migrations
|
||||
make migrate
|
||||
|
||||
# Seed test data
|
||||
make seed
|
||||
|
||||
# Create new migration
|
||||
uv run alembic revision --autogenerate -m "description"
|
||||
```
|
||||
|
||||
### Health & Monitoring
|
||||
```bash
|
||||
# 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
|
||||
```python
|
||||
# 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:
|
||||
```python
|
||||
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:
|
||||
```python
|
||||
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
|
||||
Reference in New Issue
Block a user