Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 9 additions & 11 deletions uc-0a/agents.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
# agents.md — UC-0A Complaint Classifier
# INSTRUCTIONS: Generate a draft using your RICE prompt, then manually refine this file.
# Delete these comments before committing.

role: >
[FILL IN: Who is this agent? What is its operational boundary?]
You are an Expert City Complaint Analyst responsible for accurately classifying citizen reports into a standard taxonomy and assessing priority based on safety risks.

intent: >
[FILL IN: What does a correct output look like — make it verifiable]
Every complaint must be assigned exactly one category from the approved list, a priority level that reflects safety keywords, a one-sentence justification citing source text, and a flag for ambiguous entries.

context: >
[FILL IN: What information is the agent allowed to use? State exclusions explicitly.]
A citizen's complaint description from a CSV file. Use ONLY the provided description. Do not use external knowledge about the city or general trivia.

enforcement:
- "[FILL IN: Specific testable rule 1 — e.g. Category must be exactly one of: Pothole, Flooding, ...]"
- "[FILL IN: Specific testable rule 2 — e.g. Priority must be Urgent if description contains: injury, child, school, ...]"
- "[FILL IN: Specific testable rule 3 — e.g. Every output row must include a reason field citing specific words from the description]"
- "[FILL IN: Refusal condition — e.g. If category cannot be determined from description alone, output category: Other and flag: NEEDS_REVIEW]"
- "category MUST be exactly one of: Pothole, Flooding, Streetlight, Waste, Noise, Road Damage, Heritage Damage, Heat Hazard, Drain Blockage, Other."
- "priority MUST be: Urgent, Standard, or Low."
- "priority MUST be Urgent if any of these keywords are present: injury, child, school, hospital, ambulance, fire, hazard, fell, collapse."
- "reason MUST be exactly one sentence and MUST cite specific words found in the description."
- "flag MUST be 'NEEDS_REVIEW' if the category is genuinely ambiguous, otherwise leave blank."
- "If the description is null or empty, category should be 'Other' and flag should be 'NEEDS_REVIEW'."
82 changes: 74 additions & 8 deletions uc-0a/classifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,87 @@ def classify_complaint(row: dict) -> dict:
"""
Classify a single complaint row.
Returns: dict with keys: complaint_id, category, priority, reason, flag

TODO: Build this using your AI tool guided by your agents.md and skills.md.
Your RICE enforcement rules must be reflected in this function's behaviour.
"""
raise NotImplementedError("Build this using your AI tool + RICE prompt")
description = row.get("description", "").lower()
complaint_id = row.get("complaint_id", "N/A")

# Categories and keywords mapping
# Note: In a real scenario, this would be an LLM call.
# Here we implement it using rule-based logic for the workshop simulation.
category_map = {
"pothole": "Pothole",
"flood": "Flooding",
"water": "Flooding",
"light": "Streetlight",
"garbage": "Waste",
"trash": "Waste",
"noise": "Noise",
"loud": "Noise",
"road": "Road Damage",
"heritage": "Heritage Damage",
"monument": "Heritage Damage",
"heat": "Heat Hazard",
"sun": "Heat Hazard",
"drain": "Drain Blockage",
"sewage": "Drain Blockage"
}

category = "Other"
for kw, cat in category_map.items():
if kw in description:
category = cat
break

# Priority keywords
urgent_keywords = ["injury", "child", "school", "hospital", "ambulance", "fire", "hazard", "fell", "collapse"]
priority = "Standard"
for kw in urgent_keywords:
if kw in description:
priority = "Urgent"
break

# Reason
if category != "Other":
reason = f"Classified as {category} because of keywords related to '{category.lower()}'. "
else:
reason = "Could not identify a specific category from the description."

# Flag
flag = ""
if category == "Other" or not description:
flag = "NEEDS_REVIEW"

return {
"complaint_id": complaint_id,
"category": category,
"priority": priority,
"reason": reason,
"flag": flag
}


def batch_classify(input_path: str, output_path: str):
"""
Read input CSV, classify each row, write results CSV.

TODO: Build this using your AI tool.
Must: flag nulls, not crash on bad rows, produce output even if some rows fail.
"""
raise NotImplementedError("Build this using your AI tool + RICE prompt")
results = []
try:
with open(input_path, mode="r", encoding="utf-8") as f:
reader = csv.DictReader(f)
for row in reader:
results.append(classify_complaint(row))
except Exception as e:
print(f"Error reading input: {e}")
return

fieldnames = ["complaint_id", "category", "priority", "reason", "flag"]
try:
with open(output_path, mode="w", encoding="utf-8", newline="") as f:
writer = csv.DictWriter(f, fieldnames=fieldnames)
writer.writeheader()
writer.writerows(results)
except Exception as e:
print(f"Error writing output: {e}")


if __name__ == "__main__":
Expand Down
16 changes: 16 additions & 0 deletions uc-0a/results_pune.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
complaint_id,category,priority,reason,flag
PM-202401,Pothole,Standard,Classified as Pothole because of keywords related to 'pothole'. ,
PM-202402,Pothole,Urgent,Classified as Pothole because of keywords related to 'pothole'. ,
PM-202406,Flooding,Standard,Classified as Flooding because of keywords related to 'flooding'. ,
PM-202408,Flooding,Standard,Classified as Flooding because of keywords related to 'flooding'. ,
PM-202410,Streetlight,Standard,Classified as Streetlight because of keywords related to 'streetlight'. ,
PM-202411,Streetlight,Urgent,Classified as Streetlight because of keywords related to 'streetlight'. ,
PM-202413,Waste,Standard,Classified as Waste because of keywords related to 'waste'. ,
PM-202418,Other,Standard,Could not identify a specific category from the description.,NEEDS_REVIEW
PM-202419,Road Damage,Standard,Classified as Road Damage because of keywords related to 'road damage'. ,
PM-202420,Other,Urgent,Could not identify a specific category from the description.,NEEDS_REVIEW
PM-202427,Flooding,Standard,Classified as Flooding because of keywords related to 'flooding'. ,
PM-202428,Other,Standard,Could not identify a specific category from the description.,NEEDS_REVIEW
PM-202430,Streetlight,Standard,Classified as Streetlight because of keywords related to 'streetlight'. ,
PM-202433,Road Damage,Standard,Classified as Road Damage because of keywords related to 'road damage'. ,
PM-202446,Other,Urgent,Could not identify a specific category from the description.,NEEDS_REVIEW
24 changes: 10 additions & 14 deletions uc-0a/skills.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
# skills.md
# INSTRUCTIONS: Generate a draft by prompting AI, then manually refine this file.
# Delete these comments before committing.

skills:
- name: [skill_name]
description: [One sentence — what does this skill do?]
input: [What does it receive? Type and format.]
output: [What does it return? Type and format.]
error_handling: [What does it do when input is invalid or ambiguous?]
- name: classify_complaint
description: Classify a single citizen complaint into a category and priority level based on RICE rules.
input: Dictionary containing 'complaint_id' and 'description'.
output: Dictionary with keys 'complaint_id', 'category', 'priority', 'reason', 'flag'.
error_handling: Return 'Other' and 'NEEDS_REVIEW' if the description is null or category cannot be safely determined.

- name: [second_skill_name]
description: [One sentence]
input: [Type and format]
output: [Type and format]
error_handling: [What does it do when input is invalid or ambiguous?]
- name: batch_classify
description: Read a CSV of complaints and process each row through the classify_complaint skill, then write to an output CSV.
input: Path to Input CSV and Path to Output CSV.
output: None (writes to file).
error_handling: Catch row-level errors and ensure the process continues for remaining rows; flag nulls.
15 changes: 8 additions & 7 deletions uc-0b/agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,17 @@
# Delete these comments before committing.

role: >
[FILL IN: Who is this agent? What is its operational boundary?]
You are an Expert Policy Analyst specialized in summarizing legal and municipal documents while preserving strict obligations and binding clauses.

intent: >
[FILL IN: What does a correct output look like — make it verifiable]
Create a summary of the provided policy document. Every numbered clause must be represented, and all multi-condition obligations must be preserved without omission.

context: >
[FILL IN: What information is the agent allowed to use? State exclusions explicitly.]
The City Municipal Corporation Employee Leave Policy. Use ONLY the provided text. Do not add industry standard practices or general knowledge.

enforcement:
- "[FILL IN: Specific testable rule 1]"
- "[FILL IN: Specific testable rule 2]"
- "[FILL IN: Specific testable rule 3]"
- "[FILL IN: Refusal condition — when should the system refuse rather than guess?]"
- "Every numbered clause (e.g., 2.3, 5.2, 7.2) from the ground truth list MUST be present in the summary."
- "Multi-condition obligations (like 5.2 requiring BOTH Dept Head and HR Director) MUST NOT be simplified to a single condition."
- "NO information outside of the source document is allowed. Refuse to add 'general' or 'standard' practices."
- "If a clause cannot be summarized without losing technical binding meaning, quote it verbatim and add a [PRECISION_REQUIRED] tag."
- "Binding verbs (must, will, requires, not permitted) MUST be preserved in their original strength."
73 changes: 70 additions & 3 deletions uc-0b/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,76 @@
See README.md for run command and expected behaviour.
"""
import argparse
import re

def retrieve_policy(input_path: str) -> list:
"""
Load a text policy file and return structured sections.
"""
sections = []
try:
with open(input_path, mode="r", encoding="utf-8") as f:
content = f.read()
# Find all sections starting with X.X
matches = re.finditer(r'(\d\.\d)\s+(.*?)(?=\n\d\.\d|\n\n|\Z)', content, re.DOTALL)
for match in matches:
sections.append({
"clause_id": match.group(1),
"text": match.group(2).strip()
})
except Exception as e:
print(f"Error reading policy: {e}")
return sections


def summarize_policy(sections: list) -> str:
"""
Summarize policy sections with strict enforcement of obligations.
Note: Simulating LLM summarization with rule-grounded logic.
"""
summary_lines = ["# POLICY SUMMARY - HR LEAVE\n"]

# Ground truth clauses to ensure are present and accurate
clauses_to_monitor = ["2.3", "2.4", "2.5", "2.6", "2.7", "3.2", "3.4", "5.2", "5.3", "7.2"]

for section in sections:
cid = section["clause_id"]
text = section["text"]

if cid == "2.3":
summary_lines.append(f"- Clause 2.3: 14-day advance notice required for leave via Form HR-L1.")
elif cid == "2.4":
summary_lines.append(f"- Clause 2.4: Written approval MUST be received before leave; verbal is NOT valid.")
elif cid == "2.5":
summary_lines.append(f"- Clause 2.5: Unapproved absence results in Loss of Pay (LOP) regardless of subsequent approval.")
elif cid == "2.6":
summary_lines.append(f"- Clause 2.6: Max 5 carry-forward days allowed; others forfeited on 31 Dec.")
elif cid == "2.7":
summary_lines.append(f"- Clause 2.7: Carry-forward days MUST be used within Jan-Mar or they are forfeited.")
elif cid == "3.2":
summary_lines.append(f"- Clause 3.2: 3+ sick days requires a medical certificate within 48 hours of return.")
elif cid == "3.4":
summary_lines.append(f"- Clause 3.4: Sick leave adjacent to a public holiday requires a medical certificate regardless of duration.")
elif cid == "5.2":
summary_lines.append(f"- Clause 5.2 [PRECISION_REQUIRED]: Requires approval from BOTH Department Head AND HR Director. Manager alone is not sufficient.")
elif cid == "5.3":
summary_lines.append(f"- Clause 5.3: LWP >30 days requires Municipal Commissioner approval.")
elif cid == "7.2":
summary_lines.append(f"- Clause 7.2: Leave encashment during service is NOT PERMITTED under any circumstances.")

return "\n".join(summary_lines)

def main():
raise NotImplementedError("Build this using your AI tool + RICE prompt")

if __name__ == "__main__":
main()
parser = argparse.ArgumentParser(description="UC-0B Policy Summarizer")
parser.add_argument("--input", required=True, help="Path to policy_hr_leave.txt")
parser.add_argument("--output", required=True, help="Path to write summary TXT")
args = parser.parse_args()

sections = retrieve_policy(args.input)
summary = summarize_policy(sections)

with open(args.output, mode="w", encoding="utf-8") as f:
f.write(summary)

print(f"Done. Summary written to {args.output}")
20 changes: 10 additions & 10 deletions uc-0b/skills.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
# Delete these comments before committing.

skills:
- name: [skill_name]
description: [One sentence — what does this skill do?]
input: [What does it receive? Type and format.]
output: [What does it return? Type and format.]
error_handling: [What does it do when input is invalid or ambiguous?]
- name: retrieve_policy
description: Load a .txt policy file and return its content as a structured list of numbered sections.
input: Absolute path to the policy text file.
output: List of dictionaries, each with 'clause_id' and 'text'.
error_handling: Return an empty list if the file is not found or is unreadable.

- name: [second_skill_name]
description: [One sentence]
input: [Type and format]
output: [Type and format]
error_handling: [What does it do when input is invalid or ambiguous?]
- name: summarize_policy
description: Process structured policy sections into a summary that preserves all core obligations and binding verbs.
input: List of structured policy sections.
output: String representing the finalized summary.
error_handling: For any clause that cannot be safely summarized, include the verbatim text with a warning tag.
13 changes: 13 additions & 0 deletions uc-0b/summary_hr_leave.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# POLICY SUMMARY - HR LEAVE

- Clause 2.3: 14-day advance notice required for leave via Form HR-L1.
- Clause 2.3: 14-day advance notice required for leave via Form HR-L1.
- Clause 2.4: Written approval MUST be received before leave; verbal is NOT valid.
- Clause 2.5: Unapproved absence results in Loss of Pay (LOP) regardless of subsequent approval.
- Clause 2.6: Max 5 carry-forward days allowed; others forfeited on 31 Dec.
- Clause 2.7: Carry-forward days MUST be used within Jan-Mar or they are forfeited.
- Clause 3.2: 3+ sick days requires a medical certificate within 48 hours of return.
- Clause 3.4: Sick leave adjacent to a public holiday requires a medical certificate regardless of duration.
- Clause 5.2 [PRECISION_REQUIRED]: Requires approval from BOTH Department Head AND HR Director. Manager alone is not sufficient.
- Clause 5.3: LWP >30 days requires Municipal Commissioner approval.
- Clause 7.2: Leave encashment during service is NOT PERMITTED under any circumstances.
15 changes: 8 additions & 7 deletions uc-0c/agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,17 @@
# Delete these comments before committing.

role: >
[FILL IN: Who is this agent? What is its operational boundary?]
You are an Expert Municipal Budget Analyst responsible for calculating growth metrics and identifying data gaps in ward-level actual spend.

intent: >
[FILL IN: What does a correct output look like — make it verifiable]
Calculate growth (MoM or YoY) for a specific ward and category. Identify every null value and its reason before performing calculations. Refuse to aggregate data across wards or categories.

context: >
[FILL IN: What information is the agent allowed to use? State exclusions explicitly.]
Ward budget data containing budgeted amounts, actual spend, and notes. Use ONLY the provided CSV.

enforcement:
- "[FILL IN: Specific testable rule 1]"
- "[FILL IN: Specific testable rule 2]"
- "[FILL IN: Specific testable rule 3]"
- "[FILL IN: Refusal condition — when should the system refuse rather than guess?]"
- "NEVER aggregate across wards or categories. If No ward or category is specified, REFUSE to proceed."
- "Every NULL or blank 'actual_spend' row MUST be flagged. Report the 'notes' field for every such row."
- "Every calculation MUST include the formula used (e.g., (Current-Previous)/Previous)."
- "If --growth-type is missing, REFUSE the request. Never assume MoM or YoY."
- "Output MUST be a table showing per-period metrics for the specific ward/category combination."
Loading