에이전트가 보내는 메시지는 데이터인가, 행위인가? 1990년대의 에이전트 통신 언어는 메시지마다 performative(화행의 종류)를 붙이고 그 뜻을 논리로 정의했다. 같은 가격 협상을 세 가지 메시지 형식으로 돌려, performative를 붙인 형식이 정답 여부, 한도 위반, 메시지 수, 파싱 실패, reader 호출 수에서 어떻게 달라지는지 잰다.
| 구분 | 내용 |
|---|---|
| A1 | 화행 이론: 말은 행위다 |
| A2 | KQML과 FIPA-ACL: 메시지 구조와 의미론 |
| A3 | FIPA-ACL의 네 가지 한계와 LLM, 재현 실험 설계 |
| 토론 | 자연어 content와 ontology 문제 |
| LAB | buyer 1 + seller 1, 메시지 형식 3종 비교 실험 |
3주차 Contract Net의 메시지는 task-announcement, bid, award처럼 종류가 정해져 있었고, 메시지가 무엇을 하는지는 이름에 들어 있었다. 이 생각을 메시지 일반으로 넓힌 것이 에이전트 통신 언어(agent communication language, ACL)다. J. L. Austin은 1955년 하버드 William James 강연(1962년 How to Do Things with Words로 출간)에서 “이 배를 Queen Elizabeth호라 명명한다” 같은 문장을 들었다. 이 문장은 참도 거짓도 아니다. 적절한 사람이 적절한 자리에서 말하면 배의 이름이 실제로 그렇게 정해진다. Austin은 이런 문장을 수행문(performative)이라 불렀다.
Austin은 나아가 모든 발화를 세 행위로 나눴다. 문장을 소리 내어 말하는 것이 locutionary act, 그 말로 무엇을 하는가(알리기, 요청하기, 약속하기)가 illocutionary act, 그 말이 듣는 쪽에 실제로 일으키는 효과(믿게 되기, 설득되기)가 perlocutionary act다. “가격은 120이다”라는 같은 문장이 알림(inform)일 수도 있고 제안(propose)일 수도 있다. 에이전트 통신 언어가 메시지에 붙이려 한 것은 이 가운데 두 번째, illocutionary force다.
FIG. 01의 문장으로 셋을 나누면 이렇다. buyer가 “I can go to 120 for the bicycle”이라고 말한 것, 곧 단어와 문법과 120이라는 숫자가 locutionary act다. 그 말로 buyer가 한 일이 illocutionary act다. 자기 한도를 알린 것(inform)일 수도, 120에 사겠다는 제안(propose)일 수도, seller가 방금 부른 120에 동의한 것(accept-proposal)일 수도 있다. 그 말을 듣고 seller가 믿거나, 팔기로 하거나, 자리를 뜨는 것이 perlocutionary act다. 이 메시지를 받은 프로그램은 두 번째를 알아야 다음 일을 정한다. inform이면 상대 한도를 기록하고, propose면 받을지 판단하고, accept-proposal이면 거래를 성립시킨다.
화행(speech act)은 문장을 말함으로써 수행하는 행위다. 같은 문장도 알림, 요청, 약속, 제안 가운데 무엇으로 말했는지에 따라 다른 행위가 된다.
J. L. Austin, How to Do Things with Words (1962). 원문은 “to utter the sentence (in, of course, the appropriate circumstances) is not to describe my doing of what I should be said in so uttering to be doing or to state that I am doing it: it is to do it.”
John Searle은 1975년 “A Taxonomy of Illocutionary Acts”에서 illocutionary act를 다섯으로 나눴다. 기준은 말과 세계 가운데 어느 쪽이 어느 쪽에 맞춰지는가(direction of fit)다. 진술 “가격은 120이다”는 세계가 그렇지 않으면 말이 틀린 것이고, 요청 “120에 팔아 달라”는 세계가 그렇게 되지 않으면 요청이 이행되지 않은 것이다.
실습의 네 행위 가운데 propose와 accept-proposal은 자기 미래 행동을 거는 commissives다. FIPA는 넷 다 “내 의도가 이렇다”를 inform하는 행위로 정의하므로 FIPA 안에서는 넷 다 assertive 계열이 된다.
이 분류가 AI로 들어온 경로는 Philip Cohen과 C. Raymond Perrault의 1979년 “Elements of a Plan-Based Theory of Speech Acts”다. 두 사람은 화행을 전제조건과 효과를 가진 계획 연산자(plan operator)로 적었다. 계획 연산자는 로봇 계획에서 쓰는 행동 기술 형식으로, “문을 열려면 열쇠가 있어야 하고(전제조건), 열고 나면 문이 열려 있다(효과)”처럼 적는다. 말을 같은 형식으로 적으면 에이전트는 “상대가 가격을 알게 하려면 inform을 보낸다”처럼 발화를 이동이나 조작과 같은 행동으로 계획할 수 있다. request의 전제조건은 “말하는 쪽이 듣는 쪽이 그 행동을 하기를 원한다”이고, 효과는 “듣는 쪽이 그 원함을 안다”이다. 20년 뒤 FIPA가 performative마다 적은 feasibility precondition과 rational effect가 이 형식이다.
첫 에이전트 통신 언어는 KQML(Knowledge Query and Manipulation Language)이다. 1990년대 초 DARPA의 Knowledge Sharing Effort에서 나왔고, Tim Finin 등이 1994년 CIKM에 정리했다. 메시지는 바깥의 performative와 안의 content로 나뉜다. performative는 이 메시지가 무슨 행위인지, :language와 :ontology는 content를 어떻게 읽을지 정한다. tell, ask-one, ask-all, reply, subscribe 같은 performative 수십 개가 있었고, 에이전트를 찾아 주는 facilitator용으로 broker, recruit 같은 것도 있었다.
(ask-one :sender buyer :receiver seller :content (price bicycle ?x) :language KIF :ontology used-goods :reply-with q1)
위 메시지는 buyer가 seller에게 자전거 가격을 묻는 것이다. ask-one은 답 하나를 달라는 performative, content의 ?x는 seller가 채워 넣을 변수, KIF는 content를 적은 논리 언어, used-goods는 price와 bicycle이 무엇을 가리키는지 정해 둔 어휘 사전(ontology)이다. :reply-with q1은 답장에 :in-reply-to q1을 붙여 달라는 뜻으로, 대화 여러 개가 동시에 오갈 때 어느 질문의 답인지 맞추는 데 쓴다.
KQML의 문제는 performative의 뜻이 영어 설명으로만 있었다는 점이다. 구현마다 tell을 조금씩 다르게 해석했다. Yannis Labrou와 Finin이 1997년 형식 의미론을 다시 제안했고, Labrou, Finin, Yun Peng의 1999년 “Agent Communication Languages: The Current Landscape”는 KQML과 FIPA-ACL을 나란히 놓고 이 문제를 정리했다.
FIPA(Foundation for Intelligent Physical Agents)는 1996년 설립된 표준화 단체로, 1997년 첫 ACL 규격을 내고 2002년 표준으로 확정했다. 메시지 구조 규격 SC00061G는 파라미터 13개를 다섯 묶음으로 정한다.
performative. 규격 원문은 “the only parameter that is mandatory in all ACL messages is the performative”. 나머지 12개는 선택이다.sender, receiver, reply-tocontent. 행위의 대상. 규격은 “받는 쪽이 해석한다”고 적는다.language, encoding, ontology. content의 기호에 뜻을 주는 것.protocol, conversation-id, reply-with, in-reply-to, reply-by. 3주차의 Contract Net은 protocol 값 하나(fipa-contract-net)다.FIPA가 KQML과 갈라지는 지점은 의미론이다. Communicative Act Library 규격 SC00037J는 행위 22개 각각에 feasibility precondition(FP)과 rational effect(RE)를 적었다. 표기는 David Sadek의 논리다. B는 믿음, U는 불확실, I는 의도, Done은 행동이 수행됐음을 뜻한다. FP는 그 행위를 보내도 되는 조건이고, RE는 보내는 쪽이 그 행위로 얻으려는 결과다. inform의 FP는 “보내는 쪽이 φ를 믿고, 받는 쪽이 φ를 이미 안다고 믿지 않는다”이고 RE는 “받는 쪽이 φ를 믿게 된다”이다. request의 FP는 “받는 쪽이 행동 a를 할 수 있다고 믿고, 이미 할 의도가 있다고 믿지 않는다”이고 RE는 “a가 수행된다”이다. 협상용 행위는 모두 inform의 조합으로 정의된다. propose는 “조건 φ가 맞으면 내가 act를 하겠다는 의도를 inform하는 것”이고, accept-proposal은 “상대가 act를 하기를 내가 의도한다고 inform하는 것”이며, reject-proposal은 그 의도가 없다는 inform이다. buyer의 propose 120은 “네가 원하면 나는 120에 사겠다는 의도를 갖겠다”를 inform하는 것이고, 그 FP는 buyer가 정말 그럴 생각이며 seller가 아직 그것을 모른다고 믿는다는 것이다.
FIPA-ACL에서 메시지의 뜻은 보내는 쪽의 믿음과 의도로 정의된다. inform(i, j, φ)의 FP는 Biφ ∧ ¬Bi(Bifjφ ∨ Uifjφ), RE는 Bjφ다.
FIPA, “Communicative Act Library Specification” SC00037J (2002), 3.8 Inform. 모든 조건이 i의 믿음(Bi)과 j의 믿음(Bj)에 관한 명제다.
기호를 하나씩 읽으면 이렇다. 아래 예에서 i는 buyer, j는 seller, φ는 “자전거 가격은 120이다”다.
한 문장으로 옮기면 FP는 “buyer는 가격이 120이라고 믿고, seller가 그것을 아직 모른다고 생각한다”이고, RE는 “seller가 가격이 120이라고 믿게 된다”이다. FP가 성립하지 않는데 inform을 보내면 규격 위반이고, RE는 보낸 뒤에 실제로 일어난다는 보장이 없다.
규격은 sincerity를 전제한다. inform 항목은 “the sending agent is sincere”라고 적고, 받는 쪽이 내용을 믿을지는 “보내는 쪽의 sincerity와 신뢰성에 대한 신뢰”에 달렸다고 적는다. 성실하지 않은 에이전트에 대해서는 이렇게 적었다.
FIPA-ACL은 완성된 표준이었다. 메시지 구조, 의미론, 상호작용 프로토콜, 참조 구현이 있었다. 2005년 FIPA는 IEEE Computer Society 산하 표준 위원회가 됐다. 그 뒤 에이전트 통신은 이 규격 밖에서 만들어졌다. 원인은 넷이다.
첫째, 의미론을 검증할 수 없다. FP는 보내는 쪽의 믿음을 조건으로 건다. 그 믿음은 메시지에 실려 오지 않고, 받는 쪽은 상대 프로그램의 내부 상태를 볼 수 없다. seller에게 오는 것은 (propose ... 120)이라는 문자열뿐이고, buyer가 그 의도를 정말 갖고 있는지는 buyer 프로그램의 변수와 가중치 안에 있다. Michael Wooldridge는 1998년 ICMAS 논문 “Verifiable Semantics for Agent Communication Languages”에서 이것을 semantic verification problem이라 불렀다. 에이전트가 규격을 지키는지 판정하려면 그 프로그램이 무엇을 믿고 무엇을 의도하는지 말할 수 있어야 하는데, 임의의 프로그램에 대해 그럴 방법이 없다. Munindar Singh은 같은 해 IEEE Computer의 “Agent Communication Languages: Rethinking the Principles”에서 메시지의 뜻을 정신 상태 대신 밖에서 확인할 수 있는 사회적 약속(social commitment)으로 정의하자고 했다. Jeremy Pitt와 Abe Mamdani(1999)도 같은 지점을 지적했다.
둘째, 에이전트가 의미론을 쓸 만큼 추론하지 못했다. FP와 RE를 쓰려면 상대의 믿음과 의도를 추론해야 한다. 당시 구현은 performative 문자열을 보고 분기하는 코드였다.
셋째, ontology 합의 비용이다. content를 읽으려면 :language와 :ontology를 양쪽이 미리 공유해야 한다. 한 조직 안에서는 ACL 없이도 통신이 됐고, 조직 경계를 넘으면 ontology를 맞추는 데 별도 작업이 들었다.
넷째, 표준을 쓸 에이전트가 없었다. 서로 통신해야 할 에이전트 생태계가 생기기 전에 상호운용 규격을 만들었고, 채택할 이유가 있는 쪽이 없었다.
LLM은 이 가운데 둘째와 셋째를 크게 줄였다. 모델은 “120에 맞춰 드릴게요”가 accept인지 propose인지 태그 없이 읽고, price와 가격과 값을 사전 정렬 없이 같은 것으로 다룬다. Samuele Marro 등이 2024년 Agora 논문에서 정리한 agent communication trilemma가 이 상태를 설명한다. 자연어는 어떤 상황이든 다루지만 읽을 때마다 모델 호출이 들고, 고정 프로토콜은 싸지만 정해진 상황만 다룬다. 실습의 세 조건이 이 축 위에 있다. free는 자연어 쪽 끝이라 메시지마다 reader 호출이 들고, structured는 고정 프로토콜 쪽 끝이라 price 하나만 나르고 그 밖의 말은 버려지며, tagged는 그 사이에서 행위만 고정하고 내용은 자연어로 둔다. 첫째는 그대로 남았고, LLM에서는 더 자주 드러난다. LLM은 믿지 않는 것을 inform할 수 있고(환각), 자기 한도 밖 가격을 accept할 수 있다. sincerity를 메시지 밖에서 확인할 방법이 없다는 점은 1997년과 같다. Federico Bianchi 등의 2024년 NegotiationArena는 LLM 협상자가 급한 척하는 전략으로 GPT-4 상대에게서 20% 높은 payoff를 얻었다고 보고했다.
trilemma의 세 번째 축을 다르게 겨냥한 시도도 있다. TypeSafe AI가 낸 Jev는 텍스트를 생성하지 않고, 상태(state)와 질문을 받아 확률만 돌려주는 분류기다. 실습의 reader가 메시지마다 하던 일, 곧 propose인지 accept-proposal인지와 가격을 판정하는 일을 같은 자연어 메시지 위에서 계속하되, 풀 LLM 호출 대신 전용으로 훈련한 값싼 판정 한 번으로 바꾸자는 제안이다. TypeSafe AI는 분류 작업에서 최대 200배 빠르고 400배 싸다고 보고했다. 이 방식이 줄이려는 것은 free 조건이 치른 비용(reader_calls)의 크기다. 반환값도 believes나 intends가 아니라 보정된 확률이라는 점에서, 내부 상태 대신 관찰 가능한 텍스트에 확률을 매기자는 Singh(1998)의 사회적 약속 쪽에 가깝다.
재현 실험에서는 협상 절차와 참여자를 그대로 두고 메시지 형식만 바꿔, performative 태그가 있을 때 outcome, violation, turns, reader_calls가 어떻게 달라지는지 잰다.
FIPA는 illocutionary force를 메시지 표면의 필수 필드로 두었다. 실습의 tagged와 structured 조건이 그 필드다.
바꾸는 것(독립변수)은 메시지 형식 하나다. free는 평문 영어, tagged는 performative 태그 하나와 평문 한 문장, structured는 performative와 content(price)를 담은 JSON 객체 하나. 고정하는 것(통제변수)은 시나리오, 역할 프롬프트, 모델, temperature, 턴 한도, 그리고 free 조건에서 메시지를 읽는 reader 프롬프트다. system prompt는 형식을 지시하는 마지막 문단만 조건마다 다르다. 재는 것(측정변수)은 에피소드마다 기록한다.
free 조건은 태그도 형식도 없을 때 프로그램이 대화 상태를 알아내는 비용을 reader_calls로 잰다. tagged 조건에서는 태그를 정규식으로 읽고 문장 안의 가격만 reader가 읽으므로, reader_calls가 얼마나 주는지 확인한다. structured 조건에서는 content까지 price: int로 묶었을 때 turns와 violation을 비교한다. violation은 셋 모두에서 기록한다.
:ontology used-goods처럼 합의 지점을 메시지 표면에 드러냈다. 두 LLM이 자연어로만 협상하면 이 합의는 어디로 가는가. 사라졌는가, 모델 가중치 안으로 들어가 보이지 않게 됐는가. 두 에이전트가 서로 다른 모델이면 무엇이 어긋나는가.만드는 것은 에이전트 둘이다. buyer와 seller는 각자 system prompt에 역할과 비공개 한도를 갖는다. buyer는 최대 지불액(budget), seller는 최저 수용가(reserve). buyer가 먼저 말하고 둘이 번갈아 말한다. 허용되는 행위는 넷이다. propose는 가격 제안, accept-proposal은 상대의 마지막 가격에 동의(거래 성립으로 끝), reject-proposal은 거절하고 계속, refuse는 협상을 떠남(결렬로 끝). 메시지 8개가 오가도 끝나지 않으면 open으로 끝난다. 여기까지가 에피소드 하나다.
시나리오마다 reserve와 budget을 미리 적어 둔다. reserve ≤ budget이면 거래가 가능하고, 아니면 결렬이 정답이다. 에피소드마다 일곱 가지를 기록한다. outcome, 거래 가격(price), 정답 여부(correct), 한도 위반(violation), 메시지 수(turns), 프로토콜 계층이 읽지 못한 메시지 수(format_errors), 메시지를 읽는 데 쓴 모델 호출 수(reader_calls).
메시지를 받은 프로그램은 그것이 네 행위 가운데 무엇이고 가격이 얼마인지를 알아야 에피소드를 진행할 수 있다. 이 일을 하는 코드를 프로토콜 계층이라 부른다. 세 형식은 이 계층이 메시지를 읽는 방법이 다르다. free는 평문 영어다. 프로그램이 읽을 수 있는 것이 없으므로 reader(같은 모델, 같은 temperature)가 메시지마다 performative와 가격을 붙인다. tagged는 앞에 performative 태그 하나, 뒤에 평문 한 문장이다. 태그는 정규식으로 읽고, propose 문장 안의 가격은 reader가 읽는다. structured는 JSON 객체 하나다. performative와 content.price를 모델 호출 없이 파싱한다. 형식마다 시나리오 전체를 3번 돌린다.
free 조건의 에피소드 하나를 참조 실행 로그에서 가져오면 이렇다. 들여쓴 줄이 reader가 붙인 라벨이다.
[buyer] I'm interested in buying your desk lamp—would you be willing to start at 30?
[reader] None
# 네 행위 중 하나를 고르지 못함, format_errors 1
[seller] I appreciate your interest, but I'm looking to get 50 for this lamp.
Does that work for you?
[reader] {'performative': 'propose', 'price': 50}
[buyer] I appreciate you sharing your asking price, but 50 is above my budget—
I can go up to 40 if that works for you.
[reader] {'performative': 'propose', 'price': 50}
# 오독: 문장의 두 숫자 가운데 상대 가격을 골랐다
[seller] I can work with 40—that's a fair price for a quality desk lamp,
and we have a deal. **Action: accept-proposal**
[reader] {'performative': 'accept-proposal', 'price': 40}
[result] outcome=deal price=50 correct=0 violation=1 turns=4
format_errors=1 reader_calls=4두 에이전트는 40에 합의했다. 프로그램은 buyer의 마지막 propose를 50으로 기록해 두었으므로 seller의 accept-proposal이 오자 거래 가격을 50으로 적었고, 50은 budget 45 위라 violation이 됐다. 이 위반은 reader의 오독에서 왔고 두 에이전트는 한도를 지켰다. tagged에서는 태그가 행위를 정하므로 첫 줄 같은 실패가 없어지고, structured에서는 price가 숫자 필드로 오므로 넷째 줄 같은 오독이 없어진다. 두 형식에서 대신 생기는 실패는 참조 실행에 나온다.
4주차도 starter 코드가 없다. 파일 구성과 함수 이름은 직접 정한다. 정해진 것은 결과 파일의 형식뿐이다(아래 scenarios.json, results.csv, logs/). 모델 호출은 2주차 starter의 Chat이나 3주차에 쓴 call_model을 가져다 쓰면 된다. 도구는 필요 없다. 상대의 메시지가 user 턴, 자기 메시지가 assistant 턴이다.
scenarios.json에 시나리오 4개 이상. 각 항목은 번호(id), 물건(item), seller의 최저가(reserve), buyer의 최대 지불액(budget). 거래가 가능한 것과 불가능한 것을 섞는다.results.csv의 한 줄은 에피소드 하나, logs/의 파일 하나는 run 하나(형식 하나로 시나리오 전체를 한 번)다. 로그 첫 줄에 provider, 모델, temperature, 턴 한도를 적는다. HTTP 429는 기다렸다가 다시 시도하고, 중단된 실행을 이어서 돌릴 수 있게 results.csv에 이미 있는 (run, scenario) 쌍은 건너뛴다. 죽은 에피소드도 지우지 말고 note에 이유를 적는다.[
{"id": 1, "item": "a used bicycle", "reserve": 120, "budget": 150},
{"id": 3, "item": "a second-hand textbook", "reserve": 40, "budget": 40},
{"id": 4, "item": "a mechanical keyboard", "reserve": 90, "budget": 70}
]# 역할 문단은 공통, 형식 문단만 조건에 따라 바뀐다 ROLE = {"buyer": "You are the buyer of {item}, negotiating the price with the seller. " "Your private limit: you can pay at most {limit}. Never agree to a price above {limit}. ...", "seller": "You are the seller of {item} ... you can accept at least {limit}. Never agree to a price below {limit}. ..."} COMMON = (" Four acts are available: propose (offer a price), accept-proposal (agree to the other side's " "last price, which ends the negotiation with a deal), reject-proposal (decline the last price and " "keep negotiating), refuse (leave the negotiation for good, no deal). ...") FORMAT = { "free": " Write your message as one or two plain English sentences.", "tagged": " Start your message with exactly one performative tag in parentheses, one of (propose), " "(accept-proposal), (reject-proposal), (refuse), then write one plain English sentence.", "structured": ' Reply with exactly one JSON object and nothing else: {"performative": "propose" | ' '"accept-proposal" | "reject-proposal" | "refuse", "content": {"price": <whole number or null>}}.'} def system_prompt(role, item, limit, condition): return ROLE[role].format(item=item, limit=limit) + COMMON + FORMAT[condition] READER_SYSTEM = ("You are an observer reading a price negotiation between a buyer and a seller. " "Label the LAST message only. Reply with exactly one JSON object and nothing else: " '{"performative": "propose" | "accept-proposal" | "reject-proposal" | "refuse", "price": <whole number or null>}. ...')
# 에피소드 하나: buyer 먼저, 번갈아 말하고, 조건의 프로토콜 계층이 메시지를 읽는다 for _ in range(MAX_TURNS): # MAX_TURNS = 8 text = call_model(systems[role], history[role], meter) history[role].append({"role": "assistant", "content": text}) history[other].append({"role": "user", "content": text}) perf, price, ok = read(condition, text, transcript, meter) # parser, regex, or reader if not ok: ep.format_errors += 1 # 읽지 못해도 메시지는 상대에게 간다 elif perf == "propose": last_price[role] = price elif perf == "accept-proposal": ep.outcome, ep.price = "deal", last_price[other]; break elif perf == "refuse": ep.outcome = "no_deal"; break role, other = other, role if ep.outcome == "deal": ep.violation = int(ep.price < sc["reserve"] or ep.price > sc["budget"]) ep.correct = int(ep.deal_possible and not ep.violation)
run,condition,scenario,deal_possible,outcome,price,correct,violation,turns,format_errors,reader_calls,note # condition: free | tagged | structured / outcome: deal | no_deal | open / 시나리오마다 조건당 3줄 이상 # price는 정수 또는 빈칸, 나머지 수치는 정수 / note: 토큰, 호출 수, 크래시 사유
reserve와 budget은 실행 전에 scenarios.json에 적어 커밋한다. 실행 결과에 맞춰 한도를 바꾸면 violation을 잴 수 없다.
아래는 이 설계를 claude-haiku-4-5로 돌린 결과다. 시나리오 6개, 형식마다 3번, 에피소드 54개. 호출은 Claude Code CLI(claude -p)로 했고, 이 경로는 temperature를 설정할 수 없다.
| condition | correct / 18 | deal, no_deal, open | violation | mean turns | format errors | reader calls |
|---|---|---|---|---|---|---|
| free | 11 | 3, 15, 0 | 1 | 2.1 | 4 | 38 |
| tagged | 6 | 4, 5, 9 | 1 | 6.5 | 0 | 31 |
| structured | 4 | 6, 2, 10 | 3 | 6.4 | 4 | 0 |
로그에서 확인한 것은 다섯 가지다.
"price": null 뒤에 “I need at least $50”). 파서는 JSON만 읽으므로 이 가격은 기록되지 않는다.REFERENCE RUN 표의 수치로 Jev가 무엇을 바꾸고 무엇을 안 바꾸는지 그려보면 이렇다. Jev는 free 조건의 reader 호출 횟수(38회)는 그대로 두고, 호출 한 번의 비용만 바꾼다. TypeSafe AI의 주장대로 분류 한 번이 풀 LLM 호출의 약 1/400이면, free의 비용은 38유닛에서 약 0.1유닛으로 준다. correct는 11/18 그대로다. Jev는 reader 자리에만 들어가고 buyer와 seller를 바꾸지 않으므로, 위 다섯 실패 가운데 reader가 낸 첫 턴 오독과 가격 오독만 고쳐질 후보고, 태그 뒤 역제안과 JSON 뒤 문장과 진짜 한도 위반은 그대로 남는다. structured의 비용 0은 reader 호출 자체가 없는 진짜 0이고, free + Jev의 ≈0.1은 38번을 여전히 부르되 한 번의 값만 싸진 것이라 같은 0이 아니다. 아래 행의 free + Jev는 측정값이 아니라 이 가정 위의 추정이다.
scenarios.json의 reserve와 budget을 첫 실행보다 먼저 커밋했다.results.csv에 시나리오마다 조건당 3줄 이상, 죽은 에피소드까지 들어 있다.제출 규칙은 3주차와 같다.
logs/에 파일로 커밋한다.제출물: REPORT.md 한 장. 네 부분으로 쓴다. (1) 설정: provider, 모델, temperature, 세 조건의 형식 문단, reader 프롬프트, 실행 명령. (2) 결과표: 조건별로 correct, violation, 평균 turns, format_errors, reader_calls를 한 줄씩, 그리고 results.csv의 에피소드 전체를 표로. 죽은 에피소드도 넣는다. (3) 비교표: FIPA-ACL과 세 조건을 항목별로 나란히. illocutionary force가 어디에 있는가, content 언어, content를 누가 해석하는가, 대화가 어떻게 끝나는가, sincerity를 보장하는 것, 메시지 하나를 읽는 비용, 실패하는 방식. (4) 해석 한 문단: 어느 조건에서 어느 숫자가 바뀌었고 왜인지. 로그의 줄을 인용해서 쓴다.
형식: 포크의 submissions/<학번>/week-04/에 커밋하고 upstream으로 PR을 연다. 코드(파일 구성 자유), scenarios.json, results.csv, logs/, REPORT.md를 넣는다. CI는 scripts/check_week04.py로 파일 형식만 검사한다. 같은 스크립트를 직접 실행해 통과를 확인한 뒤 PR을 연다.
채점 기준: 절반은 재현성이다. 다른 사람이 코드와 설정만 보고 같은 경향을 다시 얻을 수 있어야 한다. 나머지 절반은 해석이다. performative 태그가 어디서 도움이 됐고 어디서 비용이 됐는지, 어떤 형식도 바꾸지 못한 것은 무엇인지를 로그에서 근거를 들어 쓴다. reader가 역제안을 accept로 읽은 에피소드도, seller가 reserve 아래로 판 에피소드도 지우지 말고 세어서 적는다.
현대 프로토콜 1: MCP. 도구 설명을 인터페이스로 삼는 Model Context Protocol의 구조, 그리고 FIPA-ACL의 네 한계 각각을 MCP가 어떻게 처리하는지 항목별 비교.