📅
🕵️‍♂️ อ่าน ~3 นาที

คดีที่ 35: ผ่าพิมพ์เขียว harness-sdk — ถอดรหัสสถาปัตยกรรม AI ยุคใหม่ (8.2k Stars)


🕵️‍♂️ ปมคดีและที่มา: ทำไมวงการถึงต้องจับตามอง?

ทำไมโปรเจกต์ strands-agents/harness-sdk ถึงได้รับความนิยมและมียอดกด Star ทะลุ 8.2k บน GitHub?

เบื้องหลังความสำเร็จนี้ไม่ใช่แค่การเป็นเครื่องมือสำเร็จรูป แต่คือการแก้ปัญหาทางวิศวกรรมที่เจ็บปวด: Build an agent harness. Control it end-to-end.

  • Build your way. Any model, any cloud. Context management, execution limits, and observability built in before you write a line of config. Swap backends when you scale; your code stays the same.
  • Model agnostic. First-class support for Amazon Bedrock, Anthropic, OpenAI, and Gemini, plus many more providers and custom ones.
  • Stay in control. The agent loop traces every decision by default. Hooks let you intercept any step to log it, validate it, or redirect it.
  • Deliver outcomes that work. Guardrails catch mistakes before they run. Steering handlers let agents correct themselves instead of failing silently.

MCP, streaming, multi-agent patterns, and structured output are all built in.


📊 ตารางเปรียบเทียบเชิงลึก: วิธีดั้งเดิม vs สถาปัตยกรรมสมัยใหม่

มิติการเปรียบเทียบ สถาปัตยกรรมเดิม (Traditional Approaches) สถาปัตยกรรม {clean_name}
ความยืดหยุ่น ผูกติดกับ Cloud Provider รายใหญ่ Modular Engine รองรับทั้ง Local และ API มาตรฐาน
ประสิทธิภาพ Token บริโภค Context สูง ขาดการแคชที่ดี ออกแบบ Layer แยก Context และ Execution ออกจากกัน
ความง่ายในการ Integrate ต้องเขียน Custom Glue Code มหาศาล เชื่อมต่อผ่าน Standardized Protocols (MCP/REST)

🔍 แกะรอยสถาปัตยกรรมระบบ (Deep Architecture Breakdown)

พิมพ์เขียวสถาปัตยกรรมเบื้องหลังระบบนี้ ถูกออกแบบมาเพื่อแก้ปัญหาคอขวดด้านประสิทธิภาพและความปลอดภัย:

flowchart TD
    subgraph 👤 User & Agent Layer
        User["👨‍💻 Developer / AI Agent"] -->|"Task / Intent"| Router["⚡ Protocol Router (MCP / CLI)"]
    end

    subgraph 🧠 Core Intelligence Engine
        Router --> Engine["⚙️ harness-sdk Engine"]
        Engine --> Decision["🎯 Intelligent Decision Core"]
        Engine --> Memory["💾 Persistent Session & Cache"]
    end

    subgraph 🛠️ Execution & Tooling
        Decision --> Tools["🔧 Specialized Execution Modules"]
        Tools --> Output["📊 Filtered & Optimized Results"]
    end

    Output -->|"Clean Context"| User

3 เสาหลักของการออกแบบระบบ (System Design Pillars):

  1. Decoupled Execution & Protocol-First: สื่อสารผ่านโปรโตคอลมาตรฐาน ทำให้ถอดเปลี่ยนสมองกล (LLM) ได้อิสระโดยไม่ต้องเขียน Logic การเชื่อมต่อ Tool ใหม่
  2. Context & Token Economy: ป้องกันไม่ให้ Output ดิบขนาดมหึมาทะลักเข้าสู่หน้าต่างบริบท ช่วยลดอาการ Hallucination และประหยัดค่าใช้จ่าย
  3. Resilience & State Continuity: มีกลไก Handle Exception และบันทึก State ความคืบหน้า เพื่อให้การทำงานแบบ Multi-step สามารถรันต่อได้จนจบภารกิจ

💻 ผ่ารหัสลับของจริง (Source Code Autopsy)

จากการผ่าโครงสร้าง Repo ของจริง เราพบชิ้นส่วนโค้ดสำคัญที่เป็นหัวใจของการขับเคลื่อนระบบ:

📄 ผ่าไฟล์จริง: package.json

{
  "name": "strands",
  "version": "0.0.0",
  "private": true,
  "bin": {
    "strands-dev": "strands-cli/bin/strands.js"
  },
  "workspaces": [
    "strands-ts"
  ],
  "devDependencies": {
    "husky": "^9.1.7",
    "prettier": "^3.9.4"
  },
  "scripts": {
    "prepare": "husky",
    "build": "npm run build -w strands-ts",
    "setup": "node strands-cli/scripts/setup-source.js",
    "test": "npm run test -w strands-ts",
    "test:coverage": "npm run test:coverage -w strands-ts",
    "test:all": "npm run test:all -w strands-ts",
    "test:all:coverage": "npm run test:all:coverage -w strands-ts",
    "test:integ": "npm run test:integ -w strands-ts",
    "test:integ:all": "npm run test:integ:all -w strands-ts",
    "test:integ:selective": "bash ./test-infra/scripts/run-selective-ts.sh",
    "test:browser:install": "npm run test:browser:install -w strands-ts",
    "test:package": "npm run test:package -w strands-ts",
    "lint": "npm run lint -w strands-ts",
    "lint:py": "sh .husky/lint-python.sh",
    "format": "npm run format -w strands-ts",
    "format:check": "npm run format:check -w strands-ts",
    "type-check": "npm run type-check -w strands-ts",
    "check": "npm run check -w strands-ts",
    "check:browser-bundle": "npm run check:browser-bundle -w strands-ts",
    "complexity": "node .github/scripts/pr-metrics/run-analysis.mjs",
    "complexity:setup": "npm ci --ignore-scripts --prefix .github/scripts/pr-metrics/tools && pip install -r .github/scripts/pr-metrics/tools/requirements.txt",
    "test:pr-metrics": "node --test .github/scripts/pr-metrics/pr-metrics.test.mjs"
  }
}

การทำงานทางวิศวกรรม:

  • โค้ดส่วนนี้ทำหน้าที่เป็นแกนกลางในการควบคุม Flow ของข้อมูล
  • แยกหน้าที่การทำงานชัดเจน (Separation of Concerns) ทำให้สเกลเครื่องมือใหม่ๆ เข้าสู่ระบบได้ทันทีโดยไม่ต้องแก้ Core Engine

📄 ผ่าไฟล์จริง: pyproject.toml

[project]
name = "strands-monorepo-tools"
version = "0.0.0"
description = "Shared Python tooling for the Strands monorepo. Not published."
requires-python = ">=3.10"
dependencies = [
    # Linter/formatter. Range matches strands-py and harness-py so the whole monorepo
    # (and the pre-commit hook) resolves the same ruff.
    "ruff>=0.16.0,<0.17.0",
    # Type checker.
    "pyright>=1.1.400",
    # Test runner for the shared venv.
    "pytest>=9.0.3",
    "pytest-asyncio>=1.3.0",
]

[tool.setuptools]
# We don't ship a distribution. Suppress the auto-discovery warning.
packages = []

[tool.pytest.ini_options]
asyncio_mode = "auto"
cache_dir = ".pytest_cache"


# Ruff config lives here because ruff walks up from the file it's linting
# and the monorepo has a single style.
[tool.ruff]
line-length = 120

[tool.ruff.lint]
select = [
  "B",   # flake8-bugbear
  "E",   # pycodestyle
  "F",   # pyflakes
  "I",   # isort
  "UP",  # pyupgrade
]


# Pyright config lives here for the same reason as ruff: one rule for
# the whole monorepo. Pyright walks up to find the nearest pyproject.
# `exclude` is omitted so pyright inherits its built-in defaults
# (**/node_modules, **/__pycache__, **/.*); setting it would replace
# those, not merge.
[tool.pyright]
pythonVersion = "3.10"
pythonPlatform = "All"
typeCheckingMode = "standard"
reportMissingTypeStubs = false
reportMissingImports = "warning"

การทำงานทางวิศวกรรม:

  • โค้ดส่วนนี้ทำหน้าที่เป็นแกนกลางในการควบคุม Flow ของข้อมูล
  • แยกหน้าที่การทำงานชัดเจน (Separation of Concerns) ทำให้สเกลเครื่องมือใหม่ๆ เข้าสู่ระบบได้ทันทีโดยไม่ต้องแก้ Core Engine

📄 ผ่าไฟล์จริง: CLAUDE.md

@AGENTS.md

การทำงานทางวิศวกรรม:

  • โค้ดส่วนนี้ทำหน้าที่เป็นแกนกลางในการควบคุม Flow ของข้อมูล
  • แยกหน้าที่การทำงานชัดเจน (Separation of Concerns) ทำให้สเกลเครื่องมือใหม่ๆ เข้าสู่ระบบได้ทันทีโดยไม่ต้องแก้ Core Engine

📄 ผ่าไฟล์จริง: AGENTS.md

# Agent Development Guide - Strands Agents Monorepo

This document provides guidance for AI agents working in the Strands Agents monorepo. For human contributor guidelines, see [CONTRIBUTING.md](CONTRIBUTING.md).

This file is shared by agents with different goals — writing code, opening PRs, helping contributors — and is organized by task.

## Context

### Monorepo Layout

strands-agents/ ├── strands-py/ # Python SDK (hatch) — see strands-py/AGENTS.md ├── strands-ts/ # TypeScript SDK (npm workspace) — see strands-ts/AGENTS.md ├── site/ # Documentation site (Astro) — see site/AGENTS.md ├── team/ # Governance + cross-SDK process (tenets, decisions, API bar-raising, PR & compatibility guidelines, designs/ proposals) ├── test-infra/ # CDK stack for integ tests that require provisioned AWS infra ├── .agents/ # Agent skills and references ├── package.json # npm workspace root └── .github/workflows/ # CI (ci.yml is the merge gate)


Determine which sub-project you're in and follow its conventions — each has its own `AGENTS.md`.

### Where the "why" lives: `team/`

Before designing a feature or changing an API, read the relevant context in `team/`. It captures the reasoning the code itself doesn't:

- **`team/designs/`** — RFC-style proposals for significant features (numbered `NNNN-*.md`). The richest source of architectural context: problem framing, the chosen approach, alternatives considered, and consequences. If you're touching a major subsystem, find its design doc first.
- **`team/DECISIONS.md`** — lightweight architecture decision records for smaller calls.
- **`team/TENETS.md`** — the principles a contribution should align with.
- **`team/API_BAR_RAISING.md`** and **`team/FEATURE_LIFECYCLE.md`** — the bar and process for API changes and feature deprecation.

## Writing Code

- **Code conventions**: Follow the conventions in the sub-project's own `AGENTS.md` (`strands-py/AGENTS.md`, `strands-ts/AGENTS.md`, `site/AGENTS.md`) — they define the style, patterns, and directory layout for that toolchain.
- **Write for low complexity**: every PR is labeled with the [cognitive complexity](https://www.sonarsource.com/docs/CognitiveComplexity.pdf) of the most complex function it touches, and nesting drives the score. Write flat control flow (guard clauses, extracted helpers, lookup tables over branch ladders) and keep refactors in their own PR. Why we measure and how the score works: [team/COMPLEXITY.md](./team/COMPLEXITY.md). Check before pushing with `npm run complexity` (or `hatch run complexity` from `strands-py/`).
- **Branching**: `git checkout -b agent-tasks/{ISSUE_NUMBER}`
- **Commits**: Use [conventional commits](https://www.conventionalcommits.org/) — `feat:`, `fix:`, `refactor:`, `docs:`, etc.
- **CI**: The `ci.yml` merge gate detects which paths changed and runs only relevant checks.
- **Skills**: Reusable, repo-specific workflows live under `.agents/skills/` — for PRs (`pr-create`, `pr-writer`, `pr-feedback`), docs (`docs-writer`, `docs-reviewer`, `docs-audit`, `docs-planner`), and code review (`strands-review`). See [`.agents/skills/README.md`](./.agents/skills/README.md) for what each does and when to use it.
- **Doc `sourceLinks` track source files**: Doc pages under `site/` point at their backing implementation via `sourceLinks` frontmatter — repo-relative paths into `strands-py/` and `strands-ts/`. When you **rename or move a source file**, update any `sourceLinks` that reference its old path in the same change. The site build only fails on a malformed path or an unmapped file extension, **not** on a path that still resolves to the wrong (or now-nonexistent) file, so a stale reference rots silently. Find affected pages with `grep -rn "<old/path>" site/src/content/docs`.

### Cross-SDK Conventions

These rules apply to **both** SDKs. Each sub-guide (`strands-py/AGENTS.md`, `strands-ts/AGENTS.md`) shows the language-idiomatic form; the shared intent lives here so the two cannot drift apart. The two SDKs aim for parity in *concepts and names*, not identical code.

- **Plugin / construct naming**: name a construct for what it *does*, not for the interface it implements. `AgentSkills`, `ContextOffloader`, `GoalLoop` — never an `…Plugin` suffix. (Python's `vended_plugins` and TS's `vended-plugins` already follow this.)
- **Cross-SDK parity**: when a name, constant, or hook event exists in both SDKs, keep them in sync.
  - **Identifiers** match, re-cased to the language idiom (`snake_case` ↔ `camelCase`).
  - **Single-word string-literal values** are byte-identical (`'user'`, `'success'`).
  - **Multi-word string-literal values** are `snake_case` in Python and `camelCase` in TypeScript (`tool_use` ↔ `toolUse`); convert via an explicit map, never emit the other language's casing.
  - **Wire field names** (keys exchanged with a provider API) keep their wire format in both SDKs even when it breaks the language's casing convention (`inputSchema`, `tool_use_id`).
  - **Hook event names are shared** across SDKs (modulo the suffix convention). When you add a hook event in one SDK, add the matching name in the other.
- **Public vs internal API**: mark anything exported-but-not-public so consumers don't depend on it — Python keeps it out of `__all__` (and should prefix the module `_`); TypeScript keeps it out of the `index.ts` barrel and tags it `@internal`.
- **Structured logging format**: `field=<value>, field=<value> | lowercase human-readable message`, no punctuation, pipe-separate multiple statements. Python interpolates with `%s` (never f-strings; ruff `G` enforces it); TypeScript uses template literals (never printf `%s`/`%d`).
- **Evergreen, to-the-point comments**: a comment states only what cannot be inferred from the code (a constraint, an invariant, a non-obvious why) and states it briefly. Reasoning that explains or defends a change to its reviewer belongs in the PR description, not the source. Never narrate how the code changed or what it used to be ("improved", "previously", "used to", "which would previously have crashed"). This applies to tests too — a regression test for a discovered bug links the issue it guards against and states the behavior it guarantees; a test written as part of feature development carries no issue reference. ("deprecated"/"legacy" is fine when it describes a stable API surface or runtime state; it's forbidden only when narrating how the code itself changed.)

- **Directory & file naming parity**: subsystem directories use the language-idiomatic separator (`snake_case` in Python, `kebab-case` in TypeScript) but the stem matches word-for-word so they are mechanically translatable (`vended_plugins/` ↔ `vended-plugins/`, `conversation_manager/` ↔ `conversation-manager/`).

### Testing

When writing tests, follow the sub-project's testing guidance — `strands-py/docs/TESTING.md` for the Python SDK, `strands-ts/docs/TESTING.md` for the TypeScript SDK.

**`test-infra/` guardrails.** The `test-infra/` CDK stack deploys real AWS resources (Bedrock KBs, EC2 instances) that a small subset of integration tests depend on. Most tests do not need it — they run without provisioned infrastructure.

- **Do not deploy this stack** unless you are explicitly working on the test infrastructure itself or iterating on tests that resolve SSM parameters from it.
- **Never set `STRANDS_TEST_INFRA_INTERNAL=true`** unless deploying to the Strands team's own test account. This attaches a broad internal policy and GitHub OIDC trust that is meaningless (and wasteful) outside the internal account.
- **To run infrastructure-dependent integ tests without deploying anything**, open a PR — CI runs them against pre-provisioned resources automatically.

## Creating PRs

See [PR guidelines](./team/PR.md). Use the `pr-create` and `pr-writer` skills under `.agents/skills/` to draft and open PRs.

If you are opening a PR on behalf of a contributor, the human is the author and is accountable for everything you submit. A small, focused change that its author fully understands is the single biggest predictor of a fast review and an accepted PR. (See [CONTRIBUTING.md](./CONTRIBUTING.md#using-ai-tools) for the human-facing version.)

- **Understand before you submit.** The contributor must be able to explain why every line works and defend the design. If you produced code you cannot explain plainly, simplify or explain it before opening the PR.
- **Keep it small and focused.** One logical change per PR. A branch that touches several sub-projects (`strands-py/`, `strands-ts/`, `site/`) is almost always several PRs. Smaller PRs are easier to understand, guide, and merge.
- **Open an issue first for anything significant**, so maintainers can align on the approach before time is invested.
- **Don't pad the change.** No drive-by reformatting, unrelated refactors, or speculative abstractions — they make the diff hard to review and the change hard to trust.

การทำงานทางวิศวกรรม:

  • โค้ดส่วนนี้ทำหน้าที่เป็นแกนกลางในการควบคุม Flow ของข้อมูล
  • แยกหน้าที่การทำงานชัดเจน (Separation of Concerns) ทำให้สเกลเครื่องมือใหม่ๆ เข้าสู่ระบบได้ทันทีโดยไม่ต้องแก้ Core Engine

💰 3 พิมพ์เขียวสร้างรายได้จริงจากสถาปัตยกรรมนี้

  1. Enterprise Security & Architecture Consulting (รับงานที่ปรึกษาองค์กร)

    • องค์กรขนาดใหญ่ต้องการนำ AI Agent มาใช้ แต่ติดปัญหา Data Leak และการควบคุม Tool Calling
    • นำสถาปัตยกรรม FastMCP / Sandboxed Context ไปติดตั้งแบบ On-premise ค่าบริการเริ่มต้น 150,000 - 300,000 บาท/โปรเจกต์
  2. Specialized AI Automation Micro-SaaS (สร้างบริการเฉพาะทาง)

    • พัฒนาบริการ Agent สำหรับตรวจสอบช่องโหว่เว็บ (Bug Bounty as a Service) หรือเครื่องมือคุม Context สำหรับทีม Dev
    • ตั้งราคาแบบ Subscription รายเดือน ($29 - $99/เดือน/ผู้ใช้)
  3. Developer Tools & Workflow Optimization Retainer

    • ให้บริการตรวจสอบและ Optimize สถาปัตยกรรม Token Consumption ให้แก่บริษัท Startup หรือ Tech Agency
    • ช่วยลดค่า API OpenAI / Anthropic จากหลักแสนเหลือหลักหมื่นบาทต่อเดือน โดยคิดส่วนแบ่งจากยอดเงินที่ช่วยประหยัดได้ (Cost-Saving Share 20-30%)

💬 ร่วมสืบคดีและแลกเปลี่ยนความรู้ด้าน AI Engineering กับเราได้ที่เพจ Facebook: นักสืบอัลกอริทึม

🕵️‍♂️

ชอบคดีนี้ไหม? ส่งต่อให้เพื่อนในวงการ Dev!

แชร์บทความวิเคราะห์สถาปัตยกรรม AI & โค้ดจริงที่นำไปใช้สร้างเงินได้ทันที

💬

ร่วมอภิปรายคดีลับ (Case Discussion)

มีข้อสงสัย บัค หรือไอเดียต่อยอดสถาปัตยกรรมนี้? แลกเปลี่ยนกับเพื่อนสาย Dev ได้ด้านล่าง: