find_user_quote
ChatGPTFind validated verbatim user quotes from Knowledge Graph record IDs. Use this whenever the user asks for quotes, verbatims, snippets, sample customer language, or examples in the customer's words. First call the graph query tool available on the current surface only to fetch candidate NaturalLanguageInteraction record IDs, then pass those IDs here: run_graph_query on v2/admin schema-decomposition surfaces, execute_cypher_query on the legacy auth-scoped surface. Do not use fi.content/nli.content rows as quotes. If this tool returns no validated quotes, do not substitute summaries or paraphrases. Do not treat product, feature, or customer names in quote requests as source filters unless the user explicitly asks for a source or integration. If a narrow source-like filter returns no candidate records or no quotes, broaden the candidate query across sources before concluding no quotes exist. If the user specified a date window, keep record timestamps from the candidate query and state the actual earliest/latest dated records analyzed as the first sentence of the final response before any title, heading, or quote list. Do not merely restate the user's requested window. Preserve numeric citation links only on exact numeric values returned by the graph query tool. Do not reuse a citation URL for computed, summed, rounded, or approximate numbers. Preserve returned quote text and speaker attribution links in final answers. For prose, keep the exact blockquote shape, including the > marker on both the quote line and the attribution line: > "Quote text" > — [Speaker](link) For tables, keep the quote text and speaker attribution link in the same table cell as "Quote text" — [Speaker](link); do not split them into a generic record-link column. If the user asks for a table, include the requested columns such as date, channel/title, and record link, while preserving quote + attribution in one quote cell. If the user asks for direct customer quotes plural, return at least two validated quotes when available; if a narrow candidate query yields too few quotes, broaden candidate records before concluding none. Args: criteria: What the quotes should be about record_ids: NLI record IDs returned by a previous graph query count: Number of quotes to return, between 1 and 5 user_question: The user's original question for language and speaker intent detection Returns: Validated quotes formatted as blockquotes with speaker attribution and record links.
find_user_quote
ChatGPTFind validated verbatim user quotes from Knowledge Graph record IDs. Use this whenever the user asks for quotes, verbatims, snippets, sample customer language, or examples in the customer's words. First call the graph query tool available on the current surface only to fetch candidate NaturalLanguageInteraction record IDs, then pass those IDs here: run_graph_query on v2/admin schema-decomposition surfaces, execute_cypher_query on the legacy auth-scoped surface. Do not use fi.content/nli.content rows as quotes. If this tool returns no validated quotes, do not substitute summaries or paraphrases. Do not treat product, feature, or customer names in quote requests as source filters unless the user explicitly asks for a source or integration. If a narrow source-like filter returns no candidate records or no quotes, broaden the candidate query across sources before concluding no quotes exist. If the user specified a date window, keep record timestamps from the candidate query and state the actual earliest/latest dated records analyzed as the first sentence of the final response before any title, heading, or quote list. Do not merely restate the user's requested window. Preserve numeric citation links only on exact numeric values returned by the graph query tool. Do not reuse a citation URL for computed, summed, rounded, or approximate numbers. Preserve returned quote text and speaker attribution links in final answers. For prose, keep the exact blockquote shape, including the > marker on both the quote line and the attribution line: > "Quote text" > — [Speaker](link) For tables, keep the quote text and speaker attribution link in the same table cell as "Quote text" — [Speaker](link); do not split them into a generic record-link column. If the user asks for a table, include the requested columns such as date, channel/title, and record link, while preserving quote + attribution in one quote cell. If the user asks for direct customer quotes plural, return at least two validated quotes when available; if a narrow candidate query yields too few quotes, broaden candidate records before concluding none. Args: criteria: What the quotes should be about record_ids: NLI record IDs returned by a previous graph query count: Number of quotes to return, between 1 and 5 user_question: The user's original question for language and speaker intent detection Returns: Validated quotes formatted as blockquotes with speaker attribution and record links.
find_user_quote
ChatGPTFind validated verbatim user quotes from Knowledge Graph record IDs. Use this whenever the user asks for quotes, verbatims, snippets, sample customer language, or examples in the customer's words. First call the graph query tool available on the current surface only to fetch candidate NaturalLanguageInteraction record IDs, then pass those IDs here: run_graph_query on v2/admin schema-decomposition surfaces, execute_cypher_query on the legacy auth-scoped surface. Do not use fi.content/nli.content rows as quotes. If this tool returns no validated quotes, do not substitute summaries or paraphrases. Do not treat product, feature, or customer names in quote requests as source filters unless the user explicitly asks for a source or integration. If a narrow source-like filter returns no candidate records or no quotes, broaden the candidate query across sources before concluding no quotes exist. If the user specified a date window, keep record timestamps from the candidate query and state the actual earliest/latest dated records analyzed as the first sentence of the final response before any title, heading, or quote list. Do not merely restate the user's requested window. Preserve numeric citation links only on exact numeric values returned by the graph query tool. Do not reuse a citation URL for computed, summed, rounded, or approximate numbers. Preserve returned quote text and speaker attribution links in final answers. For prose, keep the exact blockquote shape, including the > marker on both the quote line and the attribution line: > "Quote text" > — [Speaker](link) For tables, keep the quote text and speaker attribution link in the same table cell as "Quote text" — [Speaker](link); do not split them into a generic record-link column. If the user asks for a table, include the requested columns such as date, channel/title, and record link, while preserving quote + attribution in one quote cell. If the user asks for direct customer quotes plural, return at least two validated quotes when available; if a narrow candidate query yields too few quotes, broaden candidate records before concluding none. Args: criteria: What the quotes should be about record_ids: NLI record IDs returned by a previous graph query count: Number of quotes to return, between 1 and 5 user_question: The user's original question for language and speaker intent detection Returns: Validated quotes formatted as blockquotes with speaker attribution and record links.
get_graph_schema
ChatGPTGet the Knowledge Graph schema structure for a specific organization. Retrieves entity names, relationships, and static properties. IMPORTANT — check this flag in the response: has_source_fields: - If true: call search_graph_fields(query) before each Cypher query to get relevant integration fields (Zendesk, Salesforce, Gong, etc.) - If false: no source-specific metadata, skip search_graph_fields Returns: Schema structure with has_source_fields flag
get_graph_schema
ChatGPTGet the Knowledge Graph schema structure for a specific organization. Retrieves entity names, relationships, and static properties. IMPORTANT — check this flag in the response: has_source_fields: - If true: call search_graph_fields(query) before each Cypher query to get relevant integration fields (Zendesk, Salesforce, Gong, etc.) - If false: no source-specific metadata, skip search_graph_fields Returns: Schema structure with has_source_fields flag
get_graph_schema
ChatGPTGet the Knowledge Graph schema structure for a specific organization. Retrieves entity names, relationships, and static properties. IMPORTANT — check this flag in the response: has_source_fields: - If true: call search_graph_fields(query) before each Cypher query to get relevant integration fields (Zendesk, Salesforce, Gong, etc.) - If false: no source-specific metadata, skip search_graph_fields Returns: Schema structure with has_source_fields flag
get_organization_details
ChatGPTGet details about your organization. Fetches organization metadata including name, slug, and dashboard URL. This is useful for understanding your organization context and generating proper links to customer records. Returns: Organization details including name, slug, and dashboard URL
get_organization_details
ChatGPTGet details about your organization. Fetches organization metadata including name, slug, and dashboard URL. This is useful for understanding your organization context and generating proper links to customer records. Returns: Organization details including name, slug, and dashboard URL
get_organization_details
ChatGPTGet details about your organization. Fetches organization metadata including name, slug, and dashboard URL. This is useful for understanding your organization context and generating proper links to customer records. Returns: Organization details including name, slug, and dashboard URL
get_query_examples
ChatGPTGet sample Cypher queries similar to what you need to build. Returns reference examples showing correct compiler dialect. Some examples include a wrong_cypher field showing a common mistake and why it fails — study these to avoid the same errors. HOW TO USE THE RESULTS: 1. Study the correct_cypher patterns — relationships, COUNT DISTINCT, date functions, MISC filters, category_enum usage 2. If wrong_cypher is present, understand what mistake it shows and avoid it 3. ADAPT the patterns with entity names from search_graph_values results 4. Build your own query — do NOT copy-paste 5. For month/month-range requests with no year, use the most recent fully completed occurrence relative to the current date. If the range is upcoming or in progress this year, use the previous year. 6. For "last month" / "past month" without a named calendar month, use a rolling window: now() - INTERVAL 30 DAY. For daily/weekly/monthly analysis, keep that same filter for every query; group daily/weekly with toStartOfDay/toStartOfWeek, and make monthly the window total unless the user asks for a multi-month trend. 7. For quote requests inside explicit date windows, candidate-record queries should return record_id plus record_timestamp/source/summary. Make the first sentence of the final answer state the actual earliest/latest records analyzed, before any title/heading/quote list; do not only repeat the requested window. 8. For CASE-bucket counts, prefer WITH ... CASE ... END AS bucket, then RETURN bucket, COUNT(DISTINCT ...) so the compiler groups by the bucket. 9. Preserve explicit source filters from the user. If the user says Zendesk/App Store/Gong/G2, do not swap in a different source discovered from exploratory results. 10. "General" and "Generic" taxonomy values are not Miscellaneous. Unless the user explicitly says "miscellaneous", still filter only type != 'MISC' and do not add type != 'GENERIC'. 11. For source metadata prompts, include the source and field intent in search_graph_fields: "appstore rating country", "zendesk tags customer experience", "gong account name call title", "g2 star rating". HOW TO WRITE THE INTENT (affects which examples you get): The intent argument is a short natural-language description of the Cypher you want to build. It is matched against the example corpus by a hybrid lexical + semantic retriever that prioritizes example user_query phrasing, then rule descriptions and notes. Specificity beats keywords alone. Include the four aspects of your query: - Operation: "count", "top themes", "sample records", "sentiment breakdown" - Filter: "complaints", "from Zendesk", "in last 30 days" - Grouping: "by theme", "by source", "by month" - Special needs: "exclude billing", "with account info" Good intents (specific → better matches): "count complaints by theme from Zendesk in last month" "monthly trend analysis of feedback volume" "sample records with sentiment and source filtering" "top L2 categories with MISC filtering" "exclude specific topics from source analysis" "compare complaint volume before and after a date" Bad intents (too vague → poor matches): "query" / "get data" / "feedback" / "help" Args: intent: Short natural-language description of the Cypher you want to build. Include operation, filters, grouping, and special needs. top_k: Number of examples to return (default: 5) Returns: JSON array of examples with correct_cypher, notes, and optionally wrong_cypher. Current date for relative date resolution: 2026-06-04 UTC.
get_query_examples
ChatGPTGet sample Cypher queries similar to what you need to build. Returns reference examples showing correct compiler dialect. Some examples include a wrong_cypher field showing a common mistake and why it fails — study these to avoid the same errors. HOW TO USE THE RESULTS: 1. Study the correct_cypher patterns — relationships, COUNT DISTINCT, date functions, MISC filters, category_enum usage 2. If wrong_cypher is present, understand what mistake it shows and avoid it 3. ADAPT the patterns with entity names from search_graph_values results 4. Build your own query — do NOT copy-paste 5. For month/month-range requests with no year, use the most recent fully completed occurrence relative to the current date. If the range is upcoming or in progress this year, use the previous year. 6. For "last month" / "past month" without a named calendar month, use a rolling window: now() - INTERVAL 30 DAY. For daily/weekly/monthly analysis, keep that same filter for every query; group daily/weekly with toStartOfDay/toStartOfWeek, and make monthly the window total unless the user asks for a multi-month trend. 7. For quote requests inside explicit date windows, candidate-record queries should return record_id plus record_timestamp/source/summary. Make the first sentence of the final answer state the actual earliest/latest records analyzed, before any title/heading/quote list; do not only repeat the requested window. 8. For CASE-bucket counts, prefer WITH ... CASE ... END AS bucket, then RETURN bucket, COUNT(DISTINCT ...) so the compiler groups by the bucket. 9. Preserve explicit source filters from the user. If the user says Zendesk/App Store/Gong/G2, do not swap in a different source discovered from exploratory results. 10. "General" and "Generic" taxonomy values are not Miscellaneous. Unless the user explicitly says "miscellaneous", still filter only type != 'MISC' and do not add type != 'GENERIC'. 11. For source metadata prompts, include the source and field intent in search_graph_fields: "appstore rating country", "zendesk tags customer experience", "gong account name call title", "g2 star rating". HOW TO WRITE THE INTENT (affects which examples you get): The intent argument is a short natural-language description of the Cypher you want to build. It is matched against the example corpus by a hybrid lexical + semantic retriever that prioritizes example user_query phrasing, then rule descriptions and notes. Specificity beats keywords alone. Include the four aspects of your query: - Operation: "count", "top themes", "sample records", "sentiment breakdown" - Filter: "complaints", "from Zendesk", "in last 30 days" - Grouping: "by theme", "by source", "by month" - Special needs: "exclude billing", "with account info" Good intents (specific → better matches): "count complaints by theme from Zendesk in last month" "monthly trend analysis of feedback volume" "sample records with sentiment and source filtering" "top L2 categories with MISC filtering" "exclude specific topics from source analysis" "compare complaint volume before and after a date" Bad intents (too vague → poor matches): "query" / "get data" / "feedback" / "help" Args: intent: Short natural-language description of the Cypher you want to build. Include operation, filters, grouping, and special needs. top_k: Number of examples to return (default: 5) Returns: JSON array of examples with correct_cypher, notes, and optionally wrong_cypher. Current date for relative date resolution: 2026-06-04 UTC.
get_query_examples
ChatGPTGet sample Cypher queries similar to what you need to build. Returns reference examples showing correct compiler dialect. Some examples include a wrong_cypher field showing a common mistake and why it fails — study these to avoid the same errors. HOW TO USE THE RESULTS: 1. Study the correct_cypher patterns — relationships, COUNT DISTINCT, date functions, MISC filters, category_enum usage 2. If wrong_cypher is present, understand what mistake it shows and avoid it 3. ADAPT the patterns with entity names from search_graph_values results 4. Build your own query — do NOT copy-paste 5. For month/month-range requests with no year, use the most recent fully completed occurrence relative to the current date. If the range is upcoming or in progress this year, use the previous year. 6. For "last month" / "past month" without a named calendar month, use a rolling window: now() - INTERVAL 30 DAY. For daily/weekly/monthly analysis, keep that same filter for every query; group daily/weekly with toStartOfDay/toStartOfWeek, and make monthly the window total unless the user asks for a multi-month trend. 7. For quote requests inside explicit date windows, candidate-record queries should return record_id plus record_timestamp/source/summary. Make the first sentence of the final answer state the actual earliest/latest records analyzed, before any title/heading/quote list; do not only repeat the requested window. 8. For CASE-bucket counts, prefer WITH ... CASE ... END AS bucket, then RETURN bucket, COUNT(DISTINCT ...) so the compiler groups by the bucket. 9. Preserve explicit source filters from the user. If the user says Zendesk/App Store/Gong/G2, do not swap in a different source discovered from exploratory results. 10. "General" and "Generic" taxonomy values are not Miscellaneous. Unless the user explicitly says "miscellaneous", still filter only type != 'MISC' and do not add type != 'GENERIC'. 11. For source metadata prompts, include the source and field intent in search_graph_fields: "appstore rating country", "zendesk tags customer experience", "gong account name call title", "g2 star rating". HOW TO WRITE THE INTENT (affects which examples you get): The intent argument is a short natural-language description of the Cypher you want to build. It is matched against the example corpus by a hybrid lexical + semantic retriever that prioritizes example user_query phrasing, then rule descriptions and notes. Specificity beats keywords alone. Include the four aspects of your query: - Operation: "count", "top themes", "sample records", "sentiment breakdown" - Filter: "complaints", "from Zendesk", "in last 30 days" - Grouping: "by theme", "by source", "by month" - Special needs: "exclude billing", "with account info" Good intents (specific → better matches): "count complaints by theme from Zendesk in last month" "monthly trend analysis of feedback volume" "sample records with sentiment and source filtering" "top L2 categories with MISC filtering" "exclude specific topics from source analysis" "compare complaint volume before and after a date" Bad intents (too vague → poor matches): "query" / "get data" / "feedback" / "help" Args: intent: Short natural-language description of the Cypher you want to build. Include operation, filters, grouping, and special needs. top_k: Number of examples to return (default: 5) Returns: JSON array of examples with correct_cypher, notes, and optionally wrong_cypher. Current date for relative date resolution: 2026-06-04 UTC.
run_graph_query
ChatGPTExecute a Cypher query against Enterpret's Knowledge Graph. Translates your Cypher to SQL and returns structured results. Enterpret's KG unifies 100+ sources (Zendesk, Gong, Slack, AppStore, etc.) into a single graph. Source identity: nli.source. Source-specific metadata fields live on whichever entity the field belongs to — qualify each one with the alias matching its entity group as returned by search_graph_fields (nli. for NLI/FeedbackRecord, the joined account/user alias for Account/User fields). BEFORE writing Cypher: - If your query involves source-specific fields (ratings, tags, account names, plan types, custom metadata), you MUST call search_graph_fields first to discover exact field names. - Call get_query_examples to see proven patterns for similar queries. You MUST follow the Enterpret-specific Cypher rules below. Standard Neo4j Cypher patterns will often fail or return incorrect results due to the KG's structure (relationship fan-out creates duplicate rows). LIMIT Only apply LIMIT when the user explicitly requests a subset or examples. No LIMIT — user wants a true total or complete breakdown: - "how many" / "total" / "count" → COUNT query, no LIMIT - "all feedback" / "every" / "locate all" → exhaustive retrieval, no LIMIT - "breakdown by X" / GROUP BY → all groups needed, no LIMIT Good: RETURN COUNT(DISTINCT nli.record_id) AS total Good: RETURN language, COUNT(DISTINCT nli.record_id) AS total ORDER BY total DESC Bad: RETURN COUNT(DISTINCT nli.record_id) AS total LIMIT 100 -- caps the count! Bad: RETURN ... LIMIT 50 -- when user asked for "all", hides results Use LIMIT — user wants a ranked subset or examples: - "top 5" / "biggest" / "most common" → LIMIT N (default 50) - "show me some examples" / "sample feedback" → platform-aware limit: Gong/Zoom calls ~3-5, Zendesk tickets ~10, Surveys ~20-50 Counting Always use COUNT(DISTINCT entity.record_id). Never COUNT() or COUNT(entity). Graph traversals create duplicate rows: 1 NLI → many FI → many CFT. Good: COUNT(DISTINCT nli.record_id) AS total Bad: COUNT(*) AS total -- inflated by fan-out duplicates Feedback count → COUNT(DISTINCT nli.record_id) User/customer count → COUNT(DISTINCT <user_alias>.record_id) via the user-join path your org's schema exposes (PROVIDED_BY_USER → User or HAS_USER → DerivedUser; see get_graph_schema and search_graph_fields). DISTINCT Always use DISTINCT when returning NLI rows for samples or examples. Without it, the same record_id may appear multiple times due to fan-out. Good: RETURN DISTINCT nli.record_id AS record_id, fi.content AS summary Bad: RETURN nli.record_id, fi.content -- duplicates likely MISC Filter Every query using Theme, Subtheme, L1, L2, or L3 MUST include: entity.type != 'MISC' This applies to all query types — counts, samples, filtered, exploratory. Good: WHERE t.category_enum CONTAINS 'COMPLAINT' AND t.type != 'MISC' Bad: WHERE t.category_enum CONTAINS 'COMPLAINT' -- includes junk MISC entries For OPTIONAL MATCH: WHERE (t IS NULL OR t.type != 'MISC') Only omit if user explicitly says "include miscellaneous". Dates Relative dates — always use INTERVAL, never hardcode: Good: WHERE nli.record_timestamp >= now() - INTERVAL 30 DAY Good: WHERE opp.close_date >= now() - INTERVAL 6 MONTH Bad: WHERE opp.close_date >= toDateTime('2024-06-01') -- breaks over time Literal dates: toDateTime('YYYY-MM-DD HH:MM:SS') Month or month range without year: use the most recent fully completed occurrence relative to the current date. If the month/range is upcoming or still in progress this year, use the previous year. For "last month" or "past month" without a named calendar month, use a rolling window: now() - INTERVAL 30 DAY. For daily/weekly/monthly analysis over a relative window, keep the same rolling filter in every query; group daily with toStartOfDay, weekly with toStartOfWeek, and make monthly the total for that same rolling window unless the user asks for a multi-month trend. For explicit time windows, preserve or query actual coverage with returned timestamps or MIN/MAX timestamp fields, and state the actual ana…
run_graph_query
ChatGPTExecute a Cypher query against Enterpret's Knowledge Graph. Translates your Cypher to SQL and returns structured results. Enterpret's KG unifies 100+ sources (Zendesk, Gong, Slack, AppStore, etc.) into a single graph. Source identity: nli.source. Source-specific metadata fields live on whichever entity the field belongs to — qualify each one with the alias matching its entity group as returned by search_graph_fields (nli. for NLI/FeedbackRecord, the joined account/user alias for Account/User fields). BEFORE writing Cypher: - If your query involves source-specific fields (ratings, tags, account names, plan types, custom metadata), you MUST call search_graph_fields first to discover exact field names. - Call get_query_examples to see proven patterns for similar queries. You MUST follow the Enterpret-specific Cypher rules below. Standard Neo4j Cypher patterns will often fail or return incorrect results due to the KG's structure (relationship fan-out creates duplicate rows). LIMIT Only apply LIMIT when the user explicitly requests a subset or examples. No LIMIT — user wants a true total or complete breakdown: - "how many" / "total" / "count" → COUNT query, no LIMIT - "all feedback" / "every" / "locate all" → exhaustive retrieval, no LIMIT - "breakdown by X" / GROUP BY → all groups needed, no LIMIT Good: RETURN COUNT(DISTINCT nli.record_id) AS total Good: RETURN language, COUNT(DISTINCT nli.record_id) AS total ORDER BY total DESC Bad: RETURN COUNT(DISTINCT nli.record_id) AS total LIMIT 100 -- caps the count! Bad: RETURN ... LIMIT 50 -- when user asked for "all", hides results Use LIMIT — user wants a ranked subset or examples: - "top 5" / "biggest" / "most common" → LIMIT N (default 50) - "show me some examples" / "sample feedback" → platform-aware limit: Gong/Zoom calls ~3-5, Zendesk tickets ~10, Surveys ~20-50 Counting Always use COUNT(DISTINCT entity.record_id). Never COUNT() or COUNT(entity). Graph traversals create duplicate rows: 1 NLI → many FI → many CFT. Good: COUNT(DISTINCT nli.record_id) AS total Bad: COUNT(*) AS total -- inflated by fan-out duplicates Feedback count → COUNT(DISTINCT nli.record_id) User/customer count → COUNT(DISTINCT <user_alias>.record_id) via the user-join path your org's schema exposes (PROVIDED_BY_USER → User or HAS_USER → DerivedUser; see get_graph_schema and search_graph_fields). DISTINCT Always use DISTINCT when returning NLI rows for samples or examples. Without it, the same record_id may appear multiple times due to fan-out. Good: RETURN DISTINCT nli.record_id AS record_id, fi.content AS summary Bad: RETURN nli.record_id, fi.content -- duplicates likely MISC Filter Every query using Theme, Subtheme, L1, L2, or L3 MUST include: entity.type != 'MISC' This applies to all query types — counts, samples, filtered, exploratory. Good: WHERE t.category_enum CONTAINS 'COMPLAINT' AND t.type != 'MISC' Bad: WHERE t.category_enum CONTAINS 'COMPLAINT' -- includes junk MISC entries For OPTIONAL MATCH: WHERE (t IS NULL OR t.type != 'MISC') Only omit if user explicitly says "include miscellaneous". Dates Relative dates — always use INTERVAL, never hardcode: Good: WHERE nli.record_timestamp >= now() - INTERVAL 30 DAY Good: WHERE opp.close_date >= now() - INTERVAL 6 MONTH Bad: WHERE opp.close_date >= toDateTime('2024-06-01') -- breaks over time Literal dates: toDateTime('YYYY-MM-DD HH:MM:SS') Month or month range without year: use the most recent fully completed occurrence relative to the current date. If the month/range is upcoming or still in progress this year, use the previous year. For "last month" or "past month" without a named calendar month, use a rolling window: now() - INTERVAL 30 DAY. For daily/weekly/monthly analysis over a relative window, keep the same rolling filter in every query; group daily with toStartOfDay, weekly with toStartOfWeek, and make monthly the total for that same rolling window unless the user asks for a multi-month trend. For explicit time windows, preserve or query actual coverage with returned timestamps or MIN/MAX timestamp fields, and state the actual ana…
run_graph_query
ChatGPTExecute a Cypher query against Enterpret's Knowledge Graph. Translates your Cypher to SQL and returns structured results. Enterpret's KG unifies 100+ sources (Zendesk, Gong, Slack, AppStore, etc.) into a single graph. Source identity: nli.source. Source-specific metadata fields live on whichever entity the field belongs to — qualify each one with the alias matching its entity group as returned by search_graph_fields (nli. for NLI/FeedbackRecord, the joined account/user alias for Account/User fields). BEFORE writing Cypher: - If your query involves source-specific fields (ratings, tags, account names, plan types, custom metadata), you MUST call search_graph_fields first to discover exact field names. - Call get_query_examples to see proven patterns for similar queries. You MUST follow the Enterpret-specific Cypher rules below. Standard Neo4j Cypher patterns will often fail or return incorrect results due to the KG's structure (relationship fan-out creates duplicate rows). LIMIT Only apply LIMIT when the user explicitly requests a subset or examples. No LIMIT — user wants a true total or complete breakdown: - "how many" / "total" / "count" → COUNT query, no LIMIT - "all feedback" / "every" / "locate all" → exhaustive retrieval, no LIMIT - "breakdown by X" / GROUP BY → all groups needed, no LIMIT Good: RETURN COUNT(DISTINCT nli.record_id) AS total Good: RETURN language, COUNT(DISTINCT nli.record_id) AS total ORDER BY total DESC Bad: RETURN COUNT(DISTINCT nli.record_id) AS total LIMIT 100 -- caps the count! Bad: RETURN ... LIMIT 50 -- when user asked for "all", hides results Use LIMIT — user wants a ranked subset or examples: - "top 5" / "biggest" / "most common" → LIMIT N (default 50) - "show me some examples" / "sample feedback" → platform-aware limit: Gong/Zoom calls ~3-5, Zendesk tickets ~10, Surveys ~20-50 Counting Always use COUNT(DISTINCT entity.record_id). Never COUNT() or COUNT(entity). Graph traversals create duplicate rows: 1 NLI → many FI → many CFT. Good: COUNT(DISTINCT nli.record_id) AS total Bad: COUNT(*) AS total -- inflated by fan-out duplicates Feedback count → COUNT(DISTINCT nli.record_id) User/customer count → COUNT(DISTINCT <user_alias>.record_id) via the user-join path your org's schema exposes (PROVIDED_BY_USER → User or HAS_USER → DerivedUser; see get_graph_schema and search_graph_fields). DISTINCT Always use DISTINCT when returning NLI rows for samples or examples. Without it, the same record_id may appear multiple times due to fan-out. Good: RETURN DISTINCT nli.record_id AS record_id, fi.content AS summary Bad: RETURN nli.record_id, fi.content -- duplicates likely MISC Filter Every query using Theme, Subtheme, L1, L2, or L3 MUST include: entity.type != 'MISC' This applies to all query types — counts, samples, filtered, exploratory. Good: WHERE t.category_enum CONTAINS 'COMPLAINT' AND t.type != 'MISC' Bad: WHERE t.category_enum CONTAINS 'COMPLAINT' -- includes junk MISC entries For OPTIONAL MATCH: WHERE (t IS NULL OR t.type != 'MISC') Only omit if user explicitly says "include miscellaneous". Dates Relative dates — always use INTERVAL, never hardcode: Good: WHERE nli.record_timestamp >= now() - INTERVAL 30 DAY Good: WHERE opp.close_date >= now() - INTERVAL 6 MONTH Bad: WHERE opp.close_date >= toDateTime('2024-06-01') -- breaks over time Literal dates: toDateTime('YYYY-MM-DD HH:MM:SS') Month or month range without year: use the most recent fully completed occurrence relative to the current date. If the month/range is upcoming or still in progress this year, use the previous year. For "last month" or "past month" without a named calendar month, use a rolling window: now() - INTERVAL 30 DAY. For daily/weekly/monthly analysis over a relative window, keep the same rolling filter in every query; group daily with toStartOfDay, weekly with toStartOfWeek, and make monthly the total for that same rolling window unless the user asks for a multi-month trend. For explicit time windows, preserve or query actual coverage with returned timestamps or MIN/MAX timestamp fields, and state the actual ana…
search_graph_fields
ChatGPTGet source-specific metadata fields available for filtering and grouping. Only call this if get_graph_schema returned has_source_fields=true. Returns fields like zendesksupport_status, gong_metadata_title, appstore_rating, g2_metadata_starrating — these are NOT in get_graph_schema. HOW TO CALL: Include the source name in your query for better matches. Good: "zendesk ticket status and tags" Good: "zendesk tags customer experience" Good: "gong call title and account name" Good: "appstore rating country" Good: "g2 star rating" Bad: "billing issues" (too vague, may return wrong source's fields) HOW TO USE RETURNED FIELDS: - Use the exact field names in Cypher WHERE and RETURN clauses - Format: {alias}.{field_name}, where {alias} matches the entity group each field is listed under in the response. NLI/FeedbackRecord fields use nli.*; Account/User fields use the joined alias (see the per-entity prefix block appended to results for the exact MATCH pattern). - PREFER source-specific fields over fi.content search when the field exists Good (NLI): WHERE nli.zendesksupport_status CONTAINS 'solved' Good (Account): MATCH (...)-[:PROVIDED_BY_ACCOUNT]->(acc:Account) WHERE acc.industry CONTAINS 'Retail' Bad: WHERE fi.content CONTAINS 'Acme' (broad, may match irrelevant content) Bad: WHERE nli.industry CONTAINS 'Retail' (industry is on Account, not NLI) - When multiple similar fields exist (e.g., countryiso2 vs country_full_name), prefer the human-readable variant for filtering RATING/SCORE FIELDS (stored as lists — use CONTAINS, not =): Good: WHERE nli.appstore_rating CONTAINS '1' (filters 1-star reviews) Good: WHERE nli.g2_metadata_starrating CONTAINS '2' (filters 2-star G2 reviews) Bad: WHERE nli.appstore_rating = 1 (fails — ratings are list fields) To combine with other filters, always parenthesize OR groups: Good: WHERE nli.appstore_rating CONTAINS '1' AND (nli.appstore_country_full CONTAINS 'US' OR nli.appstore_country_full_name CONTAINS 'United States') Args: query: Include the source name (zendesk, gong, appstore, etc.) for best results top_k: Number of source groups to return (default: 10) Returns: Matched fields grouped by entity and source platform.
search_graph_fields
ChatGPTGet source-specific metadata fields available for filtering and grouping. Only call this if get_graph_schema returned has_source_fields=true. Returns fields like zendesksupport_status, gong_metadata_title, appstore_rating, g2_metadata_starrating — these are NOT in get_graph_schema. HOW TO CALL: Include the source name in your query for better matches. Good: "zendesk ticket status and tags" Good: "zendesk tags customer experience" Good: "gong call title and account name" Good: "appstore rating country" Good: "g2 star rating" Bad: "billing issues" (too vague, may return wrong source's fields) HOW TO USE RETURNED FIELDS: - Use the exact field names in Cypher WHERE and RETURN clauses - Format: {alias}.{field_name}, where {alias} matches the entity group each field is listed under in the response. NLI/FeedbackRecord fields use nli.*; Account/User fields use the joined alias (see the per-entity prefix block appended to results for the exact MATCH pattern). - PREFER source-specific fields over fi.content search when the field exists Good (NLI): WHERE nli.zendesksupport_status CONTAINS 'solved' Good (Account): MATCH (...)-[:PROVIDED_BY_ACCOUNT]->(acc:Account) WHERE acc.industry CONTAINS 'Retail' Bad: WHERE fi.content CONTAINS 'Acme' (broad, may match irrelevant content) Bad: WHERE nli.industry CONTAINS 'Retail' (industry is on Account, not NLI) - When multiple similar fields exist (e.g., countryiso2 vs country_full_name), prefer the human-readable variant for filtering RATING/SCORE FIELDS (stored as lists — use CONTAINS, not =): Good: WHERE nli.appstore_rating CONTAINS '1' (filters 1-star reviews) Good: WHERE nli.g2_metadata_starrating CONTAINS '2' (filters 2-star G2 reviews) Bad: WHERE nli.appstore_rating = 1 (fails — ratings are list fields) To combine with other filters, always parenthesize OR groups: Good: WHERE nli.appstore_rating CONTAINS '1' AND (nli.appstore_country_full CONTAINS 'US' OR nli.appstore_country_full_name CONTAINS 'United States') Args: query: Include the source name (zendesk, gong, appstore, etc.) for best results top_k: Number of source groups to return (default: 10) Returns: Matched fields grouped by entity and source platform.
search_graph_fields
ChatGPTGet source-specific metadata fields available for filtering and grouping. Only call this if get_graph_schema returned has_source_fields=true. Returns fields like zendesksupport_status, gong_metadata_title, appstore_rating, g2_metadata_starrating — these are NOT in get_graph_schema. HOW TO CALL: Include the source name in your query for better matches. Good: "zendesk ticket status and tags" Good: "zendesk tags customer experience" Good: "gong call title and account name" Good: "appstore rating country" Good: "g2 star rating" Bad: "billing issues" (too vague, may return wrong source's fields) HOW TO USE RETURNED FIELDS: - Use the exact field names in Cypher WHERE and RETURN clauses - Format: {alias}.{field_name}, where {alias} matches the entity group each field is listed under in the response. NLI/FeedbackRecord fields use nli.*; Account/User fields use the joined alias (see the per-entity prefix block appended to results for the exact MATCH pattern). - PREFER source-specific fields over fi.content search when the field exists Good (NLI): WHERE nli.zendesksupport_status CONTAINS 'solved' Good (Account): MATCH (...)-[:PROVIDED_BY_ACCOUNT]->(acc:Account) WHERE acc.industry CONTAINS 'Retail' Bad: WHERE fi.content CONTAINS 'Acme' (broad, may match irrelevant content) Bad: WHERE nli.industry CONTAINS 'Retail' (industry is on Account, not NLI) - When multiple similar fields exist (e.g., countryiso2 vs country_full_name), prefer the human-readable variant for filtering RATING/SCORE FIELDS (stored as lists — use CONTAINS, not =): Good: WHERE nli.appstore_rating CONTAINS '1' (filters 1-star reviews) Good: WHERE nli.g2_metadata_starrating CONTAINS '2' (filters 2-star G2 reviews) Bad: WHERE nli.appstore_rating = 1 (fails — ratings are list fields) To combine with other filters, always parenthesize OR groups: Good: WHERE nli.appstore_rating CONTAINS '1' AND (nli.appstore_country_full CONTAINS 'US' OR nli.appstore_country_full_name CONTAINS 'United States') Args: query: Include the source name (zendesk, gong, appstore, etc.) for best results top_k: Number of source groups to return (default: 10) Returns: Matched fields grouped by entity and source platform.
search_graph_values
ChatGPTSearch the Knowledge Graph to discover exact entity names before writing Cypher. Pass a list of related terms for ONE concept. The tool searches each term separately and merges results — giving you the best coverage for spelling and case variants. ONE CONCEPT = ONE CALL with related terms in the queries list: search(queries=["UI", "changes", "interface"]) — UI changes topic search(queries=["ApplePay", "Apple Pay"]) — spelling variants search(queries=["event", "forwarding"]) — feature name search(queries=["SMB", "smb"]) — case variants DIFFERENT CONCEPTS = SEPARATE PARALLEL CALLS: search_graph_values(queries=["Zendesk"]) ← source (call 1) search_graph_values(queries=["NPS"]) ← metric (call 2) search_graph_values(queries=["bucket"]) ← field (call 3) search_graph_values(queries=["spenders"]) ← value (call 4) EXTRACTION PROCESS Before calling, extract ALL concepts from the user's question: 1. List every distinct concept: source names, filter values, field names, topics 2. Group related terms/variants under the same concept 3. Each concept = one search call, execute SEPARATE PARALLEL calls Example: "NPS for bucket values 'spenders' from Zendesk for SMB" → 5 parallel calls: search(queries=["Zendesk", "zendesk"]) — source search(queries=["bucket"]) — field search(queries=["NPS", "nps"]) — metric search(queries=["spenders"]) — value search(queries=["SMB", "smb"]) — segment ⚠️ NEVER combine UNRELATED concepts in one call: ✗ search(queries=["Zendesk", "NPS", "bucket"]) — 3 different concepts ✗ search(queries=["churn", "cancellation", "leave", "uninstall"]) — kitchen sink WHY: Combining hides which term failed. Each concept needs individual validation. ⚠️ ALWAYS search SOURCE NAMES (often missed!): "Compare Zendesk vs Gong" → two calls: search(queries=["Zendesk"]), search(queries=["Gong"]) Source names vary by org: Zendesk vs Zendesk-Tickets vs zendesksupport. WHEN SEARCH RETURNS EMPTY — do NOT use the unverified term: 1. search(queries=["spenders"]) → EMPTY 2. Run discovery: RETURN DISTINCT nli.{field} LIMIT 20 3. Map user's term to closest actual value 4. Use discovered value in Cypher Args: queries: List of related terms for ONE concept — each term is searched separately and results are merged. Use a single-element list for the simple case (e.g. ["Zendesk"]). entity_types: Optional list of entity types to search, such as ["Theme", "L2"] limit: Maximum number of results per term (default: 10) Returns: Search results with matched entities, property paths, values, and scores.
search_graph_values
ChatGPTSearch the Knowledge Graph to discover exact entity names before writing Cypher. Pass a list of related terms for ONE concept. The tool searches each term separately and merges results — giving you the best coverage for spelling and case variants. ONE CONCEPT = ONE CALL with related terms in the queries list: search(queries=["UI", "changes", "interface"]) — UI changes topic search(queries=["ApplePay", "Apple Pay"]) — spelling variants search(queries=["event", "forwarding"]) — feature name search(queries=["SMB", "smb"]) — case variants DIFFERENT CONCEPTS = SEPARATE PARALLEL CALLS: search_graph_values(queries=["Zendesk"]) ← source (call 1) search_graph_values(queries=["NPS"]) ← metric (call 2) search_graph_values(queries=["bucket"]) ← field (call 3) search_graph_values(queries=["spenders"]) ← value (call 4) EXTRACTION PROCESS Before calling, extract ALL concepts from the user's question: 1. List every distinct concept: source names, filter values, field names, topics 2. Group related terms/variants under the same concept 3. Each concept = one search call, execute SEPARATE PARALLEL calls Example: "NPS for bucket values 'spenders' from Zendesk for SMB" → 5 parallel calls: search(queries=["Zendesk", "zendesk"]) — source search(queries=["bucket"]) — field search(queries=["NPS", "nps"]) — metric search(queries=["spenders"]) — value search(queries=["SMB", "smb"]) — segment ⚠️ NEVER combine UNRELATED concepts in one call: ✗ search(queries=["Zendesk", "NPS", "bucket"]) — 3 different concepts ✗ search(queries=["churn", "cancellation", "leave", "uninstall"]) — kitchen sink WHY: Combining hides which term failed. Each concept needs individual validation. ⚠️ ALWAYS search SOURCE NAMES (often missed!): "Compare Zendesk vs Gong" → two calls: search(queries=["Zendesk"]), search(queries=["Gong"]) Source names vary by org: Zendesk vs Zendesk-Tickets vs zendesksupport. WHEN SEARCH RETURNS EMPTY — do NOT use the unverified term: 1. search(queries=["spenders"]) → EMPTY 2. Run discovery: RETURN DISTINCT nli.{field} LIMIT 20 3. Map user's term to closest actual value 4. Use discovered value in Cypher Args: queries: List of related terms for ONE concept — each term is searched separately and results are merged. Use a single-element list for the simple case (e.g. ["Zendesk"]). entity_types: Optional list of entity types to search, such as ["Theme", "L2"] limit: Maximum number of results per term (default: 10) Returns: Search results with matched entities, property paths, values, and scores.
search_graph_values
ChatGPTSearch the Knowledge Graph to discover exact entity names before writing Cypher. Pass a list of related terms for ONE concept. The tool searches each term separately and merges results — giving you the best coverage for spelling and case variants. ONE CONCEPT = ONE CALL with related terms in the queries list: search(queries=["UI", "changes", "interface"]) — UI changes topic search(queries=["ApplePay", "Apple Pay"]) — spelling variants search(queries=["event", "forwarding"]) — feature name search(queries=["SMB", "smb"]) — case variants DIFFERENT CONCEPTS = SEPARATE PARALLEL CALLS: search_graph_values(queries=["Zendesk"]) ← source (call 1) search_graph_values(queries=["NPS"]) ← metric (call 2) search_graph_values(queries=["bucket"]) ← field (call 3) search_graph_values(queries=["spenders"]) ← value (call 4) EXTRACTION PROCESS Before calling, extract ALL concepts from the user's question: 1. List every distinct concept: source names, filter values, field names, topics 2. Group related terms/variants under the same concept 3. Each concept = one search call, execute SEPARATE PARALLEL calls Example: "NPS for bucket values 'spenders' from Zendesk for SMB" → 5 parallel calls: search(queries=["Zendesk", "zendesk"]) — source search(queries=["bucket"]) — field search(queries=["NPS", "nps"]) — metric search(queries=["spenders"]) — value search(queries=["SMB", "smb"]) — segment ⚠️ NEVER combine UNRELATED concepts in one call: ✗ search(queries=["Zendesk", "NPS", "bucket"]) — 3 different concepts ✗ search(queries=["churn", "cancellation", "leave", "uninstall"]) — kitchen sink WHY: Combining hides which term failed. Each concept needs individual validation. ⚠️ ALWAYS search SOURCE NAMES (often missed!): "Compare Zendesk vs Gong" → two calls: search(queries=["Zendesk"]), search(queries=["Gong"]) Source names vary by org: Zendesk vs Zendesk-Tickets vs zendesksupport. WHEN SEARCH RETURNS EMPTY — do NOT use the unverified term: 1. search(queries=["spenders"]) → EMPTY 2. Run discovery: RETURN DISTINCT nli.{field} LIMIT 20 3. Map user's term to closest actual value 4. Use discovered value in Cypher Args: queries: List of related terms for ONE concept — each term is searched separately and results are merged. Use a single-element list for the simple case (e.g. ["Zendesk"]). entity_types: Optional list of entity types to search, such as ["Theme", "L2"] limit: Maximum number of results per term (default: 10) Returns: Search results with matched entities, property paths, values, and scores.