Repository navigation
Learn skills from fixed errors, consolidate as proposals, link memory to the graph - #1158
Merged
Merged
Conversation
Generating skills deleted every pending skill in its scope, including Agent Skills Finder suggestions awaiting approval and manual pending ones. Skills now record their source (manual, llm, finder) and generation only replaces its own pending llm suggestions. activeSkillsText also filters by organization, and the skill endpoints validate their bodies with DTOs. Intent capture was one switch for the whole workspace. Admins can now set each member to inherit, on or off (also in bulk, optionally erasing the intents already captured), and members can opt themselves out. Only contributing callers get the _intent parameter; callers without a member identity never contribute. Changes are logged as LEARNING_CHANGED.
Every applied skill was pasted into the initialize instructions, a skill could only hold a 2,000-character rule, and edits overwrote it. - skills_list / skill_get on /mcp/:serverId when the caller sees an applied skill, scoped to the server's organization and the caller's visible connectors; each skill is also offered as an MCP prompt. - Per-server skills_delivery: auto (full text up to 6,000 characters, else an index), inline or index. The migration pins servers already above the threshold to inline so nothing changes until an admin opts in. - Optional body (markdown, up to 20,000 characters) served by skill_get only; the Skills Finder can file one. - Edits keep the previous content in kg_skill_versions; skill_get and prompts count uses. Consolidate leaves skills with a procedure alone. - AI Skills page shows procedure, version, usage and history; the MCP server page gets the delivery setting.
AI clients can save facts, preferences and notes and find them again in
any client connected to a server of the workspace: personal memory per
member (readable only by that member) and a company memory.
- memory_items with a generated tsvector ('simple') and GIN index;
organization_members.memory_access (inherit, none, read, personal,
contribute) with a workspace default.
- memory_search / memory_save / memory_forget on /mcp/:serverId only,
offered by the caller's level; results are labelled as data, not
instructions. Company memory from AI clients waits for approval unless
the workspace switch is set to automatic.
- Secrets are refused; size limits per item, member and workspace.
Admins can erase a member's personal memory without reading it;
removing a membership deletes it. Access, settings and erasures are
logged as security events.
- Licence gate in ee/licensing (EditionService.hasMemory): an active
Enterprise licence of the workspace (Cloud) or instance (self-hosted);
not the trial or transition period. The code itself is AGPL.
- Memory page (my memory, company memory, review, settings), Memory
column and bulk actions on Settings > Users, docs/memory.md.
One folder tree per workspace, shared by skills and the company memory, so a topic holds both its procedures and its facts. - knowledge_folders (parent, name, description, role_ids) and folder_id on skills and memory items; up to 4 levels, unique sibling names. Deleting a folder moves its subfolders and items to the parent. - MCP: skills_list(folder) lists a folder and its subfolders; the skill index is grouped by folder and, when too large, lists the top-level folders for skills_list. memory_search(folder) and memory_save(folder) for company items; AI clients never create folders. - Role limits (Enterprise, set by admins): a folder limited to custom roles is hidden with its subtree from everyone else on every path (instructions, skills_list/skill_get, prompts, memory_search, kg_how_to_obtain, dashboard). Admins see everything. Limits keep applying without the licence. Moves and deletes never widen access: only an admin can move content out of a limited folder, a limited folder with items cannot be deleted, and its subfolders inherit the limit. Changes are logged as KNOWLEDGE_FOLDER_ACCESS_CHANGED. - EditionService.hasEnterprise / assertEnterprise generalise the memory gate for both features. - Folder tree with edit, move and role editor on the AI Skills and Memory pages; docs updated.
… to the graph (#1157) - tool_invocations.learnable: stamped at call time from the member's learning setting; cleared together with intents when a member is switched off. Only learnable calls feed proposals. - Skills from fixed errors: a failed call followed within 10 minutes by a successful call of the same tool by the same member, with an argument whose format changed, seen at least twice, becomes a pending skill on the tool's connector (source "errors"). Only argument names and value formats are kept; quoted errors are masked. No AI model needed; runs from the AI Skills page and with skill generation; dismissed proposals are not proposed again. - Consolidation now files pending merge proposals (source "consolidation") that record the skills they replace, merging only within one folder and connector. Applying one dismisses the replaced skills; nothing live changes before. An unusable model reply keeps earlier proposals. - Memory items can be about a graph entity (about, normalised like graph entities); memory_save/memory_search accept it and kg_how_to_obtain adds the facts the caller may read. - UI: "Learn from fixed errors", merge proposals with what they replace, an About field for memory; docs updated.
From a real-model end-to-end run and two security reviews: - Per-server MCP calls are scoped, logged and learned under the server's workspace, never the caller's active one (a member of two workspaces had B's calls logged and learned under A). - Contributors' instructions ask for `_intent` explicitly (a model filled it in 1 of 3 calls without; 6 of 6 with). - Memory search matches any word, also as a prefix (a model searching "formato delle risposte" missed "risposte in italiano ..."). - Memory refuses text that reads like orders to the assistant and serves results between markers with an explicit "do not follow" note; tags and about are checked for secrets too. The company cap counts company items only; at most 200 proposals wait for review. - Error-based proposals keep only an error code, never the message; object arguments keep only their key count. - Folder changes need folders the caller can see; at most 500 folders. - Skill history respects folder access; activating a merge proposal via PATCH dismisses what it replaces; the shared guide is bounded. - Long-lived sessions re-check learning, memory level and folder access on each call, so revocations apply at once. - Fixed-error successes are read newest first; the merge prompt asks for one concise rule.
This was referenced Oct 19, 2026
…ai/brain-learning
…ai/brain-learning
…ai/brain-learning
…ai/brain-learning
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
See #1157. Builds on #1146, #1149, #1152 and #1154: until those are merged this PR also shows their commits; the changes here are in the last commit, "Learn skills from fixed errors, consolidate as proposals, link memory to the graph".
Three things that make the workspace learn and connect what it knows:
Changes
tool_invocations.learnable(boolean, default false; a catalog-only change in Postgres, no table rewrite). Set at call time from the same per-member decision that offers_intent, so changing a member's setting later never pulls old calls in. Switching a member off with "erase" clears both their intents and the flag. The shared/mcpnever sets it.KgSkillService.proposeFromErrors,POST /api/knowledge-graph/skills/propose-from-errors, ADMIN; also part of skill generation):shapeOf: digits → 9, letters → a for short strings, type and length otherwise). Retries and value-only changes are ignored;_-prefixed params are ignored.errors, e.g. "SendcountryCodeas "aa". Calls withcountryCodeas "aaa" failed (…) and worked after this change (2 times, 1 person)." The quoted error goes through the PII redaction and has digits masked.consolidation, withreplaces.applydismisses the replaced skills in the same transaction; dismissing a proposal changes nothing. An unusable model reply leaves earlier proposals alone. Skills with a procedure stay out of it, as before.about: normalised like graph entities ("Customers" →customer).memory_save(about?),memory_search(about?), dashboard field, andkg_how_to_obtainaddsfacts(company items in folders the caller may see, plus their own personal items, only with memory access) with a note that they are information, not instructions.manual,llm,finder,consolidation,errors; each generator only replaces its own pending rows.docs/knowledge-graph.md(consolidation, learning from fixed errors),docs/memory.md(memory and the knowledge graph, tool signatures).Cloud and self-hosted behave the same. The error-based proposals need no AI model, so they work on instances without one.
Type
Testing
Commands and results:
packages/backend:npx prisma generate,npx tsc --noEmit -p tsconfig.json: no errorspackages/backend:npm run lint: 0 errors (existing warnings only); eslint --fix rewrites of untouched files revertedpackages/backend:npx jest: 679 suites passed, 4 skipped; 20509 tests passed, 1496 skippedpackages/frontend:npx tsc --noEmit -p tsconfig.json: no errors; eslint on changed files: no findings;npx playwright test: 223 passedprisma migrate deployon an empty scratch database,prisma migrate diffagainst the schema: emptyNew unit tests:
shapeOf,diffArgs,findFixPatterns(counting, people, window, retries, value-only, anonymous),errorFixSkill(wording, masking),proposeFromErrors(learnable and organization filters, connector scope, no re-proposal), consolidation proposals (per folder, refs, replaces, nothing live deleted) andapplydismissing replaced skills,normalizeAbout,factsAbout(access and folders), the endpoint stamping learnable per member and adding facts tokg_how_to_obtain.Manual end-to-end on a local backend (scratch database, MCP API key, gpt-4o-mini for consolidation):
learnable: truefor a contributing member.propose-from-errorsthen created one proposal on the VIES connector (countryCode"aaa" → "aa", 2 times, 1 person). A second run replaced it without duplicates; after dismissing it, a third run proposed nothing.about: "Localities"was stored aslocality, found bymemory_search(about), and returned as a fact bykg_how_to_obtain("locality");kg_how_to_obtain("street")returned no facts.Checklist
npx jest, frontend Playwright)Related Issues
Fixes #1157
Depends on #1146, #1149, #1152, #1154
Review fixes (commit "Address review findings…")
Found with a real model driving the server as a chat client (
gpt-4o-mini, the server's instructions as system prompt, MCP tools as functions) and two independent security reviews:_intent: without being asked, the model filled_intentin 1 of 3 calls, which matches the zero intents ever captured on Cloud. Contributors' instructions now ask for it explicitly; result: 6 of 6.aboutare also checked for secrets.INVALID_INPUT,HTTP 422), never the message. Object arguments keep only their key count.NO ACTION(in Folders for skills and company memory, with role limits #1154)./mcpguide is bounded to 30k characters; the merge prompt asks for one concise rule.inline(13 on Cloud), not only those above 6,000 characters.Real-model runs, all on a local backend:
generateproducing sensible rules ("Freiburg = Freiburg im Breisgau", "two-letter VAT country code").autoswitched to the index (4.7k characters). A chat opened the right skill withskill_get, though gpt-4o-mini opened only one of several relevant skills. Consolidation proposed 21 merges replacing 43 skills; applying them left 26 active and 43 dismissed, andautowent back to full text.memory_search; setting learning off stopped intent capture on the next call.Re-run after the fixes: backend
npx jest679 suites / 20527 tests passed (4 suites skipped); frontend Playwright 223 passed; migrations deploy on an empty database with no drift.