-
-
Notifications
You must be signed in to change notification settings - Fork 0
AI Task Suggester
On
main, not yet released. The release afterIRIS-NG-v2.2.0carries this page's feature; until that tag exists, an installed image does not have it. See Changelog.
A case accumulates findings faster than it accumulates a plan: a webshell is confirmed and nobody opens the eradication task, a command-and-control domain is recorded and nobody is asked to block it, evidence is collected and the analysis task never appears. The task suggester reads the case and proposes the tasks that are missing. It only proposes — a task exists when an analyst accepts it, never before.
Open it with the sparkle button on the Tasks tab's toolbar, to the left of Refresh.
A review panel lists up to eight suggestions, the work that should start first at the top. Each card carries:
| Part | What it is |
|---|---|
| Priority |
high (delay blocks containment or loses evidence), medium or low
|
| Title and description | Editable in place. What you accept is what is on screen, not what the model wrote |
| Why | One sentence naming the fact in the case that makes the task necessary |
| Assign to | A picker limited to the people who can work the case — see Who gets proposed |
| Skill chips | The skills the task needs, from your skill catalog |
| Depends on | Work that has to finish first: an existing task (#12 Collect artifacts) or another suggestion (dashed outline) |
Accept creates that one task. Accept all creates everything still listed, in one request. Dismiss removes a card for the rest of the session — it is not remembered, so Regenerate may offer the idea again. Edits you have typed into other cards survive an accept or a dismiss.
An accepted task is an ordinary task from then on. It is created through the same path as
the Add task dialog, so module hooks run, the activity log records it, and the assignee
receives the usual task-assigned notification. Two things mark where it
came from and nothing else: the status is To do, and the task carries the tag
ai-suggested.
A suggestion can depend on a task the case already has, or on another suggestion in the
same list. Accepting creates depends_on task links, so the Tasks tab's
tree view shows the order the work should happen in.
A dependency on another suggestion is kept only when that suggestion becomes a task too — accept it first, or accept both together. Accept a task alone while the thing it depends on is still pending (or was dismissed) and the link is dropped; the panel says so rather than dropping it silently. A dependency on a suggestion you accepted a moment ago is followed to the real task it became.
The model never sees your analysts. No name, login or skill profile is part of what is sent to the AI backend. The model's part is to tag each suggestion with the skills it needs, chosen from your skill catalog; the server does the rest:
- Candidates are the active, human users with full access to the case. Read-only users, service accounts, disabled accounts and people without access to the case are never candidates — a task assigned to someone who cannot open the case helps nobody.
- The candidate whose skills overlap the task's skills the most is proposed. Ties go to whoever holds fewer open tasks on this case, so the suggestions spread work instead of stacking it on one person.
- When nobody's skills overlap, nobody is proposed and the picker defaults to you, the analyst accepting. You can always choose anyone in the list, or Unassigned.
The proposal is worked out each time the panel opens, not stored with the suggestions, so it reflects today's access and today's skill profiles. If every suggestion comes back without a proposal, the usual reason is that nobody on the case has skills recorded yet — see Analyst Skills and Teams.
The request carries the same case context as the case chat — notes, timeline (honouring event verdicts), IOCs, assets, evidence — plus the case's existing tasks with their ids, status and links, and the list of skill slugs it may use.
The answer is treated as untrusted. Before anything is shown, the server drops:
- a suggestion that duplicates an existing task — open or closed — or another suggestion (titles are compared ignoring case, punctuation and spacing);
- skills that are not in your catalog;
- a dependency on a task that does not exist in this case, on itself, or one that would close a loop;
- anything past the eighth suggestion.
A reply that is not the expected JSON is an error, and is never stored. Hosted models and organisational AI gateways sometimes answer security content with prose — a refusal, a policy notice, an authentication error. If that were read leniently it would surface as "no tasks needed", which is a claim about the case that nobody made. The panel shows the failure and what the backend said instead. An empty list from a model that answered properly is a different thing and is accepted: it looked, and found nothing to add.
The suggester runs when you open the panel for the first time on a case, and when you press Regenerate. Otherwise the panel shows the stored run immediately:
- the key is the model, the prompt and the case content — no clock value, so reopening the panel an hour later is still a cache hit;
- when the case has changed since the run, a notice says so and Regenerate takes the new data into account;
- suggestions that have since become tasks are withheld from a stored run, so accepting three of five and coming back tomorrow shows the remaining two.
The surface has its own row — Task suggester (Tasks tab) — in the per-feature backend
overrides on /manage/settings → AI, so it can be pinned to either backend slot. See
AI Features.
| Action | Needs |
|---|---|
| Open the panel, read a stored run | Read access to the case |
| Generate or regenerate | Full access to the case |
| Accept suggestions | Full access to the case |
No new permission is introduced; the case's existing access levels decide.
All under /api/v2/cases/<cid>/ai/. Session POSTs carry csrf_token in the JSON body.
| Method | Path | Purpose |
|---|---|---|
GET |
/task-suggestions |
The newest stored run, assignees ranked live. 404 when the suggester has never run for the case |
POST |
/task-suggestions |
Generate. 202 {task_id} — poll /api/v2/ai/jobs/<task_id> for result. ?sync=true runs inline; ?force=true bypasses the cache |
POST |
/task-suggestions/accept |
Create tasks and links from the accepted suggestions |
A run looks like this:
{
"suggestions": [
{
"ref": "S1",
"title": "Eradicate the webshells and restore the modified core files",
"description": "…",
"rationale": "Eight core files failed the integrity check and several webshells were deployed.",
"priority": "high",
"skills": ["malware-static", "linux-forensics"],
"depends_on": [{"ref": "T12", "kind": "task", "task_id": 12, "title": "Collect artifacts"}],
"assignee": {"user_id": 7, "user_name": "…", "matched_skills": ["linux-forensics"]}
}
],
"assignable_users": [{"user_id": 7, "user_name": "…", "skills": ["…"], "open_tasks": 1}],
"withheld_now_tasks": 0,
"stale": false,
"cached": true,
"model": "…",
"prompt_id": "TaskSuggesterSystemPrompt-v1",
"generated_at": "…"
}assignee is null when no candidate's skills overlap. stale is true when the case
changed since the run, false when it did not, and null when that could not be
determined because no AI backend is configured — unknown is not the same as unchanged.
The accept body lists what to create, as the analyst left it:
{
"tasks": [
{"ref": "S1", "title": "…", "description": "…", "assignee_id": 7, "depends_on": ["T12"]},
{"ref": "S2", "title": "…", "description": "…", "assignee_id": null, "depends_on": ["S1"]}
],
"csrf_token": "…"
}and the answer reports each item separately — one bad item never blocks the others:
{
"created": [{"ref": "S1", "task_id": 140, "title": "…", "assignee_id": 7}],
"failed": [{"ref": "S3", "error": "assignee cannot work this case"}],
"links_created": 2,
"links_dropped": [{"ref": "S5", "depends_on": "S9", "reason": "that suggestion was not accepted"}]
}What the accept endpoint refuses, per item: a title shorter than two characters, a title the
case already has a task for, and an assignee who is not among the case's assignable users.
A batch is at most eight items; an empty batch is a 400.
| File | Role |
|---|---|
source/app/iris_engine/ai/task_suggester.py |
Payload, validation, assignee ranking, cache |
source/app/resources/ai_prompts/task_suggester.md |
System prompt |
source/app/business/task_suggestions.py |
Accept: tasks through tasks_create, links, per-item results |
source/app/blueprints/rest/v2/cases/ai.py |
The three endpoints |
source/app/blueprints/pages/case/templates/modal_task_suggestions.html |
The review panel |