---
title: "Triết lý kiến trúc"
description: "SotaAgents chủ động tách platform control plane khỏi app data plane. Platform biết user, organization, workspace, installation, selected environment, exact artifact và quyền đã…"
url: "https://sotaagents.ai/vi/manual/developer-guide/architecture-philosophy"
generated_by: "sotaagents-ldp"
docs_index: "https://sotaagents.ai/manual/llms.txt"
locale: "vi"
---

# Triết lý kiến trúc

SotaAgents chủ động tách **platform control plane** khỏi **app data plane**. Platform biết user, organization, workspace, installation, selected environment, exact artifact và quyền đã cấp. Backend của app hiểu domain: tìm tài liệu, tạo CAD, gọi API riêng hay áp dụng business rules.

![Sơ đồ request đi qua SotaAgents tới exact app environment](/manual/assets/developer/app-architecture.svg?v=2)
_Một lần quyết định authority bind request với exact artifact, UI bundle, backend và data theo environment._

### Tại sao app cần backend riêng?

- **Ownership độc lập:** ship business logic theo nhịp riêng mà không nhét domain code vào Core.
- **Security boundary:** giữ credentials và third-party integration ở server; chỉ nhận signed platform context.
- **Data boundary:** tự chọn database, retention, residency và scaling theo domain.
- **Operational isolation:** app scale, fail và recover mà không kéo theo app khác.

> [!WARNING]
> Backend riêng không có nghĩa là identity system riêng.
>
> Không tin actor/workspace/environment tùy ý từ browser. Hãy verify signed Sota request và dùng exact context platform đã chọn.

### Trợ lý gọi tool của bạn như thế nào?

Khai báo tool trong manifest mới là một nửa câu chuyện. Đây là những gì nền tảng làm với khai báo đó lúc runtime — phần khiến một app không chỉ là một web service được host.

1. #### Tool được đưa cho model

   Khi workspace resolve app surface, mỗi tool đã publish trở thành một function model gọi được, tên `app_<appId>_<toolName>`. Development và Staging chèn thêm environment (`app_my-app_stg_example`); Production bỏ qua phần đó nên tên model học được ở Production luôn gọn. Ký tự mà tên model-facing không mang được — kể cả dấu chấm trong tên kiểu `documents.query` — chuyển thành gạch dưới; tên dài quá 64 ký tự bị cắt và thêm hậu tố hash ngắn; hai app trùng tên thì thêm hậu tố số. Description model đọc là `description` trong manifest của bạn, nối thêm `App: <appId>. Runtime tool: <name>.`, còn `inputSchema` được đưa nguyên văn cho model làm parameter schema. Vì vậy description mơ hồ hay schema lỏng lẻo làm giảm chất lượng chọn tool ngay lập tức: đó là toàn bộ căn cứ để model quyết định.

2. #### Nền tảng resolve và authorize

   Trước khi có request nào rời SotaAgents, arguments được validate theo input schema của bạn, và nền tảng resolve organization, workspace, installation, environment cùng đúng một artifact áp dụng. Tool không được publish trong artifact đó, hoặc app chưa cài cho workspace đó, bị từ chối ngay tại đây — backend của bạn không hề bị gọi tới.

3. #### Một signed request tới backend của bạn

   Nền tảng gửi HTTP request server-to-server tới `service.baseUrl` đã resolve cho environment đang dùng, tại path bạn khai báo. Ở Development, chính request đó đi qua tunnel của `sota dev` về máy bạn. Browser không nằm trong đường đi này, nên backend của bạn không cần user truy cập được từ internet công cộng.

4. #### Backend verify, xử lý và trả JSON

   Verify token, làm phần việc nghiệp vụ, trả JSON. Response non-2xx, body không phải JSON hợp lệ, hoặc timeout đều bị coi là tool thất bại.

5. #### Kết quả quay lại conversation

   Nền tảng stream tool result vào conversation dưới dạng output part của tool, lưu lại, và nếu có UI contribution khai báo `surface: tool-view` với tool này trong `toolNames` thì render React module của bạn thay cho kết quả thô. Model nhìn thấy một bản JSON gọn của cùng kết quả đó.

### Request nền tảng gửi đi

Body là một envelope. Arguments của model nằm lồng ở `body.input` — đó là lý do route trong scaffold đọc `request.body.body.input` chứ không phải `request.body`.

HTTP

```
POST https://my-app.example.com/tools/example
content-type: application/json
authorization: Bearer <invocation JWT>
x-sota-core-token: <delegated Core token>   # chỉ khi app khai báo Core tool grants

{
  "requestId": "01K...",
  "organizationId": "6a66...",
  "workspaceId": "6a66...",
  "appId": "my-first-project-demo",
  "hook": { "type": "tool", "name": "example" },
  "body": {
    "input":   { "message": "hello" },
    "context": { "organizationId": "…", "workspaceId": "…", "userId": "…",
                 "conversationId": "…", "agentId": "…", "toolCallId": "…", "config": {} }
  }
}
```

> [!WARNING]
> Tin token, đừng tin envelope.
>
> Các field `organizationId`, `workspaceId` và `context` trong body chỉ để tiện log và debug. Mọi quyết định authorization phải lấy từ claims đã verify trong JWT, vì chỉ chúng mới được ký.

### Invocation token

Header `Authorization` mang một JWT ngắn hạn ký bằng EdDSA, mint riêng cho lần gọi này. Backend của bạn verify nó bằng public key của nền tảng, công bố tại `<core origin>/.well-known/jwks.json`. App không giữ private key của Sota và không bao giờ tự mint token.

| Thuộc tính | Giá trị |
| --- | --- |
| Algorithm / `typ` | `EdDSA` (Ed25519), header type `sota-invocation+jwt` |
| `iss` | `sota/invocation-token` |
| `aud` | `appId` của bạn — hãy từ chối token mint cho app khác |
| Thời hạn | 60 giây, chấp nhận lệch đồng hồ 30 giây |
| `oid` / `wid` | Organization và workspace của lần gọi |
| `sub` | User thực hiện, khi lần gọi có actor |
| `iid` | Compatibility install identity dạng opaque; không suy ra Development/Staging/Production từ prefix. |
| `ae` / `aei` / `ag` | Tên App Environment, environment identity và Backend Access Generation chính xác. Ba claim đi cùng nhau để bind quyền vào đúng backend đã resolve. |
| `aer` | Execution reference chính xác đã ký. Công việc persist/replay phải giữ reference này thay vì resolve artifact mới hơn. |
| `scp` | Các họ scope hiện tại gồm `tool:<name>`, `route:prompt:<name>`, `route:systemPrompt:<name>`, `event:<name>`, `route:artifact:<name>`, `route:resolver:<path>`, scope route lifecycle và `app:http`. Mỗi endpoint phải yêu cầu đúng scope chính xác được cấp cho bề mặt đó. |
| `rid` | `requestId` trong envelope, để đối chiếu log |

Mỗi route hãy yêu cầu scope hẹp nhất: handler `/tools/example` nên đòi `tool:example`, không rộng hơn. File `src/backend/sota-auth.ts` trong scaffold có sẵn toàn bộ bước kiểm tra — issuer, audience, algorithm, type, hạn dùng, tenant claims và scope — trong một middleware dùng lại được.

### Response phải trông như thế nào

Trả JSON HTTP bình thường. Không có wrapper riêng nào của Sota phải dựng.

| Kết quả | Nền tảng xử lý |
| --- | --- |
| 2xx kèm body JSON | Thành công. Body trở thành tool result. |
| Non-2xx | Thất bại. Nếu body là `{ "code": "…", "message": "…" }` — có thể kèm `details` và `hint` — đúng các giá trị đó được mang đi thay cho thông báo chung chung. Hãy trả status code thật thay vì body lỗi đội lốt thành công. |
| Body không phải JSON hợp lệ | Thất bại với `VALIDATION_ERROR`. |
| Không kịp trả lời trong deadline | Thất bại với `TIMEOUT`. |

`outputSchema` không được kiểm tra với response lúc runtime — nó được kiểm tra khi publish, nơi nó dùng để phát hiện breaking change giữa các version. Hãy tự validate output ở boundary; schema là contract bạn cam kết với consumer và renderer, không phải một lớp chặn lúc chạy.

Hai reserved key tùy chọn cho phép một kết quả phục vụ hai đối tượng. Đặt kết luận gọn cho model và payload đầy đủ cho renderer trong cùng một response, rồi dùng `_sota.modelOutput` để đưa cho model một bản chiếu hoàn toàn khác, hoặc `_sota.modelProjection.omitKeys` để loại bỏ các key chỉ dành cho renderer khỏi phần model đọc. Bản chiếu model-visible bị cắt ở khoảng 32.000 ký tự, nên hãy giữ nó ở mức kết luận, id, số đếm và citation thay vì tài liệu thô.
