Jun 29 - Improve AI analysis function fix 413 error
This commit is contained in:
+74
-85
@@ -182,27 +182,29 @@ def _extract_text(file_obj, ext: str) -> str:
|
||||
# Office address used as origin for distance/travel-time estimates.
|
||||
_OFFICE_ADDRESS = "2815 Hartland Road, Falls Church, VA 22043, USA"
|
||||
|
||||
# Text character limit sent to the API per stage-1 call.
|
||||
# Groq's HTTP payload limit is ~100 KB; our prompt template adds ~2.5 KB of overhead,
|
||||
# so 30K chars of document text keeps the total JSON body safely under that ceiling.
|
||||
_TEXT_LIMIT = 30_000
|
||||
# Document text limit per API call.
|
||||
# Stage 1 prompt template is ~1.8 KB overhead; 14 KB of doc text keeps the total
|
||||
# JSON payload well under Groq's request size limit on all plan tiers.
|
||||
_TEXT_LIMIT = 14_000
|
||||
|
||||
# Analyst persona injected as the system message in every API call.
|
||||
# Summary passed to stage 2. Stage 1 can produce up to ~16 KB of output;
|
||||
# capping it here prevents stage 2's payload from growing unbounded.
|
||||
_SUMMARY_LIMIT = 8_000
|
||||
|
||||
# Analyst persona — injected as the system message in every API call.
|
||||
_SYSTEM_PROMPT = (
|
||||
"You are a senior government procurement analyst supporting a small business BD team. "
|
||||
"Your role is to extract, organize, and evaluate information from solicitation documents "
|
||||
"with precision and clarity. Format all output in well-structured Markdown using tables "
|
||||
"and headers. Never invent or assume information not present in the source documents — "
|
||||
"write \"Not specified\" for any missing field."
|
||||
"Extract and organize solicitation information with precision. "
|
||||
"Format all output in clean Markdown with tables and section headers. "
|
||||
"Write \"Not specified\" for any field not found in the documents — never guess."
|
||||
)
|
||||
|
||||
# Stage 1 — extraction prompt (always sent)
|
||||
# Stage 1 — extraction prompt (always sent). Kept compact to minimise payload size.
|
||||
_EXTRACTION_PROMPT = """\
|
||||
Analyze the {n} attached government solicitation document(s) as one combined package.
|
||||
Complete every section below exactly as structured. Write **"Not specified"** for any field
|
||||
not found in the documents. Do not guess or invent details.
|
||||
Analyze the {n} solicitation document(s) below as one combined package.
|
||||
Fill in every table and section. Write **"Not specified"** for missing fields.
|
||||
|
||||
Our office is located at: **{office}** — use this as the origin for all travel estimates.
|
||||
Our office: **{office}** — use as origin for travel estimates.
|
||||
|
||||
---
|
||||
|
||||
@@ -210,37 +212,37 @@ Our office is located at: **{office}** — use this as the origin for all travel
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Solicitation Number** | |
|
||||
| **Solicitation Type** | (RFP / RFQ / IFB / IDIQ / BPA / etc.) |
|
||||
| **Issuing Agency / Office** | |
|
||||
| **Set-Aside** | (Small Business / 8(a) / SDVOSB / HUBZone / WOSB / Unrestricted / etc.) |
|
||||
| **Contract Type** | (Firm-Fixed-Price / T&M / Cost-Plus / IDIQ / etc.) |
|
||||
| **Contract Period** | |
|
||||
| **NAICS Code** | |
|
||||
| **Estimated Value** | |
|
||||
| Solicitation Number | |
|
||||
| Solicitation Type | (RFP / RFQ / IFB / IDIQ / BPA / etc.) |
|
||||
| Issuing Agency / Office | |
|
||||
| Set-Aside | (Small Business / 8(a) / SDVOSB / HUBZone / WOSB / Unrestricted / etc.) |
|
||||
| Contract Type | (FFP / T&M / Cost-Plus / IDIQ / etc.) |
|
||||
| Contract Period | |
|
||||
| NAICS Code | |
|
||||
| Estimated Value | |
|
||||
|
||||
---
|
||||
|
||||
## Scope of Work
|
||||
|
||||
*Describe what is required in full detail: services, deliverables, performance standards, and any technical requirements. Use bullet points.*
|
||||
*(Detail required services, deliverables, and performance standards. Use bullet points.)*
|
||||
|
||||
---
|
||||
|
||||
## Performance Location(s)
|
||||
|
||||
*List all work sites. Note whether remote or on-site work is permitted.*
|
||||
*(All work sites; whether remote or on-site work is permitted.)*
|
||||
|
||||
---
|
||||
|
||||
## Key Dates & Deadlines
|
||||
|
||||
| Milestone | Date & Time (with timezone) |
|
||||
| Milestone | Date & Time |
|
||||
|---|---|
|
||||
| Pre-Proposal Conference / Site Visit | |
|
||||
| Questions Due | |
|
||||
| **Proposal Due** | |
|
||||
| Award Date (if stated) | |
|
||||
| Award Date | |
|
||||
| Period of Performance Start | |
|
||||
| Other Deadlines | |
|
||||
|
||||
@@ -250,33 +252,33 @@ Our office is located at: **{office}** — use this as the origin for all travel
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Date & Time** | |
|
||||
| **Full Address** | |
|
||||
| **Attendance** | (Mandatory / Optional / Not applicable) |
|
||||
| **RSVP Required** | (Yes — deadline & method / No) |
|
||||
| Date & Time | |
|
||||
| Full Address | |
|
||||
| Attendance | (Mandatory / Optional / N/A) |
|
||||
| RSVP Required | |
|
||||
|
||||
---
|
||||
|
||||
## Point of Contact (POC)
|
||||
## Point of Contact
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Name / Title** | |
|
||||
| **Phone** | |
|
||||
| **Email** | |
|
||||
| **Questions Submission Method** | |
|
||||
| Name / Title | |
|
||||
| Phone | |
|
||||
| Email | |
|
||||
| Questions Submission Method | |
|
||||
|
||||
---
|
||||
|
||||
## Proposal Requirements
|
||||
|
||||
*Summarize format, page limits, required sections/volumes, submission method (portal/email/mail), and number of copies. Use bullet points.*
|
||||
*(Format, page limits, volumes, submission method, number of copies.)*
|
||||
|
||||
---
|
||||
|
||||
## Evaluation Factors
|
||||
|
||||
*List evaluation criteria and their weights or order of priority as stated in the solicitation.*
|
||||
*(Government's evaluation criteria and weights as stated in the solicitation.)*
|
||||
|
||||
---
|
||||
|
||||
@@ -284,49 +286,45 @@ Our office is located at: **{office}** — use this as the origin for all travel
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Origin** | {office} |
|
||||
| **Destination** | (Pre-Proposal / Primary Work Site address) |
|
||||
| **Estimated Driving Distance** | |
|
||||
| **Estimated Drive Time** | (normal traffic conditions) |
|
||||
| Origin | {office} |
|
||||
| Destination | |
|
||||
| Estimated Driving Distance | |
|
||||
| Estimated Drive Time | |
|
||||
|
||||
*(AI estimate — verify with a mapping service before scheduling travel.)*
|
||||
*(AI estimates — verify with a mapping service before scheduling.)*
|
||||
|
||||
---
|
||||
|
||||
## Notable Requirements & Red Flags
|
||||
## Red Flags & Notable Requirements
|
||||
|
||||
*List anything that affects bid/no-bid decisions: unusual insurance or bonding levels, required security clearances, certifications, teaming or subcontracting restrictions, incumbent advantage indicators, aggressive timelines, or any other risk factors.*
|
||||
*(Security clearances, bonding, certifications, teaming restrictions, tight timelines, incumbent indicators, or other bid/no-bid risk factors.)*
|
||||
|
||||
---
|
||||
|
||||
## Total Square Footage
|
||||
|
||||
*(If applicable to the scope of work; write "N/A" if not.)*
|
||||
*(If applicable; write "N/A" if not.)*
|
||||
|
||||
---
|
||||
|
||||
## Overall Summary
|
||||
|
||||
Provide a concise but thorough summary covering:
|
||||
|
||||
- **What is Being Procured** — Scope and key deliverables.
|
||||
- **Contract Period & Estimated Value** — Duration and any stated ceiling or estimate.
|
||||
- **Key Constraints & Requirements** — Timeline, location, special certifications, etc.
|
||||
- **Immediate Action Items** — What the team must do and by when.
|
||||
- **What Is Being Procured:**
|
||||
- **Contract Period & Value:**
|
||||
- **Key Constraints:**
|
||||
- **Immediate Action Items & Deadlines:**
|
||||
|
||||
---
|
||||
|
||||
DOCUMENTS:
|
||||
{documents}"""
|
||||
|
||||
# Stage 2 — criteria evaluation (sent as a separate API call when active criteria exist).
|
||||
# Receives the clean Stage 1 extraction output as {summary}, not the raw documents,
|
||||
# so the model can focus entirely on the evaluation without re-parsing document noise.
|
||||
# Stage 2 — criteria evaluation (separate API call, only when active criteria exist).
|
||||
# Receives a capped slice of the stage 1 summary — not the raw documents — so the
|
||||
# model can focus on evaluation without re-parsing source noise.
|
||||
_CRITERIA_PROMPT = """\
|
||||
Below is an extracted and summarized government solicitation opportunity.
|
||||
Evaluate whether our company should pursue it based on our evaluation criteria.
|
||||
|
||||
---
|
||||
Below is an extracted summary of a government solicitation. Evaluate whether our
|
||||
company should pursue it based on the criteria listed.
|
||||
|
||||
## OPPORTUNITY SUMMARY
|
||||
|
||||
@@ -336,42 +334,32 @@ Evaluate whether our company should pursue it based on our evaluation criteria.
|
||||
|
||||
## ALIGNMENT EVALUATION
|
||||
|
||||
**Our evaluation criteria:**
|
||||
|
||||
Our criteria:
|
||||
{criteria_list}
|
||||
|
||||
For **each criterion** listed above, provide:
|
||||
- A status: ✅ **MEETS** / ⚠️ **PARTIALLY MEETS** / ❌ **DOES NOT MEET** / ❓ **CANNOT DETERMINE**
|
||||
- A 1–2 sentence explanation citing specific details from the summary above.
|
||||
For each criterion, state:
|
||||
- ✅ MEETS / ⚠️ PARTIALLY MEETS / ❌ DOES NOT MEET / ❓ CANNOT DETERMINE
|
||||
- One or two sentences citing specific details from the summary.
|
||||
|
||||
---
|
||||
|
||||
## Overall Recommendation
|
||||
|
||||
State your recommendation on its own line in **exactly** this format (required for system parsing — do not alter the label):
|
||||
Write your recommendation on its own line in exactly one of these forms
|
||||
(machine-read — do not alter the label):
|
||||
|
||||
```
|
||||
RECOMMENDATION: PURSUE
|
||||
```
|
||||
or
|
||||
```
|
||||
RECOMMENDATION: PASS
|
||||
```
|
||||
or
|
||||
```
|
||||
RECOMMENDATION: UNCLEAR
|
||||
```
|
||||
|
||||
**PURSUE** — opportunity clearly aligns with most criteria and is competitive.
|
||||
**PASS** — fails one or more critical criteria or presents unacceptable risk.
|
||||
**UNCLEAR** — insufficient information to make a confident determination.
|
||||
|
||||
---
|
||||
PURSUE = clearly aligns with most criteria and is competitive.
|
||||
PASS = fails one or more critical criteria or presents unacceptable risk.
|
||||
UNCLEAR = insufficient information for a confident decision.
|
||||
|
||||
## Executive Summary
|
||||
|
||||
Write 3–4 sentences in plain business language explaining your recommendation:
|
||||
the strongest reasons to pursue or pass, and the single biggest risk or opportunity."""
|
||||
3–4 sentences in plain business language: strongest reasons to pursue or pass,
|
||||
and the single biggest risk or opportunity."""
|
||||
|
||||
|
||||
def _call_groq_api(api_key: str, model: str, user_message: str) -> str:
|
||||
@@ -388,7 +376,7 @@ def _call_groq_api(api_key: str, model: str, user_message: str) -> str:
|
||||
{"role": "system", "content": _SYSTEM_PROMPT},
|
||||
{"role": "user", "content": user_message},
|
||||
],
|
||||
"max_tokens": 8192,
|
||||
"max_tokens": 4096,
|
||||
"temperature": 0.1,
|
||||
},
|
||||
timeout=120,
|
||||
@@ -402,9 +390,9 @@ def _call_groq_api(api_key: str, model: str, user_message: str) -> str:
|
||||
def _call_groq(api_key: str, model: str, text: str, criteria: list) -> dict:
|
||||
"""
|
||||
Two-stage Groq analysis:
|
||||
Stage 1 (always): extract structured fields + overall summary from documents.
|
||||
Stage 2 (optional): evaluate criteria against the Stage 1 output (not raw docs),
|
||||
producing a focused RECOMMENDATION with per-criterion scoring.
|
||||
Stage 1 (always): extract structured fields + summary from the documents.
|
||||
Stage 2 (optional): evaluate criteria against the capped stage-1 summary,
|
||||
not the raw docs, for a focused and reliable result.
|
||||
"""
|
||||
truncated = len(text) > _TEXT_LIMIT
|
||||
doc_text = text[:_TEXT_LIMIT]
|
||||
@@ -419,13 +407,14 @@ def _call_groq(api_key: str, model: str, text: str, criteria: list) -> dict:
|
||||
if not criteria:
|
||||
return {"verdict": None, "summary": summary, "truncated": truncated}
|
||||
|
||||
# ── Stage 2: criteria evaluation (uses clean summary, not raw docs) ────────
|
||||
# ── Stage 2: criteria evaluation ───────────────────────────────────────────
|
||||
criteria_list = "\n".join(
|
||||
f"{i+1}. **{c['title']}**: {c['description']}"
|
||||
for i, c in enumerate(criteria)
|
||||
)
|
||||
stage2_prompt = _CRITERIA_PROMPT.format(
|
||||
summary=summary, criteria_list=criteria_list
|
||||
summary=summary[:_SUMMARY_LIMIT], # cap to control payload size
|
||||
criteria_list=criteria_list,
|
||||
)
|
||||
evaluation = _call_groq_api(api_key, model, stage2_prompt)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user