commit to gitea

This commit is contained in:
2026-03-01 07:01:34 +08:00
parent d8237ae76d
commit 4dc199d44f
+250
View File
@@ -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