It is an issue reader in fact

This commit is contained in:
2026-09-20 00:11:49 +03:00
parent a7fbf1461d
commit 74577cb50e
10 changed files with 27 additions and 27 deletions
+3
View File
@@ -0,0 +1,3 @@
### 2026-09-19
1. Created an agent that can accept YouTrack™ task ID, read an issue, create a context for a task (body, comments, image attachments) and decompose it for implementing
2. **[missing]** extract a decomposing because it's manner depends on the task context (i.e. backend-dev, frontend-dev, seller, marketing, ad, design, ...) so an issue_reader agent should listen for not delivered tasks, create a context and delegate a decomposition for specific agent. If there is no agent can create this work - add comment that it cannot be resolved until appropriate agent will be created, assign a task to admin and change status to "To Be Discussed". There should be an agent that has information about all the rest of agents and can provide the information, who can do that task and how (in case of confusing - ask another agents if they can do the task). As this architecture is a very future automatization, it is enough to have just a registry of agents and omit the conversation of issue reader with another agents.
@@ -1,25 +1,22 @@
""" """
Backend Developer Agent Issue Decomposing Agent
This agent specializes in backend development tasks including: This agent specializes in development tasks including:
- API development with FastAPI - Web systems architecture
- Database design and management
- Server-side programming
- System architecture
""" """
from typing import Dict, Any from typing import Dict, Any
import json import json
from agents.backend_dev.models import TaskPlan from agents.issue_reader.models import TaskPlan
from agents.backend_dev.decomposer import IssueDecomposer from agents.issue_reader.decomposer import IssueDecomposer
from agents.backend_dev.validator import enforce from agents.issue_reader.validator import enforce
from common.youtrack_mcp_client import IssueNotFound from common.youtrack_mcp_client import IssueNotFound
class BackendDeveloperAgent: class IssueReaderAgent:
def __init__(self, context_builder, llm): def __init__(self, context_builder, llm):
self.name = "Backend Developer" self.name = "Issue decomposing agent"
self.ctx_builder = context_builder self.ctx_builder = context_builder
self.decomposer = IssueDecomposer(llm) self.decomposer = IssueDecomposer(llm)
@@ -5,8 +5,8 @@ import re
import httpx import httpx
from agents.backend_dev.models import IssueContext from agents.issue_reader.models import IssueContext
from agents.backend_dev.project_loader import load_project from agents.issue_reader.project_loader import load_project
from common.as_type import _as_dict, _as_list, _first, _extract_base64 from common.as_type import _as_dict, _as_list, _first, _extract_base64
from common.youtrack_mcp_client import IssueNotFound from common.youtrack_mcp_client import IssueNotFound
@@ -3,10 +3,10 @@ import json
from openai import images from openai import images
from agents.backend_dev.models import IssueContext, Task, TaskPlan, TaskKind, Atomicity from agents.issue_reader.models import IssueContext, Task, TaskPlan, TaskKind, Atomicity
SYSTEM_PROMPT = """You are a senior backend engineer. SYSTEM_PROMPT = """You are a senior engineer.
Your job: given a YouTrack issue, its comments, and the project context, Your job: given a YouTrack issue, its comments, and the project context,
produce the SMALLEST possible sequence of tasks to resolve it. produce the SMALLEST possible sequence of tasks to resolve it.
@@ -1,6 +1,6 @@
from pathlib import Path from pathlib import Path
import yaml import yaml
from agents.backend_dev.models import ProjectContext from agents.issue_reader.models import ProjectContext
PROJECTS_DIR = Path(__file__).parent.parent.parent / "projects" PROJECTS_DIR = Path(__file__).parent.parent.parent / "projects"
@@ -1,5 +1,5 @@
# validator.py # validator.py
from agents.backend_dev.models import Task, TaskPlan, Atomicity from agents.issue_reader.models import Task, TaskPlan, Atomicity
MAX_HOURS = 8 MAX_HOURS = 8
VAGUE_VERBS = ("handle", "support", "improve", "refactor", "manage", "deal with") VAGUE_VERBS = ("handle", "support", "improve", "refactor", "manage", "deal with")
+10 -10
View File
@@ -5,8 +5,8 @@ from contextlib import asynccontextmanager
import httpx import httpx
from dotenv import load_dotenv from dotenv import load_dotenv
from fastapi import FastAPI, HTTPException from fastapi import FastAPI, HTTPException
from agents.backend_dev.BackendDeveloperAgent import BackendDeveloperAgent from agents.issue_reader.IssueReaderAgent import IssueReaderAgent
from agents.backend_dev.context_builder import YouTrackContextBuilder from agents.issue_reader.context_builder import YouTrackContextBuilder
from common.llm_client import LLMClient from common.llm_client import LLMClient
from common.youtrack_mcp_client import YouTrackMCPClient, IssueNotFound from common.youtrack_mcp_client import YouTrackMCPClient, IssueNotFound
@@ -34,7 +34,7 @@ async def lifespan(app: FastAPI):
follow_redirects=True, follow_redirects=True,
) )
app.state.backend_agent = BackendDeveloperAgent( app.state.issue_reader_agent = IssueReaderAgent(
YouTrackContextBuilder(mcp, http_for_attachments), YouTrackContextBuilder(mcp, http_for_attachments),
LLMClient( LLMClient(
base_url=env("LLM_ADDRESS"), base_url=env("LLM_ADDRESS"),
@@ -59,18 +59,18 @@ async def say_hello(name: str):
return {"message": f"Hello {name}"} return {"message": f"Hello {name}"}
@app.get("/backend") @app.get("/decomposing")
async def backend_task(task: str): async def decomposing_issue(issue: str):
agent: BackendDeveloperAgent = app.state.backend_agent agent: IssueReaderAgent = app.state.issue_reader_agent
try: try:
logger.info(f"Received task: {task}") logger.info(f"Received an issue: {issue}")
plan = await agent.plan_issue(task) plan = await agent.plan_issue(issue)
logger.info("Task planning successful.") logger.info("Task planning successful.")
print(agent.render(plan)) print(agent.render(plan))
return {"plan": plan} return {"plan": plan}
except IssueNotFound: except IssueNotFound:
logger.error(f"Issue {task} not found.") logger.error(f"Issue {issue} not found.")
raise HTTPException(status_code=404, detail=f"Issue {task} not found") raise HTTPException(status_code=404, detail=f"Issue {issue} not found")
except Exception as e: except Exception as e:
logger.exception("An unexpected error occurred") logger.exception("An unexpected error occurred")
return {"error": str(e)} return {"error": str(e)}