AI 세일즈 에이전트에게 도구를 줬더니: 추측에서 실제 조사로 전환한 B2B 파이프라인 구축기
계속 우리를 괴롭혔던 문제
세일즈 파이프라인이 작동하고 있었습니다. 여섯 개의 Claude 에이전트가 각자 역할을 수행했습니다: 회사를 조사하고, 솔루션을 매핑하고, 제안서를 작성하고, 딜 규모를 추정하고, 이메일을 작성하고, 빠른 적합성 요약을 생성합니다. CLI 명령어 하나면 몇 분 안에 개인화된 세일즈 제안서가 완성되었습니다.
하지만 거기에는 거짓말이 내재되어 있었습니다.
"리서처" 에이전트는 실제로 아무것도 조사하지 않았습니다. "Muster Metall GmbH, Germany"를 입력하면, 그들의 제조 공정, 에너지 문제, ESG 리스크에 대해 자신감 넘치는 브리프를 작성했습니다. 모든 단어가 그럴듯했습니다. 하지만 검증된 것은 하나도 없었습니다. 조사가 아니라 추론이었습니다. 단순한 사례에서는 충분히 잘 작동했습니다 — 그렇지 않을 때까지요.
잘 알려지지 않은 회사로 테스트했을 때 격차가 분명해졌습니다. 에이전트는 실제로는 플라스틱 사출 성형 부품을 만드는 회사에 대해 "예상되는 스탬핑 작업"이라고 자세한 단락을 작성했습니다. 동남아시아의 회사에 대해 해당 규정이 적용되지 않는데도 "EU 탄소 보고 요건"을 언급했습니다.
해결책은 더 나은 프롬프트를 작성하는 것이 아니었습니다. 해결책은 에이전트에게 실제 도구를 주고 스스로 사용 방법을 결정하게 하는 것이었습니다.
무엇이 바뀌었는가
두 개의 새로운 모듈. 리서처 작동 방식의 근본적인 전환.
이전
사용자 입력 → [리서처: "모든 것을 추론"] → 리서치 브리프 → [나머지 4개 에이전트]
모든 에이전트가 단일 턴 프롬프트 → 응답 호출이었습니다. 리서처는 회사 이름을 받고 학습 데이터만으로 리서치 브리프를 작성해야 했습니다. 웹 접근도, 데이터베이스도, 검증할 방법도 없었습니다.
이후
사용자 입력
│
▼
[RAG 초기화] ── 제품 지식 + 과거 아웃리치를 ChromaDB에 로드
│
▼
[에이전틱 리서처] ── 멀티턴 도구 사용 루프 (최대 5턴)
├── 턴 1: query_knowledge_base("과거 유사 기업 아웃리치")
├── 턴 2: scrape_company_website("https://company.com")
├── 턴 3: search_web("회사 제조 세부사항")
├── 턴 4: query_knowledge_base("관련 사례 연구 에너지")
└── 턴 5: 최종 텍스트 응답 (구조화된 리서치 브리프)
│
▼
[나머지 에이전트 변경 없음 — 단일 턴]
│
▼
[저장 + 향후 실행을 위해 RAG에 인덱싱]
리서처는 이제 세 가지 도구를 갖습니다: 웹 검색, 지식 베이스 쿼리, 웹사이트 스크레이퍼. 무엇을 어떤 순서로 조사할지 스스로 결정합니다. 나머지 다섯 에이전트는 정확히 동일합니다 — 더 나은 입력을 받아 더 나은 출력을 생성합니다.
세 가지 도구
도구 1: search_web
DuckDuckGo를 통해 회사 정보, 산업 데이터, 뉴스를 검색합니다. 이미 회사를 찾기 위한 검색 모듈(prospector.py)이 있었는데, 리서처가 호출할 수 있는 도구로 노출시켰습니다.
에이전트는 상위 5개 결과의 제목, URL, 스니펫을 받습니다. 이를 통해 회사가 무엇을 하는지 이해하고, 웹사이트를 찾고, 산업 맥락을 수집합니다.
도구 2: query_knowledge_base
다음을 포함하는 ChromaDB 벡터 스토어에 대한 시맨틱 검색:
- 제품 지식 (~16개 청크): MV900 기능, Machine365.Ai 능력, 사례 연구, ROI 데이터, 이상적인 고객 프로필
- 과거 아웃리치 기록 (시간이 지남에 따라 증가): 완료된 모든 검색 실행이 인덱싱되어 리서처가 유사한 회사에 대한 과거 활동을 참조할 수 있음
시스템의 메모리가 사는 곳입니다. 6개월 전에 캘리포니아의 동 피팅 제조업체에 제안했다면, 리서처는 유사한 회사를 조사할 때 그 맥락을 찾아 사용할 수 있습니다.
도구 3: scrape_company_website
URL을 가져와 HTML을 제거하고 깨끗한 텍스트(최대 4,000자)를 반환합니다. requests + BeautifulSoup으로 구축했습니다.
핵심 설계 결정: 에러는 예외가 아닌 문자열을 반환합니다. 웹사이트가 다운되거나 요청을 차단하면, 에이전트는 "Error: Could not connect to https://company.com"을 보고 적응합니다 — 대신 웹 검색을 하거나 브리프에 한계를 기록합니다. 예외를 발생시키면 전체 파이프라인이 충돌할 것입니다.
에이전틱 루프
이것이 개선의 핵심입니다. 하나의 프롬프트 → 응답 호출 대신, 리서처는 Claude의 도구 사용 API를 통해 멀티턴 대화를 수행합니다.
def call_agentic_researcher(company_input: str, max_turns: int = 5) -> str:
messages = [{"role": "user", "content": f"이 회사를 조사해주세요: {company_input}..."}]
for turn in range(max_turns):
response = client.messages.create(
model=MODEL,
system=AGENTIC_RESEARCHER_SYSTEM,
tools=RESEARCHER_TOOLS,
messages=messages,
)
if response.stop_reason == "tool_use":
# 모델이 도구를 사용하려 함 — 실행하고 계속
tool_results = []
for block in response.content:
if block.type == "tool_use":
result = _execute_tool(block.name, block.input)
tool_results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": result,
})
messages.append({"role": "assistant", "content": response.content})
messages.append({"role": "user", "content": tool_results})
else:
# 모델이 최종 텍스트 응답을 반환 — 완료
return response.content[0].text
루프는 이렇게 작동합니다:
- 3개 도구 스키마와 함께 리서치 요청을 Claude에 전송
- Claude가
stop_reason: "tool_use"를 반환하면, 요청된 도구를 실행하고 결과를 피드백 - Claude가
stop_reason: "end_turn"을 반환하면, 텍스트를 추출 — 이것이 최종 리서치 브리프 - 안전 제한: 과도한 API 사용을 방지하기 위해 최대 5턴
강력한 점은 에이전트가 리서치 전략을 결정한다는 것입니다. 글로벌 대기업처럼 잘 알려진 회사의 경우 웹 검색 한 번으로 바로 브리프를 작성할 수 있습니다. 생소한 회사의 경우 먼저 웹사이트를 스크레이핑하고, 산업 맥락을 검색한 다음, 지식 베이스에서 유사한 과거 아웃리치를 확인할 수 있습니다.
RAG 지식 베이스
왜 ChromaDB인가?
필요한 것은:
- 로컬 실행 (배포할 외부 서버 없음)
- 디스크에 데이터 영속화 (재시작해도 유지)
- 시맨틱 검색 처리 (키워드 매칭만이 아닌)
- Python 3.13과 호환
ChromaDB 임베디드 모드는 네 가지 요건을 모두 충족합니다. 임베딩에 Sentence Transformers(all-MiniLM-L6-v2)를 사용합니다 — 한 번 다운로드(~80MB), 로컬에 캐시, API 키 불필요.
청킹 전략
knowledge.py의 제품 지식은 ~16개의 시맨틱 청크로 분할됩니다:
- 회사 프로필 — 1 청크
- MV900 — 5 청크 (개요, 기능, 함수, 혜택, 스펙)
- Machine365.Ai — 4 청크 (개요, 기능, 함수, 혜택)
- 통합 솔루션 — 1 청크
- 이상적인 고객 + ROI — 1 청크
- 사례 연구 — 연구당 1 청크 (4개 연구)
왜 하나의 큰 청크가 아닌가? 시맨틱 검색은 집중된 청크에서 더 잘 작동합니다. 에이전트가 "스탬핑 공장을 위한 에너지 모니터링"을 쿼리하면, 에너지 모니터링을 구체적으로 언급하는 Machine365.Ai 함수 청크를 얻습니다 — 그 정보가 묻혀있는 거대한 문서 대신에요.
과거 아웃리치 인덱싱
완료된 모든 검색 실행이 인덱싱됩니다. 이는 피드백 루프를 생성합니다: 파이프라인이 더 많이 실행될수록 리서처가 향후 리서치에 더 많은 맥락을 갖게 됩니다. 이전에 동 피팅 제조업체와 교류했다면, 리서처는 어떤 고충이 공감을 얻었는지, 어떤 딜 규모가 추정되었는지, 어떤 접근 방식이 사용되었는지 알게 됩니다.
더 많이 사용할수록 시스템이 더 똑똑해지는 플라이휠 효과입니다.
실제 출력: 독일 금속 스탬핑 업체 테스트
python run.py proposal "Muster Metall GmbH, Germany"
이전 리서처가 했을 것
레거시 리서처는 "Muster Metall GmbH, Germany"를 받고 이런 브리프를 작성했을 것입니다:
"독일 제조업체로서 해당 기업은 스탬핑 프레스를 운영하고 에너지 비용 상승에 직면하고 있을 가능성이 있습니다..."
일반적입니다. 모호합니다. 추론에 기반합니다.
새로운 리서처가 실제로 찾은 것
"해당 기업은 바덴뷔르템베르크주 소도시에 위치한 90년 역사의 가족 기업으로, 자동차 부문을 위한 금형 제작 및 금속 스탬핑을 전문으로 합니다. 씰 서포트 프레임과 히트 쉴드를 생산합니다. 인증된 에너지 관리 시스템을 보유하고 있습니다..."
구체적입니다. 사실적입니다. 실제 웹사이트에서 출처를 확인했습니다.
이 차이는 전체 파이프라인에 걸쳐 연쇄적으로 작용합니다. 솔루션 아키텍트는 실제 운영에 기능을 매핑합니다. 제안서 작성자는 실제 제품과 인증을 참조합니다. 최종 제안서에는 실제 소재지, 자동차 OEM 고객, 기존 에너지 관리 시스템이 언급됩니다 — 아무리 많은 추론도 만들어내지 못할 세부사항입니다.
아키텍처 결정
왜 리서처만 도구를 받는가
나머지 다섯 에이전트(솔루션 아키텍트, 제안서 작성자, 딜 추정자, 이메일 작성자, 빠른 요약)는 단일 턴 프롬프트 → 응답 호출을 유지합니다. 왜일까요?
도구가 필요 없기 때문입니다. 리서처로부터 풍부한 맥락을 받아 전문화된 추론을 적용합니다:
- 솔루션 아키텍트는 시스템 프롬프트에 전체 제품 지식 베이스를 가지고 있습니다. 조사가 아니라 매핑 작업입니다.
- 제안서 작성자는 리서치 + 솔루션 매핑을 받아 산문을 생성합니다. 검색이 아닌 생성 작업입니다.
- 딜 추정자는 브리프에 가격 공식을 적용합니다. 구조화된 출력, 낮은 온도(0.3).
- 이메일 작성자는 모든 것을 200단어로 압축합니다. 정보 수집이 아닌 창의적 제약입니다.
- 빠른 요약 에이전트는 딜 추정과 회사 정보를 3–8문장의 간결한 적합성 평가로 요약합니다. 전망 고객 파이프라인 테이블에서 빠른 자격 검증에 사용됩니다.
이러한 에이전트에 도구를 추가하면 품질 향상 없이 지연과 비용만 증가합니다. 리서처가 병목입니다 — 입력을 수정하면 전체 파이프라인이 개선됩니다.
왜 최대 5턴인가
각 도구 사용 턴은 API 토큰을 소비합니다(대화 기록이 쌓이면서 입력이 증가). 5턴으로 제한한 이유:
- 대부분의 회사는 3-4턴이면 충분히 조사됩니다
- 5턴을 넘으면 에이전트가 중복 검색을 시작합니다
- 안전 제한은 에이전트가 루프에 빠질 경우 비용 폭주를 방지합니다
왜 에러가 문자열을 반환하는가
scrape_company_website가 예외를 발생시키면, 에이전틱 루프가 예외를 캐치하고, 에러 메시지를 포맷하고, 모델에 다시 피드백해야 합니다. 에러 문자열을 직접 반환하면, 에이전트는 도구 결과에서 에러를 보고 무엇을 할지 결정할 수 있습니다 — 다른 URL을 시도하거나, 웹 검색으로 대체하거나, 브리프에 제한을 기록합니다.
이 패턴 — 절대 충돌하지 않고 에러만 보고하는 도구 — 은 에이전틱 루프 구현을 훨씬 단순하게 만듭니다.
왜 단순한 Dict 대신 ChromaDB인가
제품 지식을 Python dict로 저장하고 키워드 매칭을 할 수도 있었습니다. 하지만:
- 시맨틱 검색이 중요합니다. "스탬핑 공장을 위한 에너지 모니터링"은 단어가 많이 겹치지 않더라도 "AI 기반 에너지 모니터링"과 매칭되어야 합니다.
- 과거 아웃리치는 비정형 텍스트입니다. 리서치 브리프에는 매칭할 고정 키가 없습니다.
- 영속성이 중요합니다. 세션마다 리셋되는 것이 아니라 세션 간에 지식 베이스가 성장하길 원합니다.
ChromaDB 임베디드 모드는 인프라 제로로 이 모든 것을 제공합니다. 서버 실행 불필요, Docker 컨테이너 불필요, 관리형 서비스 비용 불필요.
폴백 설계
ChromaDB가 실패하면(잘못된 Python 버전, 손상된 데이터, 누락된 의존성), 파이프라인은 여전히 작동합니다:
def run_researcher(company_input: str) -> str:
try:
return call_agentic_researcher(company_input)
except Exception as e:
print(f"⚠️ 에이전틱 리서처 실패 ({e}). 레거시 모드로 폴백합니다.")
return call_agent(
system_prompt=RESEARCHER_SYSTEM_LEGACY,
user_message=f"이 회사를 조사하세요: {company_input}...",
agent_name="Prospect Researcher (Legacy)",
)
레거시 리서처(추론만, 도구 없음)는 품질은 낮지만 절대 충돌하지 않습니다. 세일즈 도구에서 이것은 중요합니다 — 최적이 아닌 제안서가 제안서가 없는 것보다 낫습니다.
배운 교훈
1. 에이전틱 리서치는 추가 토큰의 가치가 있다
이전 리서처는 호출당 ~2,000 입력 토큰을 사용했습니다. 새로운 것은 5턴에 걸쳐 ~16,000을 사용합니다. 회사당 8배 비쌉니다.
하지만 출력 품질 차이는 극적입니다. 실제 회사 세부사항, 실제 제품 라인, 검증된 사실. 세일즈 제안서를 생성하는 도구에서 정확성은 전환율에 직접 영향을 미칩니다. 잘못된 산업을 참조하는 제안서를 보내는 것과 비교하면 건당 $0.05 추가 비용은 아무것도 아닙니다.
2. 에이전트의 리서치 전략은 놀라울 정도로 좋다
리서치 순서를 하드코딩하지 않았습니다. 시스템 프롬프트는 지식 베이스부터 시작하고 URL이 있으면 웹사이트를 스크레이핑하라고 제안하지만, 에이전트가 자체 경로를 선택합니다.
관찰한 것:
- 잘 알려진 회사의 경우, 스크레이핑을 건너뛰고 바로 지식 베이스 쿼리로 이동하는 경우가 많음
- 생소한 회사의 경우, 웹 검색과 웹사이트 스크레이핑을 우선시함
- 지식 베이스 쿼리를 전략적으로 사용 — 먼저 과거 아웃리치, 그다음 제품/사례 연구 매칭
- 웹사이트 스크레이핑이 실패하면, 웹 검색을 더 수행하여 적응함
모델은 어떤 정보가 필요하고 어디서 찾아야 하는지 결정하는 데 진정으로 뛰어납니다.
3. ChromaDB + Python 버전 호환성은 실제 함정이다
처음에 Python 3.14에서 구축했습니다. ChromaDB가 pydantic v1 호환성 에러로 충돌했습니다. 해결 방법이 없습니다 — ChromaDB는 Python 3.14에서 작동하지 않는 pydantic.v1.BaseSettings에 의존합니다.
해결책: Python 3.13으로 다운그레이드. 고통스럽지만 필요했습니다. 다른 개발자들도 같은 문제에 부딪힐 것이므로 눈에 띄게 문서화했습니다.
4. 과거 아웃리치 인덱싱이 플라이휠을 만든다
파이프라인을 처음 실행하면 지식 베이스에는 제품 지식만 있습니다. 하지만 몇 번의 검색 실행 후 과거 아웃리치 데이터가 축적되기 시작합니다. 리서처가 이를 참조하기 시작합니다:
"캘리포니아의 동 피팅 제조업체에 대한 과거 아웃리치를 기반으로, 주요 고충은 에너지 비용과 품질 관리였습니다..."
더 많은 사용 → 더 많은 인덱싱된 데이터 → 더 나은 리서치 → 더 나은 제안서. 사용할수록 시스템이 더 똑똑해집니다.
5. 도구 디스패치 패턴은 깔끔하다
def _execute_tool(tool_name: str, tool_input: dict) -> str:
if tool_name == "search_web":
results = search_companies(tool_input["query"], max_results=5)
return format_results(results)
elif tool_name == "query_knowledge_base":
from rag import query_knowledge_base
return query_knowledge_base(tool_input["query"])
elif tool_name == "scrape_company_website":
return scrape_website(tool_input["url"])
else:
return f"Unknown tool: {tool_name}"
모든 도구가 문자열을 반환합니다. 모든 에러가 문자열입니다. 디스패치 함수는 간단한 if/elif입니다. 새 도구(search_linkedin이나 check_crm 같은)를 추가하는 것은 elif 브랜치 하나와 import 하나입니다.
숫자로 보기
제안서 실행당 토큰 사용량
| 에이전트 | 입력 토큰 | 출력 토큰 | 비용 (~) |
|---|---|---|---|
| 에이전틱 리서처 (5턴) | ~16,000 | ~1,400 | $0.06 |
| 솔루션 아키텍트 | ~2,900 | ~1,800 | $0.02 |
| 제안서 작성자 | ~4,900 | ~1,900 | $0.03 |
| 합계 | ~23,800 | ~5,100 | ~$0.11 |
검색 실행당 토큰 사용량 (5개 회사 선택)
| 단계 | 토큰 | 비용 (~) |
|---|---|---|
| 딜 추정 (10개 회사) | ~30,000 | $0.10 |
| 리서치 (5개 회사, 에이전틱) | ~80,000 | $0.30 |
| 이메일 작성 (5개 회사) | ~15,000 | $0.06 |
| 합계 | ~125,000 | ~$0.46 |
이 결과가 대체하는 수 시간의 수동 조사 및 작성 작업과 비교해 보세요.
결론
단일 턴에서 에이전틱 리서치로의 전환은 이 파이프라인에 가한 가장 높은 레버리지의 변화였습니다. 동일한 여섯 에이전트, 동일한 제품 지식, 동일한 출력 형식 — 하지만 이제 기반은 추론 대신 사실 위에 구축되었습니다.
핵심 통찰: 모든 에이전트가 에이전틱일 필요는 없습니다. 리서처만 도구의 혜택을 받습니다. 나머지 다섯 에이전트는 조사자가 아닌 생성자입니다. 적절한 에이전트에게 적절한 도구를 주면 전체 시스템이 개선됩니다.
세 개의 새 파일. 하나의 근본적인 아키텍처 변화. 전망 고객 검색과 배치 제안서를 지원하는 풀 Streamlit UI. 극적으로 나아진 출력.
Repository: agentic-search-engine