multi-agent-system / app /core /srs_assembler.py
firepenguindisopanda
feat(04): Consistency node + SRS KG + SSE PRD + integration test
9ba692f
Raw
History Blame Contribute Delete
6.43 kB
"""
SRS Assembler
Deterministic assembly of SRS document from agent outputs, followed by
a single LLM cross-validation pass. Replaces the spec_coordinator agent.
"""
import logging
from datetime import datetime
from langchain_core.prompts import ChatPromptTemplate
from .llm_factory import get_chat_model
from .schemas import TeamRole
logger = logging.getLogger("srs_assembler")
# Static documentation guidelines appendix (replaces technical_writer agent)
STATIC_DOC_APPENDIX = """# Documentation Guidelines
## Recommended Documentation Structure
### For Development Teams
1. **README.md** - Project overview, setup instructions, quick start
2. **ARCHITECTURE.md** - System architecture, component diagrams, technology decisions
3. **API.md** - API documentation, endpoint reference, authentication
4. **DEPLOYMENT.md** - Deployment guide, environment configuration, CI/CD pipeline
5. **CONTRIBUTING.md** - Coding standards, PR process, code review guidelines
### For Product Teams
1. **PRD.md** - Product requirements, user stories, acceptance criteria
2. **ROADMAP.md** - Feature roadmap, priorities, timeline
3. **DECISIONS.md** - Architecture decision records (ADRs)
### Documentation Standards
- Use Markdown format for all documentation
- Keep documentation close to code (in repository)
- Update documentation with every code change
- Use diagrams (Mermaid, PlantUML) for complex concepts
- Maintain a single source of truth
## Diátaxis Framework
Organize documentation into four categories:
1. **Tutorials** - Learning-oriented, step-by-step guides
2. **How-to Guides** - Problem-oriented, practical instructions
3. **Reference** - Information-oriented, technical specifications
4. **Explanation** - Understanding-oriented, conceptual background
"""
def assemble_srs(
all_outputs: dict[str, str],
knowledge_graph: dict | None = None,
contradictions: list[dict] | None = None,
) -> str:
"""
Deterministically assemble SRS document from agent outputs.
Args:
all_outputs: Dictionary mapping role names to their markdown output
knowledge_graph: Optional merged knowledge graph dict for entity context
contradictions: Optional list of cross-agent contradiction dicts
Returns:
Complete SRS document as markdown string
"""
sections = [
("1. Product Requirements", all_outputs.get("product_owner", "")),
("2. Functional Requirements", all_outputs.get("business_analyst", "")),
("3. Technical Architecture", all_outputs.get("solution_architect", "")),
("4. Data Architecture", all_outputs.get("data_architect", "")),
("5. Security Requirements", all_outputs.get("security_analyst", "")),
("6. User Experience Design", all_outputs.get("ux_designer", "")),
("7. API Specifications", all_outputs.get("api_designer", "")),
("8. Testing Strategy", all_outputs.get("qa_strategist", "")),
("9. DevOps & Infrastructure", all_outputs.get("devops_architect", "")),
]
# Build table of contents
toc_lines = ["# Software Requirements Specification\n"]
toc_lines.append(
f"**Generated:** {datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S UTC')}\n"
)
toc_lines.append("---\n")
toc_lines.append("## Table of Contents\n")
for title, _ in sections:
anchor = title.lower().replace(" ", "-").replace(".", "")
toc_lines.append(f"- [{title}](#{anchor})")
toc_lines.append("- [A. Documentation Guidelines](#a-documentation-guidelines)")
if contradictions:
toc_lines.append("- [B. Consistency Warnings](#b-consistency-warnings)")
toc_lines.append("\n---\n")
# Build document body
body_parts = ["\n".join(toc_lines)]
for title, content in sections:
if content and content.strip():
body_parts.append(f"\n## {title}\n\n{content}\n")
# Add consistency warnings section if contradictions exist
if contradictions:
from .consistency_validator import ConsistencyValidator
validator = ConsistencyValidator()
warnings = validator.format_warnings_section(contradictions)
if warnings:
body_parts.append(f"\n{warnings}\n")
# Add static documentation appendix
body_parts.append(f"\n## A. Documentation Guidelines\n\n{STATIC_DOC_APPENDIX}\n")
return "\n".join(body_parts)
async def validate_srs(srs_document: str, all_outputs: dict[str, str]) -> str:
"""
Cross-validate assembled SRS for internal consistency.
Single LLM pass to check for contradictions, gaps, and completeness.
Args:
srs_document: The assembled SRS document
all_outputs: Original agent outputs for reference
Returns:
Validation report string
"""
validator_prompt = """You are a senior technical reviewer validating a Software Requirements Specification.
Review the assembled SRS below for:
1. **Internal Consistency**: Are there contradictions between sections? (e.g., architecture says PostgreSQL but data model says MongoDB)
2. **Completeness**: Are all required sections present and substantive?
3. **Traceability**: Do requirements link to architecture, tests, and API specs?
4. **Gaps**: Is there missing critical information?
## SRS Document
{srs_document}
## Your Task
Return a validation report in this format:
```markdown
## Validation Report
### Consistency Check
[PASS/FAIL] - [Details]
### Completeness Check
[PASS/FAIL] - [Details]
### Traceability Check
[PASS/FAIL] - [Details]
### Identified Gaps
- [Gap 1]
- [Gap 2]
### Overall Assessment
[VALID / NEEDS_REVISION]
```
Be specific and actionable. If the SRS passes all checks, return "VALID" as the overall assessment."""
llm = get_chat_model(
role=TeamRole.SPEC_COORDINATOR, temperature=0.2, max_tokens=2048
)
prompt_template = ChatPromptTemplate.from_messages(
[
("system", validator_prompt),
("human", "Please validate the SRS document above."),
]
)
chain = prompt_template | llm
try:
response = await chain.ainvoke({"srs_document": srs_document[:15000]})
return response.content if hasattr(response, "content") else str(response)
except Exception as e:
logger.error(f"SRS validation failed: {e}")
return f"## Validation Report\n\n**Status:** Validation failed due to error: {e}\n\n**Recommendation:** Review SRS manually."