커넥터와 웹 검색으로 퀵스타트 생성기 만들기

Polars(Python용 빠른 DataFrame 라이브러리)를 배우고 싶다고 가정해 볼게요. 직접 문서를 읽고 블로그 포스트를 훑어보며 퀵스타트를 조립할 수도 있지만, Mistral에게 적절한 도구 접근권을 주고 대신 처리하게 할 수도 있어요. 이 노트북은 같은 프롬프트를 네 번 보내면서 각각 다른 도구 구성을 사용해서, 모델에 더 나은 소스를 줄수록 출력 품질이 어떻게 향상되는지 보여줘요.

출처: 문서

본문

Polars(Python용 빠른 DataFrame 라이브러리)를 배우고 싶다고 가정해 볼게요. 문서를 읽고, 블로그 포스트를 훑어보고, 퀵스타트를 직접 조립할 수도 있지만 — 올바른 도구에 접근할 수 있는 Mistral에게 맡길 수도 있어요.

이 노트북은 동일한 프롬프트를 네 번 보내되, 각각 다른 도구 구성을 사용해요. 모델에 더 나은 소스를 줄수록 출력 품질이 어떻게 향상되는지 볼 수 있어요.

Step Tools What the model can access
1 None Training data only
2 Web search Blog posts, Stack Overflow, release notes
3 Context7 connector Official Polars documentation
4 Both Docs + web — the model picks the best source per sub-topic
5 Filtered connector A single doc-retrieval tool (skip the resolver)

API 상태 (API status): 이 노트북은 client.beta.connectors와 client.beta.conversations를 사용해요. 이들은 베타(beta) 엔드포인트로 변경될 수 있어요. 최신 API 참조는 Connectors 문서를 참조하세요.

셀을 위에서 아래로 실행하세요. Conversations API의 TypeScript 버전은 레퍼런스 쿡북에서 다룹니다.

사전 준비사항 (Prerequisites)

이 노트북을 완료하려면 다음이 필요해요.

  • Python 3.9 이상
  • Mistral 계정과 API 키

환경 설정 (Environment setup)

아래 셀을 실행해서 Mistral Python SDK를 설치해요.

이 쿡북을 완료하려면 Mistral API 키가 필요해요. Studio에서 API keys 섹션으로 이동해서, Connector access scope에 대해 Private and shared connectors를 선택하고 새 API 키를 만들어요.

클라이언트 셀을 실행하기 전에 다음 옵션 중 하나로 설정하세요.

옵션 1 — 환경 변수 (로컬 사용에 권장):

MISTRAL_API_KEY=your-mistral-api-key

옵션 2 — 프롬프트 때 입력: MISTRAL_API_KEY가 환경에 아직 설정되어 있지 않다면, 다음 코드 셀에서 키를 직접 붙여넣을 수 있는 안전한 입력 필드가 표시돼요.

Python

%pip install mistralai --quiet

SDK를 가져오고 클라이언트를 만들어요. MISTRAL_API_KEY가 환경 변수로 설정되어 있지 않다면, 안전한 입력 프롬프트가 나타나요.

import getpass
import os

from mistralai.client import Mistral

if not os.environ.get("MISTRAL_API_KEY"):
    os.environ["MISTRAL_API_KEY"] = getpass.getpass("Mistral API key: ")

client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])

1단계 — 기준선: 도구 없음 (Step 1 — Baseline: no tools)

먼저 도구 없이 모델에게 Polars 퀵스타트를 생성하도록 요청해요. 모델은 학습 데이터만 사용할 수 있으므로, 지식 컷오프 이후 바뀐 것은 빠지거나 틀릴 거예요.

공정한 비교를 위해 PROMPT를 한 번 정의하고 1~4단계에서 재사용해요.

Python

PROMPT = (
    "Write a Polars quickstart for a developer who knows pandas. "
    "Cover: installation, reading a CSV, filtering rows, groupby aggregation, "
    "and lazy evaluation. End with 3 gotchas when migrating from pandas. "
    "Include runnable code examples."
)

print("--- Step 1: Baseline (no tools) ---\n")
response = await client.beta.conversations.start_async(
    model="mistral-medium-latest",
    inputs=[{"role": "user", "content": PROMPT}],
)
for output in response.outputs:
    if getattr(output, "type", None) == "message.output":
        content = output.content
        if isinstance(content, str):
            print(content)
        else:
            print("".join(getattr(c, "text", str(c)) for c in content))

내장 도구로 web_search를 추가하면 모델이 최신 정보(최근 블로그 포스트, Stack Overflow 답변, 릴리스 노트)를 끌어올 수 있어요. 커넥터 설정은 필요 없어요.

출력을 1단계와 비교해 보세요. 더 최신 구문과 커뮤니티 팁을 볼 수 있을 거예요.

print("--- Step 2: Web search ---\n")
response = await client.beta.conversations.start_async(
    model="mistral-medium-latest",
    inputs=[{"role": "user", "content": PROMPT}],
    tools=[{"type": "web_search"}],
)
for output in response.outputs:
    if getattr(output, "type", None) == "message.output":
        content = output.content
        if isinstance(content, str):
            print(content)
        else:
            print("".join(getattr(c, "text", str(c)) for c in content))

3단계 — Context7 커넥터: 공식 문서 (Step 3 — Context7 connector: official docs)

Context7는 인기 오픈소스 라이브러리의 최신 문서를 제공하는 MCP 서버예요. 인증이 필요 없어서 첫 커넥터로 좋아요.

이 단계는 세 부분으로 나뉘어요.

  • Create: Context7의 MCP 엔드포인트를 가리키는 커넥터를 만들어요.
  • Register credentials: 자격 증명을 등록해요(Context7은 공개 서비스라 비어있지만, 레코드는 존재해야 해요).
  • List tools: 커넥터가 노출하는 도구를 확인해요 (5단계에서 이 이름들을 사용할 거예요).

CONTEXT7_URL = "https://mcp.context7.com/mcp"

connector = await client.beta.connectors.create_async(
    name="quickstart_context7",
    description="Context7 connector — library documentation lookup",
    server=CONTEXT7_URL,
    visibility="private",
)
print(f"Created: {connector.name}  (id={connector.id})")

await client.beta.connectors.create_or_update_user_credentials_async(
    connector_id_or_name=connector.name,
    name=f"{connector.name}-default",
    credentials={"headers": {}},
    is_default=True,
)
print(f"Credentials registered for {connector.name}")

tools_list = await client.beta.connectors.list_tools_async(
    connector_id_or_name=connector.name,
)
print(f"\nTools exposed by {connector.name}:")
DOC_TOOL_NAME = None
for tool in tools_list:
    print(f"  - {tool.name}: {tool.description}")
    if "documentation" in (tool.description or "").lower() or "doc" in tool.name.lower():
        DOC_TOOL_NAME = tool.name

if DOC_TOOL_NAME:
    print(f"\nDoc-retrieval tool for Step 5: {DOC_TOOL_NAME}")
else:
    print("\nNo doc-retrieval tool auto-detected — check tool names above and set DOC_TOOL_NAME manually.")

Context7 커넥터가 연결된 에이전트를 만들어요. 커넥터 도구 호출(tool.execution.started, tool.execution.delta, tool.execution.done)을 실시간으로 스트리밍하고 지켜볼 수 있도록 conversations API 대신 에이전트를 사용해요. 지시사항은 모델이 학습 데이터만으로 의존하지 않고 커넥터를 사용하도록 안내해요.

connector_agent = await client.beta.agents.create_async(
    name="quickstart_context7_agent",
    model="mistral-medium-latest",
    instructions=(
        "You are a helpful programming assistant. "
        "When asked about a library, always use the Context7 connector to look up "
        "the official documentation before answering. Do not rely on training data alone."
    ),
    tools=[{"type": "connector", "connector_id": connector.id}],
)
print(f"Agent ready: {connector_agent.name}  (id={connector_agent.id})")

이제 대화에서 에이전트를 사용해요. 모델이 공식 Polars 문서에 접근하므로 권위 있는 API 참조를 볼 수 있어야 하지만, 블로그에 있는 커뮤니티 팁과 마이그레이션 조언은 놓칠 수 있어요.

Python

print("--- Step 3: Context7 connector (official docs) ---\n")

conversation_id = None
async for event in await client.beta.conversations.start_stream_async(
    agent_id=connector_agent.id,
    inputs=[{"role": "user", "content": PROMPT}],
    timeout_ms=300_000,
):
    data = event.data
    event_type = getattr(data, "type", None)
    if event_type == "conversation.response.started":
        conversation_id = data.conversation_id
    elif event_type == "message.output.delta":
        print(".", end="", flush=True)
    else:
        name = getattr(data, "name", "")
        print(f"\n[{event_type}]{' ' + name if name else ''}")

messages = await client.beta.conversations.get_messages_async(
    conversation_id=conversation_id
)
last_output = next(
    (m for m in reversed(messages.messages)
     if getattr(m, "type", None) == "message.output"),
    None,
)
if last_output:
    print("\n")
    content = last_output.content
    if isinstance(content, str):
        print(content)
    else:
        print("".join(getattr(c, "text", str(c)) for c in content))

4단계 — 두 도구 결합 (Step 4 — Both tools combined)

이것이 핵심이에요. 에이전트를 다시 업데이트해서, 이번에는 웹 검색 그리고 Context7 커넥터를 둘 다 주는 거예요. 모델은 정확한 API 예시를 위해 공식 문서를, 커뮤니티 지혜·마이그레이션 함정·최근 릴리스 노트를 위해 웹 결과를 끌어올 수 있어요. 각 하위 주제에 어떤 소스를 쓸지 스스로 결정해요.

이 출력을 1~3단계와 비교해 보세요. 결합 버전이 눈에 띄게 더 풍부해요.

Python

print("--- Step 4: Web search + Context7 connector ---\n")

combined_agent = await client.beta.agents.update_async(
    agent_id=connector_agent.id,
    tools=[
        {"type": "web_search"},
        {"type": "connector", "connector_id": connector.id},
    ],
)

conversation_id = None
async for event in await client.beta.conversations.start_stream_async(
    agent_id=combined_agent.id,
    inputs=[{"role": "user", "content": PROMPT}],
    timeout_ms=300_000,
):
    data = event.data
    event_type = getattr(data, "type", None)
    if event_type == "conversation.response.started":
        conversation_id = data.conversation_id
    elif event_type == "message.output.delta":
        print(".", end="", flush=True)
    else:
        name = getattr(data, "name", "")
        print(f"\n[{event_type}]{' ' + name if name else ''}")

messages = await client.beta.conversations.get_messages_async(
    conversation_id=conversation_id
)
last_output = next(
    (m for m in reversed(messages.messages)
     if getattr(m, "type", None) == "message.output"),
    None,
)
if last_output:
    print("\n")
    content = last_output.content
    if isinstance(content, str):
        print(content)
    else:
        print("".join(getattr(c, "text", str(c)) for c in content))

5단계 — 도구 필터링 (Step 5 — Tool filtering)

Context7은 여러 도구를 노출해요: 라이브러리 ID를 이름에서 찾는 리졸버(resolver)와 라이브러리 ID로 페이지를 가져오는 문서 검색 도구가 있어요. 라이브러리 ID를 이미 안다면 tool_configuration.include를 사용해 커넥터를 문서 검색 도구로만 제한해서 리졸버를 건너뛸 수 있어요.

에이전트를 마지막으로 업데이트해서, 도구를 단일 필터링된 커넥터로 교체해요. 이 단계는 또한 Polars 라이브러리를 미리 지정해서 리졸버가 불필요하도록 하는, 더 집중된 다른 프롬프트를 사용해요.

Python

FILTERED_PROMPT = (
    "Using the Polars documentation, explain lazy evaluation in Polars. "
    "Cover: what LazyFrame is, how to build a lazy query with .lazy(), "
    "how .collect() triggers execution, and when to prefer lazy over eager. "
    "Include a runnable before/after code example."
)

if DOC_TOOL_NAME:
    print(f"--- Step 5: Filtered connector (only {DOC_TOOL_NAME}) ---\n")

    filtered_agent = await client.beta.agents.update_async(
        agent_id=combined_agent.id,
        tools=[
            {
                "type": "connector",
                "connector_id": connector.id,
                "tool_configuration": {
                    "include": [DOC_TOOL_NAME],
                },
            },
        ],
    )

    conversation_id = None
    async for event in await client.beta.conversations.start_stream_async(
        agent_id=filtered_agent.id,
        inputs=[{"role": "user", "content": FILTERED_PROMPT}],
        timeout_ms=300_000,
    ):
        data = event.data
        event_type = getattr(data, "type", None)
        if event_type == "conversation.response.started":
            conversation_id = data.conversation_id
        elif event_type == "message.output.delta":
            print(".", end="", flush=True)
        else:
            name = getattr(data, "name", "")
            print(f"\n[{event_type}]{' ' + name if name else ''}")

    messages = await client.beta.conversations.get_messages_async(
        conversation_id=conversation_id
    )
    last_output = next(
        (m for m in reversed(messages.messages)
         if getattr(m, "type", None) == "message.output"),
        None,
    )
    if last_output:
        print("\n")
        content = last_output.content
        if isinstance(content, str):
            print(content)
        else:
            print("".join(getattr(c, "text", str(c)) for c in content))
else:
    print("Skipped — DOC_TOOL_NAME was not set. Set it manually from the tool list in Step 3.")

비교 (Comparison)

각 구성이 어떻게 수행됐는지 살펴볼게요.

Step Tools Strengths Weaknesses
1 None Fast, no setup May have outdated syntax, no citations
2 Web search Current info, community tips, migration advice May surface low-quality sources
3 Context7 connector Authoritative API docs, correct signatures Misses community wisdom and gotchas
4 Both Best of both — accurate docs + practical tips Slightly longer response time
5 Filtered connector Precise — skips unnecessary tool calls Requires knowing tool names upfront

정리 (Cleanup)

작업이 끝나면 에이전트와 커넥터를 삭제해요. 2~5단계에서 단일 에이전트를 재사용(매번 도구를 업데이트)했으므로 정리할 에이전트는 하나뿐이에요.

Python

await client.beta.agents.delete_async(agent_id=connector_agent.id)
print(f"Agent deleted: {connector_agent.name}")

result = await client.beta.connectors.delete_async(connector_id=connector.id)
print(f"Connector deleted: {connector.name}  —  {result.message}")

요약 (Summary)

이 노트북은 Mistral 대화에 도구를 추가하면 출력 품질이 점진적으로 어떻게 향상되는지 보여줬어요. 학습 데이터만 사용하는 기준선 응답부터, 웹 검색과 문서 커넥터를 거쳐, 둘을 결합해 가장 풍부한 결과를 얻기까지요.

만든 것 (What you built):

  • 도구를 추가할 때마다 개선되는 Polars 퀵스타트 생성기
  • 공식 라이브러리 문서를 가져오는 Context7 커넥터
  • 불필요한 커넥터 도구를 건너뛰는 필터링된 도구 구성

사용한 Mistral 기능 (Mistral features used):

  • Connectors (beta)
  • Conversations API (beta)
  • Agents API (beta) — 도구 실행 이벤트 스트리밍에 사용
  • Web search 내장 도구
  • Tool filtering (tool_configuration.include)

기타 서비스 (Other services):

  • Context7 — 오픈소스 라이브러리 문서용 MCP 서버

Connector를 Studio에서 확인할 수 있어요.

더 알아보기 (Learn more)