AI-генерация пользовательских историй (User Stories)
Написание качественных user stories — навык, который требует понимания как технической стороны, так и бизнес-контекста. На практике либо истории слишком абстрактные («As a user, I want to manage my account»), либо содержат технические детали реализации. AI-генератор создаёт структурированные, тестируемые user stories с acceptance criteria на основе описания фичи, данных о пользователях и бизнес-контексте.
Генерация с контекстом
Ключевое отличие от простого промпта к ChatGPT — использование контекста из нескольких источников:
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_community.vectorstores import Qdrant
from pydantic import BaseModel
from typing import Optional
import re
class UserStory(BaseModel):
title: str
role: str
action: str
benefit: str
acceptance_criteria: list[str]
edge_cases: list[str]
story_points_estimate: Optional[int]
priority: str # Must/Should/Could/Won't
class UserStoryGenerator:
SYSTEM_PROMPT = """Ты — опытный продукт-менеджер с техническим бэкграундом.
Генерируй user stories по стандарту: As a [role], I want [action], so that [benefit].
Правила качественной user story:
- Роль — конкретный пользовательский сегмент, не «пользователь»
- Действие — одно, измеримое, не смешивай несколько действий
- Польза — бизнес-результат, не техническая реализация
- Acceptance criteria — тестируемые условия в формате Given/When/Then
- Каждая история должна быть выполнима за 1–3 дня разработки"""
def __init__(self, context_store: Qdrant, llm: ChatOpenAI):
self.context_store = context_store
self.llm = llm
def generate_stories(
self,
feature_description: str,
persona_data: dict,
existing_stories: list[str] = None,
n_stories: int = 5
) -> list[UserStory]:
# Извлекаем релевантный контекст из базы знаний
relevant_docs = self.context_store.similarity_search(
feature_description, k=5
)
context = "\n".join([d.page_content for d in relevant_docs])
prompt = f"""Контекст продукта:
{context}
Персонажи пользователей:
{self._format_personas(persona_data)}
Описание фичи: {feature_description}
{"Уже существующие истории (избегай дублирования): " + str(existing_stories) if existing_stories else ""}
Создай {n_stories} user stories. Для каждой:
1. Заголовок (до 10 слов)
2. Role, Action, Benefit
3. 3–5 acceptance criteria (Given/When/Then)
4. 2–3 edge cases
5. Оценка в SP (1/2/3/5/8)
6. Приоритет (Must/Should/Could/Won't)
Верни JSON-массив объектов UserStory."""
response = self.llm.invoke([
{"role": "system", "content": self.SYSTEM_PROMPT},
{"role": "user", "content": prompt}
])
return self._parse_stories(response.content)
Глубокий разбор: acceptance criteria через Given/When/Then
Это самая частая точка отказа при генерации. Плохая acceptance criteria: «Система должна работать корректно». Хорошая: «Given пользователь находится на странице заказа, When он нажимает "Подтвердить" и сессия активна, Then заказ создаётся со статусом "Pending", пользователь получает email с номером заказа в течение 30 секунд».
Для получения качественных AC используем few-shot prompting с реальными примерами из проекта:
FEW_SHOT_EXAMPLES = [
{
"story": "As a shop manager, I want to bulk update product prices, so that I can react to market changes quickly",
"ac": [
"Given manager has >0 products selected in catalog, When they click 'Bulk edit prices', Then modal opens with current prices listed",
"Given modal is open with 50 products, When manager sets +10% adjustment and clicks Apply, Then all prices update within 5 seconds, success count shown",
"Given price update would result in price < cost_price, When applying, Then system warns and skips those items, shows count of skipped"
]
}
]
def build_ac_prompt(story: UserStory, examples: list) -> str:
examples_text = "\n\n".join([
f"Story: {e['story']}\nAC:\n" + "\n".join(f"- {ac}" for ac in e["ac"])
for e in examples
])
return f"""Примеры качественных acceptance criteria:
{examples_text}
Теперь создай AC для: {story.action}
Роль: {story.role}
Польза: {story.benefit}
Каждый AC должен быть полностью тестируемым (Given/When/Then)."""
Кейс: e-commerce платформа, 8 product teams. Раньше quality review user stories занимал 2 часа в sprint planning — треть историй возвращались на доработку из-за расплывчатых AC. После внедрения генератора с контекстной базой (150 примеров качественных историй + документация домена) — процент возвратов снизился с 34% до 9%, время на написание историй сократилось на 60%.
Генерация эпиков и декомпозиция
def decompose_epic(
self,
epic_description: str,
team_capacity_sp: int = 40 # SP на спринт
) -> dict:
"""Разбивает эпик на спринты с user stories"""
prompt = f"""Декомпозируй эпик на спринты.
Эпик: {epic_description}
Capacity команды: {team_capacity_sp} SP/спринт
Верни структуру:
- Sprint 1 (MVP): N SP, истории
- Sprint 2 (Enhancement): N SP, истории
- Sprint 3+ (Polish): N SP, истории
Учти зависимости между историями."""
return self.llm.invoke(prompt).content
Интеграция с Jira и Linear
Автоматическое создание тикетов после генерации:
from jira import JIRA
def push_stories_to_jira(
stories: list[UserStory],
project_key: str,
epic_link: str,
jira_client: JIRA
):
for story in stories:
ac_text = "\n".join(f"- {ac}" for ac in story.acceptance_criteria)
jira_client.create_issue(
project=project_key,
summary=story.title,
description=f"**As a** {story.role}\n**I want to** {story.action}\n**So that** {story.benefit}\n\n**Acceptance Criteria:**\n{ac_text}",
issuetype={"name": "Story"},
story_points=story.story_points_estimate,
customfield_10014=epic_link # Epic Link
)
Сроки
- Базовый генератор (GPT-4o + шаблоны): 1–2 недели
- Контекстная база с RAG и примерами проекта: 2–3 недели дополнительно
- Интеграция с Jira/Linear: 1 неделя







