---
slug: graph-query
title: "관계로 묻기: ak 그래프"
description: "`ak 그래프`로 이름·태그 범위를 정해 두고 묻는 그래프 질의. 요청하는 법, 유형별 예문과 옮겨진 질의, 문법, 응답 읽는 법, 상한."
lang: ko
---


# 25. 관계로 묻기: `ak 그래프`

[검색하기](searching)의 "관계로 묻기" 절을 한 자리에 모아 넓힌 문서다. 요청하는
법, 질문 유형별 예문과 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: $a, k: 200) MATCH (a)-[:PARTICIPATED_IN]-(n {name: $b}) RETURN DISTINCT a LIMIT 200` · `params {"a": "인벤토리", "b": "세이브 포맷"}` | 인벤토리 기억의 최신 200건 안에서 모은다. `start.hub`가 `true`면 그 밖은 못 봤다 |
| **⑥ 공유 엔티티로 이웃 찾기.** "이 두 기억과 이름을 가장 많이 나눠 가진 다른 기억" 또는 "방금 그 기록들이랑 겹치는 게 많은 기억" | `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. 문법 한눈에

읽기 전용 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 |

**관계 (`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:` 단독에 실린다.
- `start.candidates.count`: 범위의 후보 수. `exact: false`면 하한이다. "224건 중 가까운
  20건"은 "224건 전부"가 아니다.
- `start.hub: true`: `entity:`나 `tag:` 단독에서 돌려준 최신 k건보다 더 많았다.
  **`entity:` 단독에서는 이것이 유일한 신호다.**
- 기본 LIMIT: `LIMIT`을 안 쓰면 행이 20개에서 멈춘다(최대 200).

**예산 절단** (`partial: true`, `truncated`에 항목)

- `filled: false`: 큰 범위를 근사 검색으로 찾았는데 k건을 못 채웠다.
- 이름 상한: 같은 이름의 엔티티·태그가 20개를 넘어 일부만 썼다
  (`start.entities_truncated`·`tags_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'`

범위를 다 못 본 경우에는 첫째 안내 대신 셋째가 붙는다. 예산 절단이 결과를 비운 0건에도
첫째 안내는 붙지 않는다.

**0건은 "확인 못 함"이지 "없음"이 아니다.** 절단이 있었으면 확인 못 한 것이다. 절단이
없어도 상위 k만 봤으면(`has_more`, `hub`) 나머지는 안 본 것이다. "없다"고 말하려면
범위를 끝까지 본 0건이거나, 이름 조회처럼 전체를 보는 다른 길의 증거가 있어야 한다.

**거절**은 `{error, blocked_by: syntax|grammar|params, hint, allowed_*}`로 온다. AI는
`hint`와 허용 목록을 읽고 한 번 고쳐 다시 부른다.

## 7. 상한과 한계

| 항목 | 값 |
|---|---|
| `k` 기본값 | 5 |
| `k` 최대 (`text:`, 범위 + 문장) | 20. 넘기면 20으로 줄이고 `notes`에 적는다 |
| `k` 최대 (`entity:`·`tag:` 단독) | 200 |
| `ids:` 개수 | 최대 20 |
| 한 이름이 가리키는 엔티티·태그 | 최대 20 |
| `LIMIT` | 기본 20, 최대 200 |
| 여러 걸음 (`NEXT`·`SHARES`의 `*`) | 1~3 |
| `CONTAINS` 조각 길이 | ASCII만이면 2자 이상, 한글·한자면 1자 이상 |
| 요약 길이 | 행마다 줄여서 온다. 원문은 원문 조회로 |

- **권한 밖 기억은 애초에 보이지 않는다.** 행에도, 후보 수에도 잡히지 않는다. 내가 읽을
  수 있는 범위가 곧 질의의 범위다.
- **범위 안 의미 검색은 한 번에 가까운 20건까지다.** 그보다 많이 보려면 목록 모양(단독
  범위 + `k: 200`)으로 묻거나 문장·태그로 더 좁힌다.
- **서버에 따라 이 기능이 없거나 일부가 꺼져 있을 수 있다.** 도구 목록에
  `query_memory_graph`가 없으면 그래프 질의를 쓸 수 없다. 새 문법(범위 + 문장, `tag:`,
  `MATCH` 생략, `a.score`, `CONTAINS`, 화살표 채우기)만 꺼진 서버는
  `… is not available on this server (scoped match is off)`로 거절한다. 그때 AI는
  `text:`나 `entity:` 단독으로 바꾸거나 일반 검색으로 찾고, 꺼져 있다는 사실을 알려 준다.

## 8. 관련 문서

- 일반 검색과 결과 읽는 법 → [검색하기](searching)
- AI가 쓰는 도구 지도 → [AiAkiv에 연결된 AI를 위한 안내](for-ai-agents)
- 다른 팀 메모리에 걸친 그래프 질의 → [연결](links)
- 클릭하며 보는 슬라이드 "관계로 묻기" → [팁 페이지](https://www.aiakiv.com/learn)
- AI가 읽는 같은 내용의 참조 문서 → [llms-graph-query.txt](https://www.aiakiv.com/llms-graph-query.txt)
