Intelligent skills management engine — technical guide
This guide traces multi-referential skill search and personalized skill suggestions, from the employee profile to catalog resolution and assignment. It is intended for implementation and support teams. Business users should start with the functional guide; tenant administrators should use the configuration guide.
Unless explicitly marked otherwise, implementation statements below are Verified in code in the local repositories. Deployed revision parity and effective PS Testing service configuration are Not verified.
Architecture and responsibilities
| Owner | Responsibility |
|---|---|
Legacy SkillLibraryController and skillsLibrary/ Twig templates | Search form, source grouping, selection/rating controls and bulk profile assignment. |
Legacy SkillSearcher | Eligible source IDs, search contract, result ordering and local skill resolution. |
Legacy ObservatorySkillPersister | Company-owned copies of external observatory skills and their translations. |
Legacy SkillSuggestionService / SkillSuggestionRepository | Persisted older suggestions, eligibility, decisions and synchronization. |
Legacy SkillSuggestionV2Service / ExpToSkillAdapter | Newer profile-based inference, source priorities and exclusion inputs. |
Legacy SkillStorageService / IgnoredSkillManager | Session queue and ignored-skill handling for the newer path. |
AIHub skills_library | Keyword search contracts, query orchestration and skill retrieval. |
AIHub talent_intelligence | Experience/diploma/interest inference and mapping to eligible skill IDs. |
Library request and data flow
- The profile's library route supplies the target employee context.
SkillLibraryController::indexActionchooses employee/company templates and calculates relevant permissions. - Employee search posts
textandcustomerto routemain_config_skill_suggestion; its route declaration ismain/config/skill/suggestionand requires XHR POST. The controller returns a rendered HTML partial, not an AIHub JSON contract. SkillSearcher::buildApiInputresolvesskillLibraryObservatoriesto referential IDs and combines them with available company referentials. Explicit referentials in supported caller paths replace the default company list, while configured observatory IDs are still included.SkillLibraryUrlManagerselects the default or newer configured search URL usingskill_library_api_url_skills_qdrant_single.- Returned IDs are resolved to local
Skillentities in score order. Observatory skills are copied into the company catalog when needed. Results that still belong to another company are excluded. - The controller groups by skill group/type and separates Neobrain-referential results as external (
out) from other results (in). The UI headings describe these families, not each original source catalog. - Selection remains in the browser until the employee saves. XHR POST route
main_config_add_skillshas declaration/addSkills; it receivescustomeranddatacontainingskillId,level,motivationand supported optional fields. Existing employee-skill associations are skipped; new associations are assigned and events dispatched.
Observed in application: search for gestion de projet displayed internal and observatory candidates, and selecting MS Project exposed the assessment controls. The live final assignment was not tested.
Search service contract
The legacy caller sends this shape; IDs below are synthetic:
{
"search_query": "project management",
"repositories_id": [101, 202],
"language": "en",
"results_number": 40,
"collection_name": "<configured collection>",
"repositories_id_prio": [101],
"threshold_deduplicate": 0.95
}
Priority fields are conditional. In the newer search mode, available client referentials (type === null) are prioritized, with the main referential first and ID as a tie-breaker. threshold_deduplicate is then sent as 0.95. The caller's result limit is 40. These are search settings, not proficiency thresholds.
The local AIHub router exposes POST /api/v1/skills-library/skills_single with API-key dependency and a compatibility POST /skills-library/skills_single without that route-level dependency. Network/perimeter controls for compatibility routes were not established. Its SkillInputSolo schema has a default result limit of 10 and deduplication threshold of 0.98; those schema defaults do not override explicit legacy caller values. The legacy resolver consumes fields including skill_id and score. An exact deployed URL-to-router match was not verified.
AIHub query_service.find_single selects PostgreSQL-backed search when PostgreSQL runtime resources are present; otherwise it uses the MongoDB path. The inspected MongoDB flow normalizes and translates search queries using cached translations and AI configuration. Do not describe this as literal substring search or assert a deployed vector-storage technology from the legacy feature-switch name alone.
Personalized suggestion modes
Both modes are selected by suggestion_skills_v2_ds_api in the same legacy DefaultController. This switch is independent of the library search URL switch.
Older persisted mode
SkillSuggestionRepository::getSkillSuggestions selects suggestions for the current user with unresolved approval/refusal, userHasSkill = false, no matching Userskill, and an available (public = 1) referential. It scopes skill ownership to the company, with an additional configured observatory branch when applicable. checkSuggestions also excludes already represented external-source skills.
SkillSuggestionService::getSuggestion randomly selects an eligible candidate and records its proposal date. If none exists, it requires work experience and attempts synchronization once, then retries selection once. Synchronization is eligible when no prior sync exists or the latest sync is older than one month. It extracts experience job titles and requests up to 16 skills per title by default through CustomerProfileApiSkillsSuggestion. This code does not establish a monthly scheduled task.
The older provider contract uses jobs, language, max_skills and job_referentiel_ids, and consumes fields such as sm_skill_ids. It differs from the newer camel-case experience contract; do not interchange the two payloads.
| Decision | Persisted older-mode effect |
|---|---|
Accept, state 1 | Sets approved true/refused false; assigns the skill with mastery and motivation if not already recorded as owned. |
Refuse, state 0 | Sets approved false/refused true. |
Reset, state 3 | Clears both decisions; if previously approved, can remove the matching employee-skill association and clear userHasSkill. |
Decisions dispatch UserSkillSuggestedInfoEvent. The service voter requires module access and, for edits, ownership of the suggestion. A compatibility condition involving the old homepage switch can grant voter access even when the panel switch is off; the profile template independently checks module_suggestion_skill before rendering the panel.
Newer profile-inference mode
SkillSuggestionV2Service collects experiences, diplomas and interests, available company referential IDs, and skillSuggestionObservatories. Company IDs are also sent as priority sources. Existing and ignored skill IDs are combined into exclusion input through UserSkillManager.
ExpToSkillAdapter calls relative service path talent-intelligence/experience. It sends up to 15 requested skills, threshold 0.9, language: "en", environment-specific collection selection and these principal fields:
{
"jobs": [{"title": "Project coordinator", "description": "Coordinate delivery", "startDate": "2024-01-01", "endDate": "2026-10-23"}],
"diplomas": [],
"interests": [],
"maxSkills": 15,
"referentielIds": [101, 202],
"referentielIdsPrio": [101],
"userSkills": [303],
"language": "en",
"threshold": 0.9,
"debug": false,
"collection": "<configured collection>"
}
The example is synthetic. The adapter returns an empty list without a request when all three profile-input families are empty or no referential is supplied. API/decoding failures dispatch PostCatchEvent and return an empty list. Returned IDs are resolved through SkillService.
AIHub exposes POST /api/v1/talent-intelligence/experience with an API-key dependency and compatibility POST /talent-intelligence/experience. ExperienceInput defaults to 10 skills, English, and rejects nonpositive explicit maxSkills. The legacy caller explicitly sends 15. AIHub's service chooses PostgreSQL or MongoDB retrieval according to runtime resources and maps inferred content to skills in the requested referentials; it is separate from Lexi's job-catalog enrichment workflow.
The resulting candidates are mapped into session key suggestedSkills. The first item is displayed; acceptance assigns it and removes it from the queue. Refusal records it through IgnoredSkillManager and removes it from the queue. Profile experience/diploma/interest persistence changes clear the queue through SkillSuggestionRefreshEventListener after flush.
Undo limitation: the newer reset controller branch returns success without reversing employee-skill or ignored-skill persistence. The browser's previous-suggestion control must not be documented as equivalent to the older persisted reset.
Browser-facing ongoing-suggestion contracts
Paths below are declarations in Customer/ProfileBundle/Resources/config/routing.yml; deployment routing prefixes still apply. All require XHR.
| Method and path | Input / outcome |
|---|---|
GET skills/suggestions | Checks quota first, then selected mode. Success includes response and suggestion; older mode can return experience_not_found or suggestion_not_found. |
POST skills/suggestion/accept | skill_suggestion_id, level, motivation; ID resolution differs by mode. |
POST skills/suggestion/refuse | skill_suggestion_id; rejects the candidate through the selected mode. |
POST skills/suggestion/reset | skill_suggestion_id; mode-dependent behavior described above. |
Missing decision IDs return 403. Retrieval maps forbidden to 403 and other unsuccessful service results to 404. A logical quota failure on retrieval returns 422 with response: quota_exceeded; unexpected quota-check errors return 500. A successful newer-mode retrieval can still contain an empty suggestion array. Older and newer empty states therefore do not have identical HTTP semantics.
Data lifecycle, permissions and privacy
Setting owns the two observatory lists. Referentiel identifies source availability/type; Skill carries the catalog item; Userskill carries the employee association and assessment. Older SkillSuggestion records retain user, skill and decision state, while SkillSuggestionLog tracks synchronization.
ObservatorySkillPersister finds or creates a company Neobrain referential marked non-main and non-public. It reuses external-source copies when possible, stores externalSource: observatory and the original ID, copies translations and rating labels, and makes the copy noneditable. Search itself can persist catalog data, even if the employee never saves the selection. This is distinct from assigning a skill to the profile.
The bulk-add endpoint requires authentication, rejects a target in another company, and allows the same employee, their direct manager, or the inspected master-school branch. The library page also checks add_skills_collab for some other-user access and computes allow_modifying_manager_skill_rating. Search handlers resolve the target customer; these checks should not be assumed to provide an exhaustive authorization audit across every endpoint and caller.
SkillQuotaService checks the configured max_skill_outside_job against employee skills absent from their assigned job. It applies only for cap values at least 1 and employees with a job. Library bulk addition validates the submitted item count; suggestion retrieval validates one prospective addition. Enforcement locations and status codes differ between flows.
Experience text, qualifications, interests and skill IDs are personnel information sent to the configured inference service. Search/inference logs can contain request and response content. Restrict diagnostic access and avoid copying real employee payloads, account hashes, credentials or service URLs into tickets or API examples.
Failures, runtime dependencies and observability
| Failure / symptom | Trace |
|---|---|
| No configured library URL / invalid configuration | SkillLibraryUrlManager validates its configured URLs; SkillSearcher also returns no results if no URL is resolved. |
| Search transport error | SkillSearcher logs context and throws a runtime exception. |
| Invalid older-provider JSON | CustomerProfileApiSkillsSuggestion dispatches PostCatchEvent and returns empty suggestions. |
| Newer inference failure | ExpToSkillAdapter catches failures, emits PostCatchEvent, and returns an empty list. |
| Stale ongoing candidates | Inspect session queue, profile-input refresh events and mode; a source-list save alone is not a proven invalidation trigger. |
| Older refresh not performed | Inspect latest synchronization date and unresolved eligible records. |
| Duplicate / irrelevant candidates | Compare source IDs, priority list, exclusion IDs, returned scores and local external-source mapping. |
| Addition denied | Inspect tenant/relationship authorization, quota, submitted ratings and response status. |
Runtime dependencies include the authenticated Symfony session, Doctrine-backed catalogs/profile entities, configured search/inference clients, translation support and AIHub retrieval resources when those services are selected. AIHub request schemas are not proof of a particular production provider or deployment topology. The inspected code provides API-call logs, application logging and exception events; dedicated feature metrics, retry guarantees and a live monitoring dashboard were not established.
Legacy, redesign and extension points
Observed in application: PS Testing's current navigation hosted legacy profile/library screens. Verified in code: targeted searches in smartmobility-nextgen-front/src and smartmobility-nextgen-back/src did not establish a replacement for these flows; development-plan suggestion DTOs/renderers implement another context. Newer AI service selection can coexist with the legacy UI.
To add a source, follow referential availability → observatory settings → request IDs → indexed catalog → local ID resolution → source grouping. To change inference, preserve the distinction between ranking, exclusions, proposal persistence and employee assignment. Validate both modes and document any changes to reset, quota and refresh behavior. Client-side control visibility alone is insufficient evidence of server authorization.
Evidence and limitations
- Observed in application: Chrome, PS Testing 2026 as Jim Carter, 23 October 2026; personal profile/library, internal and external results, selection, ratings and temporary removal. See the functional screenshots. The continuous card was not visible.
- Verified in code — legacy:
src/Main/ConfigBundle/{Controller/SkillLibraryController.php,Resources/config/routing/skillslibrary.yml,Resources/views/skillsLibrary/};src/Customer/ProfileBundle/{Controller/DefaultController.php,Resources/config/routing.yml,Services/SkillSearcher.php,Services/ObservatorySkillPersister.php,Services/ApiSkills/CustomerProfileApiSkillsSuggestion.php};src/Service/{SkillLibraryUrlManager.php,Skills/,PartnerAPI/TalentIntelligence/ExpToSkill/ExpToSkillAdapter.php};src/Repository/SkillSuggestionRepository.php;src/Main/ConfigBundle/Security/Skills/SuggestionSkillVoter.php;src/EventListener/SkillSuggestionRefreshEventListener.php. - Verified in code — AIHub:
src/skills_library/{api/search_router.py,schemas/search.py,services/query_service.py};src/talent_intelligence/{api/experience_router.py,schemas/experience.py,services/experience_to_skills.py}andsrc/common/utils/constants.py. - Inferred: diagnostic ordering and extension guidance follow the traced calls.
- Not verified: live network payloads, exact selected backend modes, deployed revisions, configuration saves, final profile assignment, continuous-card decisions, exhaustive security controls and production topology. No application source was modified.
Functional-guide evidence
These implementation and product references support the functional guide. Local source behavior does not establish which revision or optional features are deployed. Application observations remain distinct from code and documentary evidence.
- Observed in application: Chrome, PS Testing 2026, Jim Carter, 23 October 2026; manager/personal workspace selector, personal profile, library empty state, project-management search, internal/external source headings, selection, assessments and removal from the temporary selection. Screenshots show the full browser window.
- Verified in code:
smartmobility-legacy/src/Main/ConfigBundle/Controller/SkillLibraryController.php, itsResources/views/skillsLibrary/templates,Customer/ProfileBundle/Services/{SkillSearcher,ObservatorySkillPersister}.php, andService/Skills/{SkillSuggestionService,SkillSuggestionV2Service,SkillQuotaService}.php. - Inferred: troubleshooting order follows the inspected source-selection and validation flow.
- Not verified: continuous-card interactions, deployed switch values, exact enabled observatory names, deployed/local revision parity and exhaustive role coverage. No employee skill was saved during the earlier 23 October inspection; the 26 October save/reopen check is recorded below. Company catalog copies may be created by search itself.
Journey verification — 26 October 2026
Observed in application: PS Testing 2026, Jim Carter. MS Project was added through the legacy library with 2/4 mastery and motivation, saved and checked in the reloaded profile. On that new test row only, both ratings were changed to 3/4 and persisted after reload. The addition was removed through its named confirmation and the complete rendered list checked after reload. No personalized-suggestion refresh or configuration save was exercised.
Last updated on 3 November 2026