OmniRoute A2A Server (Tiếng Việt)
🌐 Languages: 🇺🇸 English · 🇪🇸 es · 🇫🇷 fr · 🇩🇪 de · 🇮🇹 it · 🇷🇺 ru · 🇨🇳 zh-CN · 🇯🇵 ja · 🇰🇷 ko · 🇸🇦 ar · 🇮🇳 hi · 🇮🇳 in · 🇹🇭 th · 🇻🇳 vi · 🇮🇩 id · 🇲🇾 ms · 🇳🇱 nl · 🇵🇱 pl · 🇸🇪 sv · 🇳🇴 no · 🇩🇰 da · 🇫🇮 fi · 🇵🇹 pt · 🇷🇴 ro · 🇭🇺 hu · 🇧🇬 bg · 🇸🇰 sk · 🇺🇦 uk-UA · 🇮🇱 he · 🇵🇭 phi · 🇧🇷 pt-BR · 🇨🇿 cs · 🇹🇷 tr
Giao thức giữa tác nhân với tác nhân v0.3— Cho phép mọi tác nhân AI sử dụng OmniRoute làm tác nhân định tuyến thông minh thông qua JSON-RPC 2.0.
Máy chủ A2A hiển thị OmniRoute với tư cách lànhân viên hỗ trợ hạng nhấtmà các nhân viên khác có thể khám phá, ủy quyền nhiệm vụ và cộng tác bằng cách sử dụng Giao thức A2A.---
Kiến trúc
┌──────────────────────────────────────────────────────────────────┐
│ Orchestrator Agent │
│ (LangChain, CrewAI, AutoGen, Custom Agent) │
└──────────────────────┬───────────────────────────────────────────┘
│ 1. GET /.well-known/agent.json (discover)
│ 2. POST /a2a (JSON-RPC 2.0)
▼
┌──────────────────────────────────────────────────────────────────┐
│ OmniRoute A2A Server │
│ ┌────────────────┐ ┌────────────────┐ ┌───────────────────┐ │
│ │ Task Manager │ │ Skill Engine │ │ SSE Streaming │ │
│ │ (lifecycle) │──│ (registry) │──│ (real-time) │ │
│ └────────────────┘ └────────┬───────┘ └───────────────────┘ │
│ │ │
│ Skills: │ │
│ ├─ smart-routing ──────────┤ ┌────────────────────────────┐ │
│ └─ quota-management ───────┘ │ Routing Decision Logger │ │
│ └────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────┘
│
▼ OmniRoute Gateway (internal)
/v1/chat/completions, /api/combos, /api/usage/quota
Bắt đầu nhanh
Agent Discovery
Mọi tác nhân tương thích với A2A đều hiển thịThẻ tác nhântại /.well-known/agent.json:```bash
curl http://localhost:20128/.well-known/agent.json
**Phản ứng:**```json
{
"name": "OmniRoute",
"description": "Intelligent AI gateway with auto-routing across 50+ providers",
"url": "http://localhost:20128/a2a",
"version": "1.8.1",
"capabilities": {
"streaming": true,
"pushNotifications": false
},
"skills": [
{
"id": "smart-routing",
"name": "Smart Routing",
"description": "Routes prompts through OmniRoute intelligent pipeline",
"tags": ["routing", "llm", "multi-provider", "cost-optimization"],
"examples": [
"Write a hello world in Python",
"Explain quantum computing using the cheapest provider"
]
},
{
"id": "quota-management",
"name": "Quota Management",
"description": "Natural-language queries about provider quotas",
"tags": ["quota", "analytics", "cost"],
"examples": [
"Which provider has the most quota remaining?",
"Suggest a free combo for coding"
]
}
],
"authentication": {
"schemes": ["bearer"],
"apiKeyHeader": "Authorization"
}
}
JSON-RPC 2.0 Methods
message/send — Synchronous Execution
Gửi tin nhắn đến một kỹ năng và nhận được phản hồi đầy đủ.```bash
curl -X POST http://localhost:20128/a2a
-H "Content-Type: application/json"
-H "Authorization: Bearer YOUR_KEY"
-d '{
"jsonrpc": "2.0",
"id": "1",
"method": "message/send",
"params": {
"skill": "smart-routing",
"messages": [{"role": "user", "content": "Write a Python hello world"}],
"metadata": {"model": "auto", "combo": "fast-coding"}
}
}'
**Phản ứng:**```json
{
"jsonrpc": "2.0",
"id": "1",
"result": {
"task": { "id": "a1b2c3d4-...", "state": "completed" },
"artifacts": [{ "type": "text", "content": "print('Hello, World!')" }],
"metadata": {
"routing_explanation": "Selected claude-sonnet via provider \"anthropic\" (latency: 1200ms, cost: $0.0030)",
"cost_envelope": { "estimated": 0.005, "actual": 0.003, "currency": "USD" },
"resilience_trace": [
{ "event": "primary_selected", "provider": "anthropic", "timestamp": "2026-03-04T..." }
],
"policy_verdict": { "allowed": true, "reason": "within budget and quota limits" }
}
}
}
message/stream — SSE Streaming
Tương tự như message/send nhưng trả về Sự kiện do máy chủ gửi để phát trực tuyến theo thời gian thực.```bash
curl -N -X POST http://localhost:20128/a2a
-H "Content-Type: application/json"
-H "Authorization: Bearer YOUR_KEY"
-d '{
"jsonrpc": "2.0",
"id": "1",
"method": "message/stream",
"params": {
"skill": "smart-routing",
"messages": [{"role": "user", "content": "Explain quantum computing"}]
}
}'
**Sự kiện SSE:**```
data: {"jsonrpc":"2.0","method":"message/stream","params":{"task":{"id":"...","state":"working"},"chunk":{"type":"text","content":"Quantum computing..."}}}
: heartbeat 2026-03-04T21:00:00Z
data: {"jsonrpc":"2.0","method":"message/stream","params":{"task":{"id":"...","state":"completed"},"metadata":{...}}}
tasks/get — Query Task Status
curl -X POST http://localhost:20128/a2a \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_KEY" \
-d '{"jsonrpc":"2.0","id":"2","method":"tasks/get","params":{"taskId":"TASK_UUID"}}'
tasks/cancel — Cancel a Running Task
curl -X POST http://localhost:20128/a2a \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_KEY" \
-d '{"jsonrpc":"2.0","id":"3","method":"tasks/cancel","params":{"taskId":"TASK_UUID"}}'
Skills Reference
smart-routing
Định tuyến các lời nhắc thông qua đường dẫn thông minh của OmniRoute với khả năng quan sát đầy đủ.
Thông số (trong siêu dữ liệu):
| Tham số | Loại | Mặc định | Mô tả |
|---|---|---|---|
người mẫu |
chuỗi |
"tự động" |
Mô hình mục tiêu (ví dụ: claude-sonnet-4, gpt-4o, auto) |
kết hợp |
chuỗi |
kết hợp hoạt động | Combo cụ thể để định tuyến |
ngân sách |
số |
không | Chi phí tối đa bằng USD cho yêu cầu này |
role |
chuỗi |
không | Gợi ý vai trò nhiệm vụ: viết mã, đánh giá, lập kế hoạch, phân tích, gỡ lỗi, tài liệu |
Trả về:
| Lĩnh vực | Mô tả | |
|---|---|---|
hiện vật[].content |
Văn bản phản hồi LLM | |
siêu dữ liệu.routing_explanation |
Giải thích dễ hiểu về quyết định định tuyến | |
siêu dữ liệu.cost_envelope |
Ước tính so với chi phí thực tế bằng đơn vị tiền tệ | |
siêu dữ liệu.khả năng phục hồi_trace |
Mảng sự kiện (primaryselected, fallback Need, v.v.) | |
siêu dữ liệu.chính sách_bản án |
Liệu yêu cầu có được cho phép hay không và tại sao | ### quota-management |
Trả lời các truy vấn bằng ngôn ngữ tự nhiên về hạn ngạch của nhà cung cấp.
Các loại truy vấn (suy ra từ nội dung tin nhắn):
| Mẫu truy vấn | Loại phản hồi | |
|---|---|---|
Chứa "xếp hạng", "hạn ngạch nhiều nhất", "tốt nhất" |
Nhà cung cấp được xếp hạng theo hạn ngạch còn lại | |
Chứa "miễn phí", "gợi ý" |
Liệt kê các combo miễn phí hoặc đề xuất các nhà cung cấp cấp miễn phí | |
| Mặc định | Tóm tắt hạn ngạch đầy đủ kèm theo cảnh báo dành cho nhà cung cấp hạn ngạch thấp | --- |
Task Lifecycle
submitted ──→ working ──→ completed
──→ failed
──────────→ cancelled
| Tiểu bang | Mô tả |
|---|---|
đã gửi |
Tác vụ đã được tạo, xếp hàng để thực hiện |
làm việc |
Trình xử lý kỹ năng đang thực thi |
hoàn thành |
Thực hiện thành công, hiện vật có sẵn |
thất bại |
Thực thi không thành công hoặc tác vụ đã hết hạn (TTL: mặc định 5 phút) |
đã hủy |
Đã bị khách hàng hủy qua tasks/cancel |
- Trạng thái đầu cuối:
đã hoàn thành,không thành công,đã hủy(không có chuyển tiếp nào nữa) - Các tác vụ đã hết hạn trong
đã gửihoặcđang làm việcđược tự động đánh dấu làkhông thành công - Các tác vụ được thu gom rác sau 2× TTL---
Client Examples
Python — Orchestrator Agent
"""
A2A Client — Python example.
Discovers OmniRoute agent, sends a task, and processes the result.
"""
import requests
import json
BASE_URL = "http://localhost:20128"
API_KEY = "your-api-key"
HEADERS = {
"Content-Type": "application/json",
"Authorization": f"Bearer {API_KEY}",
}
# 1. Discover agent capabilities
agent_card = requests.get(f"{BASE_URL}/.well-known/agent.json").json()
print(f"Agent: {agent_card['name']} v{agent_card['version']}")
print(f"Skills: {[s['id'] for s in agent_card['skills']]}")
# 2. Send a smart-routing task
response = requests.post(f"{BASE_URL}/a2a", headers=HEADERS, json={
"jsonrpc": "2.0",
"id": "task-1",
"method": "message/send",
"params": {
"skill": "smart-routing",
"messages": [{"role": "user", "content": "Write a Python quicksort implementation"}],
"metadata": {
"model": "auto",
"combo": "fast-coding",
"budget": 0.10,
}
}
})
result = response.json()["result"]
print(f"\n📝 Response: {result['artifacts'][0]['content'][:200]}...")
print(f"🔀 Routing: {result['metadata']['routing_explanation']}")
print(f"💰 Cost: ${result['metadata']['cost_envelope']['actual']}")
print(f"🛡️ Policy: {result['metadata']['policy_verdict']['reason']}")
# 3. Query quota status
quota_resp = requests.post(f"{BASE_URL}/a2a", headers=HEADERS, json={
"jsonrpc": "2.0",
"id": "task-2",
"method": "message/send",
"params": {
"skill": "quota-management",
"messages": [{"role": "user", "content": "Which provider has the most quota remaining?"}],
}
})
quota_result = quota_resp.json()["result"]
print(f"\n📊 Quota: {quota_result['artifacts'][0]['content']}")
TypeScript — Multi-Agent Orchestrator
/**
* A2A Client — TypeScript example.
* Shows agent discovery, task delegation, and streaming.
*/
const BASE_URL = "http://localhost:20128";
const API_KEY = "your-api-key";
interface JsonRpcResponse<T = any> {
jsonrpc: "2.0";
id: string | number;
result?: T;
error?: { code: number; message: string };
}
async function a2aCall<T>(method: string, params: Record<string, any>): Promise<T> {
const resp = await fetch(`${BASE_URL}/a2a`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${API_KEY}`,
},
body: JSON.stringify({
jsonrpc: "2.0",
id: `${method}-${Date.now()}`,
method,
params,
}),
});
const json: JsonRpcResponse<T> = await resp.json();
if (json.error) throw new Error(`[${json.error.code}] ${json.error.message}`);
return json.result!;
}
// ── Agent Discovery ──
const agentCard = await fetch(`${BASE_URL}/.well-known/agent.json`).then((r) => r.json());
console.log(`Connected to: ${agentCard.name} (${agentCard.skills.length} skills)`);
// ── Smart Routing: Send a coding task ──
const routingResult = await a2aCall("message/send", {
skill: "smart-routing",
messages: [{ role: "user", content: "Implement a Redis cache wrapper in TypeScript" }],
metadata: { model: "claude-sonnet-4", role: "coding" },
});
console.log("Response:", routingResult.artifacts[0].content);
console.log("Provider:", routingResult.metadata.routing_explanation);
// ── Quota Management: Find free alternatives ──
const quotaResult = await a2aCall("message/send", {
skill: "quota-management",
messages: [{ role: "user", content: "Suggest free combos for documentation" }],
});
console.log("Free combos:", quotaResult.artifacts[0].content);
// ── Streaming: Real-time response ──
const streamResp = await fetch(`${BASE_URL}/a2a`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${API_KEY}`,
},
body: JSON.stringify({
jsonrpc: "2.0",
id: "stream-1",
method: "message/stream",
params: {
skill: "smart-routing",
messages: [{ role: "user", content: "Explain microservices architecture" }],
},
}),
});
const reader = streamResp.body!.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
for (const line of chunk.split("\n")) {
if (line.startsWith("data: ")) {
const event = JSON.parse(line.slice(6));
if (event.params.chunk) {
process.stdout.write(event.params.chunk.content);
}
if (event.params.task.state === "completed") {
console.log("\n✅ Stream completed");
}
}
}
}
Python — LangChain A2A Integration
"""
LangChain integration — Use OmniRoute A2A as a custom LLM.
"""
from langchain.llms.base import BaseLLM
from langchain.schema import LLMResult, Generation
import requests
from typing import List, Optional
class OmniRouteA2A(BaseLLM):
base_url: str = "http://localhost:20128"
api_key: str = ""
model: str = "auto"
combo: Optional[str] = None
@property
def _llm_type(self) -> str:
return "omniroute-a2a"
def _call(self, prompt: str, stop: Optional[List[str]] = None, **kwargs) -> str:
response = requests.post(
f"{self.base_url}/a2a",
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {self.api_key}",
},
json={
"jsonrpc": "2.0",
"id": "langchain-1",
"method": "message/send",
"params": {
"skill": "smart-routing",
"messages": [{"role": "user", "content": prompt}],
"metadata": {
"model": self.model,
**({"combo": self.combo} if self.combo else {}),
},
},
},
)
result = response.json()["result"]
return result["artifacts"][0]["content"]
def _generate(self, prompts: List[str], stop=None, **kwargs) -> LLMResult:
return LLMResult(
generations=[[Generation(text=self._call(p, stop))] for p in prompts]
)
# Usage
llm = OmniRouteA2A(
base_url="http://localhost:20128",
api_key="your-key",
model="auto",
combo="fast-coding",
)
result = llm("Write a Python function to merge two sorted lists")
print(result)
Go — A2A Client
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
const baseURL = "http://localhost:20128"
const apiKey = "your-api-key"
type JsonRpcRequest struct {
Jsonrpc string `json:"jsonrpc"`
ID string `json:"id"`
Method string `json:"method"`
Params interface{} `json:"params"`
}
type JsonRpcResponse struct {
Jsonrpc string `json:"jsonrpc"`
ID string `json:"id"`
Result interface{} `json:"result"`
Error *struct {
Code int `json:"code"`
Message string `json:"message"`
} `json:"error"`
}
func a2aCall(method string, params interface{}) (*JsonRpcResponse, error) {
body, _ := json.Marshal(JsonRpcRequest{
Jsonrpc: "2.0",
ID: "go-1",
Method: method,
Params: params,
})
req, _ := http.NewRequest("POST", baseURL+"/a2a", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+apiKey)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
data, _ := io.ReadAll(resp.Body)
var result JsonRpcResponse
json.Unmarshal(data, &result)
return &result, nil
}
func main() {
// Discover agent
resp, _ := http.Get(baseURL + "/.well-known/agent.json")
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println("Agent Card:", string(body))
// Send smart-routing task
result, _ := a2aCall("message/send", map[string]interface{}{
"skill": "smart-routing",
"messages": []map[string]string{{"role": "user", "content": "Hello from Go!"}},
"metadata": map[string]interface{}{"model": "auto"},
})
out, _ := json.MarshalIndent(result.Result, "", " ")
fmt.Println("Result:", string(out))
}
Use Cases
🤖 Use Case 1: Multi-Agent Coding Pipeline
Tác nhân điều phối ủy quyền việc tạo mã cho OmniRoute, sau đó chuyển kết quả đầu ra cho tác nhân đánh giá.```python def coding_pipeline(task: str): # Step 1: Generate code via OmniRoute A2A code_result = a2a_send("smart-routing", [ {"role": "user", "content": f"Write production-quality code: {task}"} ], metadata={"model": "auto", "role": "coding"}) code = code_result["artifacts"][0]["content"]
# Step 2: Review the code via OmniRoute A2A (different model)
review_result = a2a_send("smart-routing", [
{"role": "user", "content": f"Review this code for bugs and improvements:\n\n{code}"}
], metadata={"model": "auto", "role": "review"})
review = review_result["artifacts"][0]["content"]
# Step 3: Check costs
print(f"Code cost: ${code_result['metadata']['cost_envelope']['actual']}")
print(f"Review cost: ${review_result['metadata']['cost_envelope']['actual']}")
return {"code": code, "review": review}
### 💡 Use Case 2: Quota-Aware Agent Swarm
Nhiều đại lý chia sẻ hạn ngạch thông qua OmniRoute, sử dụng kỹ năng hạn ngạch để phối hợp.```python
async def quota_aware_agent(agent_name: str, task: str):
# Check quota before starting
quota = a2a_send("quota-management", [
{"role": "user", "content": "Which provider has the most quota remaining?"}
])
print(f"[{agent_name}] {quota['artifacts'][0]['content']}")
# Send request with budget constraint
result = a2a_send("smart-routing", [
{"role": "user", "content": task}
], metadata={"budget": 0.05})
policy = result["metadata"]["policy_verdict"]
if not policy["allowed"]:
print(f"[{agent_name}] ⚠️ Budget exceeded: {policy['reason']}")
# Fall back to free combo
quota = a2a_send("quota-management", [
{"role": "user", "content": "Suggest free combos"}
])
print(f"[{agent_name}] Free alternatives: {quota['artifacts'][0]['content']}")
return result
📊 Use Case 3: Real-Time Streaming Dashboard
Tác nhân giám sát truyền các phản hồi và hiển thị tiến trình trong thời gian thực.```typescript
async function streamingDashboard(prompt: string) {
const response = await fetch(${BASE_URL}/a2a, {
method: "POST",
headers: { "Content-Type": "application/json", Authorization: Bearer ${API_KEY} },
body: JSON.stringify({
jsonrpc: "2.0",
id: "dash-1",
method: "message/stream",
params: { skill: "smart-routing", messages: [{ role: "user", content: prompt }] },
}),
});
let totalChunks = 0; const reader = response.body!.getReader(); const decoder = new TextDecoder();
while (true) { const { done, value } = await reader.read(); if (done) break;
for (const line of decoder.decode(value).split("\n")) {
if (line.startsWith("data: ")) {
const event = JSON.parse(line.slice(6));
const state = event.params.task.state;
if (state === "working" && event.params.chunk) {
totalChunks++;
process.stdout.write(
`\r[Chunk ${totalChunks}] ${event.params.chunk.content.slice(0, 50)}...`
);
}
if (state === "completed") {
const meta = event.params.metadata;
console.log(
`\n✅ Done | Cost: $${meta?.cost_envelope?.actual || 0} | Route: ${meta?.routing_explanation || "N/A"}`
);
}
if (state === "failed") {
console.error(`\n❌ Failed: ${event.params.metadata?.error}`);
}
}
}
} }
### 🔁 Use Case 4: Task Polling Pattern
Đối với các tác vụ chạy dài, hãy thăm dò trạng thái tác vụ thay vì chờ đợi đồng bộ.```python
import time
def poll_task(task_id: str, timeout: int = 60):
"""Poll task status until completion or timeout."""
start = time.time()
while time.time() - start < timeout:
result = requests.post(f"{BASE_URL}/a2a", headers=HEADERS, json={
"jsonrpc": "2.0",
"id": "poll-1",
"method": "tasks/get",
"params": {"taskId": task_id},
}).json()
task = result["result"]["task"]
state = task["state"]
print(f" Task {task_id[:8]}... state={state}")
if state in ("completed", "failed", "cancelled"):
return task
time.sleep(2)
# Timeout — cancel the task
requests.post(f"{BASE_URL}/a2a", headers=HEADERS, json={
"jsonrpc": "2.0",
"id": "cancel-1",
"method": "tasks/cancel",
"params": {"taskId": task_id},
})
raise TimeoutError(f"Task {task_id} timed out after {timeout}s")
Error Codes
| Mã | Hằng số | Ý nghĩa | |
|---|---|---|---|
| -32700 | — | Lỗi phân tích cú pháp (JSON không hợp lệ) | |
| -32600 | INVALID_REQUEST |
Yêu cầu JSON-RPC không hợp lệ hoặc trái phép | |
| -32601 | PHƯƠNG PHÁP_NOT_FOUND |
Phương pháp hoặc kỹ năng không xác định | |
| -32602 | INVALID_PARAMS |
Tham số bị thiếu hoặc không hợp lệ | |
| -32603 | NỘI BỘ_ERROR |
Thực hiện kỹ năng không thành công | |
| -32001 | TASK_NOT_FOUND |
Không tìm thấy ID nhiệm vụ | |
| -32002 | TASK_ALREADY_COMPLETED |
Không thể sửa đổi một nhiệm vụ đã hoàn thành | |
| -32003 | TRÁI PHÉP |
Khóa API không hợp lệ hoặc bị thiếu | |
| -32004 | BUDGET_EXCEEDED |
Yêu cầu vượt quá ngân sách đã định cấu hình | |
| -32005 | PROVIDER_UNAVAILABLE |
Không có nhà cung cấp nào | --- |
Authentication
Tất cả các yêu cầu /a2a đều yêu cầu mã thông báo Bearer thông qua tiêu đề Ủy quyền:```
Authorization: Bearer YOUR_OMNIROUTE_API_KEY
Nếu không có khóa API nào được định cấu hình trên máy chủ (`OMNIROUTE_API_KEY` trống), xác thực sẽ bị bỏ qua.---
## File Structure
src/lib/a2a/ ├── taskManager.ts # Task lifecycle (create/update/cancel/list), TTL, cleanup ├── taskExecution.ts # Generic task executor with state management ├── streaming.ts # SSE stream formatting, heartbeat, chunk/completion events ├── routingLogger.ts # Routing decision logger (stats, history, retention) └── skills/ ├── smartRouting.ts # Smart routing skill (routes via /v1/chat/completions) └── quotaManagement.ts # Quota management skill (natural-language quota queries)
src/app/a2a/ └── route.ts # Next.js API route handler (JSON-RPC 2.0 dispatch)
open-sse/mcp-server/ └── schemas/a2a.ts # Zod schemas (AgentCard, Task, JSON-RPC, SSE events)
---
## Comparison: MCP vs A2A
| Tính năng | Máy chủ MCP | Máy chủ A2A |
| ----------------- | ---------------------------- | ------------------------------------------------- |
|**Giao thức**| Giao thức bối cảnh mô hình | Giao thức giữa tác nhân với tác nhân v0.3 |
|**Giao thông**| stdio / HTTP | HTTP (JSON-RPC 2.0) |
|**Khám phá**| Danh sách công cụ qua MCP | `/.well-known/agent.json` |
|**Mức độ chi tiết**| 16 công cụ riêng lẻ | 2 kỹ năng cấp cao |
|**Tốt nhất cho**| Tác nhân IDE (Con trỏ, Mã VS) | Hệ thống đa tác nhân (LangChain, CrewAI) |
|**Truyền phát**| Không được hỗ trợ | SSE qua `tin nhắn/luồng` |
|**Theo dõi nhiệm vụ**| Không | Toàn bộ vòng đời (đã gửi → đã hoàn thành) |
|**Khả năng quan sát**| Nhật ký kiểm tra mỗi cuộc gọi công cụ | Đường bao chi phí + dấu vết khả năng phục hồi + phán quyết chính sách |---
## Giấy phép
Một phần của [OmniRoute](https://github.com/diegosouzapw/OmniRoute) — Giấy phép MIT.