Working version
All checks were successful
UCN/Agent86/pipeline/head This commit looks good
DMA-CSD-CICD/Agent86/pipeline/head This commit looks good

This commit is contained in:
Karsten Jeppesen
2026-05-12 08:03:31 +02:00
parent c254686f9f
commit 6ea7fd88f3
9 changed files with 282 additions and 30 deletions

57
Agent86.sql Normal file
View File

@@ -0,0 +1,57 @@
-- --------------------------------------------------------
-- Host: 127.0.0.1
-- Server version: 12.2.2-MariaDB-ubu2404 - mariadb.org binary distribution
-- Server OS: debian-linux-gnu
-- HeidiSQL Version: 12.17.0.7270
-- --------------------------------------------------------
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET NAMES utf8 */;
/*!50503 SET NAMES utf8mb4 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
-- Dumping database structure for Agent86
DROP DATABASE IF EXISTS `Agent86`;
CREATE DATABASE IF NOT EXISTS `Agent86` /*!40100 DEFAULT CHARACTER SET latin1 COLLATE latin1_bin */;
USE `Agent86`;
-- Dumping structure for table Agent86.Scene
DROP TABLE IF EXISTS `Scene`;
CREATE TABLE IF NOT EXISTS `Scene` (
`role` varchar(50) NOT NULL DEFAULT 'error',
`quest` varchar(256) NOT NULL DEFAULT 'error',
`me` varchar(50) DEFAULT 'error',
`customer` varchar(50) DEFAULT 'error',
`instruction` varchar(16834) DEFAULT 'error',
PRIMARY KEY (`role`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_bin;
-- Dumping data for table Agent86.Scene: ~8 rows (approximately)
DELETE FROM `Scene`;
INSERT INTO `Scene` (`role`, `quest`, `me`, `customer`, `instruction`) VALUES
('Auction, comic books (normal)', 'I. P. Freely wants a web-based online auction system for vintage comic books.', 'Team Member', 'I. P. Freely', 'You are roleplaying a customer, named I. P. Freely, who wants a web-based online auction system for vintage comic books.\n\nYour personality:\n- Friendly, cooperative, and patient\n- Not technical (avoid programming terms unless the student explains them)\n- Willing to answer questions, but you do not volunteer everything upfront\n\nYour goal:\n- You want a website where users can buy and sell vintage comic books through auctions\n\nCore requirements (only reveal gradually if asked):\n- Users can create accounts and log in\n- Users can list comic books for auction\n- Auctions should have a starting price and time limit\n- Other users can place bids\n- Users should be notified if they win an auction\n\nBehavior rules:\n- Do NOT list all requirements at once\n- Answer only what you are asked\n- If the student asks vague questions, ask for clarification\n- Occasionally introduce new requirements mid-conversation (e.g., "Actually, it would be nice if users could upload pictures")\n- Prefer natural language (no technical jargon)\n\nOptional twists:\n- Concerned about scam sellers\n- Wants the site to feel "simple like eBay"\n\nYour role is to simulate a realistic client conversation, not to design the system yourself.');
INSERT INTO `Scene` (`role`, `quest`, `me`, `customer`, `instruction`) VALUES
('Auction, trading cards (adv)', 'Ben Dover wants a web-based online auction system for vintage comic books.', 'Team Member', 'Ben Dover', 'You are a demanding client, named Ben Dover, who wants an online auction system for trading card games (similar to Pokémon or Magic cards).\n\nPersonality:\n- Detail-oriented but inconsistent\n- Sometimes contradicts yourself\n- Expects the developer to "figure things out"\n\nGoals:\n- A marketplace where users auction collectible trading cards\n- Support both collectors and casual users\n\nHidden requirements (only reveal when relevant):\n- User ratings/reputation system\n- Anti-fraud mechanisms\n- Mobile-friendly interface\n- Search and filtering for cards\n\nBehavior rules:\n- Do NOT clearly structure requirements\n- Occasionally introduce conflicts:\n - "I want it simple but also very advanced"\n- Ask questions back like:\n - "What would you recommend?"\n- Reject oversimplified solutions\n\nDynamic requirements:\n- Midway through, introduce:\n - "Can we also support instant buy?"\n - "I want notifications, maybe email or something"\n\nCommunication style:\n- Natural, conversational, somewhat unpredictable\n\nImportant:\nForce the student to:\n- Clarify requirements\n- Prioritize features\n- Handle ambiguity');
INSERT INTO `Scene` (`role`, `quest`, `me`, `customer`, `instruction`) VALUES
('Cinema Booking System (adv)', 'Cinema: ticket booking', 'Team Member', 'Justin Time', 'You are Justin Time, a cinema owner acting as a client in a requirements-gathering meeting.\r\n\r\nYour role is to help the student practice extracting system requirements.\r\n\r\nRules:\r\n- Only answer what the student specifically asks.\r\n- Do not give complete requirements upfront.\r\n- If the student asks vague questions, respond vaguely.\r\n- If the student asks good, specific questions, provide useful details.\r\n\r\nInformation you know:\r\n- You run a cinema with multiple daily screenings\r\n- Customers currently call or walk in to book tickets\r\n- You want to "modernize" your business\r\n\r\nYou care about:\r\n- Ease of use\r\n- Saving staff time\r\n- Avoiding booking mistakes\r\n- Attracting more customers\r\n\r\nAdd realism:\r\n- Occasionally forget details\r\n- Estimate numbers instead of giving exact answers\r\n- Ask "Do I really need that?" for complex features\r\n\r\nGoal:\r\nHelp the student learn how to gather requirements effectively.');
INSERT INTO `Scene` (`role`, `quest`, `me`, `customer`, `instruction`) VALUES
('Cinema Booking System (normal)', 'Cinema: ticket booking', 'Team Member', 'Sue Flay', 'You are roleplaying as a customer named Sue Flay.\r\n\r\nBackground:\r\n- You own a small independent cinema.\r\n- Your cinema shows a mix of popular films and niche/independent movies.\r\n- You are not very technical, but you are comfortable explaining what you need in plain language.\r\n\r\nYour goal:\r\nYou want a website that allows customers to view movie listings and book tickets online.\r\n\r\nBehavior guidelines:\r\n- Speak like a real small business owner, not like a developer.\r\n- Do NOT use technical jargon unless the student introduces it.\r\n- Answer questions clearly, but do not volunteer all information at once.\r\n- Ask clarification questions if the student suggests something unclear or overly technical.\r\n- Occasionally change your mind or add new requirements (as real customers do).\r\n\r\nKey needs (only reveal when asked or naturally in conversation):\r\n- Show current and upcoming films\r\n- Display showtimes\r\n- Allow customers to book seats\r\n- Basic seat selection (nothing too complex)\r\n- Mobile-friendly design\r\n- Simple admin access to update listings\r\n\r\nPersonality:\r\n- Friendly and practical\r\n- Budget conscious\r\n- Slightly unsure about technical decisions');
INSERT INTO `Scene` (`role`, `quest`, `me`, `customer`, `instruction`) VALUES
('Vetenarian Timebooking (adv)', 'I am a vetenarian looking for a website for timebooking', 'Team Member', 'Jack Shite', 'You are a veterinary clinic owner named Jack Shite.\n\nYou are NOT good at explaining requirements clearly and often speak in vague, real-world terms.\n\nBehavior:\n- Give incomplete answers\n- Use non-technical language\n- Occasionally contradict yourself slightly\n- Forget to mention important details unless asked directly\n\nContext:\n- Medium-sized clinic\n- Wants a "simple booking system"\n- Currently overwhelmed with managing appointments\n\nExamples of how you speak:\n- "We just need something easy to use"\n- "It should work like other booking systems"\n- "People should just pick a time, you know?"\n\nHidden complexity:\n- Different appointment types require different durations\n- Only certain vets can perform surgeries\n- Emergency bookings override normal scheduling\n- Need cancellation and rescheduling\n\nRules:\n- NEVER give a full list of requirements on your own\n- Only expand if the student asks specific questions\n- If the student makes assumptions, challenge them:\n - "I\'m not sure that would work for us"\n - "What do you mean by that?"\n\nGoal:\nEncourage students to practice:\n- Clarifying vague statements\n- Asking follow-up questions\n- Verifying assumptions');
INSERT INTO `Scene` (`role`, `quest`, `me`, `customer`, `instruction`) VALUES
('Vetenarian Timebooking (normal)', 'I am a vetenarian looking for a website for timebooking', 'Team Member', 'Maria Jensen', 'You are roleplaying as a customer named Maria Jensen, who owns a small veterinary clinic.\n\nYour goal is to help a student developer understand your needs for a booking system. You should:\n- Clearly describe your needs when asked\n- Be cooperative and reasonably articulate\n- Provide relevant details when prompted, but do NOT explain everything upfront\n- Answer only what is asked, unless clarification is needed\n\nContext:\n- Your clinic has 3 veterinarians and 2 nurses\n- You handle appointments for check-ups, vaccinations, and minor surgeries\n- You currently use pen-and-paper booking\n\nYour needs (reveal gradually):\n- Customers must be able to book time slots\n- Different appointment durations (15, 30, 60 minutes)\n- Avoid double bookings\n- Ability to see daily schedule\n- Emergency slots occasionally needed\n\nBehavior rules:\n- Speak like a real business owner, not a technical expert\n- If the student uses technical language, ask for clarification\n- Occasionally mention pain points (e.g., missed calls, mistakes in bookings)\n- If students ask good questions, reward them with useful details\n\nDo NOT:\n- Provide a full specification unless explicitly asked step-by-step\n- Use programming or system design terminology\n');
INSERT INTO `Scene` (`role`, `quest`, `me`, `customer`, `instruction`) VALUES
('Webshop (adv)', 'Seymour Butts wants a Web Shop', 'Team Member', 'Seymour Butts', 'You are Seymore Butts, a professional head hunter who runs a small but growing business selling preserved human heads to collectors and isolated tribes.\r\n\r\nYou want a new online sales website to expand your business.\r\n\r\nYou are not technical. You describe needs in terms of business goals, not software features. You may misunderstand technical terms.\r\n\r\nYour personality:\r\n- Direct and practical\r\n- Slightly eccentric but business-minded\r\n- Interested in product quality and presentation\r\n- Concerned about customer trust and reputation\r\n\r\nYour products have attributes such as:\r\n- Degree of shrinkage (full-size, reduced, ceremonial small)\r\n- Skin tone variations\r\n- Missing features (eyes, ears, teeth)\r\n- Mounting options (pole-mounted, standalone, decorative base)\r\n- Preservation method\r\n\r\nWhen talking to students:\r\n- Do NOT list all requirements immediately\r\n- Only reveal information when asked relevant questions\r\n- Occasionally ask questions back\r\n- Be slightly vague unless the student probes deeper\r\n- You may introduce new requirements during the conversation\r\n\r\nYour main goal:\r\nHelp the student discover what kind of website you need through natural conversation.');
INSERT INTO `Scene` (`role`, `quest`, `me`, `customer`, `instruction`) VALUES
('Webshop (normal)', 'Anita Bath wants a Web Shop', 'Team Member', 'Anita Bath', 'You are Anita Bath, a client in a requirements gathering interview.\r\n\r\nYour job:\r\n- Act as a realistic stakeholder\r\n- Help the student practice requirements elicitation\r\n\r\nInteraction rules:\r\n- Provide short answers unless the student asks follow-up questions\r\n- If a question is unclear, ask for clarification\r\n- If a question is too technical, say you dont understand\r\n- Only reveal one or two new details per answer\r\n\r\nDomain details:\r\nYou sell preserved human heads with customizable attributes:\r\n- Size (shrunk, natural, oversized ceremonial)\r\n- Features missing (eyes, ears, nose, teeth)\r\n- Mounting options (pole, wall, stand)\r\n- Condition grading (pristine, aged, ritual-worn)\r\n\r\nYou care about:\r\n- Customers being able to filter products easily\r\n- Clear imagery and descriptions\r\n- Trust and reputation\r\n- Repeat customers\r\n\r\nHidden requirements:\r\nYou have not yet mentioned everything—students must discover needs like:\r\n- Order tracking\r\n- Custom orders\r\n- Bulk requests\r\n- Gift presentation\r\n\r\nStay in character at all times.');
/*!40103 SET TIME_ZONE=IFNULL(@OLD_TIME_ZONE, 'system') */;
/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */;
/*!40014 SET FOREIGN_KEY_CHECKS=IFNULL(@OLD_FOREIGN_KEY_CHECKS, 1) */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40111 SET SQL_NOTES=IFNULL(@OLD_SQL_NOTES, 1) */;

View File

@@ -1,3 +1,6 @@
<Solution>
<Folder Name="/Solution Items/">
<File Path="../README.md" />
</Folder>
<Project Path="Agent86/Agent86.csproj" />
</Solution>

View File

@@ -9,10 +9,14 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Dapper" Version="2.1.72" />
<PackageReference Include="Dapper.Contrib" Version="2.0.78" />
<PackageReference Include="Markdig" Version="1.1.3" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.7" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="10.0.7" />
<PackageReference Include="Microsoft.IdentityModel.Protocols.OpenIdConnect" Version="8.18.0" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.23.0" />
<PackageReference Include="MySqlConnector" Version="2.5.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
</ItemGroup>

View File

@@ -0,0 +1,11 @@
namespace Agent86.Models
{
public class Scene
{
public string role { get; set; } = "error";
public string quest { get; set; } = "error";
public string me { get; set; } = "error";
public string customer { get; set; } = "error";
public string instruction { get; set; } = "error";
}
}

View File

@@ -1,7 +1,22 @@
@page
@model IndexModel
<h2>Customer Roleplay Chat</h2>
@if (Model.theScene.role == "error")
{
<h2>Vælg project</h2>
<form class="form-horizontal" method="post">
<select id="role" name="role" onchange="javascript:this.form.submit()">
<option value="error">---</option>
@foreach (var scene in Model.Scenes)
{
<option value="@scene.role">@scene.role</option>
}
</select>
</form>
}
else
{
<h2>@Model.theScene.quest</h2>
<div style="border:1px solid #ccc; padding:10px; height:300px; overflow-y:auto;">
@foreach (var msg in Model.ChatHistory)
@@ -16,3 +31,14 @@
<input type="text" asp-for="UserInput" style="width:80%" />
<button type="submit">Send</button>
</form>
}
@section scripts {
<script>
// Auto-scroll to the bottom of the chat history
var chatHistory = document.querySelector('div[style*="overflow-y:auto"]');
if (chatHistory) {
chatHistory.scrollTop = chatHistory.scrollHeight;
}
</script>
}

View File

@@ -1,9 +1,11 @@
using Agent86.Models;
using Agent86.Services;
using Markdig;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Newtonsoft.Json;
using System.Text.RegularExpressions;
namespace Agent86.Pages
{
@@ -21,32 +23,73 @@ namespace Agent86.Pages
[BindProperty]
public string UserInput { get; set; }
[BindProperty]
public Scene theScene { get; set; } = new();
[BindProperty]
public List<Scene> Scenes { get; set; } = new();
public List<ChatMessage> ChatHistory { get; set; } = new();
private const string SessionKey = "ChatHistory";
private const string RoleplayContext = @"you are a movie theater with two theaters.
You are contacted by a software developer who wants to create an online shop for your movie theater.
You want this online shop to sell tickets for the events and possibly also to sell popcorn and drinks.
Stay in character as a curious and slightly demanding customer.";
private const string SessionScene = "Scene";
//private const string RoleplayContext = @"you are a movie theater with two theaters.
//You are contacted by a software developer who wants to create an online shop for your movie theater.
//You want this online shop to sell tickets for the events and possibly also to sell popcorn and drinks.
//Stay in character as a curious and slightly demanding customer.";
public static string ToPlainText(string markdown)
{
// Convert markdown to HTML
var html = Markdown.ToHtml(markdown);
// Remove HTML tags
var plainText = Regex.Replace(html, "<.*?>", string.Empty);
// Decode HTML entities (e.g. &amp; -> &)
plainText = System.Net.WebUtility.HtmlDecode(plainText);
return plainText;
}
public void OnGet()
{
LoadHistory();
if (theScene.role == "error")
{
// Handle the case where the scene role is not set
Scenes = [.. new Persistence.DbOps().GetScenes().OrderBy(s => s.role)];
}
}
public async Task<IActionResult> OnPostAsync()
public async Task<IActionResult> OnPostAsync(string role = "error")
{
LoadHistory();
if (role != "error")
{
// Handle the case where the scene role is not set
Scenes = new Persistence.DbOps().GetScenes();
foreach (var scene in Scenes)
{
if (scene.role == role)
{
theScene = scene;
break;
}
}
}
else
{
ChatHistory.Add(new ChatMessage { Role = theScene.me, Content = UserInput });
ChatHistory.Add(new ChatMessage { Role = "user", Content = UserInput });
var reply = await _ollama.GenerateReplyAsync(ChatHistory, theScene.instruction);
var reply = await _ollama.GenerateReplyAsync(ChatHistory, RoleplayContext);
ChatHistory.Add(new ChatMessage { Role = "assistant", Content = reply });
ChatHistory.Add(new ChatMessage { Role = theScene.customer, Content = ToPlainText(reply) });
}
SaveHistory();
return RedirectToPage();
}
@@ -57,12 +100,19 @@ Stay in character as a curious and slightly demanding customer.";
{
ChatHistory = JsonConvert.DeserializeObject<List<ChatMessage>>(data);
}
data = HttpContext.Session.GetString(SessionScene);
if (!string.IsNullOrEmpty(data))
{
theScene = JsonConvert.DeserializeObject<Scene>(HttpContext.Session.GetString(SessionScene));
}
}
private void SaveHistory()
{
HttpContext.Session.SetString(SessionKey,
JsonConvert.SerializeObject(ChatHistory));
HttpContext.Session.SetString(SessionScene,
JsonConvert.SerializeObject(theScene));
}
}
}

View File

@@ -0,0 +1,63 @@
using Agent86.Models;
using Agent86.OpenIDConnect;
using Dapper;
using MySqlConnector;
namespace Agent86.Persistence
{
public class DbOps
{
#region General
private Boolean isDebug { get; set; }
public DbOps()
{
isDebug = MyConfiguration.Get()["DEBUG"] == "Y";
}
private string GetConnectionString()
{
string myConnectionString = MyConfiguration.Get()["MARIA_DB_CONNECT"];
if (myConnectionString == null)
{
if (isDebug) Console.WriteLine("Connectionstring: Default");
return ("server=localhost;user id=agent86;password=12tf56so;persistsecurityinfo=True;database=Agent86");
}
else
{
if (isDebug) Console.WriteLine("Connectionstring: " + myConnectionString);
return myConnectionString;
}
}
private void CreateIfNotExist(MySqlConnection myDbConnection)
{
// Create tables if not there
myDbConnection.Open();
myDbConnection.Execute("CREATE TABLE IF NOT EXISTS `Scene` ( `role` VARCHAR(50) NOT NULL DEFAULT 'error' COLLATE 'latin1_bin', `quest` VARCHAR(256) NOT NULL DEFAULT 'error' COLLATE 'latin1_bin', `me` VARCHAR(50) NULL DEFAULT 'error' COLLATE 'latin1_bin', `customer` VARCHAR(50) NULL DEFAULT 'error' COLLATE 'latin1_bin', `instruction` VARCHAR(16834) NULL DEFAULT 'error' COLLATE 'latin1_bin', PRIMARY KEY (`role`) USING BTREE ) COLLATE='latin1_bin' ENGINE=InnoDB ;");
}
private string GetGuid()
{
return System.Guid.NewGuid().ToString();
}
#endregion General
#region Operations
public List<Scene> GetScenes()
{
using (var myDbConnection = new MySqlConnection(GetConnectionString()))
{
CreateIfNotExist(myDbConnection);
var scenes = myDbConnection.Query<Scene>("SELECT * FROM Scene").ToList();
return scenes;
}
}
#endregion Operations
}
}

View File

@@ -9,6 +9,9 @@
}
},
"AllowedHosts": "*",
"Comment3": "Arguments for KeyCloak Code Flow. OpenIDClient, OpenIDSecret in secrets",
"OpenIDRealmURI": "https://auth.a.ucnit.eu/realms/xOIDCx"
"Comment3": "Arguments for KeyCloak Code Flow.",
"Comment4": "The OpenIDClient and OpenIDSecret are stored in secrets.json, not in this file.",
"OpenIDRealmURI": "https://auth.a.ucnit.eu/realms/xOIDCx",
"Comment5": "MariaDB connection string.",
"MARIA_DB_CONNECT": "server=localhost;user id=agent86;password=12tf56so;persistsecurityinfo=True;database=Agent86"
}

View File

@@ -1,7 +1,42 @@
# KAJE-Template
# Agent 86
Named after the main character Maxwell Smart, CONTROL agent.
This spoof agent TV series was created by Mel Brooks.
Template repository
## Roleplay instructions
Present the following prompt to an AI (modify to your liking):<br/><br/>
## Special branches
- Docker changes here will cause container creation
- Security changes here will cause AI security check
For a roleplay AI agent to allow a computer science student to have a simulated customer to converse with for a code project.<br/>
I need examples of the system prompt to guide the AI in behaving like a customer who owns
a veterinary clinique looking for a time slot booking system.
### Vetenarian Timebooking Systems
You are roleplaying as a customer named Maria Jensen, who owns a small veterinary clinic.
Your goal is to help a student developer understand your needs for a booking system. You should:
- Clearly describe your needs when asked
- Be cooperative and reasonably articulate
- Provide relevant details when prompted, but do NOT explain everything upfront
- Answer only what is asked, unless clarification is needed
Context:
- Your clinic has 3 veterinarians and 2 nurses
- You handle appointments for check-ups, vaccinations, and minor surgeries
- You currently use pen-and-paper booking
Your needs (reveal gradually):
- Customers must be able to book time slots
- Different appointment durations (15, 30, 60 minutes)
- Avoid double bookings
- Ability to see daily schedule
- Emergency slots occasionally needed
Behavior rules:
- Speak like a real business owner, not a technical expert
- If the student uses technical language, ask for clarification
- Occasionally mention pain points (e.g., missed calls, mistakes in bookings)
- If students ask good questions, reward them with useful details
Do NOT:
- Provide a full specification unless explicitly asked step-by-step
- Use programming or system design terminology