BKN 知识网络 · 能力绑定 API (0.1.0)

Download OpenAPI specification:

A capability binding records that a Skill or a ToolBox tool belongs to a knowledge network. The master data stays in the execution factory; the binding holds only the reference, so creating, publishing and indexing capabilities is unchanged by these endpoints.

Identity

A capability is identified by three parts:

capability_type box_id capability_id
skill empty skill_id
function box_id tool_id
knowledge_base empty knowledge base id

box_id is required for a function because a tool id is scoped to its box in the execution factory, and it is the same name the execution factory, Context Loader and Studio already use. A skill has no container and omits it, and so does a knowledge base.

A knowledge_base is an Enterprise knowledge base served by kb-api. Mounting one checks with kb-api that it exists (400 KnowledgeBaseNotSupported where kb-api is not deployed) and that the editor holds query_data on it. Publishing then lets the network's managed proxy read the whole knowledge base through kb-api's read API. Like a Skill, the grant is best effort: if the editor later loses access, only that knowledge base becomes unreadable through the proxy.

The stored column behind it is type-neutral (f_owner_id) so a later capability type can bring a different kind of container without a migration; that name does not appear on the wire. A box_id sent with a skill is dropped rather than stored — keeping it would create a second row for the same skill that the unique key cannot collapse, and repeated mounts would stop being idempotent.

What a binding answers

A binding answers "which capabilities does this knowledge network have", not "which capability is relevant to this question". Listing does not replace retrieval: a network with dozens of Skills and tools needs the execution factory's search endpoints.

Scope and branch

Bindings are per branch. Creating a branch copies them, deleting a branch clears them, and the count in the knowledge-network statistics follows the branch being viewed.

Idempotency

Mounting is a statement about membership, not an event. Mounting a capability that is already bound returns the existing binding rather than failing, and repeating an entry inside one request produces one row.

Mounting validates the target

A mount checks the capability against the execution factory before writing anything, and the two failures it can report are deliberately distinct: the target does not exist (check the id) and the target exists but is not usable (publish the tool box or skill, or enable the tool). Collapsing them into one error would leave the caller guessing which fix applies.

Validation runs over the whole request first. A batch whose fourth capability is unknown writes nothing at all rather than leaving three bindings behind.

When the execution factory is unreachable the answer is 502 and no write. A binding recorded without validation cannot be told apart later from one that was checked.

Two things are deliberately not checked. metadata_type is ignored, so openapi and function tools are equally bindable — a binding names a tool, and the box is only the execution factory's container for it. And mounting does not require permission to execute the target: binding is a modeling action by the network's owner. At call time the caller needs the network's execute permission; the managed proxy and execution factory then validate the exact binding, target lifecycle and delegated grant without requiring direct target permission.

Whole-box mounting

{"capability_type": "function", "box_id": "box-x", "all_tools": true} mounts every enabled tool of that box. The expansion happens at write time: the stored rows are ordinary tool-level bindings carrying bound_as_box: true, so the read path never expands anything and any single row can be released on its own.

Tools added to the box afterwards are not inherited. That is the point of expanding rather than storing a box-level scope: on a shared box, inheritance would let someone else's edit widen this network's reach. The list view reports how many tools of a box are not mounted so they can be added deliberately.

Disabled tools are skipped, on the same terms that reject them when named directly. A box with no tool to mount is a 400, not a silent success — "I mounted the box" and "nothing happened" must not look alike.

Metadata is backfilled, not stored

A binding stores only the reference. Names, descriptions and statuses are read from the execution factory when the list is served, so they never go stale and the master data stays in one place.

The reads are shaped around what costs what. Function metadata comes per tool box, not per tool: the box endpoint inlines its tools, so a page of twenty function bindings across three boxes costs three calls. Skill names are one call for the whole page. Only a skill's description and status must be read one skill at a time, which is why they are behind with_detail=true rather than always paid.

Dangling bindings are marked, never deleted

A binding whose target has been removed from the execution factory comes back with status: "missing". It is not deleted: removing it silently would erase the evidence that the network once pointed at something, and it is the owner's decision whether to clean it up or restore the capability. When a whole tool box is gone, every binding under it is marked and the box appears in boxes with box_missing: true.

This matters because retrieval treats a dangling reference as simply absent — it matches nothing and says nothing. Without the marker the only symptom is "the navigation says four functions but only three ever come back".

Whole-box top-up

A whole-box mount is expanded at write time and does not follow the box afterwards, so tools added later are not bound. boxes reports total_tools, mounted_tools and unmounted_tools per box so the list can show "20 of 23 mounted" and offer to add the rest deliberately. Disabled tools are excluded from the totals: they are not something a top-up would add.

These counts are per branch, not per page. Pagination slices the entry list, not the box, so counting a page against a box-wide total would read "10 of 23 mounted" for a box that is fully mounted and offer a top-up that does nothing. When the count cannot be established the box is omitted from boxes and metadata_available turns false, rather than published with a number that would send the caller after tools that are already bound.

When the execution factory is down

Listing degrades rather than fails: the bindings come back in full with empty metadata and metadata_available: false. Listing memberships is the point of the endpoint and the names are decoration — failing the request would take the mount UI down with the factory. The flag exists so an empty name is not read as a deleted capability; an outage and a deletion must not look alike.

Navigation-only knowledge networks

A caller who cannot view the knowledge-network detail but can view at least one child resource may open the network as a navigation-only shell. For that caller, the public list returns a canonical empty result (entries: [], total_count: 0, boxes: []) for every supported capability filter. Capability bindings belong to the network detail and have no child-level authorization resource, so the service does not read binding rows, model provenance, or execution-factory metadata in this mode. This avoids both a misleading permission error in the shell and disclosure of capabilities the caller cannot view.

Both metadata_available and sources_available are true in this canonical empty result: there are no returned rows or sources left unresolved, so the result is complete. These flags do not imply that the execution-factory or model-provenance dependencies were called.

A caller with neither knowledge-network detail access nor any visible child still receives 403. Authentication and permission-service failures also remain errors; they are never converted to an empty list.

The internal face answers differently

GET /api/bkn-backend/in/v1/knowledge-networks/{kn_id}/capabilities returns bare references — binding id, capability_type, box_id, capability_id — with no metadata, no paging and no box summaries.

Context Loader calls it on every recall to learn what is in scope before asking the execution factory to search inside that scope. Backfilling names here would add a service hop per recall to produce exactly what the caller is about to fetch itself, and paging it would hand back a subset of the scope, which is a different scope. A network with no bindings resolves to an empty list rather than an error: "this network has no skills" is an answer, not a broken dependency. The internal endpoint accepts either network view_detail (discovery) or network execute (execution scope resolution); public discovery and execution endpoints each enforce their own operation before calling it.

Public paths use /api/bkn-backend/v1; the internal face uses /api/bkn-backend/in/v1.

Capabilities

Knowledge-network capability binding

List the capabilities bound to a knowledge network

path Parameters
kn_id
required
string

Business knowledge network ID

query Parameters
branch
string

Branch. Defaults to main.

type
string
Enum: "skill" "function" "mcp_tool" "knowledge_base"

Filter by capability type. Any other value is rejected with 400.

box_id
string

Filter by owning tool box. Only meaningful for functions.

owner_id
string

Accepted spelling of box_id, kept for callers that name the tool box that way. A non-empty owner_id wins when both are sent; new callers should send box_id.

metadata_type
string
Enum: "openapi" "function"

Narrow function bindings to one kind of tool box: openapi or function. The value belongs to the box rather than the binding, so it is resolved to the set of boxes of that kind and applied as a query condition — paging and total_count are therefore over rows that already match. A box whose kind cannot be read counts as function, which keeps a dangling binding visible in one of the two lists rather than neither. The kind belongs to a tool box, so this implies type=function: skills and MCP tools are never returned alongside it. Any value other than the two is a 400.

with_detail
boolean
Default: false

Also fill description and status. A skill's detail is read one skill at a time, so this is opt-in; function detail arrives with the box response either way.

offset
integer

Offset. Defaults to 0.

limit
integer

Page size. Defaults to 10.

sort
string
Enum: "create_time" "update_time"

Sort field. Defaults to create_time.

direction
string
Enum: "asc" "desc"

Sort direction, asc or desc. Defaults to desc.

Responses

Response samples

Content type
application/json
{
  • "entries": [
    ],
  • "total_count": 0,
  • "boxes": [
    ],
  • "metadata_available": true,
  • "sources_available": true
}

Mount capabilities onto a knowledge network

Idempotent. A capability already bound resolves to its existing binding, and the response carries one entry per requested capability in request order.

path Parameters
kn_id
required
string

Business knowledge network ID

query Parameters
branch
string

Branch. Defaults to main.

Request Body schema: application/json
required
required
Array of objects (AttachCapabilityEntry) non-empty

Responses

Request samples

Content type
application/json
{
  • "capabilities": [
    ]
}

Response samples

Content type
application/json
{
  • "entries": [
    ],
  • "total_count": 0,
  • "boxes": [
    ],
  • "metadata_available": true,
  • "sources_available": true
}

Release capability bindings

Releases by binding ID, comma separated for a batch. IDs that are already gone are not an error. Binding IDs are used rather than the capability identity because a tool-level binding needs box_id as well to be located, and identifiers containing special characters would have to be escaped in the path.

path Parameters
kn_id
required
string

Business knowledge network ID

binding_ids
required
string

Comma-separated binding IDs

query Parameters
branch
string

Branch. Defaults to main.

Responses

Resolve the capability references bound to a knowledge network

Internal face. Returns bare references — no execution-factory metadata, no paging and no box summaries — so the response schema is CapabilityReferenceList, not the public CapabilityBindingList. See "The internal face answers differently" above for why.

The caller is identified by the internal account headers rather than a token. It is accepted with either network view_detail (discovery) or network execute (execution scope resolution); the public discovery and execution endpoints each enforce their own operation before calling it.

path Parameters
kn_id
required
string

Business knowledge network ID

query Parameters
branch
string

Branch. Defaults to main.

type
string
Enum: "skill" "function" "mcp_tool" "knowledge_base"

Filter by capability type. Any other value is rejected with 400.

Responses

Response samples

Content type
application/json
{
  • "entries": [
    ]
}