👥 CrewAI and AutoGen · CrewAI และ AutoGen · CrewAI 和 AutoGen

Two more Python agent libraries. One gives every agent a job title and runs them as a crew. The other lets agents talk to each other. Both on your own machine, with a free model. · ไลบรารี agent ของ Python อีกสองตัว ตัวหนึ่งให้ตำแหน่งงานกับ agent ทุกตัวแล้วรันเป็นทีม อีกตัวให้ agent คุยกันเอง ทั้งคู่รันบนเครื่องของคุณเอง ด้วยโมเดลฟรี · 另外两个 Python 智能体库。一个给每个智能体一个职位,让它们像团队一样干活;另一个让智能体互相交谈。两个都能用免费模型跑在你自己的电脑上。

🇬🇧 English

In Build an AI Agent with smolagents one agent did the work: it wrote Python, ran it, and answered. That is the smallest useful shape.

Two other libraries take a different road. CrewAI gives every agent a job title, a goal and a task, and runs them like a small team. AutoGen, from Microsoft, lets agents hold a conversation, taking turns until the job is done.

Rather than make them do the same job, this page gives each the job it is shaped for. CrewAI gets a small software team — a manager, a programmer and a tester — and the team hands back a finished HTML file you can open. AutoGen gets a table-top game: a storyteller sets a scene, two characters say what they do, and the storyteller decides how it ends. Watch for the same trick in both: the thing that has to be true — the checklist, the dice — is written in Python, and only the describing is left to the model.

This page builds the smallest working example of each, on the same free qwen3 model in Ollama, and then puts all three libraries side by side.

🇹🇭 ไทย

ใน สร้าง AI Agent ด้วย smolagents มี agent ตัวเดียวทำงานทั้งหมด มันเขียน Python รันเอง แล้วตอบ นั่นคือรูปแบบที่เล็กที่สุดที่ใช้งานได้จริง

อีกสองไลบรารีเลือกทางที่ต่างออกไป CrewAI ให้ตำแหน่งงาน เป้าหมาย และงานที่ต้องทำกับ agent ทุกตัว แล้วรันเหมือนทีมเล็ก ๆ ส่วน AutoGen จาก Microsoft ให้ agent สนทนากัน ผลัดกันพูดจนกว่างานจะเสร็จ

แทนที่จะให้ทั้งสองทำงานเดียวกัน หน้านี้ให้แต่ละตัวทำงานที่มันถูกออกแบบมาเพื่อสิ่งนั้น CrewAI ได้ทีมซอฟต์แวร์เล็ก ๆ คือผู้จัดการ โปรแกรมเมอร์ และผู้ทดสอบ แล้วทีมส่งไฟล์ HTML ที่เปิดดูได้จริงกลับมา ส่วนAutoGen ได้เกมกระดาน ผู้เล่าเรื่องตั้งฉาก ตัวละครสองตัวบอกว่าจะทำอะไร แล้วผู้เล่าเรื่องตัดสินว่าจบอย่างไร ลองสังเกตกลเม็ดเดียวกันในทั้งสองตัวอย่าง สิ่งที่ต้องเป็นความจริง ทั้งรายการตรวจและลูกเต๋า ถูกเขียนไว้ใน Python ส่วนที่เหลือให้โมเดลทำคือการบรรยายเท่านั้น

ทั้งหมดรันบนโมเดล qwen3 ฟรีตัวเดียวกันใน Ollama แล้วเอาทั้งสามไลบรารีมาวางเทียบกัน

🇨🇳 中文

用 smolagents 做一个 AI 智能体里,只有一个智能体在干活:它写 Python、运行、回答。那是最小的、能用的形态。

另外两个库走了不同的路。CrewAI 给每个智能体一个职位、一个目标和一项任务,像一个小团队那样运行。微软的 AutoGen 则让智能体互相对话,轮流发言,直到把事情做完。

这一页没有让两个库做同一件事,而是各给它们一件本来就合适的活。CrewAI 拿到一个小型软件团队 —— 经理、程序员、测试员 —— 团队交回一个你可以直接打开的 HTML 文件。AutoGen 拿到一场桌游:说书人搭好场景,两个角色说出自己要做什么,再由说书人裁定结局。注意两个例子里同一个手法:必须为真的那部分 —— 清单、骰子 —— 是用 Python 写死的,留给模型的只有描述。

全部跑在同一个 Ollama 的免费 qwen3 模型上,然后把三个库并排比较。

BEFORE YOU START Each of these libraries brings a long list of packages with it: our CrewAI environment came to 779 MB in 138 packages, and AutoGen to 60 MB in 27 packages. Give each one its own virtual environment, and keep smolagents in a third, so a version one needs cannot break another. Both examples below talk to the same Ollama you already have from Make Your Own AI Teacher. ไลบรารีทั้งสองตัวนี้พาแพ็กเกจมาด้วยเป็นรายการยาว ของเรา CrewAI รวมแล้ว 779 MB in 138 packages ส่วน AutoGen 60 MB in 27 packages ให้แต่ละตัวมี virtual environment ของตัวเอง และเก็บ smolagents ไว้อีกอันหนึ่ง เวอร์ชันที่ตัวหนึ่งต้องใช้จะได้ไม่ไปพังอีกตัว ตัวอย่างทั้งสองข้างล่างคุยกับ Ollama ตัวเดิมที่คุณมีอยู่แล้วจาก สร้างครู AI ของคุณเอง 这两个库都会带来一长串依赖包:我们的 CrewAI 环境有 779 MB in 138 packages,AutoGen 有 60 MB in 27 packages。给每个库各自一个虚拟环境,smolagents 再放第三个,这样一个库需要的版本就不会弄坏另一个。下面两个例子都连的是你在做一个你自己的 AI 老师里已经装好的那个 Ollama。

1 👷 CrewAI — a team that ships a file · CrewAI — ทีมที่ส่งไฟล์ออกมาจริง · CrewAI — 交得出一个文件的团队

🇬🇧 English

CrewAI describes agents the way a manager describes staff. Every agent gets a role (a job title), a goal, and a backstory that sets its manner. Work is described separately, as a Task with an expected_output. A Crew holds the agents and the tasks and runs them in order.

Here that order is a small software team: the manager writes down what the page must contain, the programmer writes the HTML, and the tester checks the HTML against the manager's list and returns a corrected file. Each task receives the earlier ones through context=[...], which is how the tester can see both the checklist and the code.

The point of the last step is that the crew hands you a file, not a chat. The script saves the tester's answer as revision.html, which you can open.

🇹🇭 ไทย

CrewAI อธิบาย agent แบบเดียวกับที่ผู้จัดการอธิบายพนักงาน แต่ละ agent มี role (ตำแหน่งงาน) goal (เป้าหมาย) และ backstory ที่กำหนดลักษณะการทำงาน ส่วนงานอธิบายแยกต่างหากเป็น Task ที่มี expected_output แล้ว Crew ก็เก็บทั้ง agent และงานไว้ และรันตามลำดับ

ที่นี่ลำดับนั้นคือทีมซอฟต์แวร์เล็ก ๆ ผู้จัดการเขียนว่าหน้าเว็บต้องมีอะไรบ้าง โปรแกรมเมอร์เขียน HTML และผู้ทดสอบตรวจ HTML เทียบกับรายการของผู้จัดการแล้วส่งไฟล์ที่แก้แล้วกลับมา แต่ละงานรับงานก่อนหน้าผ่าน context=[...] ผู้ทดสอบจึงเห็นทั้งรายการตรวจและโค้ด

ประเด็นของขั้นตอนสุดท้ายคือทีมส่งไฟล์ให้คุณ ไม่ใช่บทสนทนา สคริปต์บันทึกคำตอบของผู้ทดสอบเป็น revision.html ที่เปิดดูได้จริง

🇨🇳 中文

CrewAI 描述智能体的方式,就像经理描述员工。每个智能体都有 role(职位)、goal(目标),以及决定它做事风格的 backstory。工作则单独描述,写成带 expected_outputTaskCrew 把智能体和任务装在一起,按顺序执行。

这里的顺序就是一个小型软件团队:经理写下这个页面必须包含什么,程序员写出 HTML,测试员拿 HTML 对照经理的清单检查,并交回修正后的文件。每个任务通过 context=[...] 拿到前面的任务结果,测试员因此能同时看到清单和代码。

最后一步的意义在于:这个团队交给你的是一个文件,而不是一段对话。脚本把测试员的答案存成 revision.html,你可以直接打开它。

terminal

pip install crewai litellm
python crew_webpage.py

crew_webpage.py

"""crew_webpage.py - a manager, a programmer and a tester build one web page.

    python crew_webpage.py

Three agents with three job titles, working in order:
  the manager  decides what the page must contain and writes it down
  the coder    writes the HTML
  the tester   checks the HTML against the manager's list and fixes what is missing

The last agent's answer is saved as revision.html, so the crew produces a file you
can open, not just a conversation you can read.
"""
import pathlib
import re

from crewai import Agent, Crew, LLM, Process, Task

MODEL = "ollama/qwen3"
HOST = "http://localhost:11434"
OUT = pathlib.Path("revision.html")

llm = LLM(model=MODEL, base_url=HOST, temperature=0.3)

manager = Agent(
    role="Project manager",
    goal="Decide exactly what a revision page for 14-year-olds must contain",
    backstory=("You have watched too many projects fail because nobody wrote down what "
               "finished looked like. You write short, checkable lists. You do not write code."),
    llm=llm,
    verbose=False,
)
coder = Agent(
    role="Programmer",
    goal="Write one self-contained HTML file that does what the manager asked",
    backstory=("You write plain HTML and CSS in one file, with no libraries and no CDN links. "
               "You write the file and nothing else: no explanation, no markdown fence."),
    llm=llm,
    verbose=False,
)
tester = Agent(
    role="Tester",
    goal="Check the page against the manager's list and return a corrected file",
    backstory=("You are the person who actually opens the page. You check each item on the "
               "list, then return the whole corrected HTML file. You never approve something "
               "you have not checked."),
    llm=llm,
    verbose=False,
)

spec = Task(
    description=("Write a checklist of 5 things a one-page revision sheet about the water cycle "
                 "must have, for 14-year-old students. Each item must be something a tester can "
                 "look at the page and tick or not tick. Number them 1 to 5. No HTML."),
    expected_output="A numbered list of 5 checkable requirements.",
    agent=manager,
)
build = Task(
    description=("Write one complete HTML file that meets every item on the manager's checklist. "
                 "One file, inline CSS, no external links, no JavaScript. Output ONLY the HTML, "
                 "starting with <!DOCTYPE html>."),
    expected_output="A complete HTML document.",
    agent=coder,
    context=[spec],
)
check = Task(
    description=("Go through the manager's checklist item by item against the programmer's HTML. "
                 "Fix anything missing or wrong. Output ONLY the final corrected HTML document, "
                 "starting with <!DOCTYPE html>. Do not output your notes."),
    expected_output="The corrected, complete HTML document.",
    agent=tester,
    context=[spec, build],
)

# tracing=False: without it, CrewAI stops when the work is done and asks in the
# terminal whether you want to view execution traces. It skips the question when
# nothing is watching, but it is 20 seconds of nothing while you sit there.
crew = Crew(agents=[manager, coder, tester], tasks=[spec, build, check],
            process=Process.sequential, verbose=False, tracing=False)

if __name__ == "__main__":
    result = crew.kickoff()
    html = str(result)
    html = re.sub(r"<think>.*?</think>", "", html, flags=re.S)      # qwen3 thinks out loud
    html = re.sub(r"^```[a-z]*\s*|\s*```$", "", html.strip())        # and sometimes fences it
    start = html.find("<!DOCTYPE")
    if start > 0:
        html = html[start:]
    OUT.write_text(html, encoding="utf-8")
    print(f"\nthe crew wrote {OUT} ({len(html)} characters)")
    print("\n--- what the manager asked for ---")
    print(re.sub(r"<think>.*?</think>", "", str(spec.output), flags=re.S).strip())

what the manager asked for (qwen3 on a laptop, about 17 minute(s) for the whole crew)

1. The page clearly lists the four main stages of the water cycle: evaporation, condensation, precipitation, and collection.  
2. A simple diagram or visual representation of the water cycle is included, showing the movement of water through these stages.  
3. The role of the sun in driving evaporation and heating the Earth’s surface is explicitly explained.  
4. The difference between the water cycle and the carbon cycle is briefly clarified to avoid confusion.  
5. Examples of where the water cycle occurs in nature (e.g., oceans, rivers, clouds) are provided alongside the processes.

the file the crew wrote, revision.html (first lines)

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Water Cycle Explained</title>
    <style>
        body { font-family: Arial, sans-serif; line-height: 1.6; margin: 20px; background: #f0f8ff; color: #333; }
        h1 { color: #2c3e50; }
        section { margin-bottom: 30px; }
        .stage { margin-bottom: 15px; }
        .stage h2 { color: #e67e22; }
        .diagram { width: 100%; height: 300px; background: #cce5ff; border-radius: 10px; margin: 20px 0; }
        .diagram svg { width: 100%; height: 100%; }
        .sun { fill: #ffcc00; }
... (86 more lines)
YOU GET Three job titles, one file on disk. The interesting line in the log is the manager's checklist: everything after it is judged against those five items, which is the whole reason the crew produces something checkable instead of something merely plausible. สามตำแหน่งงาน ได้ไฟล์หนึ่งไฟล์ในเครื่อง บรรทัดที่น่าสนใจในล็อกคือรายการตรวจของผู้จัดการ ทุกอย่างหลังจากนั้นถูกตัดสินเทียบกับห้าข้อนั้น และนั่นคือเหตุผลทั้งหมดที่ทีมนี้ผลิตสิ่งที่ตรวจได้ ไม่ใช่แค่สิ่งที่ฟังดูเข้าท่า 三个职位,硬盘上一个文件。日志里最值得看的一行是经理的清单:它之后的一切都要拿那五条来衡量。这正是这个团队交出的东西是可核对的、而不只是看起来像回事的原因。
CAREFUL The tester is an agent, not a test suite. It re-reads the HTML and says whether it matches the checklist — it does not open the page in a browser, so nothing here proves the page renders. A model asked to check its colleague's work also tends to be agreeable. Treat this as a first draft with a second opinion, and open revision.html yourself. One practical note: tracing=False on the crew. Leave it out and CrewAI stops once the work is done and asks in the terminal whether you want to see execution traces, which looks exactly like a script that has hung. ผู้ทดสอบเป็น agent ไม่ใช่ชุดทดสอบจริง มันอ่าน HTML ซ้ำแล้วบอกว่าตรงกับรายการตรวจหรือไม่ มันไม่ได้เปิดหน้าเว็บในเบราว์เซอร์ ดังนั้นไม่มีอะไรที่นี่พิสูจน์ว่าหน้าเว็บแสดงผลได้ และโมเดลที่ถูกขอให้ตรวจงานเพื่อนร่วมงานก็มักจะเออออตามด้วย ให้ถือว่านี่คือฉบับร่างแรกที่มีความเห็นที่สอง แล้วเปิด revision.html ดูเอง ข้อสังเกตเชิงปฏิบัติอีกอย่างคือ tracing=False ที่ใส่ให้ crew ถ้าไม่ใส่ CrewAI จะหยุดรอเมื่อทำงานเสร็จ แล้วถามในเทอร์มินัลว่าอยากดู execution traces ไหม ซึ่งดูเหมือนสคริปต์ค้างไม่มีผิด 测试员是一个智能体,不是一套测试。它重新读一遍 HTML,然后说它是否符合清单 —— 它并没有在浏览器里打开这个页面,所以这里没有任何东西能证明页面真的显示得出来。而且一个被要求检查同事工作的模型,往往倾向于附和。把它当作一份有了第二意见的初稿,然后自己去打开 revision.html。还有一个实用的细节:crew 上的 tracing=False。不加它,CrewAI 在活干完之后会停下来,在终端里问你要不要看执行轨迹 —— 那看起来和脚本卡死一模一样。

2 🎲 AutoGen — a storyteller and two characters · AutoGen — ผู้เล่าเรื่องกับตัวละครสองตัว · AutoGen — 一个说书人和两个角色

🇬🇧 English

AutoGen, from Microsoft, describes agents as people in a conversation. Each AssistantAgent has a name and a system_message — that is its role. A team then decides who speaks when. RoundRobinGroupChat is the simplest one: the agents take turns, and when the list runs out it starts again.

The scene runs in two phases, and the reason is worth more than the code. Phase one is a four-message round robin: the task, then one turn each for the storyteller, Mai and Somchai. No dice exist anywhere in that conversation. Phase two calls the storyteller again, on its own, and only then hands it the numbers.

The first version did the obvious thing and put the dice in the opening message. The storyteller could see them, so on its very first turn it invented both characters' actions and announced the results, before either character had spoken. Telling it not to — twice, in capital letters — changed nothing. A model uses what is in front of it, so the fix is not a firmer instruction but a smaller context.

It plays like a tabletop game. The storyteller sets the scene and names a danger; each character says what they do, and is told not to say whether it works; then the storyteller resolves both actions and ends the scene.

🇹🇭 ไทย

AutoGen จาก Microsoft อธิบาย agent เหมือนคนในวงสนทนา แต่ละ AssistantAgent มีชื่อและ system_message ซึ่งก็คือบทบาทของมัน จากนั้นทีมเป็นตัวกำหนดว่าใครพูดเมื่อไหร่ RoundRobinGroupChat เป็นแบบที่ง่ายที่สุด agent ผลัดกันพูดตามลำดับ และเมื่อครบรายชื่อก็วนกลับมาเริ่มใหม่

ฉากนี้ทำงานเป็นสองช่วง และเหตุผลสำคัญกว่าตัวโค้ด ช่วงแรกคือ round robin สี่ข้อความ ได้แก่ โจทย์ แล้วหนึ่งตาสำหรับผู้เล่าเรื่อง ใหม่ และสมชาย ในบทสนทนาช่วงนั้นไม่มีลูกเต๋าอยู่เลย ส่วนช่วงที่สองคือเรียกผู้เล่าเรื่องอีกครั้งแยกต่างหาก แล้วค่อยยื่นตัวเลขให้ตอนนั้น

เวอร์ชันแรกทำแบบที่นึกออกง่ายที่สุด คือใส่ลูกเต๋าไว้ในข้อความเปิด ผู้เล่าเรื่องมองเห็นมัน พอถึงตาแรกมันจึงแต่งการกระทำของตัวละครทั้งสองและประกาศผลลัพธ์เสียเอง ก่อนที่ตัวละครจะได้พูดด้วยซ้ำ สั่งห้ามไปสองรอบด้วยตัวพิมพ์ใหญ่ก็ไม่ได้ช่วยอะไร โมเดลใช้สิ่งที่อยู่ตรงหน้ามัน ทางแก้จึงไม่ใช่คำสั่งที่แข็งขึ้น แต่คือบริบทที่เล็กลง

มันเล่นเหมือนเกมกระดาน ผู้เล่าเรื่องตั้งฉากและบอกว่าอันตรายคืออะไร ตัวละครแต่ละตัวบอกว่าจะทำอะไร และถูกสั่งไม่ให้บอกว่าสำเร็จหรือไม่ จากนั้นผู้เล่าเรื่องก็ตัดสินทั้งสองการกระทำและจบฉาก

🇨🇳 中文

微软的 AutoGen 把智能体描述成对话里的人。每个 AssistantAgent 有一个名字和一段 system_message,那就是它的角色。然后由团队决定谁什么时候说话。RoundRobinGroupChat 是最简单的一种:智能体轮流发言,名单走完就从头再来。

这一幕分两个阶段跑,而原因比代码更值得看。第一阶段是四条消息的轮流发言:题目,然后说书人、Mai、Somchai 各一轮。那段对话里根本不存在骰子。第二阶段再单独叫一次说书人,到这时才把点数交给它。

第一版做了最顺手的事:把骰子放进开场消息里。说书人看得见它们,于是在它的第一轮就替两个角色编好了动作、并且宣布了结果 —— 角色都还没开口。用大写字母警告了两遍,毫无作用。模型会用摆在它面前的东西,所以解法不是更严厉的指令,而是更小的上下文。

它玩起来像一场桌游。说书人搭好场景并点明危险;每个角色说出自己要做什么,并且被要求不许自己判断成不成功;然后说书人裁定这两个动作,结束这一幕。

terminal

pip install "autogen-agentchat==0.7.5" "autogen-ext[ollama]==0.7.5"
python autogen_story.py

autogen_story.py

"""autogen_story.py - three agents tell a story together, the way a D&D table does.

    python autogen_story.py

One agent is the storyteller: it sets the scene and, at the end, says what actually
happened. Two more agents each play a character and say what their character does.

The dice are NOT rolled by the model. The program rolls them and hands the numbers
to the storyteller, which must honour them. That is the important idea here: the
game rules live in Python, and the model only does the describing.

The scene runs in two phases, and that is not decoration. The first version of this
script put the dice in the opening message. The storyteller could see them, so on its
very first turn it invented both characters' actions AND announced the results, before
either character had spoken. Telling it not to, twice, in capital letters, did not
help: a model will use what is in front of it. So the dice are not in front of it.
Phase one has no dice anywhere in the conversation; phase two hands them over once
the characters have acted.
"""
import asyncio
import random
import re

from autogen_agentchat.agents import AssistantAgent
from autogen_agentchat.conditions import MaxMessageTermination
from autogen_agentchat.teams import RoundRobinGroupChat
from autogen_ext.models.ollama import OllamaChatCompletionClient

MODEL = "qwen3"
HOST = "http://localhost:11434"
SEED = 7                      # same seed, same dice, so you can re-run this and compare


def roll(sides=20):
    return random.randint(1, sides)


def clean(text):
    """qwen3 thinks out loud in <think> tags. The reader does not need that."""
    return re.sub(r"<think>.*?</think>", "", text or "", flags=re.S).strip()


def outcome(score):
    """The rule, written in Python where it cannot be talked out of anything."""
    if score >= 15:
        return "succeeds"
    if score >= 10:
        return "half works"
    return "fails"


async def main():
    random.seed(SEED)
    rolls = {"Mai": roll(), "Somchai": roll()}

    client = OllamaChatCompletionClient(model=MODEL, host=HOST)

    storyteller = AssistantAgent(
        "Storyteller",
        model_client=client,
        system_message=(
            "You run a game set in a night market in Chiang Mai where something has gone wrong. "
            "When you set a scene: describe it in four sentences, name one clear danger, then ask "
            "Mai and Somchai what they each do. Do not invent their actions and do not say how "
            "anything turns out. When you are later given dice results, say plainly what each "
            "character tried and how the scene ends, obeying the dice even when a plan was clever."
        ),
    )
    mai = AssistantAgent(
        "Mai",
        model_client=client,
        system_message=(
            "You play Mai, a mango-sticky-rice seller who is braver than she is careful. "
            "Speak only as Mai, in three sentences: ONE specific thing Mai does right now. "
            "Never write words or actions for Somchai or anyone else. Do not say whether it "
            "works - that is decided later, and not by you."
        ),
    )
    somchai = AssistantAgent(
        "Somchai",
        model_client=client,
        system_message=(
            "You play Somchai, an old songthaew driver who has seen everything and hurries "
            "for nobody. Speak only as Somchai, in three sentences: ONE specific thing Somchai "
            "does right now, different from what Mai just did. Never write words or actions for "
            "Mai or anyone else. Do not say whether it works - that is decided later, not by you."
        ),
    )

    # PHASE ONE: the scene and the two characters. No dice exist in this conversation.
    # Four messages: the task, then one turn each for the storyteller, Mai and Somchai.
    team = RoundRobinGroupChat(
        [storyteller, mai, somchai],
        termination_condition=MaxMessageTermination(4),
    )
    scene = await team.run(task="Begin the scene.")

    for message in scene.messages:
        if message.source == "user":
            continue
        print(f"--- {message.source} ---")
        print(clean(str(message.content)), "\n")

    # PHASE TWO: now the storyteller is told what the dice said, and not one moment sooner.
    print(f"the program rolled: Mai {rolls['Mai']}, Somchai {rolls['Somchai']} (d20 each)\n")
    did = "\n".join(
        f"{m.source} said: {clean(str(m.content))}"
        for m in scene.messages if m.source in ("Mai", "Somchai")
    )
    verdict = await storyteller.run(task=(
        "Finish the scene using these dice results. You must obey them.\n"
        f"Mai rolled {rolls['Mai']} - {outcome(rolls['Mai'])}.\n"
        f"Somchai rolled {rolls['Somchai']} - {outcome(rolls['Somchai'])}.\n\n"
        f"{did}"
    ))
    print("--- Storyteller, with the dice ---")
    print(clean(str(verdict.messages[-1].content)), "\n")

    await client.close()


if __name__ == "__main__":
    asyncio.run(main())

the scene, in the order it was spoken - phase one, then the dice (qwen3 on a laptop, about 7 minute(s))

--- Storyteller ---
The night market is dimly lit, its usual hum replaced by the crackle of a distant fire. Stalls flicker with unstable lanterns, and a thick haze hangs over the crowd, masking the acrid scent of burning oil. A crowd has gathered around a collapsed food cart, its wok still smoldering, while vendors panic over spilled goods. The danger is the fire spreading to the wooden scaffolding above, threatening to engulf the market.  
What does Mai do? What does Somchai do? 
--- Mai ---
Mai squats to scoop up a bucket of water from the puddle pooling near her cart, her hands steady despite the smoke stinging her eyes. She slams the lid on the rice container closest to the flames, pressing it into the dirt to smother any stray embers. Her fingers brush the edge of the wok, hesitating as the heat radiates through the metal. 
--- Somchai ---
Somchai slaps the side of his songthaew’s engine, ensuring it’s off and no sparks will fly. He grabs a nearby bucket and starts pouring water on the tires to prevent them from catching fire. He climbs onto the roof of his songthaew, using it as a makeshift platform to survey the fire’s spread. 
the program rolled: Mai 11, Somchai 5 (d20 each)
--- Storyteller, with the dice ---
Mai’s attempt to smother embers and douse the wok’s heat succeeds in halting the fire near her cart, but the main blaze surges toward the scaffolding. Somchai’s water-dousing fails to cool the tires, and his climb onto the songthaew’s roof ends in a slip, sending him crashing into a stack of lanterns. The fire spreads to the scaffolding, its flames licking the wooden beams as the market’s chaos intensifies.
YOU GET A scene that nobody wrote: the storyteller set it up, two characters answered in their own voices, and the ending was decided by two numbers the program chose before anyone spoke. Change SEED and the same three agents play the same scene to a different ending. ฉากที่ไม่มีใครเขียนไว้ ผู้เล่าเรื่องตั้งฉาก ตัวละครสองตัวตอบด้วยน้ำเสียงของตัวเอง และตอนจบถูกตัดสินด้วยตัวเลขสองตัวที่โปรแกรมเลือกไว้ก่อนที่ใครจะพูดเสียอีก เปลี่ยนค่า SEED แล้ว agent สามตัวเดิมจะเล่นฉากเดิมไปสู่ตอนจบที่ต่างออกไป 一幕没有人写过的戏:说书人搭好场景,两个角色用各自的声音作答,而结局是由程序在任何人开口之前就选好的两个数字决定的。改一下 SEED,同样这三个智能体会把同一幕演向不同的结局。
CAREFUL The dice are rolled by Python, not by the model, and the storyteller is handed the numbers. That is deliberate. Ask a model to both invent an action and judge it and it will mostly let the characters win, because agreeable text is what it was trained to produce. Keep the rules — dice, scores, money, hit points — in your own code, and let the model do the describing. It is the same lesson as the checklist in section 1: the part that must be true should not be the part the model made up. ลูกเต๋าถูกทอยโดย Python ไม่ใช่โดยโมเดล แล้วยื่นตัวเลขให้ผู้เล่าเรื่อง นี่คือความตั้งใจ ถ้าให้โมเดลทั้งคิดการกระทำและตัดสินเอง ส่วนใหญ่มันจะปล่อยให้ตัวละครชนะ เพราะข้อความที่เออออตามคือสิ่งที่มันถูกฝึกมาให้ผลิต ให้เก็บกฎ ทั้งลูกเต๋า คะแนน เงิน พลังชีวิต ไว้ในโค้ดของคุณเอง แล้วให้โมเดลทำหน้าที่บรรยาย นี่คือบทเรียนเดียวกับรายการตรวจในหัวข้อ 1 สิ่งที่ต้องเป็นความจริงไม่ควรเป็นสิ่งที่โมเดลแต่งขึ้น 骰子是 Python 掷的,不是模型掷的,点数是交给说书人的。这是有意为之。如果让模型既想出动作又自己裁定,它多半会让角色赢,因为它被训练出来产出的就是让人顺心的文字。把规则 —— 骰子、分数、金钱、生命值 —— 留在你自己的代码里,让模型负责描述。这和第一节的清单是同一个教训:必须为真的那部分,不该是模型编出来的那部分。

3 ⚖️ Three libraries, side by side · สามไลบรารี วางเทียบกัน · 三个库并排比较

smolagentsCrewAIAutoGen
The ideaแนวคิด核心想法 one agent that writes Pythonagent ตัวเดียวที่เขียน Python一个会写 Python 的智能体 a small team with job titles, in a fixed orderทีมเล็ก ๆ ที่มีตำแหน่งงาน一个有职位的小团队 a conversation, turn by turnบทสนทนาระหว่าง agent智能体之间的对话
You give a role withให้บทบาทด้วย用什么给角色 instructions=role, goal, backstorysystem_message=
Each step producesแต่ละขั้นได้อะไร每一步产出 Python that runsPython ที่ถูกรัน会被运行的 Python a finished taskงานหนึ่งชิ้นที่เสร็จแล้ว一项完成的任务 a chat messageข้อความสนทนา一条聊天消息
Who goes nextใครทำต่อ下一个是谁 the loop, until final_answer()loop จนกว่าจะเรียก final_answer()循环,直到 final_answer() the task order you wroteลำดับ task ที่คุณเขียนไว้你写下的任务顺序 the team: take turns, until it stopsทีมกำหนด ผลัดกันพูดจนหยุด团队决定:轮流发言,直到停止
Adding a toolเพิ่มเครื่องมือ添加工具 @tooltools= on the agentที่ agent写在 agent 上tools= on the agentที่ agent写在 agent 上
Sync or asyncsync หรือ async同步还是异步 agent.run(...)crew.kickoff()asyncio.run(...)
Version we testedเวอร์ชันที่เราทดสอบ我们测试的版本 1.26.01.15.210.7.5

4 👉 Which one should you use? · ควรใช้ตัวไหนดี · 你该用哪一个?

🇬🇧 English

  • One job, and code would solve it — counting, reading a file, calling an API: smolagents. It is the smallest, and it writes real Python.
  • Several jobs in a fixed order, each done by someone with a title — write, then check, then translate: CrewAI. The order is yours, written down.
  • Two voices that need to answer each other — a maker and a critic, a student and a teacher: AutoGen. You set the turns and when to stop.

One more thing worth knowing: the library is rarely what makes an agent slow or wrong. The model is. All three ran on the same free qwen3 on the same laptop, and every extra agent means another wait of minutes. Start with one agent. Add a second only when the first cannot do the job.

🇹🇭 ไทย

  • งานเดียว และแก้ด้วยโค้ดได้ เช่น นับจำนวน อ่านไฟล์ เรียก API ให้ใช้ smolagents มันเล็กที่สุด และเขียน Python จริง ๆ
  • หลายงานตามลำดับที่แน่นอน แต่ละงานทำโดยคนที่มีตำแหน่ง เช่น เขียน แล้วตรวจ แล้วแปล ให้ใช้ CrewAI ลำดับเป็นของคุณ เขียนไว้ชัดเจน
  • สองเสียงที่ต้องโต้ตอบกัน เช่น คนทำกับคนวิจารณ์ หรือนักเรียนกับครู ให้ใช้ AutoGen คุณกำหนดรอบการพูดและจุดที่จะหยุด

อีกเรื่องที่ควรรู้ สิ่งที่ทำให้ agent ช้าหรือผิดมักไม่ใช่ไลบรารี แต่เป็นโมเดล ทั้งสามตัวรันบน qwen3 ฟรีตัวเดียวกันบนแล็ปท็อปเครื่องเดียวกัน และ agent ที่เพิ่มมาทุกตัวคือการรออีกหลายนาที เริ่มจาก agent ตัวเดียวก่อน เพิ่มตัวที่สองเมื่อตัวแรกทำไม่ไหวจริง ๆ

🇨🇳 中文

  • 只有一件事,而且写代码能解决 —— 数数、读文件、调 API:用 smolagents。它最小,而且真的会写 Python。
  • 几件事按固定顺序做,每件由一个有职位的人完成 —— 先写、再检查、再翻译:用 CrewAI。顺序由你写下来。
  • 两个声音需要互相回应 —— 一个创作者和一个评论者,一个学生和一个老师:用 AutoGen。轮次和何时停止由你设定。

还有一点值得知道:让智能体变慢或出错的,通常不是库,而是模型。三个库跑的是同一台笔记本上同一个免费的 qwen3,每多一个智能体,就是多等几分钟。先从一个智能体开始。只有当它真的做不到时,才加第二个。