6. 직접 만들기: 회의록 정리 Agent
이미 있는 도구 쓰기
이 차시를 마치면
이미 검증된 읽기 도구를 그대로 재사용합니다.
회의록을 읽습니다. 새 도구를 만들지 않고 4번 트랙의 것을 그대로 씁니다.
새로 만들지 않는 이유
sec_tools.py 를 그대로 씁니다. 복사하지도 않고 고치지도 않습니다.
왜 재사용하는가
경로 검사 규칙이 두 벌이 되는 순간 위험해집니다. 나중에 한쪽만 고쳐지면 한쪽에는 있는 방어가 다른 쪽에는 없게 됩니다. 그리고 그 사실을 아무도 모릅니다.
3번 트랙의 C-1 이 정확히 이 유형입니다. 같은 것을 두 곳에 두고 한쪽만 고치는 것.
이 도구는 이미 검증되어 있습니다. 상위 폴더 탈출, 절대경로, 심볼릭 링크 우회까지 막고 테스트 10개가 통과합니다. 그것을 다시 만들 이유가 없습니다. 분석
여기서 쓰는 sec_tools.py 는 4번 트랙에서 만든 그대로입니다. 경로 차단·심볼릭 링크·상위 폴더 접근을 포함한 테스트 10개를 통과한 코드입니다. 새로 만들지 않았으므로 그 검증이 그대로 유효합니다.
회의록 읽기 Node
def collect_notes(state: NotesState) -> dict:
"""회의록을 읽는다. 읽기 도구만 쓴다."""
names = [
name
for name in sec_tools.list_workspace_files(state["meeting_dir"])
if name.endswith(".md")
]
texts = [
sec_tools.read_workspace_file("%s/%s" % (state["meeting_dir"], name))
for name in names
]
return {
"note_names": names,
"note_texts": texts,
"audit_log": ["회의록 %d건 읽음: %s" % (len(names), ", ".join(names))],
}
세 가지를 봅니다.
.md파일만 고릅니다. 폴더에 다른 것이 있어도 건드리지 않습니다- 읽기 도구만 씁니다. 이 Node 는 아무것도 바꾸지 않습니다
- 몇 건을 읽었는지 감사 로그에 남깁니다
없는 파일에 대한 처리
읽기 도구는 없는 파일에 예외를 던지지 않고 문자열을 돌려줍니다.
if not target.is_file():
return f"[없음] 파일을 찾을 수 없습니다: {relative_path}"
이 성질을 다음 차시의 plan 이 씁니다. 대상 파일이 있는지 확인할 때입니다.
target = sec_tools.read_workspace_file(state["target_path"])
exists = not target.startswith("[없음]")
분석 문자열로 판단하는 것이 우아하지는 않습니다. 예외를 던지게 만들거나 별도의 존재 확인 함수를 두는 편이 더 깔끔합니다. 다만 기존 도구를 고치지 않는다는 원칙을 지키기 위해 이 방식을 택했습니다. 도구를 고치려면 4번 트랙의 테스트 10개를 함께 봐야 하기 때문입니다.
이런 판단을 기록해 두십시오
"더 나은 방법이 있지만 이 이유로 이렇게 했다" 는 주석이나 문서에 남겨야 합니다. 남기지 않으면 나중에 보는 사람이 실수라고 여기고 고칩니다. 그러면 함께 봐야 할 것을 안 보고 고치게 됩니다.
읽기까지는 안전합니다
여기까지 Agent 는 아무것도 바꾸지 않았습니다. 폴더를 읽고 내용을 State 에 담았을 뿐입니다.
다음 차시부터 쓰기가 들어옵니다. 그때부터 승인과 백업이 필요해집니다.