Files
姚章浩 693cd952cd feat(asset): 清理被截断的结尾图片引用,修复整单 502
仓库首次入库(此前仅存在于本地工作区与测试服构建产物中,未上传)。

改动(src/knowledge_adapter/asset_content_processor.py):
- 新增 _TRUNCATED_TRAILING_MARKDOWN_RESOURCE_IMAGE 规则,清除 chunk 末尾被截断的
  ![](resource://xxxx 残片(WeKnora 分块在图片引用中间截断时产生,旧逻辑无法匹配完整引用,
  残留 resource:// 触发 ContentProcessingError,导致整单 502)
- 回归测试 tests/test_asset_content_processor.py::
  test_truncated_trailing_markdown_resource_image_is_removed

验证证据:
- 测试服 120.76.217.51,镜像 knowledge-adapter-service:reviewed-test-aeab4b5fca2d
  (release aeab4b5fca2d8661):KB d015d05c-25c8-4d63-b2ab-e8590d3c2dfb 5 个查询全部
  http=200、records=5、残留 resource:// = 0;KB b9dacd62 无退化;容器日志无 error/exception
- 本地:pytest 141 项全通过、ruff 全通过
- 该文件与 258c31e0fe07fd08 源码的差异经逐文件 md5 比对,为唯一变更文件
2026-09-16 10:01:27 +08:00

98 lines
4.7 KiB
Markdown

# Knowledge Adapter Architecture
## Scope
Knowledge Adapter is a small protocol and security boundary between Dify and an existing WeKnora deployment. It does not modify WeKnora and does not reimplement RAG, embedding, reranking, parsing, or object storage.
Public API:
- `POST /retrieval`: authenticated Dify External Knowledge retrieval.
- `GET /i/{UUID}`: anonymous stable image URL; possession of the UUID URL grants image access.
- `GET /healthz`: process liveness only; no dependency or configuration disclosure.
## Runtime topology
```text
Dify
-> HTTPS POST /retrieval
-> host Nginx
-> Adapter :8080
-> private WeKnora POST /api/v1/knowledge-search
-> projection + image rewrite + final-content budgets
-> Dify records
Browser
-> HTTPS GET /i/{UUID}
-> host Nginx
-> Adapter
-> Adapter DB asset_mapping (active row only)
-> read-only mounted static AK Secret files
-> Private OSS V4 Presigned GET URL (600 seconds)
-> HTTP 302
```
Nginx is the TLS boundary. Compose publishes Adapter only at `127.0.0.1:18080`; the Adapter joins a configurable external `WEKNORA_NETWORK_NAME` so `WEKNORA_BASE_URL=http://app:8080` reaches the existing WeKnora App without changing WeKnora source or Compose. Its separate `adapter` bridge remains the default network for isolation. No database, Redis, metadata, or OSS endpoint is exposed by this project.
## Components
### Retrieval path
1. `DifyKeyAuthenticator` compares the presented key hash against `DIFY_KEY_HASH_CURRENT` and optional `DIFY_KEY_HASH_NEXT` in constant time.
2. `RetrievalService` checks the knowledge-base allowlist and clamps TopK.
3. `WeKnoraClient` calls the existing private WeKnora search endpoint with a fixed base URL and timeout.
4. Each returned chunk is revalidated against the requested knowledge-base UUID.
5. `AssetContentProcessor` rewrites permitted image locators and rejects unprocessed internal locators.
6. Per-chunk and total limits are applied to the final rewritten content.
### Image mapping path
```text
resource://handle
-> Adapter DB lookup
-> on miss only: dedicated WeKnora read-only DB resolver
-> fixed tenant/backend/provider/bucket checks
-> Adapter DB atomic mapping insert
-> https://assets.example.com/i/{UUID}
```
`asset_mapping` stores only the stable UUID mapping, source reference, OSS object key, content type, creation time, and active/disabled status. It never stores credentials or a Presigned URL.
### Image redirect path
`GET /i/{UUID}` accepts only a UUID path value. The caller cannot provide bucket, region, object key, endpoint, expiry, or another URL. The service reads an active mapping, reads the dedicated OSS AccessKey pair from static AK Secret files, produces a V4 GET URL for the configured Private OSS bucket, and returns it only in the 302 `Location` header. `Cache-Control: no-store` forces a later visit to generate a fresh 600-second URL while the public UUID remains unchanged.
## Data and identity boundaries
- Adapter DB account: read/write only for Adapter-owned `asset_mapping`.
- WeKnora read-only DB account: field-level `SELECT` only on the required resource locator columns.
- WeKnora API key: search API only; mounted as a Secret file.
- OSS RAM user static AK: separate test/production identities; production policy is `oss:GetObject` only for the reviewed bucket/object prefix.
- Dify key: Adapter stores only SHA-256 hashes; raw keys remain outside Git and Compose environment.
The two database accounts must never be reused across databases.
## Availability and scaling
The service is stateless except for Adapter DB mappings. Multiple Adapter replicas can share the same Adapter DB; mapping creation is conflict-safe. Each process currently opens up to five Adapter DB connections and two WeKnora read-only DB connections. Capacity planning must multiply these limits by process and replica count before scaling.
`/healthz` proves only that the Adapter process can answer. Dependency readiness and business E2E are separate acceptance gates.
## Failure contract
- Invalid request or disallowed KB: fixed 400.
- WeKnora unavailable: fixed 503.
- Invalid WeKnora response or unsafe content: fixed 502.
- Missing/disabled image UUID: empty 404; signer is not called.
- Adapter DB, OSS credential files, or signing unavailable: empty 503.
- Unexpected error: fixed JSON 500.
Every response carries a sanitized `X-Request-ID`. Request logs use route templates and fixed error codes, not raw URLs or sensitive content.
## Non-goals
- Production deployment before Slice 9 review acceptance.
- WeKnora source or schema changes.
- Public OSS bucket access.
- Arbitrary URL fetch/upload, proxying, or SSRF-capable media import.
- OAuth, JWT, multi-tenancy, an administration UI, LLM summarization, or replacement retrieval logic.