Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Work Intake & Execution Agent

An agentic prototype that turns unstructured incoming work (an email, meeting notes, a founder instruction, a bug report) into a structured, reviewable, partially automated workflow. Submit raw text and the system interprets it into a strict schema, plans by routing each action to a tool, runs the safe actions automatically, holds anything that would leave the building behind a human approval gate, logs every step, and persists all of it.

Demo

output.mp4

What it does

  1. Intake. A web interface accepts a block of unstructured text.
  2. Interpret. An LLM extracts a fixed structured schema (not free-form prose): task title, summary, action items, priority, detected deadline, missing information, what could be automated, and what needs human confirmation. The output is validated by Pydantic.
  3. Plan. A second LLM step uses function calling to route each action into one of four buckets: execute_auto, human_review, cannot_execute, or needs_clarification, each with a reason.
  4. Execute. Safe tools run immediately. The one tool that produces external-facing communication is drafted but held for approval. Missing-info and unsupported actions are recorded, never faked.
  5. Approve. Any drafted communication needs an explicit Approve / Reject / Edit before it counts as done. No real email is ever sent.
  6. Persist + trace. Everything lands in SQLite with timestamps, and a plain-language activity trace is visible in the UI.
  7. Review + manage. Past requests are listed, searchable, and re-openable; each can be deleted individually, or all cleared at once, from the UI.

Architecture

flowchart TD
  U["User: unstructured text"] --> API["FastAPI /api/requests"]
  API --> INT["Interpret<br/>Groq JSON mode + Pydantic validation"]
  INT -->|valid| PLAN["Plan<br/>Groq function calling"]
  INT -->|invalid x2| FAIL["status: interpretation_failed<br/>(clear failure, no fake result)"]
  PLAN --> EXE["Execute<br/>route by boundary policy"]
  EXE -->|execute_auto| AUTO["run tool now"]
  EXE -->|human_review| GATE["draft, then await approval"]
  EXE -->|needs_clarification| CLAR["record the question"]
  EXE -->|cannot_execute| CANT["record the boundary"]
  GATE --> HUMAN["Approve / Reject / Edit"]
  AUTO --> DB[("SQLite")]
  CLAR --> DB
  CANT --> DB
  HUMAN --> DB
  DB --> TRACE["activity trace in UI"]
Loading

The agent boundary lives in one place: ROUTE_POLICY in app/tools.py. Anything that produces a communication is forced through human_review; the planner cannot override that.

Agent workflow

intake → interpretation → planning → tools → approval → persistence → completion

  • intake POST /api/requests creates a request row.
  • interpretation agent.interpret() calls Groq in JSON mode, validates with the Interpretation Pydantic model, retries once on invalid output, then fails clearly.
  • planning agent.plan() hands the model a tool catalog and reads back its tool_calls.
  • tools agent.execute() dispatches each call through ROUTE_POLICY to a real function in app/tools.py.
  • approval agent.approve() / reject() / edit() transition a gated action.
  • persistence everything is written in app/db.py (requests, action_items, activity_log).
  • completion request status is recomputed after each change.

The tools

Six real tools (assignment requires at least three):

tool route what it does
create_task_record auto persists a structured task
generate_markdown_brief auto builds a markdown brief from the interpretation
draft_communication human review drafts an email/message; never sends; gated by approval
bounded_website_check auto one real HTTP GET, 8s timeout, http/https only; reports actual status, timing, size, title, on-page signals
simulate_reminder auto records a simulated reminder (no real calendar)
search_stored_work auto searches previously stored work

Plus two control verdicts the planner can emit: request_clarification (→ needs clarification) and flag_cannot_execute (→ cannot execute).

Setup

Requirements: Python 3.10+ and a Groq API key (free tier works). Any OpenAI-compatible provider can be swapped in by editing app/llm.py.

# 1. clone
git clone https://github.com/DevAshish9090/Work_Agent.git
cd Work_Agent

# 2. create a virtual environment
python -m venv .venv
source .venv/bin/activate          # Windows: .venv\Scripts\activate

# 3. install
pip install -r requirements.txt

# 4. configure
cp .env.example .env
# open .env and paste your GROQ_API_KEY

# 5. run  (the app auto-loads .env, so no manual export is needed)
uvicorn app.main:app --reload --port 8000

# 6. open
# http://localhost:8000

Health check and quick test:

curl http://localhost:8000/api/health
curl -X POST http://localhost:8000/api/requests \
  -H "Content-Type: application/json" \
  -d '{"text":"Please take care of the documentation and send it to everyone before the meeting."}'

Generate the three sample scenario outputs:

python -m scenarios.run_scenarios
# writes scenarios/scenario_1_routine.md, scenario_2_website.md, scenario_3_ambiguous.md

Deploy (Railway)

# with the Railway CLI, from the repo root:
railway init
railway up
# then in the Railway dashboard:
#  - add a variable GROQ_API_KEY
#  - (optional) attach a volume mounted at /data and set DATABASE_PATH=/data/agent.db
#    so the SQLite file survives redeploys

The included Procfile and railway.json set the start command (uvicorn app.main:app --host 0.0.0.0 --port $PORT --workers 1). One worker is intentional: state is in-process SQLite.

Environment variables

Placeholders only. Never commit a real .env.

variable required default purpose
GROQ_API_KEY yes none Groq API key
GROQ_MODEL no llama-3.3-70b-versatile model id
DATABASE_PATH no data/agent.db SQLite location; point at a volume in prod

Design decisions

  • Two LLM steps, then deterministic execution. Interpretation and planning use the model; execution does not. The model proposes, code disposes. This keeps behaviour explainable and keeps the agent boundary in code rather than in a prompt.
  • JSON mode + Pydantic for interpretation, function calling for planning. These are the two distinct capabilities the brief asks to demonstrate, so each stage uses the right one.
  • The boundary is a table, not a vibe. ROUTE_POLICY decides what may auto-run. The planner can suggest draft_communication, but policy pins it to human review regardless.
  • Fail loud. A failed interpretation sets interpretation_failed; an unreachable site raises and is logged as a failure. The UI shows a red banner. The system never reports success it did not achieve.
  • FastAPI + vanilla JS + SQLite. A small stack I can fully explain under questioning beats a heavier one I would be guessing about. Single-origin in production, so no CORS layer.

Limitations

  • Planning quality depends on the model; a weaker model may under-route or over-route actions.
  • The website check is deliberately shallow (one GET, a few signals). It is not a crawler or a full accessibility/SEO audit.
  • Search is a substring match, not semantic search.
  • SQLite with one worker is right for a prototype, not for concurrent production load.
  • "Reminders" and "task records" are internal simulations; there is no real calendar or ticketing integration by design (the brief asks not to send real invites or email).

What I'd build next (max 5)

  1. Semantic search over stored work using embeddings.
  2. Real integrations behind the same approval gate (calendar, ticketing) with a dry-run mode.
  3. A re-plan loop: once a clarification is answered, feed it back and re-route automatically.
  4. Per-action retries with backoff and a proper dead-letter state.
  5. Auth and multi-user workspaces so each user sees only their own intake.

How I used AI

  • Tools used: Claude for scaffolding and code review; Groq (Llama 3.3 70B) is the runtime LLM inside the app itself.
  • What for: drafting the FastAPI + SQLite structure, the tool schemas, and the frontend, then reviewing my routing logic and the failure paths. I kept the architecture small on purpose so I could read and defend every file.
  • One AI mistake I caught and fixed: an early version created the database tables only inside FastAPI's startup event. That works under uvicorn, but when I ran the app a different way the tables did not exist yet and a request failed with no such table: requests. I caught it by running the endpoint directly instead of trusting it, then fixed it by also calling db.init_db() at import time in app/main.py, which is idempotent and safe under any launcher. The lesson I took from it: AI-suggested lifecycle hooks are easy to accept without checking when they actually fire, so I now test the boot path explicitly.
  • How I validated AI output generally: I ran the tools against real inputs (the website check hits a live URL and reports only what it observed), forced the no-key path to confirm it fails loudly, and read every routing branch rather than assuming the generated logic was correct.

About

Agentic work intake and execution prototype: turns unstructured text into a structured, routed, human-approved workflow. FastAPI + Groq + SQLite.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages