agent-computer-interface
Agent-Computer Interface (ACI) — the discipline of designing tools for an Agent, not for a human developer reading API docs. By analogy with HCI: tool design shapes Agent behavior the way UI design shapes human behavior. Per tw93 in 2026-04-27-agent-principles-architecture-engineering, most “Agent picked the wrong tool” debugging traces back to the tool description, not the model. ACI is the engineered descendant of Weng’s 2023 tool-use survey (2026-06-04-llm-powered-autonomous-agents), whose MRKL experiment already found that knowing when and how to call a tool — not the tool itself — is the bottleneck, “determined by the LLM capability”.
Three generations of tool design
| Gen | What it gives the Agent | Failure mode |
|---|---|---|
| API wrapping | One tool per API endpoint, e.g. get_post + update_content + update_title | Granularity matches the API, not the goal — the Agent must coordinate multiple calls to do anything |
| ACI | One tool per Agent goal, e.g. update_yuque_post(post_id, title, content_markdown) | Requires intentional design — a goal-shaped surface area, not just a transcribed API |
| Advanced Tool Use | Discovery, code-orchestration, and example-driven calling layered on top of ACI | Pure ACI tools are still lossy at scale (too many definitions, no examples) — Advanced Tool Use techniques compensate |
Five ACI principles
- Tools map to goals, not endpoints. If completing one user-visible action requires two tool calls, the boundary is wrong.
- Parameter descriptions are constraints.
post_id: stringis useless;post_id: 语雀文章 ID,纯数字字符串,如 "12345678"cuts the format-mistake rate. - Errors carry repair hints. A bare
"Error: update failed"is a dead end. Structured errors witherror_codeandsuggestion: "请先调用 list_yuque_posts 获取有效的 post_id"let the Agent self-correct. - Definition and implementation bound together. The source uses
betaZodTool(Zod schema + handler in one object) so the schema isn’t drift-prone documentation — it’s the actual contract the runtime enforces. - Description tells when to use, not what it does. Like a Skill descriptor, the most actionable bit of a tool description is its use / don’t use boundary.
Advanced Tool Use — three techniques
From the source:
| Technique | Mechanism | Reported impact |
|---|---|---|
| Tool Search | Don’t ship every tool definition every request — let the Agent discover tools via search_tools on demand | Context retention up to ~95%; Opus 4 accuracy 49% → 74% |
| Programmatic Tool Calling | Let the model write code that orchestrates multiple tool calls; intermediate data flows in the execution environment, not through the LLM context | A worked example: ~150K tokens → ~2K tokens |
| Tool Use Examples | 1–5 real call examples embedded with the tool definition — JSON Schema describes types, examples describe how to call | Tool-call accuracy 72% → 90% |
(All numbers from 2026-04-27-agent-principles-architecture-engineering — flagged as needing primary citations; see open questions on the source page.)
Tool framing as harness
ACI sits inside harness: it’s where the feedback signal component meets the execution boundary component. A well-shaped tool gives the Agent a useful error to recover from; a badly-shaped tool turns recoverable failures into infinite loops.
Worked example
Bad — parameters are typed but not described, and errors are opaque:
const tool = {
name: "update_yuque_post",
input_schema: {
properties: {
post_id: { type: "string" },
content: { type: "string" },
},
},
};
return "Error: update failed";
Good — Zod-bound schema, prose constraints in .describe(), structured error with a repair hint:
const updateTool = betaZodTool({
name: "update_yuque_post",
description: "更新语雀文章内容,不适合创建新文章",
inputSchema: z.object({
post_id: z.string().describe("语雀文章 ID,纯数字字符串,如 '12345678'"),
title: z.string().optional().describe("文章标题,不改时可省略"),
content_markdown: z.string().describe("Markdown 格式正文"),
}),
run: async (input) => {
const post = await getPost(input.post_id);
if (!post) throw new ToolError("文章 ID 不存在", {
error_code: "POST_NOT_FOUND",
suggestion: "请先调用 list_yuque_posts 获取有效的 post_id",
});
return await updatePost(input.post_id, input.title, input.content_markdown);
},
});
Internal vs. LLM-visible messages
A practical sub-rule from the source: framework-internal events (compaction triggered, notification dispatched, tool call skipped) belong in the conversation history but not in the LLM context. Use two message types — AgentMessage for application state (carries any custom fields), Message filtered to user / assistant / tool_result for the model. Conversation history stays complete; the LLM sees only what it needs.
Anti-patterns
- One generic
update(id, content)tool wrapped over every endpoint. - Tool descriptions that read like API reference, with no when to use.
- Errors as bare strings.
- Definitions decoupled from implementation — schema documents one shape, code expects another.
- 5+ MCP servers connected by default — definition tax burns the context budget before the user types (model-context-protocol).