AI Connect/How to create/Abteilungs-Copiloten (Logistik)

Abteilungs-Copiloten (Logistik)

Frag die KI über Vinkius

Modellieren Sie jede Abteilung eines Logistikunternehmens als eigenen Benutzer des AI Connect SDK: Finanzen, Disposition, Lager und Flottenbetrieb, jeweils mit eigenen Connectoren, Zugangsdaten und Fähigkeiten, auf einem einzigen Anwendungsschlüssel.

Das ist, wie eine interne KI-Plattform bis heute nie aussehen durfte: ein Logistikunternehmen, in dem Finanzen Rechnungen abgleicht, Disposition "wo ist Ladung 4412?" beantwortet, Lager die Rampe zählt, Flottenbetrieb die Wartung verfolgt, und jedes Team seinen eigenen Copiloten mit eigenen Connectors und Zugangsdaten erhält, auf einem einzigen Application Key, gestützt auf Tausende KI-Verbindungen ab Tag eins. NetSuite, das ERP, das WMS, eine Telematik-API, Slack-Kanäle, Google Sheets: keine von Ihnen gebaute Integration, und die Zugangsdaten jedes Teams gehören dem Team, nicht der Person, die gerade angemeldet ist.

Jede Integrationsplattform, die Sie je evaluiert haben, endet genau dort. Sie verbinden eine Anwendung mit einem Dienst. Keine hat jemals ein Team, eine Abteilung, eine geteilte Rolle als Benutzer erster Klasse mit eigenen isolierten Zugangsdaten angeboten, weil keine über ein Benutzermodell verfügt, das etwas anderes benennen könnte als einen menschlichen Login. Das AI Connect SDK kann es, und diese Neuausrichtung ist dieser Build: die Abteilung ist der Benutzer. Ein external_id pro Team. Menschen sind nur diejenigen, die den Copiloten bedienen; die Isolationsgrenze ist die Abteilung. Dieselbe Architektur, die eine Person mit einem Gmail bedient, bedient das komplette Organigramm, ohne neue Infrastruktur, ohne neue Plattform, ohne neue Vendor-Gespräche. Sie skalieren eine Organisation, indem Sie ids hinzufügen, nicht indem Sie Software kaufen.

Agent loop · one turn
One user turn of the quickstart, exactly as the console serves it. Click a step or press Run.
vinkius.user('alice_123').capabilities({ include: ['github'] })
HTTPGET /apps/vk_app_xxx/users/alice_123/tools?connector=github
const capabilities = await vinkius
  .user('alice_123')
  .capabilities({ include: ['github'] });
CapabilitySet (6)
  github__list_issues        read-only
  github__create_issue       POST /repos/{owner}/{repo}/issues
  github__list_pull_requests read-only
  github__search_code        read-only
  ...
Step 1 of 6
The agent loop, step by step. Press Run and follow one user turn: capabilities load, convert to tools, the model calls github__create_issue, the SDK executes on that user connection and the result feeds back. Every step shows the real SDK call and its HTTP request.

Derselbe Loop, den Sie für einen Menschen gesehen haben, läuft nun gegen dept-dispatch statt gegen alice_123. Der Copilot agiert auf dem TMS und dem Slack der Disposition, niemals auf dem NetSuite der Finanzen, denn eine Verbindung gehört dem Besitzer der external_id.

One Application keyvk_app_*dept-financeown credentialsdept-dispatchown credentialsdept-warehouseown credentialsdept-fleetown credentialsisolatedper external_id
The org chart as user table: each department owns its connections, credentials and spend, isolated by external_id under one Application key.

Warum eine Abteilung der perfekte "Benutzer" ist

  • Gemeinsamer, dauerhafter Zustand. Niemand "besitzt" die Verbindung; dept-warehouse besitzt sie. Personalfluktuation bricht die Integration nie.
  • Isolation des Schadensradius. Jede Verbindung trägt ihr eigenes Data-Plane-Token, sodass Ausgaben und sofortiger Widerruf der Disposition unabhängig von denen des Lagers sind.
  • Mindestberechtigung durch Konstruktion. Ein Copilot kann einen von einer anderen Abteilung verbundenen Konnektor buchstäblich nicht sehen, die Fähigkeitsabfrage ist auf eine einzelne external_id beschränkt.
  • Ein Anwendungsschlüssel. Alle Abteilungen leben unter einer einzigen Vinkius Application. Sie fügen ein Team hinzu, indem Sie eine neue id definieren, nicht indem Sie Infrastruktur bereitstellen.

1. Benennen Sie die Abteilungen

Verwenden Sie eine stabile, lesbare, URL-sichere id. Stellen Sie ein Präfix voran, damit sie nie mit einer menschlichen id aus einem anderen Teil Ihres Systems kollidiert.

typescript
type Department = 'finance' | 'dispatch' | 'warehouse' | 'fleet';

const departmentUserId = (dept: Department) => `dept-${dept}`;
// "dept-finance", "dept-dispatch", "dept-warehouse", "dept-fleet"

2. Deklarieren Sie die Konnektoren jeder Abteilung

Verschiedene Teams brauchen verschiedene Tools. Halten Sie das als Konfiguration, der übrige Code ändert sich nie je nach Team.

typescript
// server/departments.ts
interface DeptSpec {
  label: string;
  connectors: string[]; // Katalog-Slugs
}

export const DEPARTMENTS: Record<Department, DeptSpec> = {
  finance:   { label: 'Finanzen',        connectors: ['netsuite', 'stripe', 'gmail'] },
  dispatch:  { label: 'Disposition',     connectors: ['sap', 'slack', 'google-sheets'] },
  warehouse: { label: 'Lager',           connectors: ['wms', 'google-sheets', 'jira'] },
  fleet:     { label: 'Flottenbetrieb',  connectors: ['telematics', 'servicemax', 'slack'] },
};

Konnektor-Slugs stammen aus dem Live-Katalog. Lassen Sie Ihre Admin-Oberfläche sie entdecken, statt sie hart zu codieren: await vinkius.catalog.search('telematics') oder for await (const c of vinkius.catalog.iterate()). Siehe Konnektoren und Zugangsdaten.

3. Stellen Sie eine Abteilung einmal bereit (eine Admin-Aktion)

Wenn ein Team onboardet wird, verbinden Sie seine Konten und speichern Sie die Zugangsdaten. Die Abteilungs-id ist überall die externalId, in diesem Pfad gibt es keinen Menschen.

typescript
import { vinkius } from './vinkius';
import { DEPARTMENTS, type Department } from './departments';

async function bootstrapDepartment(dept: Department) {
  const user = vinkius.user(`dept-${dept}`);

  // hängen Sie nicht geheime Metadaten an, damit Sie später filtern/audieren können
  await user.ensure({ kind: 'department', label: DEPARTMENTS[dept].label });

  for (const slug of DEPARTMENTS[dept].connectors) {
    const connector = user.connector(slug);
    await connector.connect();
    // api_key-Konnektoren: connector.credentials.set({ API_KEY: ... })
    // oauth-Konnektoren: connect() kehrt nach der Zustimmung des Anbieters zurück
  }

  // melden Sie die Bereitschaft je Konnektor, damit Admins sehen, wo noch Zugangsdaten fehlen
  return Promise.all(
    DEPARTMENTS[dept].connectors.map(async (slug) => ({
      slug,
      status: await user.connector(slug).status(),
    })),
  );
}

4. Laden Sie die Fähigkeiten der Abteilung zur Anfragezeit

Eine Anfrage kommt mit der Abteilung, die der Copilot bedient. Lösen Sie deren Fähigkeiten auf, im richtigen Geltungsbereich und bereit, dem Modell übergeben zu werden.

typescript
async function departmentCapabilities(dept: Department) {
  const spec = DEPARTMENTS[dept];
  return vinkius.user(`dept-${dept}`).capabilities({
    include: spec.connectors,
    onConnectorError: (slug, error) => {
      // an Admins melden; die Antwort nicht unterbrechen
      console.warn(`${dept}/${slug}`, (error as Error).message);
    },
  });
}

5. Antworten Sie als Abteilung

Leiten Sie die Nachricht an den richtigen Copiloten weiter, geben Sie dem Modell nur die Tools dieser Abteilung und führen Sie die Ausführung auf der Verbindung dieser Abteilung aus.

typescript
// server/copilot.ts
import OpenAI from 'openai';
import { toOpenAITools, runOpenAIToolCall } from '@vinkius/connect/openai';
import { type Department } from './departments';
import { departmentCapabilities } from './capabilities';

const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY! });

const SYSTEM_PROMPT: Record<Department, string> = {
  finance: 'Sie sind der Copilot der Finanzen. Gleichen Sie Rechnungen ab und beantworten Sie Rechnungsfragen mit den verbundenen Buchhaltungstools.',
  dispatch: 'Sie sind der Copilot der Disposition. Beantworten Sie, wo sich Ladungen befinden, und aktualisieren Sie ETAs mit dem TMS und Slack.',
  warehouse: 'Sie sind der Copilot des Lagers. Melden Sie Rampenzählungen und offene Aufgaben mit dem WMS und Sheets.',
  fleet: 'Sie sind der Copilot des Flottenbetriebs. Melden Sie Fahrzeugzustand und Wartung mit Telematik und ServiceMax.',
};

export async function ask(dept: Department, question: string) {
  const capabilities = await departmentCapabilities(dept);

  const completion = await openai.chat.completions.create({
    model: '[MODEL_ID]',
    messages: [
      { role: 'system', content: SYSTEM_PROMPT[dept] },
      { role: 'user', content: question },
    ],
    tools: toOpenAITools(capabilities),
  });

  const call = completion.choices[0]?.message.tool_calls?.[0];
  if (!call) return { answer: completion.choices[0].message.content };

  const result = await runOpenAIToolCall(capabilities, call); // läuft als diese Abteilung
  return { answer: result.content, tool: call.function.name };
}
typescript
// Die Disposition fragt ihr eigenes TMS ab; sie kann das NetSuite der Finanzen nicht erreichen.
await ask('dispatch', 'Wie lautet die ETA für Ladung 4412 und welcher Fahrer fährt sie?');
await ask('finance', 'Welche Kundenrechnungen, die älter als 30 Tage sind, sind diesen Monat noch offen?');

6. Lassen Sie Menschen als Abteilung handeln, mit Protokollspur

Die Menschen, die einen Copiloten bedienen, sind nicht der "Benutzer" im SDK-Sinne, aber Sie sollten dennoch wissen, wer gefragt hat. Protokollieren Sie den Operator neben der Abteilungs-id, die Fähigkeitsgrenze bleibt bei der Abteilung.

typescript
async function askAs(dept: Department, operator: string, question: string) {
  const answer = await ask(dept, question);
  // Ihr eigener Audit-Store — das SDK sieht den Operator nie
  await audit.record({
    actor: operator,
    on_behalf_of: `dept-${dept}`,
    question,
  });
  return answer;
}

Halten Sie die Identität des Operators vollständig in Ihrem System. Für Vinkius ist der Akteur immer dept-finance. Das gibt Ihnen die Isolierung auf Abteilungsebene und sorgt dafür, dass sich derselbe Copilot identisch verhält, unabhängig davon, welcher Mitarbeiter gerade tippt.

7. Einem Team mit einem einzigen Schritt Zugriff gewähren und entziehen

Da eine Abteilung eine einzelne external_id ist, ist die Außerbetriebnahme trivial. Das Trennen eines Konnektors entfernt nur den Zugriff dieses Teams.

typescript
async function retireConnector(dept: Department, slug: string) {
  await vinkius.user(`dept-${dept}`).connector(slug).disconnect();
}

Leistungsstarke Funktionen, die Sie hier kennen sollten

Eine Abteilung, ein Konnektor, ohne alles neu aufzulisten

Ein fokussierter Copilot, der nur auf ein einziges System zugreift, sollte nicht die Kosten eines Fan-out über alle Verbindungen des Teams tragen. forConnector schneidet eine bereits geladene Menge zu; und um das Abrufen der übrigen ganz zu vermeiden, listen Sie von einem einzelnen Konnektor-Handle:

typescript
// aus einer aggregierten Menge:
const sheetsOnly = capabilities.forConnector('google-sheets');

// oder indem Sie das Auflisten jeder einzelnen Verbindung ganz vermeiden:
const tmsOnly = await vinkius.user(`dept-${dept}`).connector('sap').capabilities();

Der Fan-out ist nebenläufig und selbstheilend

user.capabilities() listet die Verbindungsübersichten einmal, fächert dann zu den ready-Konnektoren mit einer Nebenläufigkeitsgrenze von 8 auf und wiederverwendet jede aufgelöste Verbindungs-id (kein erneutes Auflisten je Konnektor). Wenn das Runtime eines Konnektors in einen Timeout läuft, unterbricht das die Antwort nicht, die übrigen lösen weiterhin auf, und onConnectorError zeigt an, welches Tool des Teams nicht verfügbar war, damit Sie den Admin dieser Abteilung warnen können.

Metadaten einer Abteilung zurücklesen

Das nicht geheime kind: 'department', das Sie mit ensure() angehängt haben, steht Ihnen für die Anzeige zur Verfügung. user.get() gibt die gespeicherten Metadaten und den Status zurück, genug, um ein Dashboard "Welche Teams sind onboardet?" ohne zusätzliche Datenbank zu bauen.

typescript
const profile = await vinkius.user('dept-finance').get();
console.log(profile.metadata); // { kind: 'department', label: 'Finanzen' }
console.log(profile.status);   // active | ...

Produktions-Checkliste

  • [ ] Geben Sie Abteilungs-ids ein Präfix, damit sie nie mit menschlichen ids kollidieren (dept-…).
  • [ ] Speichern Sie die Konnektorliste je Abteilung als Konfiguration, nicht als Code-Zweige.
  • [ ] Onboarden Sie mit user.ensure({ kind: 'department' }), damit metadatengetriebene Dashboards funktionieren.
  • [ ] Stellen Sie connector.status() in der Admin-Konsole dar, damit Lücken (needs_credentials) sichtbar sind.
  • [ ] Leiten Sie menschliche Operatoren zum Copiloten ihrer Abteilung weiter; behalten Sie die Protokollierung, wer gefragt hat, auf Ihrer Seite.
  • [ ] Geben Sie jeder mutierenden Copilot-Aktion einen stabilen idempotencyKey (z. B. Ticket + Abteilung).

Sie betreiben nun eine interne KI-Plattform, auf der Finanzen, Disposition, Lager und Flottenbetrieb jeweils ihre eigenen verbundenen Tools und ihren eigenen Copiloten haben, alles von einem einzigen Anwendungsschlüssel und einer external_id pro Team. Sie haben keine Plattform für Abteilungen gekauft; die Plattform hat schlicht kein Limit dafür, was ein Benutzer sein kann. Ein fünftes Team morgen hinzuzufügen ist eine Zeile Konfiguration. Das bedeutet es, die Konnektivitätsschicht zu besitzen: das Organigramm wird Ihre Benutzertabelle.

What you just got

Not a pitch: the properties this build inherits automatically.

Isolation by construction

Connections and capabilities resolve only inside one external_id. No cross-actor leakage is possible, and you wrote none of that enforcement.

Write-only credentials

Your server stores secrets and can read back which fields are configured, never the values. Not your code, the model, or a dashboard can exfiltrate them.

Metered, revocable spend

Every connection owns a vk_live_* token, so cost and revocation are per connection. One call to disconnect() is a complete, auditable stop.

Any model runtime

One CapabilitySet converts to OpenAI, Anthropic, Gemini, Vercel AI SDK, LangChain, LlamaIndex, Workers AI or neutral JSON Schema. Only the last line changes.

Production safety built in

idempotencyKey, timeoutMs and AbortSignal per call; automatic full-jitter retries on transient failures; typed VinkiusError branches. No bespoke harness.

Least privilege per team

A copilot literally cannot see another department’s connectors. Onboarding a team is a new external_id, not new infrastructure.

Give it to your AI agent

An Agent Skill (SKILL.md) for this build. Preview the first lines below, then copy or download it into your repo under .claude/skills/: Claude Code, Cursor or any Agent-Skills-compatible agent follows it to implement this pattern correctly.

Download SKILL.md6 · Available in your language
What department copilots inherit, plus the SKILL.md, in your language, for your coding agent.

Nächste Schritte