Skip to content

Commit 2498d00

Browse files
authored
feat(blog): add spec-driven-sdks (#59)
* feat(blog): add spec-driven-sdks Signed-off-by: Chojan Shang <[email protected]> * chore: add atom rss Signed-off-by: Chojan Shang <[email protected]> * feat: intro rss Signed-off-by: Chojan Shang <[email protected]> --------- Signed-off-by: Chojan Shang <[email protected]>
1 parent 7d58850 commit 2498d00

File tree

5 files changed

+256
-2
lines changed

5 files changed

+256
-2
lines changed

config.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ output_dir = "public"
1414
compile_sass = true
1515
minify_html = false # Keep this false, as it may cause issues with some styles
1616
build_search_index = false # Keep this false, search is temporarily unsupported
17-
generate_feeds = false # Whether to generate a feed file in root, read docs for more info about rss feed
18-
feed_filenames = ["feed.xml"] # "feed.xml" | "atom.xml" | "rss.xml", read docs for more info
17+
generate_feeds = true # Whether to generate a feed file in root, read docs for more info about rss feed
18+
feed_filenames = ["atom.xml"] # "feed.xml" | "atom.xml" | "rss.xml", read docs for more info
1919
taxonomies = [{ name = "tags" }, { name = "categories" }]
2020

2121
[languages.zh]
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
+++
2+
title = "Spec‑Driven SDKs in the Age of Generative AI"
3+
description = "Walk the middle path: use specs to bound AI, and AI to accelerate specs. An ACP‑based SDK with routing, golden tests, and auditability."
4+
date = 2025-10-15
5+
slug = "spec-driven-sdks"
6+
7+
[taxonomies]
8+
tags = ["AI", "SDK", "Spec", "ACP", "Agent", "MCP"]
9+
categories = ["engineering"]
10+
11+
[extra]
12+
lang = "en"
13+
+++
14+
15+
> AI writes fast — and tends to bloat.
16+
> Specs are strict — and can ossify.
17+
>
18+
> I choose the middle path —
19+
> let AI run inside boundaries, and let humans own outcomes.
20+
21+
## Why Not Pure AI, Nor Pure Spec
22+
23+
AI‑assisted coding is addictive, but as projects grow, context drifts and complexity balloons.
24+
You get something that runs, but barely holds.
25+
26+
On the other hand, pure spec‑driven frameworks — like GitHub’s [Spec Kit](https://github.com/github/spec-kit) — are often heavy for iterative work.
27+
Precise, but hard to live with.
28+
29+
So I took the middle ground: spec‑first boundaries, AI‑assisted speed — using the Agent Client Protocol (ACP).
30+
31+
## What Makes ACP a Good Base
32+
33+
ACP, started by Zed, defines how editors and agents talk.
34+
It uses JSON‑RPC 2.0 and is described in JSON Schema — clear edges, machine‑readable types, auditable behaviors.
35+
That balance makes it ideal for SDKs that must live across languages and over time.
36+
37+
I applied this while maintaining the ACP [Python SDK](https://github.com/psiace/agent-client-protocol-python) for the [Agent Client Protocol](https://agentclientprotocol.com).
38+
39+
## How I Built the Python SDK
40+
41+
### Stage 0 — Minimal, but Runnable
42+
43+
I relied heavily on a coding agent (Codex) with human‑set boundaries and review. Three constraints:
44+
45+
1. Agent‑generated Pydantic models from the datamodel.
46+
2. Minimal I/O (JSON‑RPC over stdio), with agent assistance.
47+
3. Agent‑ported examples and basic tests, human‑verified for regression.
48+
49+
Small on purpose: ship first, surface problems early.
50+
51+
### Stage 1 — Learning from Feedback
52+
53+
Early users found naming awkward and ergonomics rough.
54+
I had the agent scan docs to produce a rename map (more Pythonic names), then borrowed ideas from other SDKs:
55+
56+
- TypeScript had helpers.
57+
- Go had golden tests.
58+
- Rust had clean modular structure.
59+
- Protocol SDKs like MCP offered architecture hints.
60+
61+
With agent assistance I restructured code and tests; I set the standards and did the reviews.
62+
63+
### Stage 2 — Routing over Conditionals
64+
65+
I didn’t want endless if‑trees like `if method == "..."`.
66+
So I wrote a compact router that makes method→handler first‑class:
67+
68+
```python
69+
builder = RouterBuilder()
70+
71+
builder.request_attr(CLIENT_METHODS["fs_write_text_file"], WriteTextFileRequest, client, "writeTextFile")
72+
builder.request_attr(CLIENT_METHODS["fs_read_text_file"], ReadTextFileRequest, client, "readTextFile")
73+
```
74+
75+
Registered handlers are pluggable, testable, and auditable.
76+
By v0.4.9 the SDK wasn’t big, but it was stable and teachable.
77+
78+
### Context curation — feed the agent only what matters
79+
80+
I treat prompt context as an engineering budget:
81+
82+
- Context manifest: list only schemas, public interfaces, and minimal examples; exclude generated/vendor code.
83+
- Diff‑aware prompting: provide changed hunks plus a small window (5–15 lines), not whole files.
84+
- Golden snippets index: let the agent retrieve tagged canonical examples instead of pasting large blobs.
85+
- Guardrails first: “Do not invent fields beyond schema; fail closed; ask for missing inputs.”
86+
87+
## Principles for Spec‑Driven, AI‑Assisted SDKs
88+
89+
| Principle | Description |
90+
| :-- | :-- |
91+
| Separate generated vs human‑governed code | Models are generated; business logic is human‑owned and reviewed. Never mix. |
92+
| Ship the smallest working unit first | Real feedback drives ergonomics. |
93+
| Golden tests for major RPCs | Capture and replay requests/responses as the regression and audit baseline. |
94+
| Curate and budget context | Feed only what’s necessary — small, precise, reproducible. |
95+
96+
Specs give AI a sandbox — and AI makes specs come alive.
97+
98+
## Accountability: Why I Trust AI‑Written Code
99+
100+
AI can write, but I set the boundaries.
101+
Typed models, reproducible tests, and replayable traces let me verify every step.
102+
I trust not the model, but the engineering discipline around it.
103+
104+
## A Note in My PRs
105+
106+
I first saw this note in @Xuanwo’s PRs. I haven’t used it in mine yet, but I’m willing to adopt it — because I’m accountable for the outcome. In this SDK work I used AI heavily; I set the boundaries and own the result:
107+
108+
> This PR was primarily authored with GPT‑5‑Codex and hand‑reviewed by me.
109+
> I’m responsible for every line that lands here.
110+
111+
This isn’t about showing off AI — it’s about owning the outcome.
112+
113+
## Closing: Fuel and Rails
114+
115+
AI is the fuel.
116+
Spec is the rail.
117+
Fuel gives speed; rails keep you alive.
118+
119+
I like this balance — AI writes, I define boundaries.
120+
Every line explainable, every bug reproducible.
121+
That’s how to build responsibly in the AI age.
122+
123+
**PsiACE** ([GitHub](https://github.com/PsiACE) · [Apache OpenDAL PMC Member](https://opendal.apache.org/))
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
+++
2+
title = "用 Spec 做 SDK - 生成式 AI 的时代"
3+
description = "在 AI 时代走中间路径:用规范约束 AI,用 AI 加速规范。基于 ACP 的 SDK 实践:路由、黄金样张与可审计性。"
4+
date = 2025-10-15
5+
slug = "spec-driven-sdks"
6+
7+
[taxonomies]
8+
tags = ["AI", "SDK", "Spec", "ACP", "Agent", "MCP"]
9+
categories = ["工程实践"]
10+
11+
[extra]
12+
lang = "zh"
13+
+++
14+
15+
> AI 写得快,但容易膨胀;
16+
> Spec 很严格,但可能僵化。
17+
>
18+
> 我选择中间那条路——
19+
> 让 AI 在边界内跑,让人对结果负责。
20+
21+
## 为什么不是全 AI,也不是全 Spec
22+
23+
AI 写代码的速度让人上瘾;一旦规模扩大,很容易出现上下文漂移、结构膨胀、边界模糊——项目“能跑但不稳”。
24+
25+
另一个极端是 spec 驱动:严谨,但往往过重。GitHub 推出的 [Spec Kit](https://github.com/github/spec-kit) 倡导“先规范再生成”,对快速迭代的团队并不总是友好,容易过度设计、难以跟踪决策。
26+
27+
我走中间态:在维护 [Agent Client Protocol (ACP)](https://agentclientprotocol.com)[**Python SDK**](https://github.com/psiace/agent-client-protocol-python) 时,用规范约束 AI 生成,用 AI 加速规范适配。
28+
29+
## ACP:为 SDK 而生的协议
30+
31+
ACP 由 [Zed](https://zed.dev/blog/bring-your-own-agent-to-zed) 发起,定义**编辑器 ↔ Agent** 的通信方式。
32+
它采用 [JSON-RPC 2.0](https://www.jsonrpc.org/specification) 作为通信机制,并以 **JSON Schema** 描述数据结构(典型 schema 见 ACP 仓库)。边界明确、类型可机读、行为可审计——天生适合需要跨语言、长期维护的 SDK。
33+
34+
## 我的实现过程
35+
36+
### 起点:最小可用体(MVP)
37+
38+
我给自己三条约束(大量使用 Coding Agent,统一由我把边界与审阅):
39+
40+
1. 基于 datamodel 生成 **Pydantic** 模型([pydantic.dev](https://docs.pydantic.dev/latest/))。
41+
2. 实现最小 I/O(JSON-RPC over stdio)。
42+
3. 迁移文档示例与基本测试,由我负责核对与回归。
43+
44+
体量刻意保持小:**先上线,尽快暴露问题**
45+
46+
### 迭代:从反馈中长出结构
47+
48+
早期用户反馈“命名不一致、使用不顺手”。
49+
我让 agent 扫描文档生成 **rename map**(统一 Python 化命名),并对照其他语言实现取长补短:
50+
51+
- TypeScript SDK 里 **helpers**(降低重复样板)。
52+
- Go SDK 用 **golden tests**(固定请求/响应样张做回归)。
53+
- Rust SDK 的模块组织更克制。
54+
- 其他协议类 SDK(如 [MCP](https://modelcontextprotocol.io/))采用相似的协议,值得借鉴。
55+
56+
在 agent 协助下完成结构与测试重组,由我把控标准与审阅路径。
57+
58+
### 路由化:替代 if 判断
59+
60+
我不想看到满屏的 `if method == "..."`,于是写了极简路由,把“方法名 → 处理器”变成一等公民:
61+
62+
```python
63+
builder = RouterBuilder()
64+
65+
builder.request_attr(CLIENT_METHODS["fs_write_text_file"], WriteTextFileRequest, client, "writeTextFile")
66+
builder.request_attr(CLIENT_METHODS["fs_read_text_file"], ReadTextFileRequest, client, "readTextFile")
67+
```
68+
69+
`v0.4.9`,体量不大,但代码 **稳定且可教学**
70+
71+
## 上下文拣选:让 AI 吃对东西
72+
73+
我把提示词上下文当“工程预算”管理:只喂必要信息、限定边界,并对版本负责。
74+
75+
- **上下文清单(manifest)**:只列 schema、公共接口与最小示例;排除生成代码与外部依赖。
76+
- **Diff 感知**:只给改动的 hunk 与少量前后文(5–15 行),而不是整文件。
77+
- **黄金片段索引**:提示 agent 自检索必要片段,避免大段粘贴。
78+
- **先立护栏**:明确“不得臆造 schema 字段;失败优先;缺信息先询问”。
79+
80+
## 方法论:Spec 与 AI 的协作
81+
82+
| 原则 | 说明 |
83+
| :--------------------- | :----------------------------------------- |
84+
| **生成与人工治理分离** | 模型自动生成,业务由人编排与审阅,不混用。 |
85+
| **从最小可用体开始** | 用真实反馈驱动人体工学优化。 |
86+
| **Golden Test** | 捕获/回放请求与响应,作为回归与审计基线。 |
87+
| **上下文拣选与预算** | 只喂必要上下文,小而准、可复现。 |
88+
89+
规范给 AI 以边界;AI 让规范鲜活起来。
90+
91+
## 责任感:为什么我敢对 AI 写的代码负责
92+
93+
AI 能写,但边界我来定义。
94+
有强类型模型、可复现测试、可回放轨迹,我可以验证每一步。
95+
我信任的不是模型,而是 **它背后的工程纪律**
96+
97+
## 我在 PR 里的说明
98+
99+
这段说明最早见于 [@xuanwo](https://github.com/Xuanwo) 的 PR。我之前没有这样写,但我愿意采用这种方式——因为我对结果负责。在这个 SDK 的工作中我大量使用了 AI;**边界与质量由我把关**
100+
101+
> **This PR was primarily authored with GPT-5-Codex and hand-reviewed by me.
102+
> I’m responsible for every change that lands here.**
103+
104+
这不是“炫 AI”,而是**结果负责的声明**
105+
106+
## 结语:速度与轨道
107+
108+
AI 是燃料,Spec 是轨道。
109+
燃料给你速度,轨道让你抵达。
110+
111+
我喜欢这种平衡:模型来写,我来设边界。
112+
每一行可解释,每一次失败可复现。
113+
这就是在人工智能时代负责任地构建方式。
114+
115+
**PsiACE**[GitHub](https://github.com/PsiACE) · [Apache OpenDAL PMC Member](https://opendal.apache.org/)

templates/blog.html

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,14 @@
99
{% set translations = section.translations %}
1010
{% set current_lang = section_lang %}
1111
{% include "_language_switch.html" %}
12+
{% set rss_url = section.permalink ~ 'atom.xml' %}
13+
<div class="rss-subscribe" style="margin: 0.5rem 0 1rem; font-size: 0.95em; opacity: 0.9;">
14+
{% if section_lang == 'zh' %}
15+
<span>订阅:</span><a href="{{ rss_url }}" rel="alternate" type="application/atom+xml">RSS (atom.xml)</a>
16+
{% else %}
17+
<span>Subscribe:</span> <a href="{{ rss_url }}" rel="alternate" type="application/atom+xml">RSS (atom.xml)</a>
18+
{% endif %}
19+
</div>
1220
<main class="layout-list">
1321
{% if section.extra.categorized %}
1422
{% for category,posts in section.pages | sort(attribute="taxonomies.categories.0") | group_by(attribute="taxonomies.categories.0") %}

templates/home.html

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,14 @@
8484
{% endif %}
8585
<a class="instant" href="{{ get_url(path=config.extra.blog_section_path, lang=section_lang) }}">{{ more_text }}</a>
8686
</div>
87+
{% set rss_url = blog_section.permalink ~ 'atom.xml' %}
88+
<div class="rss-subscribe" style="margin-top: 0.5rem; font-size: 0.95em; opacity: 0.9;">
89+
{% if section_lang == 'zh' %}
90+
<span>订阅:</span><a href="{{ rss_url }}" rel="alternate" type="application/atom+xml">RSS (atom.xml)</a>
91+
{% else %}
92+
<span>Subscribe:</span> <a href="{{ rss_url }}" rel="alternate" type="application/atom+xml">RSS (atom.xml)</a>
93+
{% endif %}
94+
</div>
8795
</section>
8896
{% endif %}
8997
</main>

0 commit comments

Comments
 (0)