diff --git a/apps/webapp/app/lib/neo4j.server.ts b/apps/webapp/app/lib/neo4j.server.ts
index 24f8561..ed21625 100644
--- a/apps/webapp/app/lib/neo4j.server.ts
+++ b/apps/webapp/app/lib/neo4j.server.ts
@@ -312,6 +312,18 @@ const initializeSchema = async () => {
await runQuery(
"CREATE INDEX entity_name IF NOT EXISTS FOR (n:Entity) ON (n.name)",
);
+ await runQuery(
+ "CREATE INDEX entity_uuid IF NOT EXISTS FOR (n:Entity) ON (n.uuid)",
+ );
+ await runQuery(
+ "CREATE INDEX entity_type IF NOT EXISTS FOR (n:Entity) ON (n.type)",
+ );
+ await runQuery(
+ "CREATE INDEX entity_user_id IF NOT EXISTS FOR (n:Entity) ON (n.userId)",
+ );
+ await runQuery(
+ "CREATE INDEX statement_user_id IF NOT EXISTS FOR (n:Statement) ON (n.userId)",
+ );
await runQuery(
"CREATE INDEX cluster_user_id IF NOT EXISTS FOR (n:Cluster) ON (n.userId)",
);
@@ -322,17 +334,17 @@ const initializeSchema = async () => {
// Create vector indexes for semantic search (if using Neo4j 5.0+)
await runQuery(`
CREATE VECTOR INDEX entity_embedding IF NOT EXISTS FOR (n:Entity) ON n.nameEmbedding
- OPTIONS {indexConfig: {\`vector.dimensions\`: 1536, \`vector.similarity_function\`: 'cosine'}}
+ OPTIONS {indexConfig: {\`vector.dimensions\`: 1024, \`vector.similarity_function\`: 'cosine', \`vector.hnsw.ef_construction\`: 400, \`vector.hnsw.m\`: 32}}
`);
await runQuery(`
CREATE VECTOR INDEX statement_embedding IF NOT EXISTS FOR (n:Statement) ON n.factEmbedding
- OPTIONS {indexConfig: {\`vector.dimensions\`: 1536, \`vector.similarity_function\`: 'cosine'}}
+ OPTIONS {indexConfig: {\`vector.dimensions\`: 1024, \`vector.similarity_function\`: 'cosine', \`vector.hnsw.ef_construction\`: 400, \`vector.hnsw.m\`: 32}}
`);
await runQuery(`
CREATE VECTOR INDEX episode_embedding IF NOT EXISTS FOR (n:Episode) ON n.contentEmbedding
- OPTIONS {indexConfig: {\`vector.dimensions\`: 1536, \`vector.similarity_function\`: 'cosine'}}
+ OPTIONS {indexConfig: {\`vector.dimensions\`: 1024, \`vector.similarity_function\`: 'cosine', \`vector.hnsw.ef_construction\`: 400, \`vector.hnsw.m\`: 32}}
`);
// Create fulltext indexes for BM25 search
@@ -348,7 +360,7 @@ const initializeSchema = async () => {
await runQuery(`
CREATE FULLTEXT INDEX entity_name_index IF NOT EXISTS
- FOR (n:Entity) ON EACH [n.name, n.description]
+ FOR (n:Entity) ON EACH [n.name]
OPTIONS {
indexConfig: {
\`fulltext.analyzer\`: 'english'
diff --git a/apps/webapp/app/routes/api.v1.qa.tsx b/apps/webapp/app/routes/api.v1.qa.tsx
index 9646da3..eef6ddf 100644
--- a/apps/webapp/app/routes/api.v1.qa.tsx
+++ b/apps/webapp/app/routes/api.v1.qa.tsx
@@ -45,9 +45,13 @@ const { action, loader } = createActionApiRoute(
);
// Combine episodes and facts into context
- const context = [...searchResults.episodes, ...searchResults.facts].join("\n\n");
+ let context = [...searchResults.episodes].join("\n\n");
- console.log("Context:", context);
+ searchResults.facts.map((fact) => {
+ context += `\n\nfact: ${fact.fact}\n validAt: ${fact.validAt}`;
+ });
+
+ // console.log("Context:", context);
if (!context.trim()) {
return json({
@@ -57,32 +61,77 @@ const { action, loader } = createActionApiRoute(
}
// Generate answer using LLM
- const prompt = `Based on the following context information, please answer the question. Be specific and concise, using only information from the provided context. If the context doesn't contain enough information to answer the question, say so.
+ const prompt = `You are an analytical AI that reasons deeply about context before answering questions. Your task is to:
-Context:
-${context}
+1. FIRST: Look for direct, explicit answers in the context
+2. ANALYZE the context thoroughly for relevant information
+3. IDENTIFY patterns, connections, and implications
+4. REASON about what the context suggests or implies
+5. ANSWER based on direct evidence OR analysis
-Question: ${body.question}
+
+- Scan through ALL episodes and facts completely before answering
+- Look for every explicit statement that relates to the question
+- NEVER stop after finding the first answer - continue scanning for more
+- When asking "what did X show Y", look for ALL items X showed Y on that date
+- Collect multiple items, events, or details that answer the same question
+- If not found directly, identify all context elements related to the question
+- Look for patterns, themes, and implicit information in the context
+- Consider what the context suggests beyond explicit statements
+- Note any contradictions or missing information that affects the answer
+- Pay close attention to temporal information and dates (validAt timestamps)
+- For time-sensitive questions, prioritize more recent information
+- Consider the chronological sequence of events when relevant
+- CRITICAL: Ensure completeness by including ALL relevant items found
+- If you find 2+ items for the same question, mention them all in your answer
+- Be precise with details (specific types, colors, descriptions when available)
+- Draw logical conclusions based on available evidence
+- Don't give reasoning in the output
+
-Answer:`;
+Follow this output format. don't give the JSON with \`\`\`json
+
+`;
- let generatedAnswer = "";
+ const userPrompt = `
+ ${context}
+
+
+
+ Question: ${body.question}
+
+ `;
+ let responseText = "";
+ let generated_answer = "";
try {
- generatedAnswer = await makeModelCall(
+ await makeModelCall(
false, // Don't stream
- [{ role: "user", content: prompt }],
- (_text: string, _model: string) => {
- // onFinish callback - we can log model usage here if needed
+ [{ role: "system", content: prompt }, { role: "user", content: userPrompt }],
+ (text) => {
+ responseText = text;
}
- ) as string;
+ );
+
+ const outputMatch = responseText.match(/