On this page
- I Opened Only One Interface
- Why I Layered Feature Flags
- Passing Evidence to the Planner as a ContextBundle
- A Small Slip in Korean Search Matching
- This Is Not Full Response Injection Yet
- 한국어 버전
- 에이전트에 내 지식 베이스를 읽는 능력을 붙이며 배운 것
- 인터페이스는 하나만 열었다
- 기능 플래그를 겹겹이 둔 이유
- ContextBundle로 근거를 묶어 Planner에 넘기기
- 한국어 검색 매칭에서 한 번 미끄러졌다
- 아직 응답 주입까지 간 것은 아니다
I had wanted to connect my personal knowledge base to an agent for a long time. An agent becomes much more useful when it can answer from the notes, project records, and decisions I have already written down, instead of starting from a blank state every time.
But I did not want to open write access from the beginning. A knowledge base is close to a personal record, and I wanted to avoid a situation where the agent quietly changes something after making the wrong call. So the rule for this work was simple.
Let it read, but make the reading path very narrow.
I Opened Only One Interface
The standard interface I added in this stage is a single search API. The agent does not inspect the entire knowledge base directly. It does not open index files, and it does not need to understand the internal storage layout.
Instead, it sends a query to a fixed search endpoint and receives results back. The upstream index is exposed only in read-only mode. From the agent’s point of view, the only allowed operation is to send a search request and receive the result.
I liked this structure because the responsibility boundary is clear.
The knowledge base system owns indexing and search. The agent runtime executes the search capability as a tool. The Planner can use the returned results as evidence, but it does not depend on the storage implementation.
A general-purpose data access layer might have been more flexible. But flexibility can easily widen the permission boundary. For this stage, I intentionally kept the interface narrow.
Why I Layered Feature Flags
I also made sure this feature cannot be enabled by a single flag. There is a flag for the search capability itself, then separate flags around the read-only executor mode and Planner shadow mode. The Context Engine bridge sits behind its own shadow flag as well.
That may look a little cumbersome, but this kind of feature needs that friction.
Letting an agent read a personal knowledge base is convenient, but the blast radius is large if it turns on by accident. That is especially true for features wired into central parts of the system, such as the Planner or executor. They should not become active just because one setting was flipped by mistake.
So I layered the conditions. Even if the search capability is enabled, it does not run unless the outer execution mode is also correct. The Context bridge only runs on retrieval-shaped turns, and it skips file-attachment style messages.
The point is not to hide the feature. The point is to narrow the activation conditions.
Passing Evidence to the Planner as a ContextBundle
In the second step, I did not pass raw search results downstream. I wrapped them in a bounded, immutable ContextBundle before handing them to the Planner.
That mattered because search results grow faster than expected. Several related documents can match, each document can bring a long chunk, and if all of that flows into the Planner unbounded, the context expands quickly. The bigger problem is that the boundary of the evidence becomes blurry.
The ContextBundle exists to make that boundary explicit.
It defines what counts as retrieved evidence, how much of it is included, and the maximum size the Planner is allowed to receive. Since it is treated as an immutable object, it also reduces the chance that downstream steps quietly mutate the evidence.
An agent should not pull in every note at once just because it can read a knowledge base. It should receive only the evidence it needs, in a limited size, with a clear structure. This bridge was a first step in that direction.
A Small Slip in Korean Search Matching
There was also a small but memorable bug: Korean search did not match the way I expected.
The root cause was a word-boundary regex. In English, splitting tokens around word boundaries often feels natural. Korean is different. Particles attach to words, and expressions like “찾아줘” or “파일을” break the assumptions behind English-style word boundaries.
I eventually went back to ordered substring matching and added a regression test.
The bug was small, but it was a useful reminder. When building agent features, it is easy to import assumptions from English text without noticing. Korean usability breaks quickly when that happens, especially in search, command interpretation, and routing, where the system has to handle input sentences directly.
This Is Not Full Response Injection Yet
This work is closer to giving the agent the ability to read from the knowledge base. I added the search capability, then built a bridge that packages search evidence into a ContextBundle and passes it to the Planner.
That does not mean the evidence is now fully and reliably injected into final answer generation. The next step is to connect ContextBundle more safely into the response path. There are still open questions: when should search results be used, when should they be ignored, and how should sources be shown?
Still, the principle from this stage is clear.
When connecting a personal knowledge base to an agent, the important question is not how much you can connect. It is which interface you open, which modes are allowed to use it, and how tightly you bound the evidence.
In this stage, I started with one read-only search API, layered the feature flags, and wrapped the retrieved evidence in a bounded immutable bundle. It is not flashy, but for bringing a personal knowledge base into an agent runtime, that level of conservatism felt right.
한국어 버전
에이전트에 내 지식 베이스를 읽는 능력을 붙이며 배운 것
개인 지식 베이스를 에이전트에 연결하고 싶다는 생각은 오래전부터 있었다. 에이전트가 매번 빈 상태에서 대답하는 대신, 내가 이미 정리해 둔 메모, 프로젝트 기록, 결정 사항을 읽고 답하면 훨씬 쓸모가 커지기 때문이다.
다만 처음부터 쓰기 권한까지 열고 싶지는 않았다. 지식 베이스는 개인 기록에 가깝고, 에이전트가 잘못 판단했을 때 조용히 내용을 바꾸는 상황은 피하고 싶었다. 그래서 이번 작업의 기준은 단순했다.
“읽을 수는 있게 하되, 아주 좁게 읽게 하자.”
인터페이스는 하나만 열었다
이번에 붙인 기능의 표준 인터페이스는 검색 API 하나다. 에이전트는 지식 베이스 전체를 직접 들여다보지 않는다. 인덱스 파일을 열지도 않고, 내부 저장 구조를 알 필요도 없다.
대신 정해진 검색 엔드포인트로 질의를 보내고, 결과를 받아온다. 업스트림 인덱스는 읽기 전용 모드로만 열리도록 두었다. 에이전트 입장에서는 “검색 요청을 보내고 결과를 받는 것”만 가능하다.
이 구조가 마음에 들었던 이유는 책임 경계가 명확하기 때문이다.
지식 베이스 시스템은 인덱싱과 검색을 책임진다. 에이전트 런타임은 검색 capability를 하나의 도구처럼 실행한다. Planner는 그 결과를 근거로 사용할 뿐, 저장소의 내부 구현에 의존하지 않는다.
처음부터 범용 데이터 접근 레이어를 만들었다면 더 유연했을 수는 있다. 하지만 유연함은 권한 범위를 넓히기 쉽다. 이번 단계에서는 일부러 인터페이스를 좁게 잡았다.
기능 플래그를 겹겹이 둔 이유
이 기능은 단일 플래그 하나로 바로 켜지지 않게 했다. 검색 capability 자체의 플래그가 있고, 그 바깥에 읽기 전용 executor 플래그와 Planner shadow 플래그가 있다. Context Engine 쪽 연결도 별도 shadow 플래그 뒤에 있다.
조금 번거로워 보이지만, 이런 종류의 기능에는 이 정도의 마찰이 필요하다고 봤다.
개인 지식 베이스를 에이전트가 읽는 기능은 편리하지만, 잘못 켜졌을 때 영향이 크다. 특히 Planner나 executor처럼 시스템의 중심부에 걸리는 기능은 “어느 한 설정이 실수로 켜졌다”는 이유만으로 동작하면 안 된다.
그래서 조건을 중첩했다. 검색 capability가 켜져 있어도 바깥 실행 모드가 맞지 않으면 동작하지 않는다. Context bridge도 retrieval 성격의 턴에서만 작동하고, 파일 첨부형 메시지는 건너뛴다.
핵심은 기능을 숨기는 것이 아니라, 활성화 조건을 좁히는 것이다.
ContextBundle로 근거를 묶어 Planner에 넘기기
두 번째 단계에서는 검색 결과를 그대로 흘려보내지 않고, 크기가 제한된 불변 ContextBundle로 묶어 Planner에 전달했다.
이 부분이 중요했다. 검색 결과는 생각보다 쉽게 커진다. 관련 문서가 여러 개 나오고, 각 문서에서 긴 chunk가 붙고, 그 상태로 Planner에 들어가면 컨텍스트가 빠르게 불어난다. 더 큰 문제는 근거의 경계가 흐려진다는 점이다.
ContextBundle은 그 경계를 명확히 하기 위한 장치다.
무엇이 검색 근거인지, 어느 정도까지 포함할 것인지, Planner가 받을 수 있는 최대 크기는 어디까지인지 정해 둔다. 또한 불변 객체로 다루기 때문에 downstream 단계에서 조용히 내용이 바뀌는 일을 줄일 수 있다.
에이전트가 지식 베이스를 읽는다고 해서 모든 메모를 한꺼번에 들고 들어오면 안 된다. 필요한 근거만, 제한된 크기로, 구조화해서 넘겨야 한다. 이번 bridge는 그 방향으로 가는 첫 단계였다.
한국어 검색 매칭에서 한 번 미끄러졌다
짧지만 기억에 남는 버그도 있었다. 한국어 검색이 기대대로 매칭되지 않는 문제였다.
원인은 단어 경계 정규식이었다. 영어에서는 word boundary 기준으로 토큰을 나누는 방식이 꽤 자연스럽다. 하지만 한국어에서는 조사가 붙는다. “찾아줘”, “파일을” 같은 표현에서 영어식 단어 경계 가정이 깨졌다.
결국 순서 있는 substring 매칭으로 되돌리고 회귀 테스트를 추가했다.
이 버그는 작지만 좋은 알림이었다. 에이전트 기능을 만들 때 영어 텍스트 기준의 가정을 무심코 들여오면, 한국어 사용성은 쉽게 망가진다. 특히 검색, 명령 해석, 라우팅처럼 입력 문장을 다루는 부분에서는 더 그렇다.
아직 응답 주입까지 간 것은 아니다
이번 작업은 에이전트가 지식 베이스를 “읽을 수 있게” 만드는 단계에 가깝다. 검색 capability를 붙였고, 검색 근거를 ContextBundle로 묶어 Planner에 넘기는 bridge를 만들었다.
하지만 이것이 곧바로 최종 답변에 완전하게 주입된다는 뜻은 아니다. 다음 단계는 ContextBundle을 실제 응답 생성 흐름에 더 안정적으로 연결하는 일이다. 검색 결과가 언제 답변에 쓰이고, 언제 무시되어야 하는지, 출처 표시는 어떻게 할지 같은 문제도 남아 있다.
그래도 이번 단계에서 얻은 기준은 분명하다.
개인 지식 베이스를 에이전트에 붙일 때 중요한 것은 “얼마나 많이 연결하느냐”가 아니다. 어떤 인터페이스만 열 것인지, 어떤 모드에서만 동작하게 할 것인지, 근거를 어디까지 제한할 것인지가 더 중요하다.
나는 이번에 읽기 전용 검색 API 하나로 시작했고, 플래그를 중첩했으며, 검색 근거를 bounded immutable bundle로 묶었다. 화려한 기능은 아니지만, 개인 지식 베이스를 에이전트 런타임 안으로 들여오는 방식으로는 이 정도의 보수성이 맞다고 느꼈다.