Files
billai/AGENTS.md
clz 7022e46793
Some checks failed
Deploy BillAI / Deploy to Production (push) Has been cancelled
docs: 更新 AGENTS.md
补充 Svelte 5 runes、SvelteKit 代理架构、Go LocalTime 类型、
软删除约束、Adapter 模式、账单去重策略等细节,
扩充重要文件列表,完善测试命令说明。
2026-03-03 20:50:59 +08:00

11 KiB

AGENTS.md - AI Coding Agent Guidelines

Guidelines for AI coding agents working on BillAI - a microservices bill analysis system.

Architecture

  • web/ - SvelteKit 5 + TailwindCSS 4 + TypeScript (Frontend Proxy & UI, port 3000)
  • server/ - Go 1.21 + Gin + MongoDB (Main API & Data Storage, port 8080)
  • analyzer/ - Python 3.12 + FastAPI (Data Cleaning & Analysis Service, port 8001)

The SvelteKit frontend acts as a proxy: all /api/* browser requests are forwarded by web/src/routes/api/[...path]/+server.ts to the Go backend. The browser never contacts Go directly. API_URL env var controls the target (http://server:8080 in Docker, http://localhost:8080 in local dev).

Build/Lint/Test Commands

Frontend (web/)

Working Directory: /Users/clz/Projects/BillAI/web

npm run dev                              # Start Vite dev server
npm run build                            # Production build (adapter-node)
npm run preview                          # Preview production build
npm run check                            # TypeScript check (svelte-check)
npm run lint                             # Prettier --check + ESLint
npm run format                           # Format with Prettier
npm run test                             # Run all unit tests once (CI mode)
npm run test:unit                        # Run unit tests in watch mode
npx vitest run src/routes/+page.spec.ts  # Run single test file
npx vitest run -t "test name pattern"    # Run tests by name pattern

Backend (server/)

Working Directory: /Users/clz/Projects/BillAI/server

go run .                              # Start server
go build -o server .                  # Build binary
go mod tidy                           # Clean dependencies
go test ./...                         # Run all tests
go test ./handler/...                 # Run handler package tests
go test -run TestName ./...           # Run single test function
go test -v ./handler/...              # Verbose test output

Analyzer (analyzer/)

Working Directory: /Users/clz/Projects/BillAI/analyzer

python server.py                      # Start FastAPI server directly
uvicorn server:app --reload           # Start with hot reload
pytest                                # Run all tests
pytest test_jd_cleaner.py            # Run single test file
pytest -k "test_name"                # Run test by name pattern
pip install -r requirements.txt      # Install dependencies

Docker

Working Directory: /Users/clz/Projects/BillAI

docker-compose up -d --build          # Start/rebuild all services
docker-compose logs -f server         # Follow service logs
docker-compose down                   # Stop all services

Code Style

General

  • Comments: Existing comments often use Chinese for business logic explanations. Maintain this style where appropriate; English is also acceptable for technical explanations.
  • Conventions: Follow existing patterns strictly. Do not introduce new frameworks or libraries without checking package.json / go.mod / requirements.txt.

TypeScript/Svelte (web/)

  • Formatting: Prettier — tabs, single quotes, no trailing commas, printWidth 100, prettier-plugin-svelte.
  • Naming: PascalCase for types/interfaces/components, camelCase for variables/functions.
  • Imports: Use $lib alias for internal imports and $app/* for SvelteKit builtins. Never use relative paths for lib-level modules.
    import { browser } from '$app/environment'
    import { goto } from '$app/navigation'
    import { auth } from '$lib/stores/auth'
    import type { UIBill } from '$lib/models/bill'
    import Upload from '@lucide/svelte/icons/upload'
    
  • Svelte 5 runes: Use the new runes API — $state, $derived, $effect, $props. Event handlers use onclick={fn} syntax (not legacy on:click).
  • Types: Define export interface for all data models. Frontend models use camelCase fields (UIBill); API responses use snake_case (CleanedBill). Provide explicit converter functions (e.g., cleanedBillToUIBill, uiBillToUpdateBillRequest) in web/src/lib/models/bill.ts.
  • Error Handling: Check response.ok; throw new Error(\HTTP ${response.status}`)for the UI to catch. On 401, callauth.logout()and redirect to/login`.
  • Auth pattern: createAuthStore() factory in $lib/stores/auth.ts. Token stored in localStorage under key auth. All API calls go through apiFetch() in $lib/api.ts, which injects Authorization: Bearer <token> and handles 401 centrally.
  • Testing: Vitest + vitest-browser-svelte + Playwright. Test files co-located with routes as *.spec.ts. Use describe / it / expect from vitest, render from vitest-browser-svelte.

Go Backend (server/)

  • Layer structure: handler (HTTP) → service (logic) → adapter (external Python service) and repository (DB) → model (structs). Handlers must not contain business logic.
  • Struct tags: JSON uses snake_case. omitempty on optional response fields. Use form tags for query/form binding. Use pointer fields (*string) for optional patch request fields. Sensitive fields get json:"-".
    type CleanedBill struct {
        ID            primitive.ObjectID `bson:"_id,omitempty" json:"id,omitempty"`
        BillType      string             `bson:"bill_type" json:"bill_type"`
    }
    type UpdateBillRequest struct {
        Category *string `json:"category,omitempty"`
    }
    
  • Error Handling: Return 500 for DB/internal errors, 400 for bad requests, 404 for not found. Wrap errors with context using fmt.Errorf("context: %w", err). Check err == repository.ErrNotFound for 404 disambiguation. Use Result bool (not Success) in response envelopes.
    if err != nil {
        c.JSON(http.StatusInternalServerError, Response{Result: false, Message: err.Error()})
        return
    }
    
  • Response envelope: Most endpoints: Result bool, Message string, Data *T. Auth endpoints use success bool, error string, data interface{}.
  • Interfaces: Use adapter.Cleaner and repository.BillRepository interfaces. Access global singletons via adapter.GetCleaner() and repository.GetRepository().
  • Time: Use the custom LocalTime type (wraps time.Time) for all timestamp fields. It serializes as "2006-01-02 15:04:05" in both JSON and BSON, preserving local time.
  • Soft delete: Bills are never hard-deleted. All queries must filter is_deleted: false.

Python Analyzer (analyzer/)

  • Style: PEP 8. snake_case for variables, functions, and filenames. UPPER_CASE for module-level constants. Prefix private module globals with _.
  • Type Hints: Mandatory for all function arguments and return types. Use Optional[str] from typing or str | None (Python 3.10+ union syntax).
  • Models: Use pydantic.BaseModel for all API request/response schemas.
    class CleanRequest(BaseModel):
        input_path: str
        output_path: str
        year: Optional[str] = None
        bill_type: Optional[str] = "auto"
    
  • FastAPI patterns: Use HTTPException(status_code=400, detail=message) for user errors. Manage temporary files with tempfile.NamedTemporaryFile + os.unlink in finally blocks.
  • Cleaner classes: Extend BaseCleaner(ABC) from cleaners/base.py. Implement clean() and optionally reclassify(). Category inference reads rules from config/category.yaml via yaml.safe_load.
  • Docstrings: Triple-quoted. Chinese descriptions are common for API endpoint docs.

Key Patterns

API Flow

Browser → SvelteKit proxy (/api/[...path]/+server.ts)
        → Go server (Gin, AuthRequired middleware)
          → handler → service → adapter.GetCleaner() → HTTP POST to Python FastAPI
                              → repository.GetRepository() → MongoDB

Authentication

  • JWT (HS256). Token in localStorage under key auth.
  • Header: Authorization: Bearer <token>.
  • middleware.AuthRequired() wraps all /api/* routes except /api/auth/*.
  • Passwords in config.yaml support plaintext or SHA-256 hashed values.
  • 401 anywhere → auth.logout() + redirect to /login.

File Processing

Upload flow: Upload (ZIP/XLSX) → Extract → Convert to UTF-8 CSV (Python /convert) → Auto-detect bill type → Deduplicate against DB → Clean/normalize (Python /clean/upload) → Save raw + cleaned bills to MongoDB.

Deduplication: raw bills check transaction_id; cleaned bills check transaction_id + merchant_order_no. JD bills trigger soft-deletion of overlapping records in other sources to prevent double-counting.

Adapter (Go ↔ Python)

adapter.Cleaner interface has two implementations: HTTP-based (adapter/http, default) and subprocess-based (adapter/python, legacy). Controlled by ANALYZER_MODE env var.

Important Files

File Role
web/src/lib/api.ts Central API client; apiFetch() injects auth and handles 401
web/src/lib/stores/auth.ts Auth state; JWT in localStorage; login/logout/validate
web/src/lib/models/bill.ts UIBill model + converters to/from API CleanedBill shape
web/src/routes/api/[...path]/+server.ts SvelteKit proxy to Go backend
server/main.go Entry point; wires config, adapters, repository, router
server/config/config.go YAML + env config; priority: defaults → config.yaml → env vars
server/router/router.go All route definitions and middleware assignment
server/middleware/auth.go JWT validation + user context injection
server/handler/upload.go Full upload pipeline (extract → convert → clean → store)
server/handler/bills.go List/filter bills with pagination and monthly stats
server/model/bill.go RawBill, CleanedBill, MonthlyStat; custom LocalTime type
server/adapter/adapter.go Cleaner interface definition
server/repository/repository.go BillRepository interface (14 persistence methods)
server/repository/mongo/repository.go MongoDB implementation with aggregation pipelines
analyzer/server.py FastAPI entry point; /health, /clean, /convert, /detect routes
analyzer/cleaners/base.py BaseCleaner ABC; shared filtering and output logic
analyzer/cleaners/alipay.py Alipay-specific normalization
analyzer/cleaners/wechat.py WeChat-specific normalization
analyzer/cleaners/jd.py JD (京东) normalization and 3-level review scoring
analyzer/category.py infer_category() using YAML keyword rules
analyzer/converter.py xlsx→csv (openpyxl), GBK→UTF-8 re-encoding, type detection
server/config.yaml Server port, MongoDB URI, JWT settings, user list
docker-compose.yaml 5 services: web, server, analyzer, mongodb, mongo-express