دليل تطوير المزود
نظرة سريعة
ما: بناء مزودي أدلة مخصصين لبوابة القرار لماذا: توسيع مصادر الأدلة دون تعديل النواة من: مطورو المزودين، مهندسو التكامل المتطلبات المسبقة: provider_protocol.md، JSON-RPC 2.0
بنية المزود
مزود هو خادم MCP يقوم بتنفيذ أداة واحدة: evidence_query.
Provider
- Provider Contract (JSON)
- provider_id, name, description
- transport = "mcp"
- config_schema
- checks (params schema, result schema, comparators, examples)
- notes
- MCP Server
- tools/call -> evidence_query
Decision Gate
- Loads contract from capabilities_path
- Validates ScenarioSpec conditions against contract
- Calls evidence_query during scenario_next
قرار بوابة القرار لا يستخدم tools/list أثناء التشغيل. قم بتنفيذه من أجل توافق MCP، ولكن احتفظ بملف العقد كمرجع موثوق.
أنواع المزودين
| النوع | الإعدادات | النقل | حالة الاستخدام |
|---|---|---|---|
| مدمج | type = "builtin" | داخل العملية | time, env, json, http |
| MCP خارجي | type = "mcp" | stdio أو HTTP | مزودون مخصصون |
يتم تكوين مزودي MCP الخارجيين مع إما:
command = ["/path/to/provider", "arg"](stdio)url = "https://provider/rpc"(HTTP)
capabilities_path مطلوب لجميع مزودي MCP. يجب أن تكون أسماء المزودين فريدة؛ المعرفات المدمجة (time, env, json, http) محجوزة لـ type = "builtin" فقط.
بدء سريع: مزود الحد الأدنى
الخطوة 1: استخدم قالب SDK
تعيش القوالب في decision-gate-provider-sdk/ (بايثون، TypeScript، Go). إنها تنفذ:
- إطار طول المحتوى (stdio)
tools/listوtools/call- تحليل JSON-RPC
الخطوة 2: تنفيذ evidence_query
يجب على المعالج الخاص بك إرجاع كائن EvidenceResult (ليس خطأ JSON-RPC) للفشل العادي.
مثال (كود زائف):
def handle_evidence_query(query, context):
if query["check_id"] != "file_exists":
return {
"value": None,
"lane": "verified",
"error": {
"code": "unsupported_check",
"message": "unknown check",
"details": {"check_id": query["check_id"]}
},
"evidence_hash": None,
"evidence_ref": None,
"evidence_anchor": None,
"signature": None,
"content_type": None
}
rel_path = query.get("params", {}).get("path")
if not rel_path:
return {
"value": None,
"lane": "verified",
"error": {
"code": "params_missing",
"message": "missing path",
"details": {"param": "path"}
},
"evidence_hash": None,
"evidence_ref": None,
"evidence_anchor": None,
"signature": None,
"content_type": None
}
root = CONFIG["root"]
root_id = CONFIG["root_id"]
abs_path = os.path.normpath(os.path.join(root, rel_path))
exists = os.path.exists(abs_path)
return {
"value": {"kind": "json", "value": exists},
"lane": "verified",
"error": None,
"evidence_hash": None,
"evidence_ref": {"uri": f"dg+file://{root_id}/{rel_path}"},
"evidence_anchor": {
"anchor_type": "file_path_rooted",
"anchor_value": json.dumps({"root_id": root_id, "path": rel_path}, separators=(",", ":"), sort_keys=True)
},
"signature": None,
"content_type": "application/json"
}
الخطوة 3: إنشاء عقد مزود الخدمة
جميع الحقول أدناه مطلوبة بموجب مخطط العقد.
{
"provider_id": "file-provider",
"name": "File Provider",
"description": "File existence checks",
"transport": "mcp",
"config_schema": {
"type": "object",
"additionalProperties": false,
"properties": {}
},
"checks": [
{
"check_id": "file_exists",
"description": "Check if a file exists",
"determinism": "external",
"params_required": true,
"params_schema": {
"type": "object",
"additionalProperties": false,
"properties": { "path": { "type": "string" } },
"required": ["path"]
},
"result_schema": { "type": "boolean" },
"allowed_comparators": ["equals", "not_equals"],
"anchor_types": ["file_path_rooted"],
"content_types": ["application/json"],
"examples": [
{
"description": "Check a report file",
"params": { "path": "report.json" },
"result": true
}
]
}
],
"notes": [
"External: depends on the local filesystem state."
]
}
قواعد العقد المهمة:
allowed_comparatorsيجب أن تكون غير فارغة وفي ترتيب قياسي.params_requiredيجب أن يتطابق مع ما إذا كانparams_schemaيتطلب حقولًا.transportيجب أن يكون"mcp"لمقدمي الخدمات الخارجيين.
الخطوة 4: تكوين بوابة القرار
[[providers]]
name = "file-provider"
type = "mcp"
command = ["python3", "/path/to/provider.py"]
capabilities_path = "contracts/file-provider.json"
مهلات
تطبق بوابة القرار فقط مهلات HTTP على مزودي HTTP MCP:
[[providers]]
name = "cloud"
type = "mcp"
url = "https://provider.example.com/rpc"
capabilities_path = "contracts/cloud.json"
timeouts = { connect_timeout_ms = 2000, request_timeout_ms = 10000 }
لا يوجد وقت انتهاء لبوابة القرار لمزودي MCP من نوع stdio؛ حافظ على سرعة محدداتهم وموثوقيتها.
دليل التوقيع
عندما trust.default_policy = { require_signature = { keys = [...] } }، يجب على المزودين تضمين التوقيعات:
"signature": {
"scheme": "ed25519",
"key_id": "/etc/decision-gate/keys/provider.pub",
"signature": [1, 2, 3, 4]
}
خوارزمية التوقيع (بالضبط):
- Compute
evidence_hashfrom the evidence value.- قيمة JSON -> بايتات JSON القياسية -> sha256
- قيمة البايت -> sha256
- قم بتسلسل كائن HashDigest كـ JSON قياسي.
- قم بتوقيع تلك البايتات باستخدام Ed25519.
تتحقق بوابة القرار من التوقيع مقابل ملف المفتاح العام المكون. إذا كان evidence_hash مفقودًا، تقوم DG بحسابه قبل التحقق. إذا كان evidence_hash موجودًا، يجب أن يتطابق مع التجزئة القياسية لقيمة الدليل أو يتم رفض الاستجابة.
معالجة الأخطاء
- ارجع EvidenceResult.error للأخطاء المتوقعة.
- استخدم أخطاء JSON-RPC فقط لفشل البروتوكول (JSON-RPC غير صالح، أعطال داخلية). يقوم DG بتحويل أخطاء JSON-RPC إلى
provider_error.
أكواد الخطأ محددة من قبل المزود. يجب أن تبقى مستقرة وقابلة للقراءة من قبل الآلات (على سبيل المثال، params_missing, file_not_found).
مرجع
- مخطط عقد المزود:
crates/decision-gate-contract/src/schemas.rs(provider_contract_schema) - عقود مزودي الخدمة المدمجة: providers.json
- قوالب SDK:
decision-gate-provider-sdk/
معجم
EvidenceQuery: طلب { provider_id, check_id, params }. EvidenceResult: قيمة استجابة المزود + البيانات الوصفية. Provider Contract: وثيقة JSON تصف الفحوصات، المخططات، المقارنات، والأمثلة. Signature: توقيع Ed25519 على JSON القياسي لـ evidence_hash.