검색하기의 "관계로 묻기" 절을 한곳에 모아 넓힌 문서입니다. 요청하는 법, 질문 유형별 예문과 AI가 옮긴 질의, 문법 전체, 응답을 읽는 법, 상한과 한계를 다룹니다.
1. 무엇을 하나, 언제 쓰나
그래프 질의는 기억을 관계와 범위를 두고 묻는 방법입니다. "이 이름이 나오는 기억 중에서만", "이 태그 안에서만", "이 기억들과 이름을 많이 나눠 가진 기억", "그다음에 무슨 일이" 같은 질문을 한 번의 질의로 씁니다. 읽기만 하고, 아무것도 바꾸지 않습니다.
찾는 방법은 세 가지입니다. 질문의 모양으로 고르세요.
| 묻고 싶은 것 | 쓰는 방법 | 까닭 |
|---|---|---|
| 문장으로 찾기: "세이브 버그 어떻게 고쳤지?" | 일반 검색 (ak …) |
의미와 연결로 찾고, 결과가 어떻게 엮이는지 지도를 같이 줍니다. 기본 방법입니다 |
| 범위를 정해 두고 뜻으로 찾기: "세이브 포맷이 나오는 기억 중에서 로드 버그를 고친 기록" | 그래프 질의 (ak 그래프 …) |
그 범위 안에서만 뜻이 가까운 순으로 고릅니다. 범위 밖의 기억이 순위를 먼저 차지하지 않습니다 |
| 관계를 따라가기: "이 두 기억과 이름을 가장 많이 나눠 가진 기억", "그다음 기록" | 그래프 질의 | 함께 나오는 이름, 기록의 순서, 뜻의 가까움을 따라 걷습니다 |
| 범위의 전부, 목록: "balance 태그가 붙은 기억 전부" | 그래프 질의의 목록 모양 | 순위가 아니라 목록이 필요합니다. 한 번에 최신 200건까지 |
| 글자 그대로의 일치, 정확한 개수: "요약에 '되돌리기'라고 적힌 기억이 몇 건?" | AI가 SQL 조회 도구로 개수를 확인합니다 | 그래프 질의는 뜻으로 가까운 순이지 글자 일치가 아닙니다. 이름 부분 일치도 엔티티와 태그 이름에만 걸리고 요약 글에는 걸리지 않습니다 |
그래프 질의는 ak 그래프로 바로 부를 수 있고, 문구 없이도 연결에 관한 질문이면 AI가
스스로 씁니다. 연결과 상관없는 평범한 질문은 평소대로 일반 검색으로 시작합니다.
2. 요청하는 법
질문 앞에 ak 그래프를 붙여 한 메시지로 보내세요. 질문은 평소 말투 그대로 쓰면 됩니다.
ak 그래프 세이브 포맷이 나오는 기억 중에서 로드 버그를 고친 기록
ak 그래프 balance 태그가 붙은 기억 중에서 적 체력을 바꾼 것
ak 그래프 인벤토리와 세이브 포맷이 모두 나오는 기억
- AI는 질문을 그래프 질의로 옮겨 실행하고, 답과 함께 실행한 질의와 그 뜻을 한 줄로 보여 줍니다. 무엇을 찾았는지 눈으로 확인할 수 있게 하려는 것입니다.
- 이름과 태그는 저장된 표기 그대로 맞춰 찾습니다. 표기가 헷갈리면 AI가 먼저 이름 조회로 확인합니다.
ak 그래프만 보내면 AI가 따라 쓸 수 있는 예문 셋을 내 기억에 있는 이름으로 바꿔 보여 줍니다.
3. 질문 유형별 예문과 옮겨진 질의
요청 칸에는 같은 뜻을 두 가지로 말해 두었습니다. 내 말투로 바꿔 물어도 됩니다. 질의 안의
$이름은 매개변수가 들어가는 곳이고, 값은 옆의 params로 넘어갑니다.
| 요청 (사용자 말투) | 옮겨진 질의 | 응답에서 볼 것 |
|---|---|---|
| ① 엔티티 범위 안 의미 검색. "세이브 포맷이 나오는 기억 중에서 로드 버그를 고친 기록" 또는 "세이브 포맷 얘기 가운데 로드 버그 잡은 거 찾아 줘" | START a = events(entity: $name, text: $q, k: 20) RETURN a, a.score ORDER BY a.score DESC, a.id, params {"name": "세이브 포맷", "q": "로드 버그를 고친 기록"} |
가까운 순 상위 20건입니다. start.has_more가 true면 범위에 더 있습니다: candidates.count를 보고 "64건 중 가까운 20건"으로 말합니다 |
| ② 태그 범위 안 의미 검색. "balance 태그가 붙은 기억 중에서 적 체력을 바꾼 것" 또는 "balance 태그 안에서 적 HP 조정한 기록만" | START a = events(tag: $tag, text: $q, k: 20) RETURN a, a.score ORDER BY a.score DESC, a.id, params {"tag": "balance", "q": "적 체력을 바꾼 것"} |
①과 같습니다. 태그 이름이 정확히 맞아야 합니다 |
| ③ 이름 부분 일치. "세이브 포맷과 같은 기억에 나오는 이름 중에 '버그'가 들어간 것 전부" 또는 "세이브 포맷이랑 같이 나온 엔티티 중 이름에 버그 들어간 거" | START a = events(entity: $name, k: 200) MATCH (a)-[:PARTICIPATED_IN]-(n) WHERE n.name CONTAINS $part RETURN DISTINCT n.name LIMIT 200, params {"name": "세이브 포맷", "part": "버그"} |
세이브 포맷 기억의 최신 200건에서 찾습니다. start.hub가 true면 200건을 넘어 오래된 쪽은 못 봤습니다 |
| ④ 전부와 목록. "balance 태그가 붙은 기억 전부" 또는 "balance 태그 목록 쭉 보여 줘" | START a = events(tag: $tag, k: 200) RETURN a LIMIT 200, params {"tag": "balance"} (이름 범위면 entity: $name) |
최신순 목록입니다. start.hub가 true면 200건을 넘었습니다: "최신 200건만 보여 준다"고 말합니다 |
| ⑤ A와 B가 모두 나오는 기억(시작에서 거르기). "인벤토리와 세이브 포맷이 모두 나오는 기억" 또는 "인벤토리 기록 중에 세이브 포맷도 언급된 거" | START a = events(entity_all: [$a, $b], k: 200) RETURN a LIMIT 200, params {"a": "인벤토리", "b": "세이브 포맷"} |
두 이름이 다 나오는 기억만 먼저 고른 뒤 최신순 200건을 가져옵니다. start.hub가 true면 겹치는 기억이 200건을 넘습니다. 예전 방식(entity: $a로 시작해 MATCH로 B를 거르기)은 인벤토리의 최신 200건만 보고 거르므로, 인벤토리 기억이 많으면 그보다 오래된 겹침을 놓칩니다 |
| ⑥ A 또는 B가 나오는 기억. "인벤토리나 세이브 포맷이 나오는 기억" 또는 "인벤토리든 세이브 포맷이든 둘 중 하나라도 언급된 거" | START a = events(entity: [$a, $b], k: 200) RETURN a LIMIT 200, params {"a": "인벤토리", "b": "세이브 포맷"} |
둘 중 하나라도 나오면 들어갑니다. 태그로 같은 일을 하려면 tag: [...] |
| ⑦ 이 이름이 나오면서 이 태그가 붙은 기억. "balance 태그가 붙은 기억 중에 인벤토리가 나오는 것" 또는 "인벤토리 얘기면서 balance 태그 달린 거만" | START a = events(entity: $name, tag: $tag, k: 200) RETURN a LIMIT 200, params {"name": "인벤토리", "tag": "balance"} |
두 조건을 다 만족하는 기억만. text: $q를 더하면 그 안에서 뜻이 가까운 순이 됩니다(k 최대 20) |
| ⑧ 공유 엔티티로 이웃 찾기. "이 두 기억과 이름을 가장 많이 나눠 가진 다른 기억" 또는 "방금 그 기록들이랑 겹치는 게 많은 기억" | START a = events(ids: $ids) MATCH (a)-[:PARTICIPATED_IN]-(e)-[:PARTICIPATED_IN]-(b) WHERE NOT b.id IN $ids RETURN b, count(DISTINCT e) AS shared ORDER BY shared DESC LIMIT 20, params {"ids": ["evt_…a", "evt_…b"]} |
shared는 함께 나오는 이름의 수입니다. 같은 일을 SHARES로 쓰면 s.via에 다리가 된 이름이 실립니다(4절 예제 4) |
| ⑨ 비슷한 기억과 다음 기억. "이 기억과 뜻이 비슷한 것" 또는 "이 기록 다음에 이어진 기록 세 단계까지" | 비슷한 것: START a = events(ids: $ids) MATCH (a)-[f:SIMILAR {k: 5}]-(b) RETURN b, f.cos ORDER BY f.cos DESC LIMIT 10, 다음 것: START a = events(ids: $ids) MATCH (a)-[n:NEXT*1..3]->(b) RETURN b, n.hops ORDER BY n.hops LIMIT 30 |
f.cos는 뜻의 가까움(클수록 가깝습니다), n.hops는 몇 걸음 뒤인지입니다 |
| ⑩ 이미 본 것 말고 새것만. "방금 본 결과 말고, 이 기억들과 이름을 둘 이상 나눠 가진 다른 기억" 또는 "아까 나온 거 빼고 연결된 거 더" | START a = events(ids: $ids) MATCH (a)-[s:SHARES {min: 2}]-(b) WHERE NOT b.id IN $seen RETURN b, s.count, s.via ORDER BY s.weight DESC LIMIT 20, params {"ids": [...], "seen": [...]} |
$seen에 이미 본 id를 넣어 뺍니다. 거르기는 걷기가 끝난 뒤에 걸립니다 |
| ⑪ 순위 근거. "왜 이 순서야? 점수도 같이 보여 줘" 또는 "얼마나 가까운지 숫자로" | START a = events(tag: $tag, text: $q, k: 10) RETURN a.id, a.summary, a.score ORDER BY a.score DESC, a.id, params {"tag": "balance", "q": "적 체력을 바꾼 것"} |
a.score는 시작 문장과의 가까움입니다. 1위와 2위의 점수 차이로 순위의 근거를 댑니다 |
4. 예제로 보기
요청부터 AI의 답까지 처음부터 끝까지 따라가 봅니다. 데이터는 가상의 게임 프로젝트이고, 응답은 요지만 줄여 적었습니다.
예제 1. 범위 안 의미 검색: "N건 중 가까운 k건"
요청: ak 그래프 세이브 포맷이 나오는 기억 중에서 로드 버그를 고친 기록
AI가 보여 주는 한 줄:
실행한 질의: START a = events(entity: $name, text: $q, k: 20) RETURN a, a.score ORDER BY a.score DESC, a.id
(뜻: "세이브 포맷"이 나오는 기억 중 "로드 버그를 고친 기록"과 뜻이 가까운 20건)
응답 요지:
rows: 20개 (evt_…, a.score 0.62에서 0.41까지)
start: scoped_mode "exact", candidates {count: 64, exact: true}, filled true, has_more true
partial: false, truncated: []
notes: "start: top-20 of 64 candidates; 44 more exist in scope (not a budget cut)"
AI의 말: "세이브 포맷이 나오는 기억 64건 가운데 뜻이 가까운 20건입니다. 나머지 44건은 가져오지 않았으니, 64건을 전부 비교한 결과가 아닙니다. 1위(0.62)는 로드 시 버전 필드를 읽는 순서를 고친 기록이고, 2위(0.55)와 차이가 커서 가장 직접적인 답입니다."
exact는 "후보 64건의 거리를 모두 쟀다"는 뜻이지 "64건을 모두 돌려줬다"는 뜻이 아닙니다.
그래서 AI는 "전부 확인했다"고 말하지 않습니다.
예제 2. 전부와 목록: 200건을 넘을 때
요청: ak 그래프 balance 태그가 붙은 기억 전부 보여 줘
AI가 보여 주는 한 줄:
실행한 질의: START a = events(tag: $tag, k: 200) RETURN a LIMIT 200
(뜻: balance 태그가 붙은 기억을 최신순으로 200건까지)
응답 요지:
rows: 200개
start: hub true, has_more true
partial: false, truncated: []
notes: "start: top-200 of 201+ candidates; 1+ more exist in scope (not a budget cut)"
AI의 말: "balance 태그 기억은 200건이 넘습니다. 최신 200건만 보여 드리고, 그보다 오래된 기록은 이번에 보지 못했습니다. 특정 주제만 필요하면 '적 체력' 같은 문장을 붙여 범위 안 의미 검색으로 좁힐 수 있습니다."
숫자 뒤의 +는 하한입니다. "적어도 201건"이지 정확한 수가 아닙니다.
예제 3. 0건: "상위 k 안에는 없음"과 "확인 못 함"
같은 모양의 질의가 두 번 0건을 돌려준 경우입니다. 뜻이 다릅니다.
(가) 절단 없는 0건
요청: ak 그래프 인벤토리 기억 중에 아이템 복제 버그와 가까운 것 가운데 세이브 포맷도 나오는 것
실행한 질의: START a = events(entity: $a, text: $q, k: 20) MATCH (a)-[:PARTICIPATED_IN]-(n {name: $b}) RETURN DISTINCT a
응답 요지:
rows: 0개
start: scoped_mode "exact", candidates {count: 90, exact: true}, filled true, has_more true
partial: false, truncated: []
notes: "start: top-20 of 90 candidates; 70 more exist in scope (not a budget cut)"
(나) 범위를 다 못 본 0건
요청: ak 그래프 전투 기억 중에 세이브 파일이 깨진 사고와 가까운 것 가운데 세이브 포맷도 나오는 것
실행한 질의: (가)와 같은 모양, $a = "전투"
응답 요지:
rows: 0개
start: scoped_mode "filtered_ann", candidates {count: 3001, exact: false}, filled false
partial: true, truncated: [{stage: "start", reason: "filled_false"}]
notes: "scoped START did not cover its whole scope (filled false, names truncated, or cut by
a statement timeout or the deadline) - rows may be missing; 0 rows means
'not confirmed here', not 'none exist'"
(가)에서 AI의 말: "인벤토리 기억 90건 중 가까운 20건 안에는 세이브 포맷이 함께 나오는 기억이 없습니다. 나머지 70건은 보지 않았으니 '없다'고는 말할 수 없습니다. 전부 확인하려면 두 이름이 모두 나오는 기억을 목록 모양으로 물어볼 수 있습니다."
(나)에서 AI의 말: "전투 기억은 3,000건이 넘어 빠른 근사 검색으로 찾았는데, 20건을 다 채우지 못했습니다. 그래서 0건은 '없음'이 아니라 '이번에 확인하지 못함'입니다. 태그를 같이 걸거나 더 좁은 이름으로 범위를 줄이면 다시 볼 수 있습니다."
"없다"고 말하려면 증거가 있어야 합니다. 범위를 끝까지 본 0건이거나, 이름 조회처럼 전체를 보는 다른 방법으로 확인한 경우입니다.
예제 4. 관계 걷기: 집계 열 읽기
요청: ak 그래프 예제 1의 1위와 2위 기록과 이름을 가장 많이 나눠 가진 다른 기억
AI가 보여 주는 한 줄:
실행한 질의: START a = events(ids: $ids) MATCH (a)-[:PARTICIPATED_IN]-(e)-[:PARTICIPATED_IN]-(b)
WHERE NOT b.id IN $ids RETURN b, count(DISTINCT e) AS shared ORDER BY shared DESC LIMIT 20
(뜻: 두 기록에 나오는 이름을 거쳐 닿는 다른 기억을, 함께 나오는 이름이 많은 순으로 20건)
응답 요지:
columns: [b, shared]
rows: 20개. 1행 {b: {id: evt_…, summary: "세이브 슬롯 이전 스크립트 작성"}, shared: 3}
2행 {b: {id: evt_…, summary: "로드 화면 진행 표시 변경"}, shared: 2} …
partial: false, truncated: []
AI의 말: "두 기록과 이름을 셋 나눠 가진 기억은 '세이브 슬롯 이전 스크립트 작성'입니다.
어떤 이름이 다리였는지 보려면 SHARES로 다시 물어 s.via를 읽으면 됩니다."
shared는 순위가 아니라 함께 나오는 이름의 개수입니다. 걸어서 닿은 기억에는 a.score가
없습니다(비어 있습니다). a.score는 시작 문장과의 가까움이라 시작점에만 실립니다.
예제 5. 두 이름이 모두 나오는 기억: 시작에서 거르기
요청: ak 그래프 인벤토리와 세이브 포맷이 모두 나오는 기억
AI가 보여 주는 한 줄:
실행한 질의: START a = events(entity_all: [$a, $b], k: 200) RETURN a LIMIT 200
(뜻: "인벤토리"와 "세이브 포맷"이 둘 다 나오는 기억을 최신순으로 200건까지)
응답 요지:
rows: 37개
start: unresolved [], hub false, has_more false
partial: false, truncated: []
AI의 말: "인벤토리와 세이브 포맷이 둘 다 나오는 기억은 37건이고, 전부 가져왔습니다. 가장 최근 것은 인벤토리 저장 필드를 세이브 포맷 3판에 맞춘 기록입니다."
hub가 false이고 절단도 없으니 겹치는 기억을 끝까지 본 것입니다. 예전처럼 인벤토리로
시작해 MATCH로 세이브 포맷을 거르면, 인벤토리 기억이 1,000건일 때 최신 200건 밖의
겹침은 보지 못합니다. entity_all은 두 이름으로 먼저 거른 뒤 200건을 고르므로 그런 빈틈이
없습니다.
이름 하나를 저장된 표기와 다르게 쓴 경우는 이렇게 옵니다.
요청: ak 그래프 인벤토리와 세이브포맷이 모두 나오는 기억 (띄어쓰기를 뺐다)
응답 요지:
rows: 0개
start: unresolved [{"kind": "entity", "name": "세이브포맷"}]
notes: "scope: no match for entity '세이브포맷' - a part of the scope found none of its names, so the scope has no rows"
AI의 말: "'세이브포맷'이라는 엔티티 이름으로 저장된 것이 없어 0건입니다. 겹치는 기억이 없다는 뜻은 아닙니다. 이름 조회로 보니 '세이브 포맷'으로 저장되어 있어, 그 표기로 다시 찾겠습니다."
unresolved에는 못 찾은 이름이 종류(entity 또는 tag)와 함께 적힙니다. 같은 글자라도 엔티티
이름과 태그 이름은 따로 찾으므로, 여기서 알 수 있는 것은 "엔티티로는 그 표기가 없다"까지입니다.
같은 표기의 태그는 있을 수 있습니다.
5. 문법 한눈에
읽기 전용 Cypher 부분집합입니다. 모양은 START … [MATCH … [WHERE …]] RETURN …입니다.
시작 (START a = events(…)). 시작점은 늘 기억(이벤트)입니다.
| 시작 | 뜻 | k 기본값, 최대 |
|---|---|---|
text: $q |
전체에서 문장과 뜻이 가까운 기억 | 5, 20 |
entity: $name |
그 이름이 나오는 기억, 최신순 | 5, 200 |
tag: $t |
그 태그가 붙은 기억, 최신순 | 5, 200 |
ids: [$id1, $id2] 또는 ids: $ids |
지정한 기억 (최대 20개, k는 무시) |
- |
entity: $name, text: $q |
그 이름이 나오는 기억 안에서 뜻이 가까운 순 | 5, 20 |
tag: $t, text: $q |
그 태그 안에서 뜻이 가까운 순 | 5, 20 |
entity: ["A", "B"] 또는 tag: ["A", "B"] |
이름 중 하나라도 나오는 기억, 최신순 | 5, 200 |
entity_all: ["A", "B"] 또는 tag_all: ["A", "B"] |
이름이 모두 나오는 기억, 최신순. 목록만 받습니다 | 5, 200 |
entity: $name, tag: $t |
그 이름이 나오면서 그 태그도 붙은 기억, 최신순 | 5, 200 |
위 셋 중 하나 + text: $q |
그 범위 안에서 뜻이 가까운 순 | 5, 20 |
이름 범위는 "묶음들을 모두 만족하고, 묶음 안에서는 이름 중 하나"로 읽습니다. 목록 하나가
묶음 하나이고, entity_all과 tag_all은 이름마다 묶음 하나입니다. 모양은 이것 하나뿐입니다.
괄호로 묶기, "빼고"(NOT), "엔티티 A 또는 태그 B"처럼 종류를 넘나드는 "또는"은 없습니다.
목록은 params로 넘겨도 됩니다(entity_all: $names). 한 시작에 이름은 모두 합쳐
10개까지이고, 넘으면 잘라 쓰지 않고 거절합니다. 빈 목록, 같은 이름 두 번, 글자가 아닌 값,
ids:와 함께 쓰기도 거절합니다.
관계 (MATCH (a)-[:관계]-(b))
| 관계 | 잇는 것 | 뜻과 쓸 수 있는 값 |
|---|---|---|
PARTICIPATED_IN |
엔티티와 기억 | 그 기억에 그 이름이 나옵니다. 기억→이름→기억으로 걸으면 "같은 이름이 나오는 기억" |
SHARES {min, min_w} |
기억과 기억 | 이름을 min개 이상 함께 가집니다. s.count(개수), s.weight(드문 이름일수록 큰 가중치), s.via(다리가 된 이름) |
SIMILAR {k, min} |
기억과 기억 | 뜻이 가까운 이웃. f.cos |
FAR {max} |
기억과 기억 | 뜻이 먼 것만 남깁니다(cos < max). SHARES와 함께 "이름은 겹치는데 뜻은 먼 것" |
NEXT {source} |
기억 → 기억 | 기록의 순서(스레드나 대화). 방향이 있습니다. *1..3으로 여러 걸음, n.hops |
MEMBER_OF {kind} |
기억 → 태그 | 태그 소속. 방향이 있습니다 |
RESOLVED_BY {relation} |
기억 → 기억 | 계획이나 기대가 실제로 이뤄진 기록으로 이어집니다. 방향이 있습니다. r.asserted_at |
CONNECTED |
엔티티와 엔티티 | 함께 나온 적이 있는 이름끼리. c.event_count |
SAME_AS |
엔티티와 엔티티 | 같은 것으로 등록된 별칭. x.confidence |
그 밖의 규칙
MATCH는 생략할 수 있습니다. 시작 자체가 답일 때START … RETURN …으로 끝냅니다(①②④⑤⑥⑦⑪).WHERE:=,<>,<,<=,>,>=,IN,CONTAINS,AND,OR,NOT, 그리고 두 기억 변수의 뜻 가까움cos(a, b).WHERE는MATCH뒤에만 옵니다.CONTAINS는 엔티티와 태그의 이름에만 씁니다. 대소문자와 전각, 반각을 가리지 않습니다. 조각이 전부 영문, 숫자, 기호(ASCII)면 2자 이상, 한글이나 한자가 들어가면 1자도 됩니다.RETURN [DISTINCT], 집계count,collect,min,max,sum,avg,AS별칭,ORDER BY,LIMIT. 집계와 함께 쓰는ORDER BY는 돌려주는 열(별칭)로 정렬합니다.a.score: 시작 문장과의 가까움. 문장 없는 시작과 걸어서 닿은 기억에서는 비어 있고, 빈 값은 맨 뒤로 갑니다. 순서를 고정하려면ORDER BY a.score DESC, a.id.$이름은 매개변수입니다. 사용자의 문장은 질의 안에 직접 쓰지 않고params로 넘깁니다.- 방향이 있는 관계에서 화살표를 빠뜨려도, 양 끝으로 방향이 하나로 정해지면 채워 주고
notes에 적습니다((a)-[:MEMBER_OF]-(t)→->). 기억과 기억을 잇는NEXT와RESOLVED_BY는 화살표를 직접 써야 합니다. 틀린 화살표는 뒤집어 주지 않고 거절합니다. - 돌려받는 행은 작게 줄여 옵니다. 기억은
{id, summary, timestamp, order_index}, 엔티티는{id, name, type}, 태그는{id, name}. 원문은 원문 조회로 따로 엽니다.
안 되는 것
WITH는 없습니다. 조건은 관계의 값(SHARES {min: 3})으로 겁니다.- 쓰기(
CREATE,SET등)는 없습니다. 읽기 전용입니다. - "…로 시작하는"(
STARTS WITH)은 없습니다.CONTAINS로 받은 뒤 AI가 거르고, 그렇게 했다고 말합니다. - 요약 글에 대한 글자 일치는 없습니다. 그런 질문은 SQL 조회로 합니다.
- 관계 없이 문장만 던지는 질문은 그래프 질의가 아니라 일반 검색이 낫습니다.
6. 응답 읽는 법
응답은 {columns, rows, row_count, start, partial, truncated, plan, budget, notes}입니다.
완결성을 알리는 신호는 두 부류로 나뉩니다.
예산 절단이 아닌 것 (partial과 truncated에 들어가지 않습니다)
start.has_more: true: 범위에 돌려준 것보다 후보가 더 있습니다. 범위 안 의미 검색,tag:단독, 이름 목록,_all, 엔티티와 태그 함께에 실립니다.start.candidates.count: 범위의 후보 수.exact: false면 하한입니다. "224건 중 가까운 20건"은 "224건 전부"가 아닙니다.start.hub: true: 문장 없는 범위에서 돌려준 최신 k건보다 더 많았습니다.entity:단독에서는 이것이 유일한 신호입니다.start.unresolved: 범위에 쓴 이름 중 저장된 것을 못 찾은 이름. 종류와 함께[{"kind": "entity", "name": "A"}]모양으로 옵니다. 엔티티 이름과 태그 이름은 따로 찾으므로, 엔티티 'X'는 찾고 태그 'X'는 못 찾았으면 태그 쪽만 적힙니다. 그래서 "X가 없다"가 아니라 "그 종류로는 그 표기가 없다"로 읽습니다(다른 종류로는 있을 수 있습니다). "A 또는 B"에서 A만 못 찾으면 B로 계속 찾고 여기에만 적으며, 안내는 붙지 않습니다. 한 묶음의 이름을 하나도 못 찾으면 범위가 비어 0건이 되고notes에 안내가 붙습니다. 이 0건은 "없음"이 아니라 "그 표기로 저장된 이름이 없음"입니다.- 기본 LIMIT:
LIMIT을 안 쓰면 행이 20개에서 멈춥니다(최대 200).
예산 절단 (partial: true, truncated에 항목)
filled: false: 큰 범위를 근사 검색으로 찾았는데 k건을 못 채웠습니다.- 이름 상한: 같은 이름의 엔티티나 태그가 20개를 넘어 일부만 썼습니다
(
start.entities_truncated와tags_truncated, 이름 범위에서는start.scope의 묶음마다truncated). - 시간 초과와 마감: 걷기가 너무 넓어 중간에 잘렸습니다. 기능 고장이 아닙니다. 같은 질의를
되풀이하지 말고
k나LIMIT을 줄이거나SHARES {min}을 올려 좁히세요.
notes에 실리는 안내 (응답에 영어로 그대로 실립니다)
- 후보가 더 있을 때:
start: top-20 of 64 candidates; 44 more exist in scope (not a budget cut) - 기본 LIMIT에 잘렸을 때:
rows cut to the default LIMIT 20; add LIMIT to return more (max 200) - 범위를 다 못 봤을 때:
scoped START did not cover its whole scope (filled false, names truncated, or cut by a statement timeout or the deadline) - rows may be missing; 0 rows means 'not confirmed here', not 'none exist' - 이름 부분 일치 뒤 걷기가 잘렸을 때:
CONTAINS filtered after the walk and the walk was truncated - 0 rows means 'not confirmed here', not 'none exist' - 범위의 한 묶음이 이름을 하나도 못 찾았을 때:
scope: no match for {names} - a part of the scope found none of its names, so the scope has no rows({names}에는 0건을 만든 묶음의 이름만 종류와 함께entity 'B', tag 'x'처럼 들어갑니다. 일부만 못 찾은 "또는" 묶음의 이름은 여기 들어가지 않고unresolved에만 있습니다)
범위를 다 못 본 경우에는 첫째 안내 대신 셋째가 붙습니다. 예산 절단이 결과를 비운 0건에도 첫째 안내는 붙지 않습니다.
0건은 "확인 못 함"이지 "없음"이 아닙니다. 절단이 있었으면 확인 못 한 것입니다. 절단이
없어도 상위 k만 봤으면(has_more, hub) 나머지는 안 본 것입니다. "없다"고 말하려면
범위를 끝까지 본 0건이거나, 이름 조회처럼 전체를 보는 다른 방법의 증거가 있어야 합니다.
거절은 {error, blocked_by: syntax|grammar|params, hint, allowed_*}로 옵니다. AI는
hint와 허용 목록을 읽고 한 번 고쳐 다시 부릅니다.
7. 상한과 한계
아래 표에서 k 최대와 ids: 개수는 기본값입니다. 서버가 다르게 정해 둘 수 있고,
새 문법이 켜진 서버는 응답의 budget.caps에 실제로 쓰는 상한을 싣습니다
({start_k_max, start_entity_max, start_scoped_k_max, limit_max}). LIMIT 최대 200은
어느 서버에서나 같습니다.
| 항목 | 값 | budget.caps 이름 |
|---|---|---|
k 기본값 |
5 | - |
k 최대 (text:) |
기본 20. 넘기면 상한으로 줄이고 notes에 적습니다 |
start_k_max |
k 최대 (범위 + 문장) |
기본 20. 넘기면 상한으로 줄이고 notes에 적습니다 |
start_scoped_k_max |
k 최대 (문장 없는 범위: entity:, tag: 단독, 목록, _all, 엔티티와 태그 함께) |
기본 200 | start_entity_max |
ids: 개수 |
기본 최대 20 | start_k_max |
| 한 이름이 가리키는 엔티티와 태그 | 최대 20 | - |
한 시작에 쓰는 이름 (목록, _all, 엔티티와 태그 합계) |
최대 10. 넘으면 거절합니다 | - |
LIMIT |
기본 20, 최대 200 | limit_max |
여러 걸음 (NEXT와 SHARES의 *) |
1~3 | - |
CONTAINS 조각 길이 |
ASCII만이면 2자 이상, 한글이나 한자면 1자 이상 | - |
| 요약 길이 | 행마다 줄여서 옵니다. 원문은 원문 조회로 | - |
- 권한 밖 기억은 애초에 보이지 않습니다. 행에도, 후보 수에도 잡히지 않습니다. 내가 읽을 수 있는 범위가 곧 질의의 범위입니다.
- 범위 안 의미 검색은 한 번에 가까운 20건까지입니다(기본값. 실제 값은
budget.caps의start_scoped_k_max). 그보다 많이 보려면 목록 모양(단독 범위 +k: 200)으로 묻거나 문장이나 태그로 더 좁히세요. - 서버에 따라 이 기능이 없거나 일부가 꺼져 있을 수 있습니다. 도구 목록에
query_memory_graph가 없으면 그래프 질의를 쓸 수 없습니다. 새 문법(범위 + 문장,tag:, 이름 목록과entity_all,tag_all,MATCH생략,a.score,CONTAINS, 화살표 채우기)만 꺼진 서버는… is not available on this server (scoped match is off)로 거절합니다. 그때 AI는text:나entity:단독으로 바꾸거나 일반 검색으로 찾고, 꺼져 있다는 사실을 알려 줍니다. "A와 B가 모두"는 예전 방식(⑤의 설명)으로 찾되, 최신 200건 안에서만 본다는 한계를 같이 말합니다.
8. 관련 문서
- 일반 검색과 결과 읽는 법 → 검색하기
- AI가 쓰는 도구 지도 → AiAkiv에 연결된 AI를 위한 안내
- 다른 팀 메모리에 걸친 그래프 질의 → 연결
- 클릭하며 보는 슬라이드 "관계로 묻기" → 팁 페이지
- AI가 읽는 같은 내용의 참조 문서 → llms-graph-query.txt