ййй
This commit is contained in:
@@ -1,380 +0,0 @@
|
||||
{
|
||||
"layers": {
|
||||
"C0_SOURCE_CHUNKS": {
|
||||
"retriever": {
|
||||
"class": "RagService",
|
||||
"file": "app/modules/rag/services/rag_service.py",
|
||||
"method": "retrieve"
|
||||
},
|
||||
"indexer": {
|
||||
"class": "CodeTextDocumentBuilder",
|
||||
"file": "app/modules/rag/indexing/code/code_text/document_builder.py",
|
||||
"method": "build"
|
||||
},
|
||||
"input": {
|
||||
"type": "observed shape",
|
||||
"fields": {
|
||||
"rag_session_id": {
|
||||
"type": "string",
|
||||
"required": true
|
||||
},
|
||||
"query": {
|
||||
"type": "string",
|
||||
"required": true
|
||||
},
|
||||
"layers": {
|
||||
"type": "implicit list[string]",
|
||||
"required": false,
|
||||
"source": "RagQueryRouter.layers_for_mode('code')"
|
||||
}
|
||||
}
|
||||
},
|
||||
"output": {
|
||||
"type": "list[dict]",
|
||||
"fields": {
|
||||
"source": "string",
|
||||
"content": "string",
|
||||
"layer": "\"C0_SOURCE_CHUNKS\"",
|
||||
"title": "string",
|
||||
"metadata": {
|
||||
"chunk_index": "int",
|
||||
"chunk_type": "\"symbol_block\" | \"window\"",
|
||||
"module_or_unit": "string",
|
||||
"artifact_type": "\"CODE\""
|
||||
},
|
||||
"score": "float | null"
|
||||
}
|
||||
},
|
||||
"examples": {
|
||||
"input": {
|
||||
"rag_session_id": "rag-123",
|
||||
"query": "where is implemented get_user"
|
||||
},
|
||||
"output": {
|
||||
"source": "app/api/users.py",
|
||||
"content": "async def get_user(user_id: str):\n service = UserService()\n return service.get_user(user_id)",
|
||||
"layer": "C0_SOURCE_CHUNKS",
|
||||
"title": "app/api/users.py:get_user",
|
||||
"metadata": {
|
||||
"chunk_index": 0,
|
||||
"chunk_type": "symbol_block",
|
||||
"module_or_unit": "app.api.users",
|
||||
"artifact_type": "CODE"
|
||||
},
|
||||
"score": 0.07
|
||||
}
|
||||
},
|
||||
"defaults": {
|
||||
"retrieve_limit": 8,
|
||||
"embed_batch_size_env": "RAG_EMBED_BATCH_SIZE",
|
||||
"embed_batch_size_default": 16,
|
||||
"window_chunk_size_lines": 80,
|
||||
"window_overlap_lines": 15
|
||||
},
|
||||
"limitations": [
|
||||
"Line spans are stored but not returned in the public retrieval item shape.",
|
||||
"No direct path or namespace filter is exposed through the retrieval endpoint."
|
||||
]
|
||||
},
|
||||
"C1_SYMBOL_CATALOG": {
|
||||
"retriever": {
|
||||
"class": "RagService",
|
||||
"file": "app/modules/rag/services/rag_service.py",
|
||||
"method": "retrieve"
|
||||
},
|
||||
"indexer": {
|
||||
"class": "SymbolDocumentBuilder",
|
||||
"file": "app/modules/rag/indexing/code/symbols/document_builder.py",
|
||||
"method": "build"
|
||||
},
|
||||
"input": {
|
||||
"type": "observed shape",
|
||||
"fields": {
|
||||
"rag_session_id": {
|
||||
"type": "string",
|
||||
"required": true
|
||||
},
|
||||
"query": {
|
||||
"type": "string",
|
||||
"required": true
|
||||
},
|
||||
"query_term_expansion": {
|
||||
"type": "list[string]",
|
||||
"required": false,
|
||||
"source": "extract_query_terms(query_text)",
|
||||
"max_items": 6
|
||||
}
|
||||
}
|
||||
},
|
||||
"output": {
|
||||
"type": "list[dict]",
|
||||
"fields": {
|
||||
"source": "string",
|
||||
"content": "string",
|
||||
"layer": "\"C1_SYMBOL_CATALOG\"",
|
||||
"title": "string",
|
||||
"metadata": {
|
||||
"symbol_id": "string",
|
||||
"qname": "string",
|
||||
"kind": "\"class\" | \"function\" | \"method\" | \"const\"",
|
||||
"signature": "string",
|
||||
"decorators_or_annotations": "list[string]",
|
||||
"docstring_or_javadoc": "string | null",
|
||||
"parent_symbol_id": "string | null",
|
||||
"package_or_module": "string",
|
||||
"is_entry_candidate": "bool",
|
||||
"lang_payload": "object",
|
||||
"artifact_type": "\"CODE\""
|
||||
},
|
||||
"score": "float | null"
|
||||
}
|
||||
},
|
||||
"examples": {
|
||||
"input": {
|
||||
"rag_session_id": "rag-123",
|
||||
"query": "where is implemented get_user"
|
||||
},
|
||||
"output": {
|
||||
"source": "app/api/users.py",
|
||||
"content": "function get_user\nget_user(user_id)",
|
||||
"layer": "C1_SYMBOL_CATALOG",
|
||||
"title": "get_user",
|
||||
"metadata": {
|
||||
"symbol_id": "sha256(...)",
|
||||
"qname": "get_user",
|
||||
"kind": "function",
|
||||
"signature": "get_user(user_id)",
|
||||
"decorators_or_annotations": [
|
||||
"router.get"
|
||||
],
|
||||
"docstring_or_javadoc": null,
|
||||
"parent_symbol_id": null,
|
||||
"package_or_module": "app.api.users",
|
||||
"is_entry_candidate": true,
|
||||
"lang_payload": {
|
||||
"async": true
|
||||
},
|
||||
"artifact_type": "CODE"
|
||||
},
|
||||
"score": 0.07
|
||||
}
|
||||
},
|
||||
"defaults": {
|
||||
"retrieve_limit": 8,
|
||||
"layer_rank": 1
|
||||
},
|
||||
"limitations": [
|
||||
"Only Python AST symbols are indexed.",
|
||||
"Cross-file resolution is not implemented.",
|
||||
"parent_symbol_id is an observed qname-like value, not guaranteed to be a symbol hash."
|
||||
]
|
||||
},
|
||||
"C2_DEPENDENCY_GRAPH": {
|
||||
"retriever": {
|
||||
"class": "RagService",
|
||||
"file": "app/modules/rag/services/rag_service.py",
|
||||
"method": "retrieve"
|
||||
},
|
||||
"indexer": {
|
||||
"class": "EdgeDocumentBuilder",
|
||||
"file": "app/modules/rag/indexing/code/edges/document_builder.py",
|
||||
"method": "build"
|
||||
},
|
||||
"input": {
|
||||
"type": "observed shape",
|
||||
"fields": {
|
||||
"rag_session_id": {
|
||||
"type": "string",
|
||||
"required": true
|
||||
},
|
||||
"query": {
|
||||
"type": "string",
|
||||
"required": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"output": {
|
||||
"type": "list[dict]",
|
||||
"fields": {
|
||||
"source": "string",
|
||||
"content": "string",
|
||||
"layer": "\"C2_DEPENDENCY_GRAPH\"",
|
||||
"title": "string",
|
||||
"metadata": {
|
||||
"edge_id": "string",
|
||||
"edge_type": "\"calls\" | \"imports\" | \"inherits\"",
|
||||
"src_symbol_id": "string",
|
||||
"src_qname": "string",
|
||||
"dst_symbol_id": "string | null",
|
||||
"dst_ref": "string | null",
|
||||
"resolution": "\"resolved\" | \"partial\"",
|
||||
"lang_payload": "object",
|
||||
"artifact_type": "\"CODE\""
|
||||
},
|
||||
"score": "float | null"
|
||||
}
|
||||
},
|
||||
"examples": {
|
||||
"input": {
|
||||
"rag_session_id": "rag-123",
|
||||
"query": "how get_user calls service"
|
||||
},
|
||||
"output": {
|
||||
"source": "app/api/users.py",
|
||||
"content": "get_user calls UserService",
|
||||
"layer": "C2_DEPENDENCY_GRAPH",
|
||||
"title": "get_user:calls",
|
||||
"metadata": {
|
||||
"edge_id": "sha256(...)",
|
||||
"edge_type": "calls",
|
||||
"src_symbol_id": "sha256(...)",
|
||||
"src_qname": "get_user",
|
||||
"dst_symbol_id": null,
|
||||
"dst_ref": "UserService",
|
||||
"resolution": "partial",
|
||||
"lang_payload": {
|
||||
"callsite_kind": "function_call"
|
||||
},
|
||||
"artifact_type": "CODE"
|
||||
},
|
||||
"score": 0.11
|
||||
}
|
||||
},
|
||||
"defaults": {
|
||||
"retrieve_limit": 8,
|
||||
"layer_rank": 2,
|
||||
"graph_build_mode": "static_python_ast"
|
||||
},
|
||||
"limitations": [
|
||||
"No traversal API exists.",
|
||||
"Edges are stored as retrievable rows, not as a graph-native store.",
|
||||
"Destination resolution is local to one indexed file."
|
||||
]
|
||||
},
|
||||
"C3_ENTRYPOINTS": {
|
||||
"retriever": {
|
||||
"class": "RagService",
|
||||
"file": "app/modules/rag/services/rag_service.py",
|
||||
"method": "retrieve"
|
||||
},
|
||||
"indexer": {
|
||||
"class": "EntrypointDocumentBuilder",
|
||||
"file": "app/modules/rag/indexing/code/entrypoints/document_builder.py",
|
||||
"method": "build"
|
||||
},
|
||||
"input": {
|
||||
"type": "observed shape",
|
||||
"fields": {
|
||||
"rag_session_id": {
|
||||
"type": "string",
|
||||
"required": true
|
||||
},
|
||||
"query": {
|
||||
"type": "string",
|
||||
"required": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"output": {
|
||||
"type": "list[dict]",
|
||||
"fields": {
|
||||
"source": "string",
|
||||
"content": "string",
|
||||
"layer": "\"C3_ENTRYPOINTS\"",
|
||||
"title": "string",
|
||||
"metadata": {
|
||||
"entry_id": "string",
|
||||
"entry_type": "\"http\" | \"cli\"",
|
||||
"framework": "\"fastapi\" | \"flask\" | \"typer\" | \"click\"",
|
||||
"route_or_command": "string",
|
||||
"handler_symbol_id": "string",
|
||||
"lang_payload": "object",
|
||||
"artifact_type": "\"CODE\""
|
||||
},
|
||||
"score": "float | null"
|
||||
}
|
||||
},
|
||||
"examples": {
|
||||
"input": {
|
||||
"rag_session_id": "rag-123",
|
||||
"query": "which endpoint handles get user"
|
||||
},
|
||||
"output": {
|
||||
"source": "app/api/users.py",
|
||||
"content": "fastapi http \"/users/{user_id}\"",
|
||||
"layer": "C3_ENTRYPOINTS",
|
||||
"title": "\"/users/{user_id}\"",
|
||||
"metadata": {
|
||||
"entry_id": "sha256(...)",
|
||||
"entry_type": "http",
|
||||
"framework": "fastapi",
|
||||
"route_or_command": "\"/users/{user_id}\"",
|
||||
"handler_symbol_id": "sha256(...)",
|
||||
"lang_payload": {
|
||||
"methods": [
|
||||
"GET"
|
||||
]
|
||||
},
|
||||
"artifact_type": "CODE"
|
||||
},
|
||||
"score": 0.05
|
||||
}
|
||||
},
|
||||
"defaults": {
|
||||
"retrieve_limit": 8,
|
||||
"layer_rank": 0
|
||||
},
|
||||
"limitations": [
|
||||
"Detection is decorator-string based.",
|
||||
"No Django, Celery, RQ, or cron entrypoints were found.",
|
||||
"Returned payload does not expose line spans."
|
||||
]
|
||||
}
|
||||
},
|
||||
"retrieval_endpoint": {
|
||||
"entrypoint": {
|
||||
"file": "app/modules/rag_session/module.py",
|
||||
"method": "internal_router.retrieve"
|
||||
},
|
||||
"request": {
|
||||
"type": "dict",
|
||||
"fields": {
|
||||
"rag_session_id": "string | optional if project_id provided",
|
||||
"project_id": "string | optional fallback for rag_session_id",
|
||||
"query": "string"
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"type": "dict",
|
||||
"fields": {
|
||||
"items": "list[retrieval item]"
|
||||
}
|
||||
},
|
||||
"defaults": {
|
||||
"mode": "docs unless RagQueryRouter detects code hints",
|
||||
"limit": 8,
|
||||
"embedding_provider": "GigaChat embeddings",
|
||||
"fallback_after_embedding_error": true,
|
||||
"fallback_to_docs_when_code_empty": true
|
||||
}
|
||||
},
|
||||
"ranking": {
|
||||
"storage": "PostgreSQL rag_chunks + pgvector",
|
||||
"query_repository": {
|
||||
"class": "RagQueryRepository",
|
||||
"file": "app/modules/rag/persistence/query_repository.py",
|
||||
"method": "retrieve"
|
||||
},
|
||||
"order_by": [
|
||||
"lexical_rank ASC",
|
||||
"test_penalty ASC",
|
||||
"layer_rank ASC",
|
||||
"embedding <=> query_embedding ASC"
|
||||
],
|
||||
"notes": [
|
||||
"lexical_rank is derived from qname/symbol_id/title/path/content matching extracted query terms",
|
||||
"test_penalty is applied only when prefer_non_tests=true",
|
||||
"layer priority is C3 > C1 > C2 > C0 for code retrieval"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,270 +0,0 @@
|
||||
# LLM Inventory
|
||||
|
||||
## Provider and SDK
|
||||
|
||||
- Provider in code: GigaChat / Sber
|
||||
- Local SDK style: custom thin HTTP client over `requests`
|
||||
- Core files:
|
||||
- `app/modules/shared/gigachat/client.py`
|
||||
- `app/modules/shared/gigachat/settings.py`
|
||||
- `app/modules/shared/gigachat/token_provider.py`
|
||||
- `app/modules/agent/llm/service.py`
|
||||
|
||||
There is no OpenAI SDK, Azure SDK, or local model runtime in the current implementation.
|
||||
|
||||
## Configuration
|
||||
|
||||
Model and endpoint configuration are read from environment in `GigaChatSettings.from_env()`:
|
||||
|
||||
- `GIGACHAT_AUTH_URL`
|
||||
- default: `https://ngw.devices.sberbank.ru:9443/api/v2/oauth`
|
||||
- `GIGACHAT_API_URL`
|
||||
- default: `https://gigachat.devices.sberbank.ru/api/v1`
|
||||
- `GIGACHAT_SCOPE`
|
||||
- default: `GIGACHAT_API_PERS`
|
||||
- `GIGACHAT_TOKEN`
|
||||
- required for auth
|
||||
- `GIGACHAT_SSL_VERIFY`
|
||||
- default: `true`
|
||||
- `GIGACHAT_MODEL`
|
||||
- default: `GigaChat`
|
||||
- `GIGACHAT_EMBEDDING_MODEL`
|
||||
- default: `Embeddings`
|
||||
- `AGENT_PROMPTS_DIR`
|
||||
- optional prompt directory override
|
||||
|
||||
PostgreSQL config for retrieval storage is separate:
|
||||
|
||||
- `DATABASE_URL`
|
||||
- default: `postgresql+psycopg://agent:agent@db:5432/agent`
|
||||
|
||||
## Default models
|
||||
|
||||
- Chat/completions model default: `GigaChat`
|
||||
- Embedding model default: `Embeddings`
|
||||
|
||||
## Completion payload
|
||||
|
||||
Observed payload sent by `GigaChatClient.complete(...)`:
|
||||
|
||||
```json
|
||||
{
|
||||
"model": "GigaChat",
|
||||
"messages": [
|
||||
{"role": "system", "content": "<prompt template text>"},
|
||||
{"role": "user", "content": "<runtime user input>"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Endpoint:
|
||||
|
||||
- `POST {GIGACHAT_API_URL}/chat/completions`
|
||||
|
||||
Observed response handling:
|
||||
|
||||
- reads `choices[0].message.content`
|
||||
- if no choices: returns empty string
|
||||
|
||||
## Embeddings payload
|
||||
|
||||
Observed payload sent by `GigaChatClient.embed(...)`:
|
||||
|
||||
```json
|
||||
{
|
||||
"model": "Embeddings",
|
||||
"input": [
|
||||
"<text1>",
|
||||
"<text2>"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Endpoint:
|
||||
|
||||
- `POST {GIGACHAT_API_URL}/embeddings`
|
||||
|
||||
Observed response handling:
|
||||
|
||||
- expects `data` list
|
||||
- maps each `item.embedding` to `list[float]`
|
||||
|
||||
## Parameters
|
||||
|
||||
### Explicitly implemented
|
||||
|
||||
- `model`
|
||||
- `messages`
|
||||
- `input`
|
||||
- HTTP timeout:
|
||||
- completions: `90s`
|
||||
- embeddings: `90s`
|
||||
- auth: `30s`
|
||||
- TLS verification flag:
|
||||
- `verify=settings.ssl_verify`
|
||||
|
||||
### Not implemented in payload
|
||||
|
||||
- `temperature`
|
||||
- `top_p`
|
||||
- `max_tokens`
|
||||
- `response_format`
|
||||
- tools/function calling
|
||||
- streaming
|
||||
- seed
|
||||
- stop sequences
|
||||
|
||||
`ASSUMPTION:` the service uses provider defaults for sampling and output length because these fields are not sent in the request payload.
|
||||
|
||||
## Context and budget limits
|
||||
|
||||
There is no centralized token budget manager in the current code.
|
||||
|
||||
Observed practical limits instead:
|
||||
|
||||
- prompt file text is loaded as-is from disk
|
||||
- user input is passed as-is
|
||||
- RAG context shaping happens outside the LLM client
|
||||
- docs indexing summary truncation:
|
||||
- docs module catalog summary: `4000` chars
|
||||
- docs policy text: `4000` chars
|
||||
- project QA source bundle caps:
|
||||
- top `12` rag items
|
||||
- top `10` file candidates
|
||||
- logging truncation only:
|
||||
- LLM input/output logs capped at `1500` chars for logs
|
||||
|
||||
`ASSUMPTION:` there is no explicit max-context enforcement before chat completion requests. The current system relies on upstream graph logic to keep inputs small enough.
|
||||
|
||||
## Retry, backoff, timeout
|
||||
|
||||
### Timeouts
|
||||
|
||||
- auth: `30s`
|
||||
- chat completion: `90s`
|
||||
- embeddings: `90s`
|
||||
|
||||
### Retry
|
||||
|
||||
- Generic async retry wrapper exists in `app/modules/shared/retry_executor.py`
|
||||
- It retries only:
|
||||
- `TimeoutError`
|
||||
- `ConnectionError`
|
||||
- `OSError`
|
||||
- Retry constants:
|
||||
- `MAX_RETRIES = 5`
|
||||
- backoff: `0.1 * attempt` seconds
|
||||
|
||||
### Important current limitation
|
||||
|
||||
- `GigaChatClient` raises `GigaChatError` on HTTP and request failures.
|
||||
- `RetryExecutor` does not catch `GigaChatError`.
|
||||
- Result: LLM and embeddings calls are effectively not retried by this generic retry helper unless errors are converted upstream.
|
||||
|
||||
## Prompt formation
|
||||
|
||||
Prompt loading is handled by `PromptLoader`:
|
||||
|
||||
- base dir: `app/modules/agent/prompts`
|
||||
- override: `AGENT_PROMPTS_DIR`
|
||||
- file naming convention: `<prompt_name>.txt`
|
||||
|
||||
Prompt composition model today:
|
||||
|
||||
- system prompt:
|
||||
- full contents of selected prompt file
|
||||
- user prompt:
|
||||
- raw runtime input string passed by the caller
|
||||
- no separate developer prompt layer in the application payload
|
||||
|
||||
If a prompt file is missing:
|
||||
|
||||
- fallback system prompt: `You are a helpful assistant.`
|
||||
|
||||
## Prompt templates present
|
||||
|
||||
- `router_intent`
|
||||
- `general_answer`
|
||||
- `project_answer`
|
||||
- `docs_detect`
|
||||
- `docs_strategy`
|
||||
- `docs_plan_sections`
|
||||
- `docs_generation`
|
||||
- `docs_self_check`
|
||||
- `docs_execution_summary`
|
||||
- `project_edits_plan`
|
||||
- `project_edits_hunks`
|
||||
- `project_edits_self_check`
|
||||
|
||||
## Key LLM call entrypoints
|
||||
|
||||
### Composition roots
|
||||
|
||||
- `app/modules/agent/module.py`
|
||||
- builds `GigaChatSettings`
|
||||
- builds `GigaChatTokenProvider`
|
||||
- builds `GigaChatClient`
|
||||
- builds `PromptLoader`
|
||||
- builds `AgentLlmService`
|
||||
- `app/modules/rag_session/module.py`
|
||||
- builds the same provider stack for embeddings used by RAG
|
||||
|
||||
### Main abstraction
|
||||
|
||||
- `AgentLlmService.generate(prompt_name, user_input, log_context=None)`
|
||||
|
||||
### Current generate callsites
|
||||
|
||||
- `app/modules/agent/engine/router/intent_classifier.py`
|
||||
- `router_intent`
|
||||
- `app/modules/agent/engine/graphs/base_graph.py`
|
||||
- `general_answer`
|
||||
- `app/modules/agent/engine/graphs/project_qa_graph.py`
|
||||
- `project_answer`
|
||||
- `app/modules/agent/engine/graphs/docs_graph_logic.py`
|
||||
- `docs_detect`
|
||||
- `docs_strategy`
|
||||
- `docs_plan_sections`
|
||||
- `docs_generation`
|
||||
- `docs_self_check`
|
||||
- `docs_execution_summary`-like usage via summary step
|
||||
- `app/modules/agent/engine/graphs/project_edits_logic.py`
|
||||
- `project_edits_plan`
|
||||
- `project_edits_self_check`
|
||||
- `project_edits_hunks`
|
||||
|
||||
## Logging and observability
|
||||
|
||||
`AgentLlmService` logs:
|
||||
|
||||
- input:
|
||||
- `graph llm input: context=... prompt=... user_input=...`
|
||||
- output:
|
||||
- `graph llm output: context=... prompt=... output=...`
|
||||
|
||||
Log truncation:
|
||||
|
||||
- 1500 chars
|
||||
|
||||
RAG retrieval logs separately in `RagService`, but without embedding vectors.
|
||||
|
||||
## Integration with retrieval
|
||||
|
||||
There are two distinct GigaChat usages:
|
||||
|
||||
1. Chat/completion path for agent reasoning and generation
|
||||
2. Embedding path for RAG indexing and retrieval
|
||||
|
||||
The embedding adapter is `GigaChatEmbedder`, used by:
|
||||
|
||||
- `app/modules/rag/services/rag_service.py`
|
||||
|
||||
## Notable limitations
|
||||
|
||||
- Single provider coupling: chat and embeddings both depend on GigaChat-specific endpoints.
|
||||
- No model routing by scenario.
|
||||
- No tool/function calling.
|
||||
- No centralized prompt token budgeting.
|
||||
- No explicit retry for `GigaChatError`.
|
||||
- No streaming completions.
|
||||
- No structured response mode beyond prompt conventions and downstream parsing.
|
||||
@@ -1,13 +0,0 @@
|
||||
| column | used_by | safe_to_drop | notes |
|
||||
| --- | --- | --- | --- |
|
||||
| `layer` | `USED_BY_CODE_V2`, `USED_BY_DOCS_INDEXING` | no | Core selector for C0-C3 and D1-D4 queries. |
|
||||
| `title` | `USED_BY_CODE_V2`, `USED_BY_DOCS_INDEXING` | no | Used in lexical ranking and prompt evidence labels. |
|
||||
| `metadata_json` | `USED_BY_CODE_V2`, `USED_BY_DOCS_INDEXING` | no | C2/C0 graph lookups and docs metadata depend on it. |
|
||||
| `span_start`, `span_end` | `USED_BY_CODE_V2` | no | Needed for symbol-to-chunk resolution and locations. |
|
||||
| `symbol_id`, `qname`, `kind`, `lang` | `USED_BY_CODE_V2` | no | Used by code indexing, ranking, trace building, and diagnostics. |
|
||||
| `repo_id`, `commit_sha` | `USED_BY_CODE_V2`, `USED_BY_DOCS_INDEXING` | no | Used by indexing/cache and retained for provenance. |
|
||||
| `entrypoint_type`, `framework` | `USED_BY_CODE_V2` | no | Used by C3 filtering and entrypoint diagnostics. |
|
||||
| `doc_kind`, `module_id`, `section_path` | `USED_BY_DOCS_INDEXING` | no | Still written by docs indexing and covered by docs tests. |
|
||||
| `artifact_type`, `section`, `doc_version`, `owner`, `system_component`, `last_modified`, `staleness_score` | `USED_BY_DOCS_INDEXING` | no | File metadata still flows through indexing/cache; left intact for now. |
|
||||
| `rag_doc_id` | `UNUSED` | yes | Written into `rag_chunks` only; no reads in runtime/indexing code. |
|
||||
| `links_json` | `UNUSED` | yes | Stored in `rag_chunks` only; reads exist for `rag_chunk_cache`, not `rag_chunks`. |
|
||||
@@ -1,31 +0,0 @@
|
||||
flowchart TD
|
||||
A["HTTP: POST /internal/rag/retrieve"] --> B["RagModule.internal_router.retrieve(payload)"]
|
||||
B --> C["RagService.retrieve(rag_session_id, query)"]
|
||||
C --> D["RagQueryRouter.resolve_mode(query)"]
|
||||
D --> E["RagQueryRouter.layers_for_mode(mode)"]
|
||||
C --> F["GigaChatEmbedder.embed([query])"]
|
||||
F --> G["GigaChatClient.embed(payload)"]
|
||||
G --> H["POST /embeddings"]
|
||||
C --> I["RagRepository.retrieve(...)"]
|
||||
I --> J["RagQueryRepository.retrieve(...)"]
|
||||
J --> K["PostgreSQL rag_chunks + pgvector"]
|
||||
K --> L["ORDER BY lexical_rank, test_penalty, layer_rank, vector distance"]
|
||||
L --> M["rows: path/content/layer/title/metadata/span/distance"]
|
||||
M --> N["normalize to {source, content, layer, title, metadata, score}"]
|
||||
N --> O["response: {items: [...]}"]
|
||||
|
||||
C --> P["embedding error?"]
|
||||
P -->|yes| Q["RagRepository.fallback_chunks(...)"]
|
||||
Q --> R["latest rows by id DESC"]
|
||||
R --> N
|
||||
|
||||
C --> S["no rows and mode != docs?"]
|
||||
S -->|yes| T["fallback to docs layers"]
|
||||
T --> I
|
||||
|
||||
U["GraphAgentRuntime for project/qa"] --> V["ProjectQaRetrievalGraphFactory._retrieve_context"]
|
||||
V --> C
|
||||
V --> W["ProjectQaSupport.build_source_bundle(...)"]
|
||||
W --> X["source_bundle"]
|
||||
X --> Y["context_analysis"]
|
||||
Y --> Z["answer_composition"]
|
||||
@@ -1,457 +0,0 @@
|
||||
# Retrieval Inventory
|
||||
|
||||
## Scope and method
|
||||
|
||||
This document describes the retrieval and indexing pipeline as implemented in code today. The inventory is based primarily on:
|
||||
|
||||
- `app/modules/rag/services/rag_service.py`
|
||||
- `app/modules/rag/persistence/*.py`
|
||||
- `app/modules/rag/indexing/code/**/*.py`
|
||||
- `app/modules/rag/indexing/docs/**/*.py`
|
||||
- `app/modules/rag_session/module.py`
|
||||
- `app/modules/agent/engine/graphs/project_qa_step_graphs.py`
|
||||
- `app/modules/agent/engine/orchestrator/*.py`
|
||||
|
||||
`ASSUMPTION:` the intended layer semantics are the ones implied by code and tests, not by future architecture plans. This matters because only `C0` through `C3` are materially implemented today; `C4+` exist only as enum constants.
|
||||
|
||||
## Current retrieval pipeline
|
||||
|
||||
1. Retrieval entrypoint is `POST /internal/rag/retrieve` in `app/modules/rag_session/module.py`.
|
||||
2. The endpoint calls `RagService.retrieve(rag_session_id, query)`.
|
||||
3. `RagQueryRouter` chooses `docs` or `code` mode from the raw query text.
|
||||
4. `RagService` computes a single embedding for the full query via `GigaChatEmbedder`.
|
||||
5. `RagQueryRepository.retrieve(...)` runs one SQL query against `rag_chunks` in PostgreSQL with `pgvector`.
|
||||
6. Ranking order is:
|
||||
- lexical rank
|
||||
- test-file penalty
|
||||
- layer rank
|
||||
- vector distance `embedding <=> query_embedding`
|
||||
7. Response items are normalized to `{source, content, layer, title, metadata, score}`.
|
||||
8. If embeddings fail, retrieval falls back to latest chunks from the same layers.
|
||||
9. If code retrieval returns nothing, service falls back to docs layers.
|
||||
|
||||
## Storage and indices
|
||||
|
||||
- Primary store: PostgreSQL from `DATABASE_URL`, configured in `app/modules/shared/db.py`.
|
||||
- Vector extension: `CREATE EXTENSION IF NOT EXISTS vector` in `app/modules/rag/persistence/schema_repository.py`.
|
||||
- Primary table: `rag_chunks`.
|
||||
- Cache tables:
|
||||
- `rag_blob_cache`
|
||||
- `rag_chunk_cache`
|
||||
- `rag_session_chunk_map`
|
||||
- SQL indexes currently created:
|
||||
- `(rag_session_id)`
|
||||
- `(rag_session_id, layer)`
|
||||
- `(rag_session_id, layer, path)`
|
||||
- `(qname)`
|
||||
- `(symbol_id)`
|
||||
- `(module_id)`
|
||||
- `(doc_kind)`
|
||||
- `(entrypoint_type, framework)`
|
||||
|
||||
`ASSUMPTION:` there is no explicit ANN index for the vector column in schema code. The code creates general SQL indexes, but no `ivfflat`/`hnsw` index is defined here.
|
||||
|
||||
## Layer: C0_SOURCE_CHUNKS
|
||||
|
||||
### Implementation
|
||||
|
||||
- Produced by `CodeIndexingPipeline.index_file(...)` in `app/modules/rag/indexing/code/pipeline.py`.
|
||||
- Chunking logic: `CodeTextChunker.chunk(...)` in `app/modules/rag/indexing/code/code_text/chunker.py`.
|
||||
- Document builder: `CodeTextDocumentBuilder.build(...)` in `app/modules/rag/indexing/code/code_text/document_builder.py`.
|
||||
- Persisted via `RagDocumentRepository.insert_documents(...)` into `rag_chunks`.
|
||||
|
||||
### Input contract
|
||||
|
||||
This is an indexing layer, not a direct public retriever. The observed upstream indexing input is a file dict with at least:
|
||||
|
||||
- required:
|
||||
- `path: str`
|
||||
- `content: str`
|
||||
- optional:
|
||||
- `commit_sha: str | None`
|
||||
- `content_hash: str`
|
||||
- metadata fields copied through by `RagService._document_metadata(...)`
|
||||
|
||||
For retrieval, the layer is queried only indirectly through:
|
||||
|
||||
- `rag_session_id: str`
|
||||
- `query: str`
|
||||
- inferred mode/layers from `RagQueryRouter`
|
||||
- fixed `limit=8`
|
||||
|
||||
### Output contract
|
||||
|
||||
Stored document shape:
|
||||
|
||||
- top-level:
|
||||
- `layer = "C0_SOURCE_CHUNKS"`
|
||||
- `lang = "python"`
|
||||
- `source.repo_id`
|
||||
- `source.commit_sha`
|
||||
- `source.path`
|
||||
- `title`
|
||||
- `text`
|
||||
- `span.start_line`
|
||||
- `span.end_line`
|
||||
- `embedding`
|
||||
- metadata:
|
||||
- `chunk_index`
|
||||
- `chunk_type`: `symbol_block` or `window`
|
||||
- `module_or_unit`
|
||||
- `artifact_type = "CODE"`
|
||||
- plus file-level metadata injected by `RagService`
|
||||
|
||||
Returned retrieval item shape:
|
||||
|
||||
- `source`
|
||||
- `content`
|
||||
- `layer`
|
||||
- `title`
|
||||
- `metadata`
|
||||
- `score`
|
||||
|
||||
No `line_start` / `line_end` are returned to the caller directly; they remain in DB columns `span_start` / `span_end` and are only used in logs.
|
||||
|
||||
### Defaults & limits
|
||||
|
||||
- AST chunking prefers one chunk per top-level class/function/async function.
|
||||
- Fallback window chunking:
|
||||
- `size = 80` lines
|
||||
- `overlap = 15` lines
|
||||
- Global retrieval limit from `RagService.retrieve(...)`: `8`
|
||||
- Embedding batch size from env:
|
||||
- `RAG_EMBED_BATCH_SIZE`
|
||||
- default `16`
|
||||
|
||||
### Known issues
|
||||
|
||||
- Nested methods/functions are not emitted as C0 chunks unless represented inside a selected top-level block.
|
||||
- Returned API payload omits line spans even though storage has them.
|
||||
- No direct filter by path, namespace, symbol, or `top_k` is exposed through the current endpoint.
|
||||
|
||||
## Layer: C1_SYMBOL_CATALOG
|
||||
|
||||
### Implementation
|
||||
|
||||
- Symbol extraction: `SymbolExtractor.extract(...)` in `app/modules/rag/indexing/code/symbols/extractor.py`.
|
||||
- AST parsing: `PythonAstParser.parse_module(...)`.
|
||||
- Document builder: `SymbolDocumentBuilder.build(...)`.
|
||||
- Retrieval reads rows from `rag_chunks`; there is no dedicated symbol table.
|
||||
|
||||
### Input contract
|
||||
|
||||
Indexing input is the same per-file payload as C0.
|
||||
|
||||
Observed symbol extraction source:
|
||||
|
||||
- Python AST only
|
||||
- supported symbol kinds:
|
||||
- `class`
|
||||
- `function`
|
||||
- `method`
|
||||
- `const` for top-level imports/import aliases
|
||||
|
||||
Retrieval input is still the generic text query endpoint. Query terms are enriched by `extract_query_terms(...)`:
|
||||
|
||||
- extracts identifier-like tokens from query text
|
||||
- normalizes camelCase/PascalCase to snake_case
|
||||
- adds special intent terms for management/control-related queries
|
||||
- max observed query terms: `6`
|
||||
|
||||
### Output contract
|
||||
|
||||
Stored document shape:
|
||||
|
||||
- top-level:
|
||||
- `layer = "C1_SYMBOL_CATALOG"`
|
||||
- `title = qname`
|
||||
- `text = "<kind> <qname>\n<signature>\n<docstring?>"`
|
||||
- `span.start_line`
|
||||
- `span.end_line`
|
||||
- metadata:
|
||||
- `symbol_id`
|
||||
- `qname`
|
||||
- `kind`
|
||||
- `signature`
|
||||
- `decorators_or_annotations`
|
||||
- `docstring_or_javadoc`
|
||||
- `parent_symbol_id`
|
||||
- `package_or_module`
|
||||
- `is_entry_candidate`
|
||||
- `lang_payload`
|
||||
- `artifact_type = "CODE"`
|
||||
|
||||
Observed `lang_payload` variants:
|
||||
|
||||
- class:
|
||||
- `bases`
|
||||
- function/method:
|
||||
- `async`
|
||||
- import alias:
|
||||
- `imported_from`
|
||||
- `import_alias`
|
||||
|
||||
### Defaults & limits
|
||||
|
||||
- Only Python source files are indexed into C-layers.
|
||||
- Import and import-from declarations are materialized as `const` symbols only at module top level.
|
||||
- Retrieval ranking gives C1 priority rank `1`, after C3 and before C2/C0.
|
||||
|
||||
### Known issues
|
||||
|
||||
- No explicit visibility/public-private model.
|
||||
- `parent_symbol_id` currently stores the parent qname string from the stack, not the parent symbol hash. This is an observed implementation detail.
|
||||
- Cross-file symbol resolution is not implemented; `dst_symbol_id` in edges resolves only against symbols extracted from the same file.
|
||||
|
||||
## Layer: C2_DEPENDENCY_GRAPH
|
||||
|
||||
### Implementation
|
||||
|
||||
- Edge extraction: `EdgeExtractor.extract(...)` in `app/modules/rag/indexing/code/edges/extractor.py`.
|
||||
- Document builder: `EdgeDocumentBuilder.build(...)`.
|
||||
- Built during `CodeIndexingPipeline.index_file(...)`.
|
||||
|
||||
### Input contract
|
||||
|
||||
Indexing input is the same per-file source payload as C0/C1.
|
||||
|
||||
Graph construction method:
|
||||
|
||||
- static analysis only
|
||||
- Python AST walk only
|
||||
- no runtime tracing
|
||||
- no tree-sitter
|
||||
|
||||
Observed edge types:
|
||||
|
||||
- `calls`
|
||||
- `imports`
|
||||
- `inherits`
|
||||
|
||||
### Output contract
|
||||
|
||||
Stored document shape:
|
||||
|
||||
- top-level:
|
||||
- `layer = "C2_DEPENDENCY_GRAPH"`
|
||||
- `title = "<src_qname>:<edge_type>"`
|
||||
- `text = "<src_qname> <edge_type> <dst>"`
|
||||
- `span.start_line`
|
||||
- `span.end_line`
|
||||
- `links` contains one evidence link of type `EDGE`
|
||||
- metadata:
|
||||
- `edge_id`
|
||||
- `edge_type`
|
||||
- `src_symbol_id`
|
||||
- `src_qname`
|
||||
- `dst_symbol_id`
|
||||
- `dst_ref`
|
||||
- `resolution`: `resolved` or `partial`
|
||||
- `lang_payload`
|
||||
- `artifact_type = "CODE"`
|
||||
|
||||
Observed `lang_payload` usage:
|
||||
|
||||
- for calls: may include `callsite_kind = "function_call"`
|
||||
|
||||
### Defaults & limits
|
||||
|
||||
- Edge extraction is per-file only.
|
||||
- `imports` edges are emitted only while visiting a class/function scope; top-level imports do not become C2 edges.
|
||||
- Layer rank in retrieval SQL: `2`
|
||||
|
||||
### Known issues
|
||||
|
||||
- There is no traversal API, graph repository, or query language over C2. Retrieval only treats edges as text/vector rows in `rag_chunks`.
|
||||
- Destination resolution is local to the file-level qname map.
|
||||
- Top-level module import relationships are incompletely represented because `visit_Import` / `visit_ImportFrom` skip when there is no current scope.
|
||||
|
||||
## Layer: C3_ENTRYPOINTS
|
||||
|
||||
### Implementation
|
||||
|
||||
- Detection registry: `EntrypointDetectorRegistry.detect_all(...)`.
|
||||
- Detectors:
|
||||
- `FastApiEntrypointDetector`
|
||||
- `FlaskEntrypointDetector`
|
||||
- `TyperClickEntrypointDetector`
|
||||
- Document builder: `EntrypointDocumentBuilder.build(...)`.
|
||||
|
||||
### Input contract
|
||||
|
||||
Indexing input is the same per-file source payload as other C-layers.
|
||||
|
||||
Detected entrypoint families today:
|
||||
|
||||
- HTTP:
|
||||
- FastAPI decorators such as `.get`, `.post`, `.put`, `.patch`, `.delete`, `.route`
|
||||
- Flask `.route`
|
||||
- CLI:
|
||||
- Typer/Click `.command`
|
||||
- Typer/Click `.callback`
|
||||
|
||||
Not detected:
|
||||
|
||||
- Django routes
|
||||
- Celery tasks
|
||||
- RQ jobs
|
||||
- cron jobs / scheduler entries
|
||||
|
||||
### Output contract
|
||||
|
||||
Stored document shape:
|
||||
|
||||
- top-level:
|
||||
- `layer = "C3_ENTRYPOINTS"`
|
||||
- `title = route_or_command`
|
||||
- `text = "<framework> <entry_type> <route_or_command>"`
|
||||
- `span.start_line`
|
||||
- `span.end_line`
|
||||
- `links` contains one evidence link of type `CODE_SPAN`
|
||||
- metadata:
|
||||
- `entry_id`
|
||||
- `entry_type`: observed `http` or `cli`
|
||||
- `framework`: observed `fastapi`, `flask`, `typer`, `click`
|
||||
- `route_or_command`
|
||||
- `handler_symbol_id`
|
||||
- `lang_payload`
|
||||
- `artifact_type = "CODE"`
|
||||
|
||||
FastAPI-specific observed payload:
|
||||
|
||||
- `lang_payload.methods = [HTTP_METHOD]` for `.get/.post/...`
|
||||
|
||||
### Defaults & limits
|
||||
|
||||
- Retrieval layer rank: `0` highest among code layers.
|
||||
- Entrypoint mapping is handler-symbol centric:
|
||||
- decorator match -> symbol -> `handler_symbol_id`
|
||||
- physical location comes from symbol span
|
||||
|
||||
### Known issues
|
||||
|
||||
- Route parsing is string-based from decorator text, not semantic AST argument parsing.
|
||||
- No dedicated entrypoint tags beyond `entry_type`, `framework`, and raw decorator-derived payload.
|
||||
- Background jobs and non-decorator entrypoints are not indexed.
|
||||
|
||||
## Dependency graph / trace current state
|
||||
|
||||
### Exists or stub?
|
||||
|
||||
- C2 exists and is populated.
|
||||
- It is not a stub.
|
||||
- It is also not a full-project dependency graph service; it is a set of per-edge documents stored in `rag_chunks`.
|
||||
|
||||
### How the graph is built
|
||||
|
||||
- static Python AST analysis
|
||||
- no runtime instrumentation
|
||||
- no import graph resolver across modules
|
||||
- no tree-sitter
|
||||
|
||||
### Edge types in data
|
||||
|
||||
- `calls`
|
||||
- `imports`
|
||||
- `inherits`
|
||||
|
||||
### Traversal API
|
||||
|
||||
- No traversal API was found in `app/modules/rag/*` or `app/modules/agent/*`.
|
||||
- No method accepts graph traversal parameters such as depth, start node, edge filters, or BFS/DFS strategy.
|
||||
- Current access path is only retrieval over indexed edge documents.
|
||||
|
||||
## Entrypoints current state
|
||||
|
||||
### Implemented extraction
|
||||
|
||||
- HTTP routes:
|
||||
- FastAPI
|
||||
- Flask
|
||||
- CLI:
|
||||
- Typer
|
||||
- Click
|
||||
|
||||
### Mapping model
|
||||
|
||||
- `entrypoint -> handler_symbol_id -> symbol span/path`
|
||||
- The entrypoint record itself stores:
|
||||
- framework
|
||||
- entry type
|
||||
- raw route/command string
|
||||
- handler symbol id
|
||||
|
||||
### Tags/types
|
||||
|
||||
- `entry_type` is the main normalized tag.
|
||||
- Observed values: `http`, `cli`.
|
||||
- `framework` is the second discriminator.
|
||||
- There are no richer endpoint taxonomies such as `job`, `worker`, `webhook`, `scheduler`.
|
||||
|
||||
## Defaults and operational limits
|
||||
|
||||
- Query mode default: `docs`
|
||||
- Code mode is enabled by keyword heuristics in `RagQueryRouter`
|
||||
- Retrieval hard limit: `8`
|
||||
- Fallback limit: `8`
|
||||
- Query term extraction limit: `6`
|
||||
- Ranked source bundle for project QA:
|
||||
- top `12` RAG items
|
||||
- top `10` file candidates
|
||||
- No exposed `namespace`, `path_prefixes`, `top_k`, `max_chars`, `max_chunks`, `max_depth` in the public/internal retrieval endpoint
|
||||
|
||||
`ASSUMPTION:` the absence of these controls in endpoint and service signatures means they are not part of the current supported contract, even though `RagQueryRepository.retrieve(...)` has an internal `path_prefixes` parameter.
|
||||
|
||||
## Known cross-cutting issues
|
||||
|
||||
- Retrieval contract is effectively text-only at API level; structured retrieval exists only as internal SQL parameters.
|
||||
- Response payload drops explicit line spans even though spans are stored.
|
||||
- Vector retrieval is coupled to a single provider-specific embedder.
|
||||
- Docs mode is the default, so code retrieval depends on heuristic query phrasing unless the project/qa graph prepends `по коду`.
|
||||
- There is no separate retrieval contract per layer exposed over API; all layer selection is implicit.
|
||||
|
||||
## Where to plug ExplainPack pipeline
|
||||
|
||||
### Option 1: replace or extend `project_qa/context_analysis`
|
||||
|
||||
- Code location:
|
||||
- `app/modules/agent/engine/graphs/project_qa_step_graphs.py`
|
||||
- Why:
|
||||
- retrieval is already complete at this step
|
||||
- input bundle already contains ranked `rag_items` and `file_candidates`
|
||||
- output is already a structured `analysis_brief`
|
||||
- Risk:
|
||||
- low
|
||||
- minimal invasion if ExplainPack consumes `source_bundle` and emits the same `analysis_brief` shape
|
||||
|
||||
### Option 2: insert a new orchestrator step between `context_retrieval` and `context_analysis`
|
||||
|
||||
- Code location:
|
||||
- `app/modules/agent/engine/orchestrator/template_registry.py`
|
||||
- `app/modules/agent/engine/orchestrator/step_registry.py`
|
||||
- Why:
|
||||
- preserves current retrieval behavior
|
||||
- makes ExplainPack an explicit pipeline stage with its own artifact
|
||||
- cleanest for observability and future A/B migration
|
||||
- Risk:
|
||||
- low to medium
|
||||
- requires one new artifact contract and one extra orchestration step, but no change to retrieval storage
|
||||
|
||||
### Option 3: introduce ExplainPack inside `ExplainActions.extract_logic`
|
||||
|
||||
- Code location:
|
||||
- `app/modules/agent/engine/orchestrator/actions/explain_actions.py`
|
||||
- Why:
|
||||
- useful if ExplainPack is meant only for explain-style scenarios
|
||||
- keeps general project QA untouched
|
||||
- Risk:
|
||||
- medium
|
||||
- narrower integration point; may create duplicate reasoning logic separate from project QA analysis path
|
||||
|
||||
## Bottom line
|
||||
|
||||
- C0-C3 are implemented and persisted in one physical store: `rag_chunks`.
|
||||
- Retrieval is a hybrid SQL ranking over lexical heuristics plus pgvector distance.
|
||||
- C2 exists, but only as retrievable edge documents, not as a traversable graph subsystem.
|
||||
- C3 covers FastAPI/Flask/Typer/Click only.
|
||||
- The least invasive ExplainPack integration point is after retrieval and before answer composition, preferably as a new explicit orchestrator artifact or as a replacement for `context_analysis`.
|
||||
@@ -0,0 +1,168 @@
|
||||
---
|
||||
id: api-rag-session-changes
|
||||
title: Применение изменений к RAG-сессии
|
||||
doc_type: api_method
|
||||
domain: rag
|
||||
status: draft
|
||||
owner: system-analyst
|
||||
source_of_truth: code
|
||||
related_docs:
|
||||
- arch-rag-package
|
||||
- logic-rag-indexing
|
||||
- entity-rag-session
|
||||
- entity-rag-index-job
|
||||
related_code:
|
||||
- src/app/modules/rag/module.py
|
||||
- src/app/schemas/rag_sessions.py
|
||||
- src/app/schemas/indexing.py
|
||||
entities:
|
||||
- RagSession
|
||||
- IndexJob
|
||||
tags:
|
||||
- rag
|
||||
- api
|
||||
- changes
|
||||
- incremental-indexing
|
||||
---
|
||||
# Применение изменений к RAG-сессии
|
||||
|
||||
## Summary
|
||||
- Purpose: поставить incremental indexing для уже существующей `RagSession`.
|
||||
- Actor: внешний клиент модуля RAG.
|
||||
- Trigger: частичное обновление индекса после изменения файлов.
|
||||
- Endpoint: `POST /api/rag/sessions/{rag_session_id}/changes`
|
||||
- Main entities: `RagSession`, `IndexJob`.
|
||||
- Main logic: проверка существования сессии, создание change job, асинхронная обработка `upsert`/`delete`.
|
||||
- Main errors: `not_found` для отсутствующей сессии, `422` для некорректного payload.
|
||||
- Source of truth: `src/app/modules/rag/module.py`, `src/app/schemas/rag_sessions.py`.
|
||||
|
||||
## Назначение
|
||||
|
||||
Метод позволяет обновить индекс без полной переиндексации проекта. Он принимает только изменённые файлы и операции удаления.
|
||||
|
||||
## Контекст
|
||||
|
||||
Endpoint относится к новому API с явной работой через `rag_session_id`. В отличие от legacy `/api/index/changes`, он не создаёт сессию молча и требует, чтобы она уже существовала.
|
||||
|
||||
## Технический use case
|
||||
|
||||
### Основной сценарий
|
||||
|
||||
1. Клиент передаёт `rag_session_id` в path и список `changed_files` в body.
|
||||
2. Endpoint проверяет наличие сессии через `RagSessionStore.get`.
|
||||
3. При успехе `IndexingOrchestrator.enqueue_changes` создаёт новую job и запускает фоновое применение изменений.
|
||||
4. API возвращает `index_job_id` и стартовый статус.
|
||||
|
||||
### Альтернативные ветки
|
||||
|
||||
- Если `rag_session_id` не найдена, endpoint бросает `AppError("not_found", ...)`.
|
||||
- Для `op=delete` в последующей логике происходит удаление документов по пути без повторной генерации embeddings.
|
||||
|
||||
## Функциональные требования
|
||||
|
||||
### Request validation
|
||||
- Path parameter `rag_session_id` обязателен.
|
||||
- `changed_files` обязателен и состоит из элементов `ChangedFile`.
|
||||
- Для каждого элемента обязательны `op` и `path`.
|
||||
- `op` допускает только `upsert` или `delete`.
|
||||
|
||||
### Processing rules
|
||||
- Сессия должна существовать до постановки change job.
|
||||
- Каждый вызов создаёт новый `IndexJob`.
|
||||
- Фактическое применение изменений выполняется асинхронно.
|
||||
|
||||
### State changes
|
||||
- В `rag_index_jobs` появляется новая задача.
|
||||
- Сам индекс меняется позже, внутри `RagService.index_changes`.
|
||||
|
||||
### Side effects
|
||||
- Публикация job events.
|
||||
- Удаление документов по `delete_paths` и upsert новых документов в фоне.
|
||||
|
||||
## Contract
|
||||
|
||||
### Endpoint
|
||||
- Method: `POST`
|
||||
- Path: `/api/rag/sessions/{rag_session_id}/changes`
|
||||
- Auth: определяется внешним слоем приложения.
|
||||
- Idempotent: нет, повторный вызов создаёт новую job.
|
||||
- Timeout: короткий, endpoint не дожидается завершения индексации.
|
||||
- Retry: только если клиент готов к созданию дополнительной job.
|
||||
|
||||
### Request
|
||||
| Field | Type | Required | Constraints | Description |
|
||||
|------|------|----------|-------------|-------------|
|
||||
| `rag_session_id` | `string` | yes | path param, non-empty | идентификатор существующей RAG-сессии |
|
||||
| `changed_files` | `array<ChangedFile>` | yes | схема каждого элемента обязательна | изменения файлов |
|
||||
| `changed_files[].op` | `enum` | yes | `upsert` or `delete` | тип операции |
|
||||
| `changed_files[].path` | `string` | yes | `min_length=1` | путь файла |
|
||||
| `changed_files[].content` | `string \| null` | no | нужен для `upsert` | содержимое файла |
|
||||
| `changed_files[].content_hash` | `string \| null` | no | повышает cache reuse | hash содержимого |
|
||||
|
||||
### Response
|
||||
| Field | Type | Description |
|
||||
|------|------|-------------|
|
||||
| `index_job_id` | `string` | идентификатор фоновой задачи |
|
||||
| `status` | `string` | стартовый статус задачи |
|
||||
|
||||
### External contract refs
|
||||
- OpenAPI: формируется FastAPI по `response_model=IndexJobQueuedResponse`.
|
||||
- Schema: `RagSessionChangesRequest`, `ChangedFile`, `IndexJobQueuedResponse`.
|
||||
- DTO / serializer: `src/app/schemas/rag_sessions.py`, `src/app/schemas/indexing.py`.
|
||||
- Additional refs: `logic-rag-indexing`.
|
||||
|
||||
## Errors
|
||||
|
||||
| error_id | http_code | when | client_behavior | retry |
|
||||
|----------|-----------|------|-----------------|-------|
|
||||
| `not_found` | `404` | `rag_session_id` отсутствует | создать новую сессию или исправить id | no |
|
||||
| `validation_error` | `422` | нарушена схема request | исправить payload | no |
|
||||
|
||||
## Нефункциональные требования
|
||||
|
||||
### Security
|
||||
- Метод доверяет внешнему слою авторизации.
|
||||
|
||||
### Observability
|
||||
- Logs: прямое логирование endpoint отсутствует.
|
||||
- Metrics: нет отдельной метрики на уровне метода.
|
||||
- Traces: отсутствуют.
|
||||
- Audit: каждая операция материализуется в `IndexJob`.
|
||||
|
||||
### Reliability
|
||||
- Проверка существования сессии защищает от случайной записи в неинициализированный scope.
|
||||
- Ошибки индексации доступны через job status и SSE events.
|
||||
|
||||
### Performance
|
||||
- Быстрый ответ за счёт фонового выполнения.
|
||||
|
||||
## Связанные блоки логики
|
||||
- `logic-rag-indexing`
|
||||
|
||||
## Связанные сущности
|
||||
- `RagSession`
|
||||
- `IndexJob`
|
||||
|
||||
## Связанный код
|
||||
|
||||
### Files
|
||||
- `src/app/modules/rag/module.py`
|
||||
- `src/app/schemas/rag_sessions.py`
|
||||
- `src/app/schemas/indexing.py`
|
||||
|
||||
### Symbols
|
||||
- `RagModule.public_router.rag_session_changes`
|
||||
- `RagSessionStore.get`
|
||||
- `IndexingOrchestrator.enqueue_changes`
|
||||
|
||||
## Связанные документы
|
||||
- `arch-rag-package`
|
||||
- `logic-rag-indexing`
|
||||
- `entity-rag-session`
|
||||
- `entity-rag-index-job`
|
||||
|
||||
## История изменений
|
||||
|
||||
| Date | Source | Changes |
|
||||
|------|--------|---------|
|
||||
| 2026-03-13 | code | Задокументирован публичный endpoint incremental indexing для существующей сессии. |
|
||||
@@ -0,0 +1,166 @@
|
||||
---
|
||||
id: api-rag-session-create
|
||||
title: Создание RAG-сессии и запуск snapshot-индексации
|
||||
doc_type: api_method
|
||||
domain: rag
|
||||
status: draft
|
||||
owner: system-analyst
|
||||
source_of_truth: code
|
||||
related_docs:
|
||||
- arch-rag-package
|
||||
- logic-rag-indexing
|
||||
- entity-rag-session
|
||||
- entity-rag-index-job
|
||||
related_code:
|
||||
- src/app/modules/rag/module.py
|
||||
- src/app/schemas/rag_sessions.py
|
||||
- src/app/schemas/indexing.py
|
||||
entities:
|
||||
- RagSession
|
||||
- IndexJob
|
||||
tags:
|
||||
- rag
|
||||
- api
|
||||
- session
|
||||
- snapshot
|
||||
---
|
||||
# Создание RAG-сессии и запуск snapshot-индексации
|
||||
|
||||
## Summary
|
||||
- Purpose: создать новую `RagSession` и асинхронно поставить полную индексацию snapshot-файлов.
|
||||
- Actor: внешний клиент модуля RAG.
|
||||
- Trigger: первичная загрузка файлов проекта в индекс.
|
||||
- Endpoint: `POST /api/rag/sessions`
|
||||
- Main entities: `RagSession`, `IndexJob`.
|
||||
- Main logic: создание UUID-сессии, постановка snapshot job, возврат идентификаторов сессии и job.
|
||||
- Main errors: в коде endpoint нет собственной бизнес-валидации сверх pydantic; ошибки индексации проявляются позже в job status.
|
||||
- Source of truth: `src/app/modules/rag/module.py`, `src/app/schemas/rag_sessions.py`.
|
||||
|
||||
## Назначение
|
||||
|
||||
Метод открывает новую RAG-сессию и запускает первичную индексацию файлов. Он используется как основной публичный вход для нового API пакета `rag`.
|
||||
|
||||
## Контекст
|
||||
|
||||
В отличие от legacy `/api/index/snapshot`, этот endpoint всегда создаёт новый `rag_session_id`, что позволяет независимо хранить несколько снимков одного проекта.
|
||||
|
||||
## Технический use case
|
||||
|
||||
### Основной сценарий
|
||||
|
||||
1. Клиент передаёт `project_id` и массив `files`.
|
||||
2. `RagSessionStore.create` создаёт новую запись в `rag_sessions`.
|
||||
3. `IndexingOrchestrator.enqueue_snapshot` создаёт `IndexJob` и запускает фоновую обработку.
|
||||
4. API сразу возвращает `rag_session_id`, `index_job_id` и стартовый статус.
|
||||
|
||||
### Альтернативные ветки
|
||||
|
||||
- Если часть файлов не подлежит индексации, они будут отфильтрованы уже внутри indexing pipeline, а не на этапе ответа API.
|
||||
- Ошибки индексации не меняют синхронный ответ create endpoint, а отражаются в последующем статусе job.
|
||||
|
||||
## Функциональные требования
|
||||
|
||||
### Request validation
|
||||
- `project_id` обязателен и не может быть пустым.
|
||||
- `files` передаются списком объектов `FileSnapshot`.
|
||||
- Для каждого файла обязательны `path`, `content`, `content_hash`.
|
||||
|
||||
### Processing rules
|
||||
- На каждый вызов создаётся новая `RagSession`.
|
||||
- Snapshot job создаётся сразу после сохранения сессии.
|
||||
- Ответ не ждёт завершения индексации.
|
||||
|
||||
### State changes
|
||||
- В `rag_sessions` появляется новая запись.
|
||||
- В `rag_index_jobs` появляется новая запись в статусе `queued`.
|
||||
|
||||
### Side effects
|
||||
- Запуск фоновой `asyncio` task.
|
||||
- Последующая публикация progress events в EventBus.
|
||||
|
||||
## Contract
|
||||
|
||||
### Endpoint
|
||||
- Method: `POST`
|
||||
- Path: `/api/rag/sessions`
|
||||
- Auth: определяется внешним слоем приложения, внутри endpoint не задана.
|
||||
- Idempotent: нет.
|
||||
- Timeout: короткий, так как endpoint не ждёт индексацию.
|
||||
- Retry: допустим только на стороне клиента с пониманием, что будет создана новая сессия.
|
||||
|
||||
### Request
|
||||
| Field | Type | Required | Constraints | Description |
|
||||
|------|------|----------|-------------|-------------|
|
||||
| `project_id` | `string` | yes | `min_length=1` | идентификатор проекта |
|
||||
| `files` | `array<FileSnapshot>` | yes | может быть пустым, но схема обязана соблюдаться | snapshot файлов для первичной индексации |
|
||||
| `files[].path` | `string` | yes | `min_length=1` | путь файла |
|
||||
| `files[].content` | `string` | yes | без дополнительных ограничений | содержимое файла |
|
||||
| `files[].content_hash` | `string` | yes | `min_length=1` | hash содержимого для cache reuse |
|
||||
|
||||
### Response
|
||||
| Field | Type | Description |
|
||||
|------|------|-------------|
|
||||
| `rag_session_id` | `string` | идентификатор созданной сессии |
|
||||
| `index_job_id` | `string` | идентификатор фоновой задачи индексации |
|
||||
| `status` | `IndexJobStatus` | стартовый статус задачи, обычно `queued` |
|
||||
|
||||
### External contract refs
|
||||
- OpenAPI: формируется FastAPI по `response_model=RagSessionCreateResponse`.
|
||||
- Schema: `RagSessionCreateRequest`, `RagSessionCreateResponse`.
|
||||
- DTO / serializer: `src/app/schemas/rag_sessions.py`, `src/app/schemas/indexing.py`.
|
||||
- Additional refs: `logic-rag-indexing`.
|
||||
|
||||
## Errors
|
||||
|
||||
| error_id | http_code | when | client_behavior | retry |
|
||||
|----------|-----------|------|-----------------|-------|
|
||||
| `validation_error` | `422` | нарушена pydantic-схема request | исправить payload | no |
|
||||
|
||||
## Нефункциональные требования
|
||||
|
||||
### Security
|
||||
- Авторизация и аутентификация находятся вне этого метода.
|
||||
|
||||
### Observability
|
||||
- Logs: прямое логирование в endpoint отсутствует.
|
||||
- Metrics: отдельные API-метрики не выделены.
|
||||
- Traces: отсутствуют.
|
||||
- Audit: факт вызова материализуется через `RagSession` и `IndexJob`.
|
||||
|
||||
### Reliability
|
||||
- Даже при дальнейшей ошибке индексации клиент может получить статус через job endpoint.
|
||||
- Фоновая задача создаётся немедленно после ответа.
|
||||
|
||||
### Performance
|
||||
- Время ответа не зависит от размера snapshot, кроме времени сериализации request.
|
||||
|
||||
## Связанные блоки логики
|
||||
- `logic-rag-indexing`
|
||||
|
||||
## Связанные сущности
|
||||
- `RagSession`
|
||||
- `IndexJob`
|
||||
|
||||
## Связанный код
|
||||
|
||||
### Files
|
||||
- `src/app/modules/rag/module.py`
|
||||
- `src/app/schemas/rag_sessions.py`
|
||||
- `src/app/schemas/indexing.py`
|
||||
|
||||
### Symbols
|
||||
- `RagModule.public_router.create_rag_session`
|
||||
- `RagSessionStore.create`
|
||||
- `IndexingOrchestrator.enqueue_snapshot`
|
||||
|
||||
## Связанные документы
|
||||
- `arch-rag-package`
|
||||
- `logic-rag-indexing`
|
||||
- `entity-rag-session`
|
||||
- `entity-rag-index-job`
|
||||
|
||||
## История изменений
|
||||
|
||||
| Date | Source | Changes |
|
||||
|------|--------|---------|
|
||||
| 2026-03-13 | code | Задокументирован публичный endpoint создания RAG-сессии. |
|
||||
@@ -0,0 +1,166 @@
|
||||
---
|
||||
id: api-rag-session-job
|
||||
title: Получение статуса и событий задачи индексации
|
||||
doc_type: api_method
|
||||
domain: rag
|
||||
status: draft
|
||||
owner: system-analyst
|
||||
source_of_truth: code
|
||||
related_docs:
|
||||
- arch-rag-package
|
||||
- entity-rag-session
|
||||
- entity-rag-index-job
|
||||
related_code:
|
||||
- src/app/modules/rag/module.py
|
||||
- src/app/modules/rag/job_store.py
|
||||
- src/app/schemas/rag_sessions.py
|
||||
entities:
|
||||
- RagSession
|
||||
- IndexJob
|
||||
tags:
|
||||
- rag
|
||||
- api
|
||||
- job-status
|
||||
- sse
|
||||
---
|
||||
# Получение статуса и событий задачи индексации
|
||||
|
||||
## Summary
|
||||
- Purpose: отдать текущее состояние job и поток событий её выполнения в рамках конкретной `RagSession`.
|
||||
- Actor: внешний клиент модуля RAG.
|
||||
- Trigger: polling или live-monitoring после запуска snapshot/change indexing.
|
||||
- Endpoint: `GET /api/rag/sessions/{rag_session_id}/jobs/{index_job_id}` и `GET /api/rag/sessions/{rag_session_id}/jobs/{index_job_id}/events`
|
||||
- Main entities: `RagSession`, `IndexJob`.
|
||||
- Main logic: чтение job по id, проверка принадлежности сессии, возврат status payload или SSE stream.
|
||||
- Main errors: `not_found` при отсутствии job или несовпадении `rag_session_id`.
|
||||
- Source of truth: `src/app/modules/rag/module.py`, `src/app/modules/rag/job_store.py`.
|
||||
|
||||
## Назначение
|
||||
|
||||
Документ описывает два связанных метода наблюдения: синхронный status endpoint и потоковый SSE endpoint. Оба работают поверх одной сущности `IndexJob`.
|
||||
|
||||
## Контекст
|
||||
|
||||
Create и changes endpoints возвращают только стартовый статус задачи, поэтому клиенту нужны отдельные методы для отслеживания выполнения. SSE-поток даёт live progress, а status endpoint нужен для простого polling.
|
||||
|
||||
## Технический use case
|
||||
|
||||
### Основной сценарий
|
||||
|
||||
1. Клиент вызывает status endpoint или открывает SSE stream по `index_job_id`.
|
||||
2. Endpoint читает job из `IndexJobStore`.
|
||||
3. Если job отсутствует или принадлежит другой `rag_session_id`, возвращается `not_found`.
|
||||
4. Status endpoint отдаёт снимок counters и error payload.
|
||||
5. SSE endpoint подписывается на `EventBus` c `replay=True` и транслирует `index_status`, `index_progress`, `terminal`.
|
||||
|
||||
### Альтернативные ветки
|
||||
|
||||
- При отсутствии новых событий SSE endpoint каждые 10 секунд отправляет `: keepalive`.
|
||||
- После события `terminal` поток завершается и отписывается от EventBus.
|
||||
|
||||
## Функциональные требования
|
||||
|
||||
### Request validation
|
||||
- `rag_session_id` и `index_job_id` обязательны как path parameters.
|
||||
- Job должна существовать и принадлежать переданной сессии.
|
||||
|
||||
### Processing rules
|
||||
- Status endpoint не подписывается на события и читает только текущее состояние job.
|
||||
- SSE endpoint использует `replay=True`, чтобы клиент получил уже опубликованные события.
|
||||
- Оба метода защищают от доступа к job другой сессии.
|
||||
|
||||
### State changes
|
||||
- Методы не меняют состояние job.
|
||||
|
||||
### Side effects
|
||||
- SSE endpoint создаёт временную подписку на EventBus.
|
||||
- При завершении или разрыве соединения выполняется `unsubscribe`.
|
||||
|
||||
## Contract
|
||||
|
||||
### Endpoint
|
||||
- Method: `GET`
|
||||
- Path: `/api/rag/sessions/{rag_session_id}/jobs/{index_job_id}` и `/api/rag/sessions/{rag_session_id}/jobs/{index_job_id}/events`
|
||||
- Auth: определяется внешним слоем приложения.
|
||||
- Idempotent: да.
|
||||
- Timeout: status endpoint короткий; SSE stream долгоживущий.
|
||||
- Retry: polling можно повторять безопасно; SSE можно переподключать.
|
||||
|
||||
### Request
|
||||
| Field | Type | Required | Constraints | Description |
|
||||
|------|------|----------|-------------|-------------|
|
||||
| `rag_session_id` | `string` | yes | path param | идентификатор сессии |
|
||||
| `index_job_id` | `string` | yes | path param | идентификатор задачи |
|
||||
|
||||
### Response
|
||||
| Field | Type | Description |
|
||||
|------|------|-------------|
|
||||
| `rag_session_id` | `string` | идентификатор сессии, только для status endpoint |
|
||||
| `index_job_id` | `string` | идентификатор задачи |
|
||||
| `status` | `IndexJobStatus` | текущее состояние job |
|
||||
| `indexed_files` | `integer` | число успешно обработанных файлов |
|
||||
| `failed_files` | `integer` | число файлов с ошибками |
|
||||
| `cache_hit_files` | `integer` | число cache hit |
|
||||
| `cache_miss_files` | `integer` | число cache miss |
|
||||
| `error` | `object \| null` | ошибка, если job завершилась с `error` |
|
||||
|
||||
### External contract refs
|
||||
- OpenAPI: status endpoint использует `response_model=RagSessionJobResponse`; SSE endpoint отдаёт `text/event-stream`.
|
||||
- Schema: `RagSessionJobResponse`.
|
||||
- DTO / serializer: `src/app/schemas/rag_sessions.py`.
|
||||
- Additional refs: `entity-rag-index-job`.
|
||||
|
||||
## Errors
|
||||
|
||||
| error_id | http_code | when | client_behavior | retry |
|
||||
|----------|-----------|------|-----------------|-------|
|
||||
| `not_found` | `404` | job отсутствует или не принадлежит переданной сессии | проверить id или создать новую задачу | no |
|
||||
|
||||
## Нефункциональные требования
|
||||
|
||||
### Security
|
||||
- Проверка `job.rag_session_id == rag_session_id` обязательна для обоих методов.
|
||||
|
||||
### Observability
|
||||
- Logs: отдельные логи чтения статуса не реализованы.
|
||||
- Metrics: отсутствуют.
|
||||
- Traces: отсутствуют.
|
||||
- Audit: история job хранится в `rag_index_jobs`, поток событий в памяти EventBus.
|
||||
|
||||
### Reliability
|
||||
- SSE heartbeat удерживает соединение активным.
|
||||
- `finally` блок гарантирует `unsubscribe`.
|
||||
|
||||
### Performance
|
||||
- Status endpoint работает как лёгкий запрос к БД.
|
||||
- SSE stream масштабируется числом активных подписчиков и объёмом событий.
|
||||
|
||||
## Связанные блоки логики
|
||||
- `logic-rag-indexing`
|
||||
|
||||
## Связанные сущности
|
||||
- `RagSession`
|
||||
- `IndexJob`
|
||||
|
||||
## Связанный код
|
||||
|
||||
### Files
|
||||
- `src/app/modules/rag/module.py`
|
||||
- `src/app/modules/rag/job_store.py`
|
||||
- `src/app/schemas/rag_sessions.py`
|
||||
|
||||
### Symbols
|
||||
- `RagModule.public_router.rag_session_job`
|
||||
- `RagModule.public_router.rag_session_job_events`
|
||||
- `IndexJobStore.get`
|
||||
|
||||
## Связанные документы
|
||||
- `arch-rag-package`
|
||||
- `entity-rag-session`
|
||||
- `entity-rag-index-job`
|
||||
|
||||
## История изменений
|
||||
|
||||
| Date | Source | Changes |
|
||||
|------|--------|---------|
|
||||
| 2026-03-13 | code | Задокументированы status и SSE endpoints для наблюдения за indexing job. |
|
||||
@@ -0,0 +1,222 @@
|
||||
---
|
||||
|
||||
## id: arch-rag-package
|
||||
title: Пакет RAG
|
||||
doc_type: architecture_overview
|
||||
domain: rag
|
||||
status: draft
|
||||
owner: system-analyst
|
||||
source_of_truth: code
|
||||
related_docs:
|
||||
- logic-rag-indexing
|
||||
- logic-rag-retrieval
|
||||
- entity-rag-session
|
||||
- entity-rag-index-job
|
||||
- api-rag-session-create
|
||||
- api-rag-session-changes
|
||||
- api-rag-session-job
|
||||
related_code:
|
||||
- src/app/modules/rag/module.py
|
||||
- src/app/modules/rag/services/rag_service.py
|
||||
- src/app/modules/rag/indexing_service.py
|
||||
- src/app/modules/rag/persistence/repository.py
|
||||
- src/app/modules/rag/persistence/schema_repository.py
|
||||
entities:
|
||||
- RagSession
|
||||
- IndexJob
|
||||
- RagDocument
|
||||
tags:
|
||||
- rag
|
||||
- indexing
|
||||
- retrieval
|
||||
- architecture
|
||||
|
||||
# Пакет RAG
|
||||
|
||||
## Summary
|
||||
|
||||
- Scope: модуль индексации проектных файлов, хранения RAG-слоёв и выдачи retrieval-контекста.
|
||||
- Purpose: построить индекс по документации и Python-коду и дать runtime доступ к релевантным фрагментам.
|
||||
- Main modules: `RagModule`, `RagService`, `IndexingOrchestrator`, `RagRepository`, `RepoWebhookService`.
|
||||
- Main domains: RAG-сессии, задачи индексации, документы индекса, blob-cache, retrieval.
|
||||
- Main integrations: PostgreSQL/pgvector, GigaChat embeddings, FastAPI, EventBus, story context.
|
||||
- Key entrypoints: `/api/rag/sessions`, `/api/rag/sessions/{rag_session_id}/changes`, `/api/rag/sessions/{rag_session_id}/jobs/{index_job_id}`, `/internal/rag-repo/webhook`.
|
||||
- Key data flows: snapshot indexing, incremental reindex, retrieval из `rag_chunks`, webhook-нормализация коммитов.
|
||||
- Source of truth: код `src/app/modules/rag/*`.
|
||||
|
||||
## Назначение
|
||||
|
||||
Пакет `rag` отвечает за полный цикл подготовки retrieval-контекста для проекта: принимает снапшоты и изменения файлов, преобразует их в набор атомарных `RagDocument`, векторизует, сохраняет в БД и предоставляет доступ к индексированным данным другим частям системы.
|
||||
|
||||
## Контекст
|
||||
|
||||
Модуль используется как инфраструктурный слой для agent/runtime и смежных интеграций. На вход он принимает либо список файлов проекта, либо webhook репозитория. На выходе формирует устойчивый индекс, ассоциированный с `rag_session_id`, и статус задач индексации, пригодный для опроса и SSE-подписки.
|
||||
|
||||
## Границы системы
|
||||
|
||||
### In scope
|
||||
|
||||
- Создание и хранение `RagSession`.
|
||||
- Постановка и выполнение задач snapshot/change indexing.
|
||||
- Индексация markdown-документации в слои `D1-D4`.
|
||||
- Индексация Python-кода в слои `C0-C4`.
|
||||
- Кэширование по `repo_id + blob_sha`.
|
||||
- Сохранение retrieval-документов в `rag_chunks`.
|
||||
- Выдача статуса задач и событий прогресса.
|
||||
- Нормализация webhook Gitea/Bitbucket и связывание коммитов со story.
|
||||
|
||||
### Out of scope
|
||||
|
||||
- Финальная генерация ответа пользователю.
|
||||
- Оркестрация LLM-диалога.
|
||||
- Управление git-репозиторием и загрузка файлов из внешних источников.
|
||||
- Политики маршрутизации intent/runtime вне собственного persistence/retrieval API.
|
||||
|
||||
## Архитектурная схема
|
||||
|
||||
`RagModule` собирает зависимости модуля и публикует HTTP endpoints. Для индексации он использует `RagSessionStore`, `IndexJobStore`, `IndexingOrchestrator` и `RagService`. `RagService` выбирает docs/code pipeline, обогащает документы метаданными файла, запрашивает embeddings и записывает результат через `RagRepository`. `RagRepository` агрегирует schema/session/job/document/cache/query репозитории. Отдельно `RagRepoModule` принимает repository webhooks и прокидывает нормализованный commit context в story storage и cache writer.
|
||||
|
||||
## Основные модули
|
||||
|
||||
|
||||
| module | responsibility | depends_on | key_code_refs |
|
||||
| ---------------------- | -------------------------------------------------------- | ------------------------------------------------------------------------ | ----------------------------------------------- |
|
||||
| `RagModule` | сборка зависимостей, публичный и internal API | `RagService`, `IndexingOrchestrator`, `RagSessionStore`, `IndexJobStore` | `src/app/modules/rag/module.py` |
|
||||
| `RagService` | синхронная бизнес-логика индексации файлов и cache reuse | docs/code pipeline, embedder, `RagRepository` | `src/app/modules/rag/services/rag_service.py` |
|
||||
| `IndexingOrchestrator` | асинхронный job lifecycle, retry, project lock, EventBus | `IndexJobStore`, `RagIndexer`, `EventBus`, `RetryExecutor` | `src/app/modules/rag/indexing_service.py` |
|
||||
| `DocsIndexingPipeline` | построение слоёв документации `D1-D4` | classifier, chunker, document builder | `src/app/modules/rag/indexing/docs/pipeline.py` |
|
||||
| `CodeIndexingPipeline` | построение слоёв кода `C0-C4` | AST parser, symbol/edge/entrypoint/role builders | `src/app/modules/rag/indexing/code/pipeline.py` |
|
||||
| `RagRepository` | единая точка persistence и retrieval | schema/session/job/document/cache/query repositories | `src/app/modules/rag/persistence/repository.py` |
|
||||
| `RepoWebhookService` | нормализация webhook payload и выделение story id | story writer, cache writer | `src/app/modules/rag/webhook_service.py` |
|
||||
|
||||
|
||||
## Основные доменные области
|
||||
|
||||
- RAG session как граница индекса конкретного проекта или его временного снапшота.
|
||||
- Index job как жизненный цикл асинхронной индексации и канал наблюдения за прогрессом.
|
||||
- RagDocument как атом индекса, который попадает в retrieval-хранилище и в cache.
|
||||
- Repo webhook context как источник commit metadata для story и cache.
|
||||
|
||||
## Основные интеграции
|
||||
|
||||
|
||||
| integration | direction | purpose | protocol / transport | related_docs |
|
||||
| ------------------------ | --------- | --------------------------------------------------- | ---------------------------------- | -------------------------------------------------------------------------- |
|
||||
| PostgreSQL + pgvector | outbound | хранение документов, jobs, sessions и vector search | SQLAlchemy / SQL / pgvector | `logic-rag-retrieval` |
|
||||
| GigaChat embeddings | outbound | получение embedding для batch документов | HTTP client через `GigaChatClient` | `logic-rag-indexing` |
|
||||
| FastAPI | inbound | публичный и internal API модуля | HTTP | `api-rag-session-create`, `api-rag-session-changes`, `api-rag-session-job` |
|
||||
| EventBus | outbound | публикация прогресса индексации и terminal events | in-process async events / SSE | `api-rag-session-job` |
|
||||
| Story context repository | outbound | запись webhook-коммитов для story | Python interface | `logic-rag-indexing` |
|
||||
|
||||
|
||||
## Основные потоки
|
||||
|
||||
### Flow 1
|
||||
|
||||
1. Клиент вызывает `POST /api/rag/sessions` с `project_id` и snapshot файлов.
|
||||
2. `RagSessionStore` создаёт `rag_session_id`, а `IndexingOrchestrator` создаёт `IndexJob`.
|
||||
3. `RagService` фильтрует файлы, переиспользует cache по `blob_sha` или строит docs/code документы заново.
|
||||
4. Документы векторизуются, записываются в `rag_chunks`, а job получает финальный статус `done` или `error`.
|
||||
|
||||
### Flow 2
|
||||
|
||||
1. Клиент вызывает `POST /api/rag/sessions/{rag_session_id}/changes`.
|
||||
2. `IndexingOrchestrator` сериализует обработку по `rag_session_id`.
|
||||
3. `RagService` удаляет документы по `delete_paths`, пересобирает upsert-файлы и применяет изменения к индексу.
|
||||
4. Клиент читает статус и события задачи через job endpoints.
|
||||
|
||||
## Архитектурные решения и ограничения
|
||||
|
||||
### Key decisions
|
||||
|
||||
- Snapshot и incremental indexing используют один и тот же `RagService`, различаясь только стратегией записи.
|
||||
- Кэш документов привязан к `repo_id + blob_sha`, а не к `rag_session_id`, что позволяет переиспользовать embeddings между сессиями одного проекта.
|
||||
- Документация и код индексируются разными pipeline, но сохраняются в общую таблицу `rag_chunks`.
|
||||
- Асинхронность вынесена в `IndexingOrchestrator`, чтобы `RagService` оставался application-service без управления job lifecycle.
|
||||
|
||||
### Constraints
|
||||
|
||||
- Code indexing поддерживает только Python-файлы.
|
||||
- Docs indexing ориентирован на markdown и frontmatter YAML.
|
||||
- Deprecated endpoint `/internal/rag/retrieve` не используется для рабочего retrieval.
|
||||
- Реальное retrieval API доступно через repository/runtime adapters, а не через публичный HTTP endpoint модуля.
|
||||
|
||||
### Risks
|
||||
|
||||
- Ошибки embeddings или временные сетевые сбои переводят job в `error` только после исчерпания retry.
|
||||
- Полное `replace_documents` для snapshot удаляет все документы сессии перед вставкой новых.
|
||||
- Retrieval ranking завязан на SQL-эвристики по layer, lexical match и metadata, поэтому качество зависит от корректности metadata builders.
|
||||
|
||||
## Нефункциональные аспекты
|
||||
|
||||
### Security
|
||||
|
||||
- Публичные endpoints не содержат собственной бизнес-авторизации внутри модуля и полагаются на внешний слой приложения.
|
||||
- Webhook provider определяется по headers/payload без явной проверки подписи в самом `RepoWebhookService`.
|
||||
|
||||
### Reliability
|
||||
|
||||
- Проектный `asyncio.Lock` предотвращает параллельную индексацию одной `rag_session`.
|
||||
- `RetryExecutor` повторяет временные сбои индексации.
|
||||
|
||||
### Observability
|
||||
|
||||
- Logs: `RagService` пишет предупреждения по cache hit/miss и skipped files.
|
||||
- Metrics: явные метрики не выделены.
|
||||
- Traces: явная трассировка не реализована.
|
||||
- Audit: job status и webhook commit binding сохраняются в БД.
|
||||
|
||||
### Performance
|
||||
|
||||
- Embeddings отправляются батчами с размером из `RAG_EMBED_BATCH_SIZE`.
|
||||
- Cache reuse исключает повторную векторизацию неизменённых blob.
|
||||
|
||||
### Scalability
|
||||
|
||||
- Индекс хранится на уровне SQL-таблиц с векторными полями и индексами по session/layer/path.
|
||||
- При росте объёма данных узким местом остаются полнотабличные delete/insert по snapshot и SQL sorting retrieval.
|
||||
|
||||
## Связанные сущности
|
||||
|
||||
- `RagSession`
|
||||
- `IndexJob`
|
||||
- `RagDocument`
|
||||
|
||||
## Связанный код
|
||||
|
||||
### Files
|
||||
|
||||
- `src/app/modules/rag/module.py`
|
||||
- `src/app/modules/rag/services/rag_service.py`
|
||||
- `src/app/modules/rag/indexing_service.py`
|
||||
- `src/app/modules/rag/persistence/repository.py`
|
||||
- `src/app/modules/rag/persistence/schema_repository.py`
|
||||
- `src/app/modules/rag/webhook_service.py`
|
||||
|
||||
### Symbols
|
||||
|
||||
- `RagModule`
|
||||
- `RagRepoModule`
|
||||
- `RagService`
|
||||
- `IndexingOrchestrator`
|
||||
- `RagRepository`
|
||||
- `RepoWebhookService`
|
||||
|
||||
## Связанные документы
|
||||
|
||||
- `logic-rag-indexing`
|
||||
- `logic-rag-retrieval`
|
||||
- `entity-rag-session`
|
||||
- `entity-rag-index-job`
|
||||
- `api-rag-session-create`
|
||||
- `api-rag-session-changes`
|
||||
- `api-rag-session-job`
|
||||
|
||||
## История изменений
|
||||
|
||||
|
||||
| Date | Source | Changes |
|
||||
| ---------- | ------ | ------------------------------------------------------------------- |
|
||||
| 2026-03-13 | code | Создан обзор архитектуры пакета `rag` на основе текущей реализации. |
|
||||
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
---
|
||||
id: entity-rag-index-job
|
||||
title: Сущность IndexJob
|
||||
doc_type: domain_entity
|
||||
domain: rag
|
||||
status: draft
|
||||
owner: system-analyst
|
||||
source_of_truth: code
|
||||
related_docs:
|
||||
- arch-rag-package
|
||||
- logic-rag-indexing
|
||||
- entity-rag-session
|
||||
- api-rag-session-job
|
||||
related_code:
|
||||
- src/app/modules/rag/job_store.py
|
||||
- src/app/modules/rag/indexing_service.py
|
||||
- src/app/modules/rag/persistence/job_repository.py
|
||||
- src/app/modules/rag/persistence/schema_repository.py
|
||||
entities:
|
||||
- IndexJob
|
||||
- RagSession
|
||||
tags:
|
||||
- rag
|
||||
- indexing
|
||||
- job
|
||||
- domain-entity
|
||||
---
|
||||
# Сущность IndexJob
|
||||
|
||||
## Summary
|
||||
- Domain: rag
|
||||
- Purpose: представить асинхронную задачу индексации и её наблюдаемый статус.
|
||||
- Entity role: operational entity для выполнения snapshot/change indexing.
|
||||
- Main attributes: `index_job_id`, `rag_session_id`, `status`, `indexed_files`, `failed_files`, `cache_hit_files`, `cache_miss_files`, `error`.
|
||||
- Lifecycle: `queued -> running -> done|error`.
|
||||
- Invariants: job всегда принадлежит одной `RagSession`, статус хранится как enum `IndexJobStatus`.
|
||||
- Related APIs: создание job косвенно через session endpoints, чтение через job status endpoint и SSE endpoint.
|
||||
- Related logic: `IndexingOrchestrator`, retry, EventBus publishing.
|
||||
- Source of truth: `src/app/modules/rag/job_store.py`, `src/app/modules/rag/indexing_service.py`.
|
||||
|
||||
## Назначение
|
||||
|
||||
`IndexJob` хранит технический прогресс и итог выполнения индексации. Он нужен, чтобы API модуля мог вернуть результат не синхронно, а через опрос статуса и подписку на события.
|
||||
|
||||
## Контекст
|
||||
|
||||
Job создаётся на каждую snapshot- или changes-операцию. Сервис индексации обновляет его counters и публикует события прогресса в EventBus под ключом `index_job_id`.
|
||||
|
||||
## Роль в доменной модели
|
||||
|
||||
Это операционная сущность, которая связывает пользовательский запрос на индексацию с фактическим процессом обработки файлов. Она не хранит сам индекс, но управляет прозрачностью выполнения и ошибками.
|
||||
|
||||
## Атрибуты
|
||||
|
||||
| attribute | type | required | description | constraints |
|
||||
|-----------|------|----------|-------------|-------------|
|
||||
| `index_job_id` | `str` | yes | уникальный идентификатор задачи | primary key, non-empty |
|
||||
| `rag_session_id` | `str` | yes | ссылка на целевую RAG-сессию | non-empty |
|
||||
| `status` | `IndexJobStatus` | yes | текущее состояние задачи | `queued`, `running`, `done`, `error` |
|
||||
| `indexed_files` | `int` | yes | число успешно обработанных файлов | `>= 0` |
|
||||
| `failed_files` | `int` | yes | число файлов с ошибками | `>= 0` |
|
||||
| `cache_hit_files` | `int` | yes | число файлов, обслуженных из cache | `>= 0` |
|
||||
| `cache_miss_files` | `int` | yes | число файлов, потребовавших embeddings | `>= 0` |
|
||||
| `error` | `ErrorPayload \| None` | no | информация о необработанной временной ошибке после retry | optional |
|
||||
|
||||
## Состояния и жизненный цикл
|
||||
|
||||
### Основные состояния
|
||||
- `queued`
|
||||
- `running`
|
||||
- `done`
|
||||
- `error`
|
||||
|
||||
### Переходы состояний
|
||||
1. `IndexJobStore.create` создаёт job в состоянии `queued`.
|
||||
2. `IndexingOrchestrator._run_with_project_lock` переводит job в `running`.
|
||||
3. Успешная индексация переводит job в `done` и заполняет counters.
|
||||
4. Ошибка после исчерпания retry переводит job в `error` и заполняет `ErrorPayload`.
|
||||
|
||||
## Инварианты и ограничения
|
||||
|
||||
- Job не мигрирует между `rag_session_id`.
|
||||
- Финальные counters сохраняются в БД перед публикацией terminal event.
|
||||
- Ошибки уровня `TimeoutError`, `ConnectionError`, `OSError` считаются временными и оборачиваются в `index_retry_exhausted` только после retry exhaustion.
|
||||
|
||||
## Связи с другими сущностями
|
||||
|
||||
| entity | relation | description |
|
||||
|--------|----------|-------------|
|
||||
| `RagSession` | many-to-one | каждая задача относится к одной сессии |
|
||||
| `RagDocument` | indirect | job обновляет набор документов сессии, но не владеет ими напрямую |
|
||||
|
||||
## Использование в системе
|
||||
|
||||
### Related API
|
||||
- `POST /api/rag/sessions`
|
||||
- `POST /api/rag/sessions/{rag_session_id}/changes`
|
||||
- `GET /api/rag/sessions/{rag_session_id}/jobs/{index_job_id}`
|
||||
- `GET /api/rag/sessions/{rag_session_id}/jobs/{index_job_id}/events`
|
||||
|
||||
### Related UI
|
||||
- Прямого UI в репозитории не обнаружено.
|
||||
|
||||
### Related logic
|
||||
- `logic-rag-indexing`
|
||||
|
||||
### Related integrations
|
||||
- EventBus SSE stream
|
||||
- PostgreSQL таблица `rag_index_jobs`
|
||||
|
||||
## Функциональные требования
|
||||
|
||||
- Job должна создаваться до запуска фоновой задачи.
|
||||
- Публичный API обязан проверять принадлежность job указанной `rag_session_id`.
|
||||
- Progress events должны публиковаться в формате, достаточном для фронта или внешнего клиента.
|
||||
|
||||
## Нефункциональные требования
|
||||
|
||||
### Audit / history
|
||||
- `created_at` и `updated_at` сохраняются в таблице `rag_index_jobs`.
|
||||
|
||||
### Security
|
||||
- Доступ к job опирается на проверку связи `job.rag_session_id == requested rag_session_id`.
|
||||
|
||||
### Observability
|
||||
- SSE stream отдаёт `index_status`, `index_progress`, `terminal`.
|
||||
|
||||
## Связанный код
|
||||
|
||||
### Files
|
||||
- `src/app/modules/rag/job_store.py`
|
||||
- `src/app/modules/rag/indexing_service.py`
|
||||
- `src/app/modules/rag/persistence/job_repository.py`
|
||||
- `src/app/modules/rag/persistence/schema_repository.py`
|
||||
|
||||
### Symbols
|
||||
- `IndexJob`
|
||||
- `IndexJobStore.create`
|
||||
- `IndexJobStore.get`
|
||||
- `IndexJobStore.save`
|
||||
- `IndexingOrchestrator._run_with_project_lock`
|
||||
|
||||
## Связанные документы
|
||||
|
||||
- `arch-rag-package`
|
||||
- `logic-rag-indexing`
|
||||
- `entity-rag-session`
|
||||
- `api-rag-session-job`
|
||||
|
||||
## История изменений
|
||||
|
||||
| Date | Source | Changes |
|
||||
|------|--------|---------|
|
||||
| 2026-03-13 | code | Добавлено описание lifecycle и контракта сущности `IndexJob`. |
|
||||
@@ -0,0 +1,143 @@
|
||||
---
|
||||
id: entity-rag-session
|
||||
title: Сущность RagSession
|
||||
doc_type: domain_entity
|
||||
domain: rag
|
||||
status: draft
|
||||
owner: system-analyst
|
||||
source_of_truth: code
|
||||
related_docs:
|
||||
- arch-rag-package
|
||||
- logic-rag-indexing
|
||||
- api-rag-session-create
|
||||
- api-rag-session-changes
|
||||
- api-rag-session-job
|
||||
related_code:
|
||||
- src/app/modules/rag/session_store.py
|
||||
- src/app/modules/rag/persistence/session_repository.py
|
||||
- src/app/modules/rag/persistence/schema_repository.py
|
||||
entities:
|
||||
- RagSession
|
||||
tags:
|
||||
- rag
|
||||
- session
|
||||
- domain-entity
|
||||
---
|
||||
# Сущность RagSession
|
||||
|
||||
## Summary
|
||||
- Domain: rag
|
||||
- Purpose: связать индекс и связанные job с конкретным проектом или его рабочим снимком.
|
||||
- Entity role: корневая сущность области RAG indexing/retrieval.
|
||||
- Main attributes: `rag_session_id`, `project_id`, `created_at`.
|
||||
- Lifecycle: создаётся до первой индексации и используется как scope всех retrieval-запросов.
|
||||
- Invariants: `rag_session_id` уникален, `project_id` обязателен.
|
||||
- Related APIs: `POST /api/rag/sessions`, `POST /api/rag/sessions/{rag_session_id}/changes`, `GET /api/rag/sessions/{rag_session_id}/jobs/{index_job_id}`.
|
||||
- Related logic: snapshot indexing, change indexing, retrieval filtering.
|
||||
- Source of truth: `src/app/modules/rag/session_store.py`, таблица `rag_sessions`.
|
||||
|
||||
## Назначение
|
||||
|
||||
`RagSession` определяет границу индекса для проекта. Все документы, задачи и retrieval-запросы внутри `rag` привязаны к этой сущности.
|
||||
|
||||
## Контекст
|
||||
|
||||
Сессия используется и в новом API с UUID, и в legacy/internal режиме, где `project_id` может совпадать с `rag_session_id`. Через неё сервис восстанавливает `repo_id`, который затем участвует в кэшировании документов.
|
||||
|
||||
## Роль в доменной модели
|
||||
|
||||
`RagSession` является владельцем набора индексированных документов и асинхронных `IndexJob`. Без неё нельзя безопасно выполнять reindex или retrieval, потому что именно она задаёт scope таблицы `rag_chunks`.
|
||||
|
||||
## Атрибуты
|
||||
|
||||
| attribute | type | required | description | constraints |
|
||||
|-----------|------|----------|-------------|-------------|
|
||||
| `rag_session_id` | `str` | yes | уникальный идентификатор сессии | primary key, non-empty |
|
||||
| `project_id` | `str` | yes | идентификатор проекта или workspace | non-empty |
|
||||
| `created_at` | `timestamp with time zone` | yes | время создания записи в БД | default `CURRENT_TIMESTAMP` |
|
||||
|
||||
## Состояния и жизненный цикл
|
||||
|
||||
### Основные состояния
|
||||
- created
|
||||
- active
|
||||
- reused via legacy/internal API
|
||||
|
||||
### Переходы состояний
|
||||
1. `RagSessionStore.create(project_id)` создаёт новую сессию с UUID.
|
||||
2. `RagSessionStore.put(rag_session_id, project_id)` создаёт или обновляет сессию с заданным ключом.
|
||||
3. После создания сессия используется для indexing и retrieval до тех пор, пока не будет заменена новым идентификатором на уровне вызывающего сервиса.
|
||||
|
||||
## Инварианты и ограничения
|
||||
|
||||
- `project_id` не должен быть пустым.
|
||||
- Для retrieval и indexing используется только один `rag_session_id` за операцию.
|
||||
- `RagService._resolve_repo_id` использует `project_id` этой сущности как `repo_id` для cache scope.
|
||||
|
||||
## Связи с другими сущностями
|
||||
|
||||
| entity | relation | description |
|
||||
|--------|----------|-------------|
|
||||
| `IndexJob` | one-to-many | одна сессия может иметь много задач индексации |
|
||||
| `RagDocument` | one-to-many | все записи в `rag_chunks` привязаны к одной сессии |
|
||||
|
||||
## Использование в системе
|
||||
|
||||
### Related API
|
||||
- `POST /api/rag/sessions`
|
||||
- `POST /api/rag/sessions/{rag_session_id}/changes`
|
||||
- `GET /api/rag/sessions/{rag_session_id}/jobs/{index_job_id}`
|
||||
|
||||
### Related UI
|
||||
- Прямого UI в репозитории не обнаружено.
|
||||
|
||||
### Related logic
|
||||
- `logic-rag-indexing`
|
||||
- `logic-rag-retrieval`
|
||||
|
||||
### Related integrations
|
||||
- PostgreSQL таблица `rag_sessions`
|
||||
|
||||
## Функциональные требования
|
||||
|
||||
- Сессия должна создаваться до постановки snapshot job.
|
||||
- При change indexing запрос должен ссылаться на существующую сессию в новом публичном API.
|
||||
- Legacy/internal API может создавать запись с предсказуемым `rag_session_id`, равным `project_id`.
|
||||
|
||||
## Нефункциональные требования
|
||||
|
||||
### Audit / history
|
||||
- Время создания фиксируется в таблице `rag_sessions`.
|
||||
|
||||
### Security
|
||||
- Отдельных прав доступа на уровне сущности внутри модуля нет.
|
||||
|
||||
### Observability
|
||||
- Основная наблюдаемость сессии идёт через связанные `IndexJob`.
|
||||
|
||||
## Связанный код
|
||||
|
||||
### Files
|
||||
- `src/app/modules/rag/session_store.py`
|
||||
- `src/app/modules/rag/persistence/session_repository.py`
|
||||
- `src/app/modules/rag/persistence/schema_repository.py`
|
||||
|
||||
### Symbols
|
||||
- `RagSession`
|
||||
- `RagSessionStore.create`
|
||||
- `RagSessionStore.put`
|
||||
- `RagSessionStore.get`
|
||||
|
||||
## Связанные документы
|
||||
|
||||
- `arch-rag-package`
|
||||
- `logic-rag-indexing`
|
||||
- `api-rag-session-create`
|
||||
- `api-rag-session-changes`
|
||||
- `api-rag-session-job`
|
||||
|
||||
## История изменений
|
||||
|
||||
| Date | Source | Changes |
|
||||
|------|--------|---------|
|
||||
| 2026-03-13 | code | Добавлено описание сущности `RagSession` и её роли в границах индекса. |
|
||||
@@ -0,0 +1,166 @@
|
||||
---
|
||||
id: logic-rag-indexing
|
||||
title: Индексация файлов в RAG
|
||||
doc_type: logic_block
|
||||
domain: rag
|
||||
status: draft
|
||||
owner: system-analyst
|
||||
source_of_truth: code
|
||||
related_docs:
|
||||
- arch-rag-package
|
||||
- entity-rag-session
|
||||
- entity-rag-index-job
|
||||
- api-rag-session-create
|
||||
- api-rag-session-changes
|
||||
related_code:
|
||||
- src/app/modules/rag/indexing_service.py
|
||||
- src/app/modules/rag/services/rag_service.py
|
||||
- src/app/modules/rag/indexing/docs/pipeline.py
|
||||
- src/app/modules/rag/indexing/code/pipeline.py
|
||||
- src/app/modules/rag/persistence/document_repository.py
|
||||
entities:
|
||||
- RagSession
|
||||
- IndexJob
|
||||
- RagDocument
|
||||
tags:
|
||||
- rag
|
||||
- indexing
|
||||
- snapshot
|
||||
- changes
|
||||
---
|
||||
# Индексация файлов в RAG
|
||||
|
||||
## Summary
|
||||
- Purpose: превратить входной список файлов в набор индексируемых `RagDocument` и сохранить их в persistence.
|
||||
- Trigger: создание RAG-сессии, запрос изменений, internal snapshot/changes endpoints.
|
||||
- Inputs: `rag_session_id`, файлы snapshot или changed_files, progress callback.
|
||||
- Outputs: обновлённые записи в `rag_chunks`, `rag_session_chunk_map`, статистика indexed/failed/cache hit/cache miss.
|
||||
- Main entities: `RagSession`, `IndexJob`, `RagDocument`.
|
||||
- Main dependencies: `IndexingOrchestrator`, `RagService`, `DocsIndexingPipeline`, `CodeIndexingPipeline`, `GigaChatEmbedder`, `RagRepository`.
|
||||
- Side effects: SQL delete/insert, cache write, SSE events, job status update.
|
||||
- Source of truth: `src/app/modules/rag/indexing_service.py`, `src/app/modules/rag/services/rag_service.py`.
|
||||
|
||||
## Назначение
|
||||
|
||||
Блок обеспечивает управляемую индексацию файлов проекта в многослойный RAG-индекс. Он должен одинаково поддерживать полный snapshot и инкрементальные изменения, не допуская гонок внутри одной `rag_session_id`.
|
||||
|
||||
## Контекст
|
||||
|
||||
Индексация запускается через HTTP API модуля. `IndexingOrchestrator` отвечает за job lifecycle и progress events, а `RagService` за фактическую переработку файлов в документы. Для уменьшения стоимости embeddings используется cache по содержимому blob.
|
||||
|
||||
## Технический use case
|
||||
|
||||
### Основной сценарий
|
||||
|
||||
1. API создаёт `IndexJob` и передаёт управление в `IndexingOrchestrator`.
|
||||
2. `IndexingOrchestrator` фильтрует входной набор, ставит статус `running`, публикует стартовое событие и захватывает lock по `rag_session_id`.
|
||||
3. `RagService` определяет `repo_id`, фильтрует индексируемые файлы, проверяет cache по `blob_sha` и либо переиспользует документы, либо заново строит docs/code слои.
|
||||
4. Для новых документов `RagService` добавляет file metadata, запрашивает embeddings батчами и сохраняет документы через `RagRepository`.
|
||||
5. `IndexingOrchestrator` обновляет job counters, публикует финальный `index_status` и `terminal`.
|
||||
|
||||
### Альтернативные ветки
|
||||
|
||||
- Для `changes` операции с `op=delete` только удаляют документы по `path`, без повторной сборки.
|
||||
- Если файл не поддержан ни docs-, ни code-pipeline, сервис делает fallback в docs pipeline.
|
||||
- При временном сбое индексации `RetryExecutor` повторяет операцию; после исчерпания попыток job получает `error`.
|
||||
|
||||
## Функциональные требования
|
||||
|
||||
### Preconditions
|
||||
- `rag_session_id` уже существует либо создаётся до запуска indexing job.
|
||||
- Файлы передаются в виде словарей, совместимых со схемами `FileSnapshot` или `ChangedFile`.
|
||||
- Для cache reuse у файла должен быть `content_hash` или доступный `content`.
|
||||
|
||||
### Processing rules
|
||||
- Snapshot перед записью выполняет полную замену документов сессии через `replace_documents`.
|
||||
- Incremental changes отделяет `delete_paths` от upsert-файлов и применяет изменения через `apply_document_changes`.
|
||||
- `repo_id` выводится из `project_id` у сессии, а при отсутствии сессии fallback равен `rag_session_id`.
|
||||
- Для поддерживаемого markdown строятся docs-слои `D1-D4`.
|
||||
- Для поддерживаемого Python-кода строятся code-слои `C0-C4`.
|
||||
- Каждый документ получает metadata файла: `blob_sha`, `repo_id`, `artifact_type`, `section`, `doc_id`, `owner`, `system_component`, `last_modified`, `staleness_score`.
|
||||
|
||||
### Validation rules
|
||||
- До индексации snapshot/changes проходят фильтрацию через `filter_snapshot_files` и `filter_changes_for_indexing`.
|
||||
- Пустые или неподходящие файлы исключаются из обрабатываемого набора.
|
||||
- Вектор сохраняется только если размерность embedding совпадает с размерностью поля `vector` при retrieval.
|
||||
|
||||
### Output / result rules
|
||||
- Результат операции всегда выражается четырьмя счётчиками: `indexed_files`, `failed_files`, `cache_hit_files`, `cache_miss_files`.
|
||||
- Для snapshot весь набор документов сессии после операции должен соответствовать текущему переданному snapshot.
|
||||
- Для changes в индексе должны остаться только документы по актуальному состоянию изменённых путей.
|
||||
|
||||
### Side effects
|
||||
- Удаление и вставка строк в `rag_chunks`.
|
||||
- Запись `rag_session_chunk_map` для документов, имеющих `repo_id` и `blob_sha`.
|
||||
- Сохранение cache в `rag_blob_cache` и `rag_chunk_cache`.
|
||||
- Публикация SSE-событий прогресса и завершения задачи.
|
||||
|
||||
## Ограничения и условия вызова
|
||||
|
||||
- Одновременно может выполняться только одна indexing operation на `rag_session_id`.
|
||||
- Code indexing работает только для Python файлов, распознаваемых `PythonFileFilter`.
|
||||
- Docs indexing рассчитывает на markdown с возможным YAML frontmatter.
|
||||
- Метод `replace_documents` делает жёсткую замену индекса сессии и не подходит для конкурентного merge разных snapshot-источников.
|
||||
|
||||
## Нефункциональные требования
|
||||
|
||||
### Security
|
||||
- Модуль не валидирует источник файлов и не выполняет контентную санацию сверх собственных парсеров.
|
||||
|
||||
### Observability
|
||||
- Logs: фиксируются skipped files и режим обработки `cache` / `embed`.
|
||||
- Metrics: отдельные счётчики не выделены, но статистика сохраняется в job.
|
||||
- Traces: не реализованы.
|
||||
- Audit: `rag_index_jobs` и `rag_session_chunk_map` образуют журнал выполнения и происхождения chunk.
|
||||
|
||||
### Reliability
|
||||
- `asyncio.Lock` сериализует операции в рамках одной сессии.
|
||||
- `RetryExecutor` покрывает временные ошибки `TimeoutError`, `ConnectionError`, `OSError`.
|
||||
|
||||
### Performance
|
||||
- Embeddings обрабатываются батчами.
|
||||
- Cache hit исключает повторный парсинг и повторный вызов embedder.
|
||||
|
||||
## Связанные API / UI / integration points
|
||||
|
||||
- `POST /api/rag/sessions`
|
||||
- `POST /api/rag/sessions/{rag_session_id}/changes`
|
||||
- `POST /internal/rag/index/snapshot`
|
||||
- `POST /internal/rag/index/changes`
|
||||
|
||||
## Связанные сущности
|
||||
|
||||
- `RagSession`
|
||||
- `IndexJob`
|
||||
- `RagDocument`
|
||||
|
||||
## Связанный код
|
||||
|
||||
### Files
|
||||
- `src/app/modules/rag/indexing_service.py`
|
||||
- `src/app/modules/rag/services/rag_service.py`
|
||||
- `src/app/modules/rag/indexing/docs/pipeline.py`
|
||||
- `src/app/modules/rag/indexing/code/pipeline.py`
|
||||
- `src/app/modules/rag/persistence/document_repository.py`
|
||||
|
||||
### Symbols
|
||||
- `IndexingOrchestrator.enqueue_snapshot`
|
||||
- `IndexingOrchestrator.enqueue_changes`
|
||||
- `IndexingOrchestrator._run_with_project_lock`
|
||||
- `RagService.index_snapshot`
|
||||
- `RagService.index_changes`
|
||||
- `RagService._index_files`
|
||||
|
||||
## Связанные документы
|
||||
|
||||
- `arch-rag-package`
|
||||
- `entity-rag-session`
|
||||
- `entity-rag-index-job`
|
||||
- `api-rag-session-create`
|
||||
- `api-rag-session-changes`
|
||||
|
||||
## История изменений
|
||||
|
||||
| Date | Source | Changes |
|
||||
|------|--------|---------|
|
||||
| 2026-03-13 | code | Описана фактическая логика snapshot и incremental indexing пакета `rag`. |
|
||||
@@ -0,0 +1,150 @@
|
||||
---
|
||||
id: logic-rag-retrieval
|
||||
title: Retrieval и ранжирование RAG-документов
|
||||
doc_type: logic_block
|
||||
domain: rag
|
||||
status: draft
|
||||
owner: system-analyst
|
||||
source_of_truth: code
|
||||
related_docs:
|
||||
- arch-rag-package
|
||||
- entity-rag-session
|
||||
related_code:
|
||||
- src/app/modules/rag/persistence/repository.py
|
||||
- src/app/modules/rag/persistence/query_repository.py
|
||||
- src/app/modules/rag/persistence/retrieval_statement_builder.py
|
||||
- src/app/modules/rag/contracts/enums.py
|
||||
entities:
|
||||
- RagSession
|
||||
- RagDocument
|
||||
tags:
|
||||
- rag
|
||||
- retrieval
|
||||
- ranking
|
||||
- pgvector
|
||||
---
|
||||
# Retrieval и ранжирование RAG-документов
|
||||
|
||||
## Summary
|
||||
- Purpose: вернуть релевантные RAG-документы из `rag_chunks` для заданной сессии и набора фильтров.
|
||||
- Trigger: вызовы runtime adapters и внутренних retrieval-компонентов.
|
||||
- Inputs: `rag_session_id`, `query_embedding`, `query_text`, `layers`, path filters, preference filters, limit.
|
||||
- Outputs: список rows с `path`, `content`, `layer`, `title`, `metadata`, `span_start`, `span_end`, ranking fields.
|
||||
- Main entities: `RagSession`, `RagDocument`.
|
||||
- Main dependencies: `RagRepository`, `RagQueryRepository`, `RetrievalStatementBuilder`, PostgreSQL/pgvector.
|
||||
- Side effects: отсутствуют, retrieval только читает БД.
|
||||
- Source of truth: `src/app/modules/rag/persistence/query_repository.py`, `src/app/modules/rag/persistence/retrieval_statement_builder.py`.
|
||||
|
||||
## Назначение
|
||||
|
||||
Блок retrieval выбирает из индекса наиболее полезные документы по конкретному `rag_session_id`. Он объединяет в одном SQL векторную близость, lexical match, слой документа и структурные сигналы из metadata.
|
||||
|
||||
## Контекст
|
||||
|
||||
Публичный HTTP endpoint retrieval внутри `rag` помечен как deprecated, поэтому рабочий доступ к retrieval идёт через repository/runtime adapters. Это означает, что контракт фактически определяется SQL-builder и форматом rows, возвращаемых `RagQueryRepository`.
|
||||
|
||||
## Технический use case
|
||||
|
||||
### Основной сценарий
|
||||
|
||||
1. Клиент runtime вызывает `RagRepository.retrieve(...)`.
|
||||
2. `RagQueryRepository` строит SQL через `RetrievalStatementBuilder.build_retrieve`.
|
||||
3. SQL ограничивает поиск текущей `rag_session_id`, при необходимости слоями и path-фильтрами.
|
||||
4. База сортирует документы по `prefer_bonus`, `test_penalty`, `layer_rank`, `lexical_rank`, `structural_rank`, `distance`.
|
||||
5. Репозиторий возвращает rows с распарсенным `metadata_json`.
|
||||
|
||||
### Альтернативные ветки
|
||||
|
||||
- Для lexical fallback по коду используется `retrieve_lexical_code`, который работает только по слою `C0_SOURCE_CHUNKS`.
|
||||
- Для точного добора файлов используется `retrieve_exact_files`, который читает заданные `path` без векторного ранжирования.
|
||||
- Если `query_text` не даёт terms, lexical retrieval возвращает пустой результат без выполнения SQL.
|
||||
|
||||
## Функциональные требования
|
||||
|
||||
### Preconditions
|
||||
- В `rag_chunks` уже должны существовать документы нужной `rag_session_id`.
|
||||
- Для vector retrieval embedding документа должен быть ненулевым и совпадать по размерности с query embedding.
|
||||
|
||||
### Processing rules
|
||||
- Базовый фильтр retrieval всегда включает `rag_session_id = :sid`.
|
||||
- При наличии `layers` запрос ограничивается указанными слоями.
|
||||
- `path_prefixes` задают include-фильтр по `LIKE prefix%`.
|
||||
- `exclude_path_prefixes` и `exclude_like_patterns` исключают части дерева путей до сортировки.
|
||||
- `prefer_path_prefixes` и `prefer_like_patterns` формируют `prefer_bonus`, поднимая приоритет совпавших путей.
|
||||
- `prefer_non_tests` создаёт `test_penalty`, если путь попадает под test-паттерны.
|
||||
|
||||
### Validation rules
|
||||
- Path filters экранируются для корректной работы `LIKE`.
|
||||
- `retrieve_exact_files` нормализует и отбрасывает пустые пути до построения SQL.
|
||||
- `retrieve_lexical_code` не выполняет SQL, если query terms отсутствуют.
|
||||
|
||||
### Output / result rules
|
||||
- Каждый row содержит контент документа и технические поля ранжирования.
|
||||
- `metadata_json` всегда декодируется в словарь `metadata`.
|
||||
- Limit применяется на уровне SQL и ограничивает итоговый набор строк.
|
||||
|
||||
### Side effects
|
||||
- Побочных эффектов нет, кроме чтения из БД.
|
||||
|
||||
## Ограничения и условия вызова
|
||||
|
||||
- Retrieval работает только внутри одной `rag_session_id` и не агрегирует несколько сессий.
|
||||
- Layer ranking зашит в код SQL-builder и требует явного обновления при появлении новых слоёв.
|
||||
- Полноценный HTTP retrieval endpoint в модуле отсутствует: `/internal/rag/retrieve` возвращает `410 deprecated`.
|
||||
|
||||
## Нефункциональные требования
|
||||
|
||||
### Security
|
||||
- Retrieval не выполняет маскирование содержимого документов.
|
||||
|
||||
### Observability
|
||||
- Logs: отдельное логирование запросов retrieval не реализовано.
|
||||
- Metrics: метрики по latency и quality не выделены.
|
||||
- Traces: отсутствуют.
|
||||
- Audit: результат зависит только от состояния `rag_chunks` и входных фильтров.
|
||||
|
||||
### Reliability
|
||||
- Пустой или некорректный lexical search безопасно возвращает пустой набор.
|
||||
- `retrieve_exact_files` работает без embeddings и может использоваться как fallback.
|
||||
|
||||
### Performance
|
||||
- Основной ranking выполняется в одном SQL-запросе.
|
||||
- Для vector retrieval используются поля `embedding` и индексы по session/layer/path.
|
||||
|
||||
## Связанные API / UI / integration points
|
||||
|
||||
- Runtime retrieval adapters в `src/app/modules/agent/runtime/steps/retrieval/adapter.py`
|
||||
- Explain retrieval gateway в `src/app/modules/agent/runtime/steps/explain/layered_gateway.py`
|
||||
- Deprecated endpoint `POST /internal/rag/retrieve`
|
||||
|
||||
## Связанные сущности
|
||||
|
||||
- `RagSession`
|
||||
- `RagDocument`
|
||||
|
||||
## Связанный код
|
||||
|
||||
### Files
|
||||
- `src/app/modules/rag/persistence/repository.py`
|
||||
- `src/app/modules/rag/persistence/query_repository.py`
|
||||
- `src/app/modules/rag/persistence/retrieval_statement_builder.py`
|
||||
- `src/app/modules/rag/contracts/enums.py`
|
||||
|
||||
### Symbols
|
||||
- `RagRepository.retrieve`
|
||||
- `RagRepository.retrieve_lexical_code`
|
||||
- `RagRepository.retrieve_exact_files`
|
||||
- `RagQueryRepository.retrieve`
|
||||
- `RetrievalStatementBuilder.build_retrieve`
|
||||
- `RetrievalStatementBuilder.build_lexical_code`
|
||||
|
||||
## Связанные документы
|
||||
|
||||
- `arch-rag-package`
|
||||
- `entity-rag-session`
|
||||
|
||||
## История изменений
|
||||
|
||||
| Date | Source | Changes |
|
||||
|------|--------|---------|
|
||||
| 2026-03-13 | code | Описан фактический retrieval contract и ranking SQL для пакета `rag`. |
|
||||
+376
@@ -0,0 +1,376 @@
|
||||
# v1.plan.md
|
||||
|
||||
## 1. Цель ближайшего этапа
|
||||
|
||||
На ближайшем этапе цель — не достраивать полного агента и не интегрировать решение в UI, а довести до зрелого состояния **изолированный code-first test pipeline**:
|
||||
|
||||
**user query -> IntentRouterV2 -> retrieval plan -> layered retrieval -> evidence gate -> LLM answer -> diagnostics**
|
||||
|
||||
Именно этот контур должен стать будущим ядром агента, которое позже заменит legacy `RouterService`.
|
||||
|
||||
---
|
||||
|
||||
## 2. Что считается результатом этапа
|
||||
|
||||
Этап можно считать успешным, когда:
|
||||
|
||||
- `IntentRouterV2` стабильно определяет `intent`, `sub_intent`, `keyword_hints`, `path_scope`, `layers`, `conversation_mode` и retrieval constraints;
|
||||
- retrieval реально следует плану, заданному роутером;
|
||||
- LLM получает контекст, собранный из retrieval, а не из побочных обходных механизмов;
|
||||
- ответ не строится без достаточного evidence;
|
||||
- диагностика тестов позволяет понять:
|
||||
- что выбрал router;
|
||||
- что извлек retrieval;
|
||||
- чего не хватило;
|
||||
- почему ответ хороший или плохой.
|
||||
|
||||
---
|
||||
|
||||
## 3. Что сейчас не делать
|
||||
|
||||
На текущем этапе не стоит:
|
||||
|
||||
- встраивать это сразу в UI;
|
||||
- подключать docs retrieval как обязательную часть MVP;
|
||||
- строить полный cross-domain runtime;
|
||||
- пытаться довести сразу весь orchestration runtime;
|
||||
- инвестировать много времени в `C6`, `D5`, `D6`;
|
||||
- лечить legacy `RouterService`, кроме минимально необходимого для совместимости.
|
||||
|
||||
---
|
||||
|
||||
## 4. Приоритетный план работ
|
||||
|
||||
## 4.1. Итерация 1 — зафиксировать канонический test-first контур
|
||||
|
||||
### 4.1.1. Принять `IntentRouterV2` как единственную точку истины для тестового MVP
|
||||
|
||||
Именно `IntentRouterV2` должен определять:
|
||||
|
||||
- `domain`
|
||||
- `intent`
|
||||
- `sub_intent`
|
||||
- `keyword_hints`
|
||||
- `path_scope`
|
||||
- `layers`
|
||||
- `conversation_mode`
|
||||
- retrieval constraints
|
||||
|
||||
### 4.1.2. Зафиксировать минимальный набор сценариев MVP-now
|
||||
|
||||
Для текущего этапа в качестве канонического набора сценариев следует оставить:
|
||||
|
||||
- `OPEN_FILE`
|
||||
- `EXPLAIN`
|
||||
- `FIND_TESTS`
|
||||
- `FIND_ENTRYPOINTS`
|
||||
- `GENERAL_QA` как fallback
|
||||
|
||||
### 4.1.3. Зафиксировать контракт тестового пайплайна
|
||||
|
||||
Нужно явно определить структуру следующих объектов:
|
||||
|
||||
- `RouterResult`
|
||||
- `RetrievalRequest`
|
||||
- `RetrievalResult`
|
||||
- `EvidenceBundle`
|
||||
- `AnswerSynthesisInput`
|
||||
- `DiagnosticsReport`
|
||||
|
||||
Это нужно, чтобы тесты настраивали устойчивый контракт, а не размытый процесс.
|
||||
|
||||
---
|
||||
|
||||
## 4.2. Итерация 2 — связать router и retrieval
|
||||
|
||||
### 4.2.1. Сделать retrieval adapter под контракт роутера
|
||||
|
||||
Output `IntentRouterV2` должен напрямую превращаться в retrieval plan.
|
||||
|
||||
Примеры:
|
||||
|
||||
- `OPEN_FILE`
|
||||
- path-priority retrieval
|
||||
- `C0_SOURCE_CHUNKS`
|
||||
|
||||
- `EXPLAIN`
|
||||
- symbol-first retrieval
|
||||
- `C1 + C0 + C2`, optionally `C3`
|
||||
|
||||
- `FIND_TESTS`
|
||||
- target symbol / path
|
||||
- lightweight `C5-lite`
|
||||
|
||||
- `FIND_ENTRYPOINTS`
|
||||
- `C3_ENTRYPOINTS`
|
||||
|
||||
- `GENERAL_QA`
|
||||
- ограниченный broad retrieval без агрессивных допущений
|
||||
|
||||
### 4.2.2. Убрать разрыв между индексатором и retrieval runtime
|
||||
|
||||
Нужна надежная связка:
|
||||
|
||||
**index data -> retrieval gateway -> normalized retrieval output**
|
||||
|
||||
Retrieval должен стать нормальной runtime-функцией, а не только наличием индексов и отдельных explain-механизмов.
|
||||
|
||||
### 4.2.3. Ввести единый формат retrieval output
|
||||
|
||||
Нужен единый формат, в который складываются найденные артефакты:
|
||||
|
||||
- symbols
|
||||
- chunks
|
||||
- relations
|
||||
- entrypoints
|
||||
- tests
|
||||
- diagnostics per layer
|
||||
|
||||
Это станет основой для `EvidenceBundle`.
|
||||
|
||||
---
|
||||
|
||||
## 4.3. Итерация 3 — перенести evidence-first логику в общий test pipeline
|
||||
|
||||
### 4.3.1. Сделать общий evidence gate
|
||||
|
||||
Для каждого sub-intent нужно определить minimum evidence.
|
||||
|
||||
#### `OPEN_FILE`
|
||||
- найден path или очень сильный lexical hit;
|
||||
- есть хотя бы 1–2 relevant chunks.
|
||||
|
||||
#### `EXPLAIN`
|
||||
- найден целевой symbol или уверенный target fragment;
|
||||
- есть минимальное число code chunks;
|
||||
- есть хотя бы базовые relations или surrounding context.
|
||||
|
||||
#### `FIND_TESTS`
|
||||
- найден target symbol/file;
|
||||
- найден хотя бы один reliable candidate test.
|
||||
|
||||
#### `FIND_ENTRYPOINTS`
|
||||
- найден хотя бы один candidate entrypoint.
|
||||
|
||||
#### `GENERAL_QA`
|
||||
- найден хоть какой-то устойчивый контекст;
|
||||
- иначе ответ должен честно говорить о слабом evidence.
|
||||
|
||||
### 4.3.2. Запретить уверенный ответ без опоры
|
||||
|
||||
Если minimum evidence не выполнен, система должна:
|
||||
|
||||
- либо честно деградировать;
|
||||
- либо выдать частичный ответ;
|
||||
- либо явно сообщить, каких данных не хватает.
|
||||
|
||||
---
|
||||
|
||||
## 4.4. Итерация 4 — унифицировать сборку контекста для LLM
|
||||
|
||||
### 4.4.1. Ввести единый `EvidenceBundle`
|
||||
|
||||
LLM должна получать структурированный пакет:
|
||||
|
||||
- target intent / sub-intent
|
||||
- target symbol / path
|
||||
- relevant code chunks
|
||||
- retrieved relations
|
||||
- entrypoints if any
|
||||
- test evidence if any
|
||||
- retrieval diagnostics summary
|
||||
- evidence sufficiency flags
|
||||
|
||||
### 4.4.2. Разделить fast context и deep context
|
||||
|
||||
#### Fast context
|
||||
Короткая выжимка:
|
||||
- target symbol
|
||||
- file
|
||||
- short relations summary
|
||||
- top evidence
|
||||
|
||||
#### Deep context
|
||||
Полные chunks, расширенные relations и supporting fragments.
|
||||
|
||||
### 4.4.3. Стандартизировать prompt contract
|
||||
|
||||
Для каждого sub-intent нужен понятный режим ответа:
|
||||
|
||||
- открыть;
|
||||
- объяснить;
|
||||
- найти тесты;
|
||||
- найти точки входа;
|
||||
- общий fallback.
|
||||
|
||||
LLM должна понимать:
|
||||
|
||||
- какой тип ответа нужен;
|
||||
- насколько evidence полон;
|
||||
- можно ли делать выводы;
|
||||
- где надо явно отметить неопределенность.
|
||||
|
||||
---
|
||||
|
||||
## 4.5. Итерация 5 — довести диагностику до рабочего инструмента настройки
|
||||
|
||||
### 4.5.1. Разделить диагностику на 2 уровня
|
||||
|
||||
#### Уровень 1 — Human-readable summary
|
||||
Коротко:
|
||||
- intent выбран правильно / нет;
|
||||
- target найден / нет;
|
||||
- retrieval успешен / нет;
|
||||
- evidence достаточен / нет;
|
||||
- ответ надежный / частичный / слабый.
|
||||
|
||||
#### Уровень 2 — Detailed diagnostics
|
||||
Подробно:
|
||||
- router fields;
|
||||
- layer-by-layer retrieval;
|
||||
- empty / failed layers;
|
||||
- ranking decisions;
|
||||
- evidence gate results;
|
||||
- context assembly details.
|
||||
|
||||
### 4.5.2. Добавить явные failure reasons
|
||||
|
||||
Рекомендуемый минимальный набор:
|
||||
|
||||
- `router_low_confidence`
|
||||
- `target_symbol_not_found`
|
||||
- `path_scope_empty`
|
||||
- `layer_c1_empty`
|
||||
- `layer_c2_empty`
|
||||
- `test_mapping_not_found`
|
||||
- `insufficient_code_evidence`
|
||||
|
||||
---
|
||||
|
||||
## 5. Как трактовать RAG-план на текущем этапе
|
||||
|
||||
## 5.1. Что оставить как целевой план
|
||||
|
||||
Целевую карту слоев оставляем без изменений:
|
||||
|
||||
- `C0–C6`
|
||||
- `D0–D6`
|
||||
|
||||
Она остается правильной как target architecture.
|
||||
|
||||
## 5.2. Что считать MVP-now
|
||||
|
||||
Для текущего этапа обязательны:
|
||||
|
||||
- `C0_SOURCE_CHUNKS`
|
||||
- `C1_SYMBOL_CATALOG`
|
||||
- `C2_SYMBOL_RELATIONS`
|
||||
- `C3_ENTRYPOINTS`
|
||||
|
||||
И в облегченном виде:
|
||||
|
||||
- `C5_TEST_MAPPINGS_LITE`
|
||||
|
||||
Пока не делать блокирующими:
|
||||
|
||||
- `C4_EXECUTION_PATHS`
|
||||
- `C6_CODE_FACTS`
|
||||
- весь docs runtime
|
||||
|
||||
## 5.3. Что считать следующим этапом
|
||||
|
||||
После стабилизации code-first MVP:
|
||||
|
||||
- подключение docs retrieval;
|
||||
- использование D-layers в runtime;
|
||||
- docs-from-code generation;
|
||||
- cross-domain linking.
|
||||
|
||||
---
|
||||
|
||||
## 6. Как строить тестовый контур
|
||||
|
||||
## 6.1. Router tests
|
||||
|
||||
Проверяют только:
|
||||
|
||||
- intent
|
||||
- sub_intent
|
||||
- layers
|
||||
- keyword_hints
|
||||
- path_scope
|
||||
- confidence / fallback behavior
|
||||
|
||||
## 6.2. Retrieval tests
|
||||
|
||||
Проверяют:
|
||||
|
||||
- что retrieval реально использует router output;
|
||||
- какие слои сработали;
|
||||
- что извлечено;
|
||||
- нет ли шума от тестов;
|
||||
- не пропал ли основной код.
|
||||
|
||||
## 6.3. Evidence gate tests
|
||||
|
||||
Проверяют:
|
||||
|
||||
- достаточен ли evidence;
|
||||
- правильно ли система деградирует;
|
||||
- не отвечает ли уверенно при слабой опоре.
|
||||
|
||||
## 6.4. Answer quality tests
|
||||
|
||||
Проверяют финальный ответ LLM:
|
||||
|
||||
- есть ли опора в коде;
|
||||
- нет ли лишних догадок;
|
||||
- хватает ли связности;
|
||||
- соответствует ли ответ типу вопроса.
|
||||
|
||||
---
|
||||
|
||||
## 7. Что скорректировать в README / архитектурном плане
|
||||
|
||||
В README нужно явно разделить:
|
||||
|
||||
### 7.1. Target Architecture
|
||||
Полная целевая система.
|
||||
|
||||
### 7.2. MVP-now
|
||||
Изолированный test-first контур для `CODE_QA`, который:
|
||||
|
||||
- не зависит от UI;
|
||||
- не зависит от full runtime orchestration;
|
||||
- нацелен на доводку `IntentRouterV2` + retrieval + evidence + LLM answers.
|
||||
|
||||
---
|
||||
|
||||
## 8. Самые важные изменения в реализации прямо сейчас
|
||||
|
||||
Приоритет:
|
||||
|
||||
### P1
|
||||
Сделать `IntentRouterV2` центром тестового MVP.
|
||||
|
||||
### P2
|
||||
Связать router output с реальным layered retrieval.
|
||||
|
||||
### P3
|
||||
Ввести единый `EvidenceBundle`.
|
||||
|
||||
### P4
|
||||
Сделать общий evidence gate для всего test pipeline.
|
||||
|
||||
### P5
|
||||
Привести диагностику к форме, удобной для настройки через тесты.
|
||||
|
||||
### P6
|
||||
Добавить `C5-lite` для `FIND_TESTS`.
|
||||
|
||||
---
|
||||
|
||||
## 9. Итоговая формулировка ближайшей цели
|
||||
|
||||
> Довести до зрелости изолированный code-first test pipeline на базе `IntentRouterV2`, в котором роутер управляет retrieval, retrieval формирует evidence bundle, evidence gate контролирует достаточность опоры, а LLM отвечает строго на основе извлеченного контекста и прозрачной диагностики.
|
||||
|
||||
Reference in New Issue
Block a user