# 只用 100+ 行扣寫一個超迷你 HackMD Agent
[English](/@EastSun5566/building-a-tiny-hackmd-agent)
**AI Agent** 大概是這陣子最常聽到的名詞了,看了一些相關介紹還是很模糊,到底是什麼鬼,竟然可以做到理解、決策並自主完成任務?感覺又很複雜,像是背後有什麼神秘的黑魔法。
其實不然。
AI 底層是個黑盒子,但 Agent 其實比想像中還明確。它就是==一個強大的 LLM 加上重複執行的迴圈以及一組與外部世界互動的工具==,工具可以直接想像成 **function / API call**,迴圈用來維持對話,讓 LLM 理解使用者意圖後並決定呼叫某個工具完成目標。所以我可以很簡單暴力的定義:
:::info
AI Agent = LLM + Loop + Tools
:::
對,其實就這樣!讓我們僅僅用 **100+** 行程式碼就可以實作一個迷你管理 HackMD 筆記的 Agent。~~沒有框架、MCP 或是 A2A 等太複雜的東西~~,完全從零開始。
也可以直接看完成的 repo:
{%preview https://github.com/EastSun5566/tiny-hackmd-agent %}
## 準備實作
我們使用:
- [Deno](https://deno.com/) v2+ (TypeScript)
- [HackMD API](https://hackmd.io/settings#api) 來管理筆記
- [Anthropic API](https://console.anthropic.com/account/keys) 做為 LLM
接下來初始化專案:
```bash
deno init tiny-hackmd-agent && cd tiny-hackmd-agent
```
安裝相依:
```bash
deno add npm:@anthropic-ai/sdk npm:@hackmd/api
```
## Agent 主體
首先是 Agent 主體函式,他接收兩個參數 `ai` 跟 `tools`,裡面直接是一個暴力的 `while (true)` 迴圈:
```ts
function runAgent(ai: Anthropic, tools: Tool[]) {
while (true) {
// ...
}
}
```
`ai` 不特別說明,就是 Anthropic 的 API client:
```ts
const ai = new Anthropic({ apiKey: '<YOUR_KEY>' });
```
## 工具定義
接下來比較有趣的是 `tools`,`tools` 是我們自己定義的工具列表,用來告訴 LLM 有哪些工具可以使用:
```ts
interface Tool {
name: string;
description: string;
input_schema: ...;
call(input: ...): Promise<string>;
}
```
- `name`:工具名稱
- `description`:工具描述,告訴 LLM 何時使用這個工具
- `input_schema`:[JSON Schema](https://json-schema.org/overview/what-is-jsonschema#what-is-json-schema) 用來告訴 LLM 使用這個工具時需要傳入參數的「形狀」
- `call`:==實際呼叫工具時的執行邏輯==
因為我們這邊要管理筆記,所以 `call` 中當然就是呼叫 HackMD API,如讀取單一筆記:
```ts
const api = new API('<YOUR_TOKEN>');
const tools: Tool[] = [
{
name: "read_note",
description: "Read a note content by ID",
input_schema: {
type: "object",
properties: {
noteId: {
type: "string",
description: "The ID of the note",
},
},
required: ["noteId"],
},
call({ noteId }) {
return api.getNote(noteId);
},
]
```
稍微包裝一下,我這邊不列出所有細節,但大概可以理解會有 `list_notes`、`read_note`、`create_note`、`update_note`、`delete_note` 等工具:
```ts
function createTools(apiToken: string): Tool[] {
const api = new API(apiToken);
return [
{
name: "list_notes",
// ...
},
{
name: "read_note",
// ...
},
{
name: "create_note",
// ...
},
// ...
]
}
```
## 對話迴圈
OK 接下來回到原本的 while 迴圈,來實作讀取使用者輸入,我們需要一個 `conversation` 陣列記錄完整對話,並把 `tools` 與對話傳給 LLM:
```ts
const conversation = [];
let shouldReadInput = true
while (true) {
if (shouldReadInput) {
// 讀取使用輸入
const input = prompt("😂: ");
if (!input) break;
conversation.push({ role: "user", content: input });
}
// 與 LLM 對話
const message = await ai.messages.create({
model: "claude-3-5-haiku-latest",
messages: conversation, // 完整對話歷史
tools, // 剛剛定義的 tools
system: "You are a helpful agent for managing HackMD notes.",
});
conversation.push({ role: "assistant", content: message.content });
// 處理回應
const toolResults = [];
for (const content of message.content) {
// 處理文字回應和工具使用
}
}
```
接下來 AI 回應的 `message.content` 會是一個陣列,陣列中每項目我們只關注兩種形態:文字回應 `text` 或是工具使用 `tool_use`,==如果是純文字,我們就直接顯示;如果要用工具,我們就需要手動執行工具==:
```ts
const toolResults = []
for (const content of message.content) {
if (content.type === "text") {
console.log(`🤖: ${content.text}`);
}
if (content.type === "tool_use") {
const tool = tools.find(({ name }) => name === content.name);
console.log(`🔧 Using: ${content.name}...`);
// 執行工具
const result = await tool.call(content.input)
toolResults.push({
type: "tool_result",
tool_use_id: content.id,
content: result,
});
}
}
const hasToolResults = toolResults.length > 0;
if (hasToolResults) {
conversation.push({ role: "user", content: toolResults });
}
shouldReadInput = !hasToolResults
```
執行完工具後,將結果加入對話讓 LLM 知道執行結果,然後回到迴圈起始並給予回應。
## 完整程式碼
沒錯!就這樣,我們已經完成了完整的 Agent 功能。最後加上進入點:
```ts
async function main() {
const ai = new Anthropic({ apiKey: '<YOUR_KEY>' });
const tools = createTools('<YOUR_TOKEN>');
await runAgent(ai, tools);
}
main()
```
把 Agent 跑起來看結果:
```bash
deno run --allow-net --allow-env main.ts
```
:::spoiler Screenshots


:::
上述展現了 **Agent** 基於使用者輸入,使用了列表與建立筆記工具的能力。比想像中簡單很多吧!完整 repo:
{%preview https://github.com/EastSun5566/tiny-hackmd-agent %}
## 總結
透過這個實作,我們可以看到 AI Agent 的核心其實很直觀:
- **LLM** 負責理解和決策
- **Loop** 維持持續的對話
- **Tools** 提供與外部世界互動的能力
以上,你也可以寫出自己的 Agent :smiley:
---
#### Credits
- [A practical guide to building agents](https://cdn.openai.com/business-guides-and-resources/a-practical-guide-to-building-agents.pdf)
- [How to Build an Agent](https://ampcode.com/how-to-build-an-agent)
- [Building effective agents](https://www.anthropic.com/engineering/building-effective-agents)
{%hackmd @EastSun5566/license %}