~22 min · read-tool, write-tool, idempotency, human-in-the-loop
Level 0호기심 많은 독자
0 XP0/48 lessons0/14 achievements
0/100 XP to next level100 XP to go0% complete
모든 tool 이 같지 않아. Read tool — 주문 목록, 고객 조회, 가격 확인 — 은 모델이 실수로 10 번 불러도 결과 없어. Write tool — 환불, 이메일 보내기, deploy 예약 — 은 모델이 흥분하면 누군가의 하루를 망쳐. Protocol 이 이 구분을 지켜주지 않아 — 네가 만들어 넣어야 해.
첫 습관은 이름으로 카테고리. Read tool 은 query 처럼 들려야 해: search_orders, get_customer, list_invoices. Write tool 은 동사+대상으로: refund_order, send_email, schedule_deploy. Naming 은 모델이 schema 너머에서 보는 몇 안 되는 신호 — write 에 동사 쓰는 건 정확성 쪽으로 조용한 bias.
둘째 습관은 write tool 의 idempotency key. 네트워크 끊김으로 같은 write 가 두 번 호출되면, 시스템이 감지해서 중복 거절해야 해. 부르는 API 들 보통 이미 지원 (Stripe 의 Idempotency-Key 같은 거); 네 tool 은 conversation context 에서 생성한 그런 key 를 받아 forward 해야 해.
셋째 습관은 위험한 가장자리에 human-in-the-loop. 되돌릴 수 없는 write — 돈 나가기, 메시지 보내기, infra 변경 — 에는 두 step tool 패턴: propose_refund 가 구조화된 proposal 반환, agent 가 사람한테 보여주고, 명시적 승인 후에야 agent 가 proposal ID 로 execute_refund 호출. Protocol 이 구조화된 proposal 을 줘 — 너는 user 한테 승인을 빚져.
이건 MCP 가 명시적으로 가져온 lens 이기도 해: spec 이 세상에 영향 줄 수 있는 tool 을 마크해서 client 가 user 한테 surface 하라고 요구해. Lens 자체는 좋은 API 디자이너들이 이미 하던 거 — MCP 는 그걸 contract 의 일부로 만들었을 뿐.
Code
Two-step write — propose 후 execute·python
{
"name": "propose_refund",
"description": "Build a refund proposal for human review. Does NOT execute.",
"input_schema": {
"type": "object",
"properties": {
"order_id": {"type": "string"},
"amount_cents": {"type": "integer"},
"reason": {"type": "string"}
},
"required": ["order_id", "amount_cents", "reason"]
}
},
{
"name": "execute_refund",
"description": (
"Execute a previously-approved refund. Requires a proposal_id from "
"propose_refund AND the human's explicit approval. Do not call without "
"approval — the user-facing client will reject it."
),
"input_schema": {
"type": "object",
"properties": {
"proposal_id": {"type": "string"},
"approved_by_user": {"type": "boolean"}
},
"required": ["proposal_id", "approved_by_user"]
}
}
경계에서 idempotency·python
import hashlib, requests
def execute_refund(proposal_id, approved_by_user, conversation_id):
if not approved_by_user:
return {"error": "Refund requires explicit user approval."}
idem_key = hashlib.sha256(f"{conversation_id}:{proposal_id}".encode()).hexdigest()
r = requests.post(
"https://api.stripe.com/v1/refunds",
data={"payment_intent": proposal_id_to_intent(proposal_id)},
headers={"Idempotency-Key": idem_key, "Authorization": "Bearer ..."},
)
return r.json()
네가 maintain 하는 (실제든 가상이든) 프로젝트의 모든 tool 적어. 각각 한 글자: pure read 면 R, write 면 W, 되돌릴 수 없는 destruction 이면 D. 모든 D 에 대해 propose/execute split 디자인. Two-step 없는 D 의 개수가 네 남은 숙제야.
Progress
Progress is local-only — sign in to sync across devices.