Skip to content

Commit 560b6a1

Browse files
Alex-Hunterzclaude
andcommitted
fix: reduce abstention, fix malformed JSON unwrap, add type-3 inference scaffold
- instr 1: clarify inference from context ≠ guessing - instr 7: positive commitment signal — prefer answering over abstaining - build_qa_prompt: inject BODHI-style two-step scaffold for type-3 questions - generate_answer: regex fallback for malformed {"answer":...} JSON (~99 cases) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 45fcfb0 commit 560b6a1

1 file changed

Lines changed: 20 additions & 4 deletions

File tree

vektori/qa/generator.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from __future__ import annotations
44

55
import logging
6+
import re
67
from typing import Protocol
78

89
logger = logging.getLogger(__name__)
@@ -23,7 +24,7 @@ async def generate(self, prompt: str, max_tokens: int | None = None) -> str:
2324
{question}
2425
2526
INSTRUCTIONS:
26-
1. Use only the provided context. Do not use outside knowledge or guesses.
27+
1. Use only the provided context. Logical inference and reasoning from context facts is expected and correct — this is NOT guessing. Do not introduce facts that have no support in any context item.
2728
2. First, silently find every fact, episode, synthesis, or transcript line that directly relates to the question. Do not print this evidence list.
2829
3. For counting, frequency, list, "all", "total", or aggregation questions:
2930
- Consider every relevant item across all sessions, not just the first match.
@@ -37,14 +38,23 @@ async def generate(self, prompt: str, max_tokens: int | None = None) -> str:
3738
- Prefer the most recent value when later context overrides earlier context.
3839
- Mention older values only if the question asks for history or change over time.
3940
6. Copy critical names, dates, places, titles, quantities, and field names exactly from the context. Do not blur them into a generic paraphrase.
40-
7. Say "I don't have that information" only when no context item supports an answer.
41+
7. If the context contains any relevant evidence, commit to the most supported answer — even if it requires reasoning. Reserve "I don't have that information" strictly for when the context has zero relevant facts about the subject. Committing to a reasoned answer is preferred over abstaining.
4142
8. For answers expressed as "N days/weeks/months before/after DATE": use the temporal note in the context to compute the actual calendar date and give it as an absolute date (e.g. "18 May 2023"). Do not echo the anchor date as the answer.
4243
9. If the context contains facts about multiple named people, only use facts about the person the question asks about. Facts labeled "User" or "Assistant" refer to the primary conversation participant — if other facts establish that person's name (e.g. "User's name is Caroline"), treat "User" facts as belonging to that person.
4344
4445
ANSWER:
4546
"""
4647

4748

49+
_TYPE3_SCAFFOLD = (
50+
"\nNOTE: This is an inference question (\"Would X...?\", \"Is X likely...?\", "
51+
"\"What might X...?\"). Step 1: silently list what the context reveals about "
52+
"the subject's relevant preferences, habits, and situation. Step 2: use that "
53+
"evidence to commit to a Yes/No answer with a one-sentence justification. "
54+
"Refusing to answer is only acceptable if the context has zero facts about the subject."
55+
)
56+
57+
4858
def build_qa_prompt(
4959
question: str,
5060
context: str,
@@ -57,11 +67,14 @@ def build_qa_prompt(
5767
date_line = f"TODAY'S DATE: {question_date}\n\n" if question_date else ""
5868
type_line = f"QUESTION TYPE: {question_type}\n" if question_type else ""
5969
template = prompt_template or QA_PROMPT
60-
return template.format(
70+
prompt = template.format(
6171
date_line=f"{date_line}{type_line}",
6272
context=context,
6373
question=question,
6474
)
75+
if str(question_type) == "3":
76+
prompt = prompt.rstrip() + _TYPE3_SCAFFOLD + "\n"
77+
return prompt
6578

6679

6780
async def generate_answer(
@@ -102,7 +115,10 @@ async def generate_answer(
102115
if isinstance(parsed, dict) and "answer" in parsed:
103116
answer = str(parsed["answer"]).strip()
104117
except Exception:
105-
pass
118+
# Fallback: regex extraction handles malformed/truncated JSON
119+
m = re.search(r'"answer"\s*:\s*"((?:[^"\\]|\\.)*)"', answer)
120+
if m:
121+
answer = m.group(1).replace('\\"', '"').replace("\\\\", "\\").strip()
106122
return answer
107123
except Exception as e:
108124
logger.warning("Answer generation failed: %s", e)

0 commit comments

Comments
 (0)