Decision Brain: AI에 작업의 '이유(Why)'를 부여하다, FalkorDB와 함께
저는 하나의 문제에 초점을 맞춰 Decision Brain을 구축했습니다. 바로 AI에게 사물들이 왜 그런 상태인지에 대한 실제 기억을 제공하는 것입니다.
기업이 운영하는 문서들—Word 파일, 스프레드시트, 정책, 코드—의 모든 변경 사항들을 가져와 그래프 형태의 단일 메모리 레이어에 매핑한다고 상상해 보세요. 여기서 모든 변경 사항은 그 배경이 된 결정(decision), 증거(evidence), 그리고 이를 아는 사람들(people)과 연결됩니다. 이 해커톤(Graph Hacks, FalkorDB × WeMakeDevs)을 위해 저는 정보가 가장 풍부한 곳, 즉 코드 저장소에서 시작했습니다. 모든 변경 사항은 이미 기록되어 있고, 좋은 팀들은 그들의 결정을 문서로 남기기 때문입니다.
그리고 코드는 실제적인 문제를 가지고 있습니다. 코드는 프로그램이 '무엇(What)'을 하는지는 보여주지만, '왜(Why)'는 보여주지 않습니다. 누군가 왜 그렇게 되었는지 모른 채 코드를 변경하면, 의도적으로 결정된 것을 되돌릴 수 있습니다. 그 '이유'는 보통 아무도 보지 않는 곳에 존재합니다: 결정 로그, 오래된 커밋 기록, 혹은 누군가의 기억 속에 말입니다. AI 코딩 에이전트들은 이 문제를 더욱 악화시킵니다. 이들은 빠르고 자신감 있게 코드를 변경하지만, 그 역사는 전혀 알지 못합니다.
제 프로젝트에서 실제로 발생한 사례가 있습니다. 제 라우터 프롬프트에는 사실과 다른 줄이 있습니다. 메모리 전문가가
그래프에 포함되는 내용
이 그래프는 두 가지 종류의 작업으로 채워지며, 그 분할이 중요합니다.
코드(Code)는 모델 없이 코드가 할 수 있는 일을 합니다. Git 기록은 Commit, File, 그리고 Person 노드로 구성되며 (AI 공동 저자는 에이전트(agent)로 표시됨), 모든 의사 결정 로그 항목 — 즉 DECISIONS.md 섹션이나 ADR 파일 — 은 날짜, 상태, 정확한 출처 라인을 가진 Decision으로 기록됩니다. git blame은 이를 해당 커밋과 연결합니다. 리포지토리의 AI 에이전트 지침 파일(CLAUDE.md, AGENTS.md, .cursorrules)의 모든 버전은 날짜가 지정된 섹션 스팬으로 보존되므로, "에이전트가 이 커밋을 작성할 때 어떤 지침을 받았는가?"라는 질문이 가능합니다.
로컬 에이전트 팀이 읽기 작업을 수행합니다. 네 명의 작업자가 공유되는 _board graph_에서 항목들을 가져와 측정값, 실험(판결 포함), 해당 항목과 관련된 파일 및 개념, 그리고 항목 간의 관계를 추출합니다. 이 보드 자체도 FalkorDB 그래프이며, 주장(claims), 발견 사항(findings), 인계(handoffs)가 기록되어 있고, 조정 작업은 순수하게 Cypher로 이루어집니다. 주장은 FalkorDB가 그래프별 쓰기 작업을 직렬화하기 때문에 원자적(atomic)입니다:
// src/board/board.ts에서 발췌
MATCH (s:Section)
WHERE (s.status = 'pending' OR (s.status = 'claimed' AND s.expiresAt < $now)) // 만료됨 = 인수 대기 상태
...
주장은 만료되므로, 충돌한 작업자의 항목은 인계됩니다. waiting 상태는 디렉터리 요약이 파일들을 기다리는 것을 의미합니다. 에이전트가 항목 A가 항목 B를 대체한다고 판단하면, _handoff_를 발생시키고 — 다른 에이전트가 각 측의 인용문과 함께 이를 확인해야만 카운트됩니다. Invarail의 151개 항목에 대해 팀은 15개의 이러한 인계를 발생시켰으며, 그중 9개가 확인되었고 나머지는 두 번째 에이전트에 의해 거부되었는데, 각 거부 이유가 보드에서 읽을 수 있습니다.
모델이 작성한 내용은 아무리 많아도 검증 없이 뇌로 들어가지 않습니다. 인용된 모든 문구는 항목에 그대로 나타나야 하며; 제시된 모든 그림은 해당 인용문 안에 있어야 하고; 파일 이름은 git에 존재해야 합니다. 심지어 파일 요약 내용 중 실제 파일에 포함되지 않은 함수나 클래스를 언급하면 거부됩니다. 이는 설명이 정확한지를 증명하는 것이 아니라, 단순히 존재하는지 여부를 확인하는 것입니다. (원래 이 시스템에는 버그가 있었습니다: 어떤 파일에도 'TaskStore.add'라는 정확한 문자열이 없기 때문에 이를 거부했습니다. 제가 알아차리기 전에 좋은 요약 13개가 무효화되었습니다. 이제는 한정된 이름의 모든 부분이 식별자(identifier)로 나타나야 하지만, 이것조차도 add가 TaskStore에 속한다는 것을 증명하지는 못합니다.)
그런 다음 파생된 RATIONALE 엣지(edge)가 각 결정을 그것이 기반을 두고 있는 파일들과 연결하고, FalkorDB의 내장 알고리즘들이 구조적인 질문들에 직접 답합니다. 예를 들어, 어떤 파일이 가장 많은 추론(reasoning)을 담고 있는지(algo.pageRank), 또는 두 가지 요소가 어떻게 연결되는지(algo.SPpaths) 등을 알 수 있습니다.
질의하기 (Asking it)
에이전트는 12개의 MCP 도구 — why, what_breaks, disproven, who_knows, trace, origin, told, review 등 —를 이용하는 단순한 도구 호출 루프이며, 각각은 작은 Cypher 쿼리입니다. 이 도구들은 모든 MCP 클라이언트에서 사용됩니다.
제가 가장 중요하게 생각하는 부분은 모델이 답변을 한 후에 무슨 일이 일어나는가 하는 점입니다. 코드가 이를 확인합니다:
-
인용된 모든 [id]는 해당 실행(run)에서 도구가 반환한 내용이어야 합니다;
-
모든 그림은 인용하는 항목의 증거(evidence) 안에 나타나야 합니다 — 잘못된 인용에 대해 '0/20'과 같은 표시가 나오면 실패합니다;
-
R1 라우터 프롬프트의 '잘못된' 규칙이 핵심 역할을 했다는 점을 측정함: 0/20 대 16/20
-
D1 "수정된 라우터 프롬프트 텍스트가 7B 모델의 메모리 라우팅 문제를 해결할 것이다" — 시도했으나 기각됨
-
P1 누가 파일인지 알 수 있음
그런 다음, 폐기 가능한 사본을 만들고 시뮬레이션으로 명확하게 표시한 후, 팀은 새로운 결정을 기록합니다. 즉, 7B 모델은 폐기되고 지원되는 모델에서는 수정된 규칙이 20/20 대 19/20을 측정한다는 것입니다. _Sync_를 한 번 클릭하자 Decision Brain은 변경된 내용만 가져옵니다. 하나의 에이전트는 "Supersedes(대체함)"를 제안하고, 두 번째 에이전트가 이를 확인합니다. 그리고 재제출된 수정 사항은 새로운 결정에 대한 지침으로 받게 되고, 이전 것은 대체됨으로 목록화됩니다... 제가 스크립팅하지 않은 한 줄 더 추가되었습니다: "수정된 규칙은 qwen2.5:7b에서 작동함"이 새로운 결정 아래 _기각됨_으로 기록됩니다. 이전 제약 조건은 여전히 살아있는 경고로 남아, 여전히 적용되는 범위에 국한됩니다. 새로운 증거가 단순히 승리하지 않았습니다.
원래의 제안을 다시 검토해 보면 여전히 9월의 증거를 보여줍니다: 변경 사항은 그것이 만들어졌을 때 알려진 것을 기준으로 판단됩니다.
그래프가 실제로 도움이 될까?
저는 세 개의 리포지토리(Invarail (제 것), GOV.UK의 콘텐츠 게시자, 그리고 제가 본 적 없는 log4brains)에 걸쳐 24개의 질문을 동결한 후 아무것도 실행하지 않았습니다. 중요한 비교는 그래프 도구 대신 키워드 검색 결과를 받은 동일한 로컬 모델입니다. 저는 이것을 두 번 실행했습니다. 두 번째에는 답변이 익명화된 A/B 시트만 본 신선한 에이전트에 의해 판단되었습니다.
| Decision Brain | 동일 모델 + 키워드 검색 |
|---|
| 필요한 증거 발견 | 20/20 · 20/20 | 15/20 · 15/20 |
기록으로 밝히는 문제점들
모든 수정 사항은 DECISIONS.md에 날짜가 기재되어 있습니다. 저에게 무언가를 가르쳐준 몇 가지 사례를 소개합니다.
- 저의 grounding checker가 세 번이나 과대 주장했습니다. "놀랍지 않게도…"라는 문구를 수치 정보가 없는 전제(figure-free premise)로 간주하고, 모든 부정문(negation)을 기권(abstention)으로 취급했으며, 잘못된 결정에 근거하여 인용된 수치를 받아들였습니다. 외부 검토자들이 이 세 가지 사례를 모두 잡아냈습니다. 현재는 각각 테스트 케이스가 되었습니다.
- FalkorDB 업그레이드가 쿼리를 조용히 망가뜨렸습니다. 6.x 버전에서 중첩 컴프리헨션(nested comprehension) 뒤에 배치된 리스트 컴프리헨션(list comprehension)이
[]를 반환했습니다. CI는 제 기기보다 최신 이미지를 실행했고 오류가 발생했으며, 저는 로드 문제인지 해시 충돌 문제인지 추측하다가 결국 원인을 찾았습니다. 해결책은 일반적인 MATCH … collect 구문이었고, 이미지는 고정되었습니다(pinned).
- 낯선 저장소에서 처음 클론하여 실행한 과정이 네 가지 방식으로 실패했습니다. 날짜로 명명된 ADRs(Architecture Decision Records), 같은 날 기록된 두 개의 충돌하는 레코드, 이 문제를 수정하는 동안 제가 삭제했던 함수, 그리고 병합되기 몇 달 전에 작성된 커밋들이었습니다. 네 가지 모두 테스트를 통해 해결되었고, 이후 한 번의 깨끗하고 중단 없는 실행을 거쳤습니다.
- 데모 자체에서 두 개의 버그가 발견되었습니다. 폐기된 결정(superseded decision)의 오래된 실험 결과가 여전히 "재시도하지 않음" 경고로 표시되었고, 에이전트는 그래프가 이미 대체되었다는 것을 알고 있는 결정에 대해 사용자들에게 재확인을 요청했습니다. 데모 역시 테스트입니다.
어떻게 작동하는가
브라우저에서 세 개의 페이지를 볼 수 있습니다. Board에서는 에이전트들이 실시간으로 작업하는 모습을 보여줍니다. 각 결정이나 파일별로 하나의 타일이 표시되며, 핸드오프(handoff)는 호(arc) 형태로 그려지고, 모든 타일을 열면 해당 타일이 생성한 결과물을 볼 수 있습니다. Ask는 일반 영어로 질문을 받아들이고, 도구 호출(tool call)이 발생하는 과정을 보여주며, 그 답변과 검증 과정, 그리고 근거가 된 경로를 제시합니다. Review는 저장소의 커밋 목록을 나열하고, 특정 커밋에 대해 해당 커밋의 diff와 이 커밋이 건드리는 결정들을 알려줍니다.
다음 계획
같은 그래프는 코드 기록뿐만 아니라 모든 결정 기록에 적용될 수 있습니다. 이것이 이 게시물의 핵심 아이디어입니다: 비즈니스 문서, 스프레드시트 및 정책 등 각각의 변경 사항이 그 이유와 연결되는 것입니다. 더 가까운 예시로는 질문 메모(다음 에이전트가 재사용할 수 있는 답변된 질문), 매우 긴 항목을 섹션별로 읽기, 그리고 모든 풀 리퀘스트에 검토 내용을 게시하는 GitHub Action 등이 있습니다.
사용해 보기
코드, 설정 및 평가는 https://github.com/PeterGreenAppliedAI/decision-brain에서 확인할 수 있습니다. Docker, Node 22.9 이상 버전, 그리고 도구 호출(tool calling) 기능을 갖춘 OpenAI 호환 모델 서버가 필요합니다 (저는 vLLM의 glm-5.3-flash를 사용했습니다. Ollama의 qwen3.8:27B도 작동합니다). DECISIONS.md 파일이나 ADR 폴더가 있는 모든 저장소에 연결하여 사용해 보세요.
FalkorDB 6.0.2, falkordb Node 클라이언트, MCP, 그리고 vLLM의 glm-5.3-flash로 구축되었으며 유료 API는 사용하지 않았습니다. 이벤트 규칙상 AI 코딩 도우미를 사용하여 작성했으며, 위에 언급된 모든 주장은 저장소와 대조하여 확인되었습니다.