# API result truncation: agents को गलत बदलाव करने से रोकें

Agents को नुकसानदेह बदलाव करने के लिए किसी malicious tool की ज़रूरत नहीं होती। ऐसा tool ही काफी है जो चुपचाप अपने जवाब का केवल एक हिस्सा लौटाए। Agent को ऐसा search result दें जो पूरा दिखाई दे, फिर उससे वह सब हटाने को कहें जो search में नहीं मिला। वह अक्सर गलत premises से निकली, पूरी तरह तार्किक गलती कर बैठेगा।

इसका समाधान agent को सावधान रहने की सलाह देने वाला लंबा system prompt नहीं है। Tool को स्थिर, machine-readable रूप में बताना चाहिए कि मांगा गया काम पूरा हुआ या नहीं, क्या छोड़ा गया, क्यों छोड़ा गया और caller आगे कैसे बढ़ सकता है। अगर tool यह नहीं बता सकता, तो agent को result में किसी चीज़ की अनुपस्थिति को broad change की अनुमति नहीं मानना चाहिए।

## Partial data और empty data अलग दावे हैं

Empty result का मतलब है कि tool ने जिस scope को वास्तव में जांचा, उसमें उसे matching items नहीं मिले। Complete empty result का मतलब है कि tool ने मांगे गए पूरे scope को जांचा और कुछ नहीं पाया। ये अलग दावे हैं, लेकिन ज़्यादातर API contracts दोनों को एक ही `[]` में समेट देते हैं।

इससे एक खास गलत निष्कर्ष निकलता है:

1. Agent उन सभी service accounts के लिए पूछता है जिनका कोई current owner नहीं है।
2. API पहले page को scan करने, result cap पर रुकने या ऐसे records छोड़ने के बाद empty array लौटाता है जिन्हें token पढ़ नहीं सकता।
3. Agent निष्कर्ष निकालता है कि हर account का owner है।
4. वह इसी निष्कर्ष के आधार पर किसी संबंधित control, report या cleanup job में बदलाव कर देता है।

Agent को English समझने में गलती नहीं हुई। Tool ने ऐसा जवाब दिया जिसकी बनावट server की जानकारी से ज़्यादा का संकेत दे रही थी।

Tool को कम से कम चार स्थितियों में अंतर करना चाहिए। Complete query items लौटा सकती है। Complete query कोई item नहीं लौटा सकती। Incomplete query कुछ items लौटा सकती है। Incomplete query कोई item नहीं लौटा सकती। केवल तीसरी स्थिति को महत्वपूर्ण मानना सबसे खतरनाक मामले को छोड़ देता है: ऐसा empty response जो agent को यकीन दिला दे कि समस्या मौजूद ही नहीं है।

Permissions इस समस्या को और बढ़ाती हैं। कई services inaccessible objects को जानबूझकर empty collection या filtered collection के रूप में छिपाती हैं, permission error के रूप में नहीं। मानव-केंद्रित interface के लिए यह व्यवहार उचित हो सकता है। लेकिन autonomous cleanup task के लिए यह तब तक स्वीकार्य evidence नहीं है, जब तक API साफ़ तौर पर न बताए कि caller को किस visibility के साथ जवाब मिला।

Contract के रूप में `Some results may be missing` जैसे prose का इस्तेमाल न करें। इससे agent को कोई भरोसेमंद branch नहीं मिलती और engineer को कोई testable condition नहीं मिलती। `complete` नाम का boolean field उबाऊ दिखता है। यही इसका उद्देश्य है।

## Successful HTTP response भी incomplete answer हो सकता है

HTTP status codes client और server के बीच हुए exchange का वर्णन करते हैं। वे अपने-आप यह साबित नहीं करते कि search, inventory या export ने मांगे गए पूरे domain को cover किया।

RFC 9110 HTTP status codes का अर्थ परिभाषित करता है। `200 OK` का मतलब है कि method के semantics के अनुसार request सफल हुई। इसका मतलब यह नहीं कि search ने हर page, हर partition, हर permission domain या deadline से पहले हर record को cover किया। Teams अक्सर `200` से protocol के वादे से ज़्यादा अर्थ निकालती हैं।

यह response देखें:

```json
HTTP/1.1 200 OK
Content-Type: application/json

{
  "items": [],
  "next_cursor": null
}
```

यह अंतिम जवाब जैसा दिखता है। लेकिन `next_cursor: null` सिर्फ़ यह बताता है कि इस खास pagination mechanism में अगला page नहीं है। यह backend result cap, expired search job, failed source, policy के कारण बाहर रखे गए records या silently limited lookback time के बारे में कुछ नहीं बताता।

Response तभी उपयोगी evidence बनता है जब contract बताए कि `complete` किस scope पर लागू है। Account search के लिए इसका मतलब calling principal को दिखाई देने वाले सभी accounts हो सकता है, किसी बताए गए snapshot पर। Code search के लिए इसका मतलब किसी निश्चित revision में indexed सभी files हो सकता है, साथ में ignored files और unindexed generated output को साफ़ तौर पर बाहर रखते हुए। Scope इतना ठोस होना चाहिए कि caller तय कर सके कि वह proposed action से मेल खाता है या नहीं।

हर partial response के लिए `500` लौटाना इसका समाधान नहीं है। Partial results उपयोगी हो सकते हैं। Dashboard उन्हें दिखा सकता है। Agent उनका सारांश बना सकता है। इंसान उन्हें देख सकता है। गलती तब होती है जब partial result को ऐसे सवाल का authoritative answer दिखाया जाता है जिसके लिए completeness ज़रूरी है।

जब requested operation atomic या complete answer देने का वादा करती है और वह वादा पूरा नहीं कर सकती, तब error इस्तेमाल करें। जब partial data का अपना वैध उपयोग हो, तब explicit incompleteness के साथ successful response लौटाएं। Client को deterministic distinction चाहिए, यह बहस नहीं कि `200` बहुत optimistic लगा या नहीं।

## हर result के साथ completeness metadata रखें

Result contract को completeness structured data के रूप में दिखाना चाहिए, चाहे item list पूरी हो, छोटी हो या empty हो। Callers को item counts, missing header या `message` field के किसी वाक्य से इसका अनुमान लगाने पर मजबूर न करें।

Collection search के लिए यह shape काम कर सकती है:

```json
{
  "items": [
    {"id": "svc-184", "owner": null}
  ],
  "complete": false,
  "truncated": true,
  "incomplete_reasons": [
    {
      "code": "RESULT_LIMIT_REACHED",
      "message": "The query stopped after the configured result limit.",
      "limit": 1000
    }
  ],
  "next_cursor": "eyJvZmZzZXQiOjEwMDB9",
  "scope": {
    "resource": "service_accounts",
    "visibility": "resources readable by this credential",
    "snapshot": "2025-03-08T14:20:11Z"
  },
  "warnings": []
}
```

Exact field names से ज़्यादा महत्वपूर्ण उनका अर्थ और consistency है। `complete` decision field है। `truncated` incompleteness तक पहुंचने का एक महत्वपूर्ण रास्ता बताता है, लेकिन इसे catch-all नहीं बनना चाहिए। Permission filter truncation नहीं है। Timed-out federated search pagination नहीं है। एक ही flag को कई अर्थ देने पर callers वह कारण खो देते हैं जिसकी मदद से वे सुरक्षित ढंग से आगे बढ़ सकते हैं।

`warnings` को `incomplete_reasons` से अलग रखें। Warning यह बता सकती है कि deprecated field आया, कोई value normalize हुई या requested sort default पर चला गया। Incomplete reason बताता है कि response लौटाए गए scope से बाहर के हिस्से के बारे में दावे का आधार नहीं बन सकता। Agent आगे बढ़ सकता है या नहीं, यह अंतर तय करता है।

केवल signal के रूप में bare `has_more` flag पर निर्भर न रहें। यह आम तौर पर pagination का एक संकीर्ण सवाल पूछता है। `has_more: false` देखकर agent उचित रूप से मान सकता है कि collection समाप्त हो गई, जबकि server-side cap या inaccessible shard ने full scan को रोक दिया हो। `has_more` रह सकता है, लेकिन completeness का पूरा भार उसी पर नहीं होना चाहिए।

Single-resource read में भी यही अनुशासन रखें। Omitted fields वाले response को बताना चाहिए कि server ने उन्हें इसलिए छोड़ा क्योंकि caller ने उन्हें request नहीं किया, caller के पास access नहीं था, data source fail हुआ या value सच में absent है। JSON omission छोटा होता है, पर अस्पष्ट होता है।

## Pagination को बड़े page size नहीं, stable boundary चाहिए

Pagination agents के लिए तभी सुरक्षित है जब API continuation को भरोसेमंद बनाए और यह बताए कि कौन से बदलाव उसे invalid कर सकते हैं। Page limit बढ़ाने से bug कुछ देर बाद दिखाई देगा। वह खत्म नहीं होगा।

Offset pagination खास तौर पर गलत निष्कर्षों के लिए संवेदनशील है। Agent records 0 से 99 पढ़ता है, कोई object delete या create करता है, फिर 100 से 199 पढ़ता है। अगर underlying order बदल गया, तो वह कोई record छोड़ सकता है या किसी को दो बार process कर सकता है। Advisory report के लिए यह सहने योग्य हो सकता है। Change plan के लिए यह विनाशकारी हो सकता है।

Cursor pagination आम तौर पर बेहतर है, क्योंकि server ordered result set में position को encode कर सकता है। फिर भी इसके लिए contract ज़रूरी है। बताएं कि cursor snapshot freeze करता है या नहीं, वह कितनी देर valid रहता है और filter, sort order या authorization बदलने पर cursor invalid होता है या नहीं। Cursor expire हो जाए तो scan को चुपचाप restart करके merged answer न लौटाएं। Explicit incomplete state लौटाएं या client को फिर से शुरू करने दें।

एक उपयोगी collection response caller को जानबूझकर पूरा करने के लिए पर्याप्त जानकारी देता है:

```json
{
  "items": ["item-001", "item-002"],
  "complete": false,
  "next_cursor": "cD0y",
  "page": {
    "returned": 2,
    "requested_size": 2,
    "ordering": "id ascending",
    "snapshot": "search-7f9c"
  },
  "incomplete_reasons": [
    {"code": "MORE_PAGES_AVAILABLE"}
  ]
}
```

Caller को तब तक जारी रखना चाहिए जब तक उसे `complete: true` न मिल जाए, केवल तब तक नहीं जब तक उसे छोटा page मिल जाए। Short pages कई कारणों से हो सकते हैं। कुछ APIs उन्हें इसलिए लौटाती हैं क्योंकि partition अस्थायी रूप से sparse है, internal worker जल्दी रुक गया या service object count के बजाय bytes के आधार पर response size सीमित करती है।

Language model से prose में इस loop को याद रखने को न कहें। Pagination behavior tool implementation में रखें। High-level `search_all` tool pages collect कर सकता है, snapshot बचा सकता है, अपना work cap कर सकता है और यह बता सकता है कि terminal state तक पहुंचा या नहीं। अगर वह अपने cap से टकराए, तो उसे `complete: false` लौटाना चाहिए और बताना चाहिए कि client-side cap इसका कारण था।

यह आखिरी स्थिति अक्सर छूट जाती है। Engineers API में metadata ठीक से जोड़ते हैं, फिर `max_pages=10` वाला agent wrapper बनाकर यह तथ्य हटा देते हैं कि scan दस pages पर रुक गया। अब wrapper incompleteness का स्रोत है। Disclosure की जिम्मेदारी outermost tool contract की है।

## Time limits, failed shards और permissions के अलग कारण होने चाहिए

Search अपना HTTP request पूरा कर सकता है, जबकि उसके काम के कुछ हिस्से पूरे न हुए हों। Distributed services आम तौर पर query को कई indexes या tenants में भेजती हैं। अगर एक source timeout हो जाए और service बाकी sources के matches लौटा दे, तो result उपयोगी हो सकता है, लेकिन incomplete रहेगा।

कारण को ऐसे code में दिखाएं जिस पर program branch कर सके। Human text उसके साथ रखें, उसकी जगह नहीं। Codes कम, stable और documented रखें। उदाहरण के लिए:

- `MORE_PAGES_AVAILABLE` का मतलब है कि caller अगला page मांग सकता है।
- `RESULT_LIMIT_REACHED` का मतलब है कि service ने सभी matches खत्म होने से पहले cap लागू कर दिया।
- `TIME_BUDGET_EXCEEDED` का मतलब है कि planned work पूरा होने से पहले search रुक गया।
- `SOURCE_UNAVAILABLE` का मतलब है कि पहचाने गए source ने जवाब नहीं दिया।
- `VISIBILITY_RESTRICTED` का मतलब है कि caller के authorization ने requested domain के एक हिस्से को बाहर रखा।

`VISIBILITY_RESTRICTED` को generic success response के पीछे न छिपाएं। Security teams कभी-कभी indistinguishable replies पसंद करती हैं, क्योंकि वे यह नहीं बताना चाहतीं कि कोई object मौजूद है। यह चिंता जायज़ है। API यह report कर सकती है कि visibility limits complete inventory को रोकती हैं, बिना hidden objects के नाम बताए। लेकिन उसे caller को partial inventory को exhaustive समझने नहीं देना चाहिए।

यही नियम rate limits और quotas पर भी लागू होता है। अगर API budget खत्म होने से पहले request का शुरुआती हिस्सा पढ़ लेती है, तो लौटाया गया data और budget condition दोनों report करें। Retry बाद में काम पूरा कर सकती है, लेकिन retry एक नया attempt है। Stable snapshot मिलने या task के drift सहन करने की स्थिति के बिना agent को दो attempts मिलाकर complete होने का दावा नहीं करना चाहिए।

Deadline input भी होनी चाहिए और output भी। जब agent broad inventory मांगता है, तो उसे time budget तय करने दें और बदले में पूरा हुआ work प्राप्त करने दें। इससे tradeoff साफ़ दिखता है। दस सेकंड की reconnaissance search human review से पहले ठीक हो सकती है। लेकिन जिस resource को search ने नहीं देखा, उसे delete करने के लिए यह कमजोर evidence है।

## Destructive changes के लिए absence कमजोर evidence है

Agent partial data का इस्तेमाल report का draft बनाने, candidates पहचानने या इंसान से छोटे target की जांच कराने के लिए सुरक्षित रूप से कर सकता है। उसे partial data के आधार पर यह निष्कर्ष नहीं निकालना चाहिए कि कोई resource unused, unowned, duplicate या हटाने के लिए सुरक्षित है।

अंतर claim की दिशा में है। `owner: null` वाला record मिलना उस record के बारे में positive evidence है, हालांकि field पुराना हो सकता है। Unowned records न मिलना पूरे search domain के बारे में universal claim है। Universal claims के लिए defined scope की complete coverage ज़रूरी है।

यह failure अक्सर efficiency improvement के रूप में सामने आता है। Team agent को `list_inactive_projects` नाम का tool देती है, फिर उसे लौटाए गए हर project को archive करने देती है या, इससे भी खराब, दूसरी list में मौजूद न होने वाले हर project को archive करने देती है। Tool की maximum result count है। कुछ महीनों बाद बड़ी organization उस count से आगे निकल जाती है। Agent prompt बदला नहीं, लेकिन उसका अर्थ `operate on the inventory` से बदलकर `operate on an arbitrary prefix of the inventory` हो गया।

Action tools को narrative स्वीकार करने के बजाय evidence मांगने के लिए बनाएं। Archive operation को पहले की complete inventory से चुने गए IDs और ऐसा snapshot token मांगने दें जो selection को read से बांधता हो। अगर inventory incomplete थी, तो tool operation अस्वीकार कर दे। इससे safety check ऐसी जगह पहुंचता है जहां model उसे हाथ हिलाकर टाल नहीं सकता।

जिन actions में snapshot tokens इस्तेमाल नहीं किए जा सकते, उनमें explicit scope मांगें और execution के समय हर target को फिर से जांचें। इससे यह साबित नहीं होता कि original search exhaustive थी, लेकिन एक stale list को unrelated mutations authorize करने से रोका जा सकता है। Action को इतना सीमित रखें कि reviewer target set को समझ सके।

लोकप्रिय विकल्प है agent से कहना, `Never delete anything unless you are certain.` सुनने में यह समझदारी लगती है, लेकिन व्यवहार में विफल होती है। Certainty prompt का एक शब्द है। `complete: false` ऐसी condition है जिसे tool लागू कर सकता है।

## Tool schemas को agent को uncertainty का सामना करने के लिए मजबूर करना चाहिए

MCP tool या agent-facing wrapper को आकर्षक prose block के बजाय typed envelope लौटाना चाहिए। Model prose पढ़ सकता है, लेकिन आसपास के software को ऐसे fields चाहिए जिन्हें वह validate, log, gate और test कर सके।

एक व्यावहारिक response type इस तरह दिख सकती है:

```json
{
  "status": "partial",
  "data": {
    "repositories": [
      {"id": "repo-a", "default_branch": "main"}
    ]
  },
  "completeness": {
    "complete": false,
    "reasons": ["TIME_BUDGET_EXCEEDED"],
    "continuation": {
      "kind": "retry_with_deadline",
      "minimum_seconds": 30
    }
  },
  "warnings": [
    {
      "code": "STALE_INDEX",
      "message": "Search index may lag the source repository."
    }
  ]
}
```

इस response के लिए `status: "success"` इस्तेमाल न करें। इससे simple clients metadata छोड़ने लगेंगे। `partial` caller को बताता है कि उसे constraint के साथ उपयोगी data मिला है। अगर protocol को एक ही success status इस्तेमाल करना ही हो, तो `complete` को mandatory बनाएं और action-capable clients से mutation से पहले उसे जांचने को कहें।

Continuation field को वास्तविक recovery route बताना चाहिए। दूसरे page के लिए `next_cursor` सही है। Rate limit के लिए `retry_after` उपयुक्त है। Server cap के लिए `narrow_query` उपयोगी हो सकता है। ऐसी continuation न दें जो वही query दोहराती हो और उम्मीद करती हो कि इस बार दुनिया अलग व्यवहार करेगी।

Agent instructions में छोटे और सख्त rules होने चाहिए:

- Agent empty collection को absence का proof केवल तब मान सकता है जब `complete` true हो।
- Agent partial response का उपयोग सीमित, read-only investigation प्रस्तावित करने के लिए कर सकता है।
- Result पर निर्भर किसी action की approval मांगने से पहले agent को `incomplete_reasons` सामने रखने चाहिए।
- Agent missing continuation token गढ़ नहीं सकता और उसके result के बिना retry सफल होने का दावा नहीं कर सकता।

ये rules छोटे हैं, क्योंकि detail data में मौजूद है। Tool ने जो जानकारी report ही नहीं की, उसे prompt वापस नहीं ला सकता।

## Warnings की ownership और expiry path होनी चाहिए

हर response में अस्पष्ट caution आने लगे तो warnings wallpaper बन जाती हैं। उन्हें specific, attributable और actionable रखें। जो warning caller का अगला चुनाव कभी नहीं बदलती, उसे आम तौर पर documentation बन जाना चाहिए या हटा देना चाहिए।

उदाहरण के लिए, `STALE_INDEX` को indexed source और, जब संभव हो, उसके observed revision या update time की जानकारी देनी चाहिए। तब agent बदलाव करने से पहले source of record देख सकता है। `PARTIAL_FIELD_SET` को बताना चाहिए कि server ने कौन से fields छोड़े और caller उन्हें request कर सकता है या नहीं। `DEFAULT_SCOPE_APPLIED` को वह scope बताना चाहिए जिसे server ने चुना, क्योंकि defaults accidental broad actions का आम स्रोत हैं।

Warnings को अनजाने में blockers न बनाएं। Caller के लिए severity rule साफ़ होना चाहिए। Completeness metadata तय करता है कि result पूरे scope के बारे में claim का आधार बन सकता है या नहीं। Warnings confidence, freshness या interpretation से जुड़ी होती हैं। Tool `complete: true` के साथ staleness warning लौटा सकता है। ऐसा result index में हर item को enumerate कर सकता है, लेकिन live state ज़रूरी वाले change के लिए फिर भी अनुपयुक्त हो सकता है।

Warnings को stable codes दें और consumers को उनके आधार पर test करें। केवल friendly message assert करने वाले tests से बचें। Writer wording सुधार सकता है, लेकिन decision rule नहीं बदलना चाहिए।

यह भी तय करें कि warning ship होने के बाद उसकी ownership किसकी है। अगर operations team छह महीने तक हर call पर वही warning देखती रहेगी, तो वह उसे पढ़ना बंद कर देगी। Underlying condition ठीक करें, जहां उचित हो उसे hard failure बनाएं या decision पर असर न होने पर हटा दें। Permanent yellow lights लोगों और agents दोनों को yellow lights अनदेखी करना सिखाती हैं।

## Tests को खतरनाक empty response पर चलना चाहिए

अधिकांश test suites results के normal page और server error को cover करती हैं। वे उस response को छोड़ देती हैं जो सबसे खराब inference करवाता है: `items: []` के साथ incompleteness।

हर reason code के लिए contract tests लिखें। Verify करें कि API populated और empty lists दोनों के लिए metadata लौटाती है, SDK उसे सुरक्षित रखता है और agent wrapper उसे text में flatten नहीं करता। किसी भी layer में regression ईमानदार server response को misleading tool result में बदल सकती है।

Test fixture में ऐसे cases रखें:

```json
{
  "case": "empty first page with more pages",
  "response": {
    "items": [],
    "complete": false,
    "truncated": false,
    "incomplete_reasons": ["MORE_PAGES_AVAILABLE"],
    "next_cursor": "cursor-2"
  },
  "expected_agent_decision": "continue_search"
}
```

फिर उस fixture के बाद mutation request test करें। Expected decision `refuse_or_request_review` होना चाहिए, `perform_cleanup` नहीं। Policy को test name में साफ़ दिखाएं। वरना future maintainers guard को जरूरत से ज़्यादा सावधान edge case समझकर automation demo को smooth बनाने के लिए हटा सकते हैं।

Mutation के दौरान pagination भी test करें। Pages के बीच records insert, delete और reorder करें। Cursor expire करें। एक shard को तब fail कराएं जब दूसरे से results आ चुके हों। Scan के बीच कोई permission हटाएं। आपका tool या तो documented snapshot बनाए रखे या report करे कि वह completeness का दावा नहीं कर सकता। केवल static fake database वाला test production में आने वाले झूठ पकड़ नहीं सकता।

Property tests भी मदद करते हैं। हर configured limit से बड़ी collections बनाएं, page sizes बदलें और एक invariant assert करें: client किसी collection को complete तभी label कर सकता है जब उसने अपने declared snapshot के हर item का हिसाब कर लिया हो। इसके लिए language model की ज़रूरत नहीं है। यह सामान्य interface correctness है।

## Human approval में missing evidence दिखना चाहिए

Human control तभी काम करता है जब approval में वह निर्णय दिखे जो व्यक्ति से लेने को कहा जा रहा है। `Allow agent action` approval नहीं है। यह assumptions की opaque chain स्वीकार करने का अनुरोध है।

जब tool incomplete data report करे, तो proposed action, target scope, evidence अधूरा होने का कारण और recovery option दिखाएं। उपयोगी prompt कह सकता है कि inventory 842 resources लौटाने के बाद timeout हो गई और पूछ सकता है कि longer deadline के साथ retry करना है, action को returned IDs तक सीमित करना है या change छोड़ देना है। तब reviewer सचमुच tradeoff कर सकता है।

Sallyport HTTP और SSH actions करते समय credential को agent process से बाहर रखता है, और उसके activity records resulting calls दिखा सकते हैं। जब reviewer को गलत निर्णय का पुनर्निर्माण करना हो, तो यह containment और trace उपयोगी हैं। लेकिन वे अस्पष्ट API answer को evidence में नहीं बदलते। इसलिए tool response में completeness state फिर भी होनी चाहिए।

Approval fatigue से बचने के लिए approval को consequential ambiguity तक सीमित रखें। Routine continuation, जैसे documented next page fetch करना, tool को बार-बार इंसान को रोककर नहीं, खुद संभालना चाहिए। उसे तब रुकना चाहिए जब policy boundary आए: expired snapshot, restricted visibility, absence पर आधारित action या collected evidence से बाहर का proposed mutation।

पहला engineering task छोटा है: हर उस API wrapper को खोजें जो list, aggregate या search result लौटा सकता है, फिर उसके outermost response में explicit complete state जोड़ें। Empty results और capped searches से शुरुआत करें। यहीं confident agents सबसे साफ़-सुथरे गलत जवाब गढ़ते हैं।
