第 14 章:最小 Agent 与面试叙事
本章解决的问题
最后一步是把前面概念变成可运行代码和可讲的项目故事。
交互图解:一次 Agent Turn 完整调用链 · 查看图解导读
大白话理解
你不需要实现 DeepSeek Harness 的全部能力。只要实现一个最小闭环,就能理解 Agent 的核心。
最小闭环:
text
Context
-> ToolRegistry
-> SessionLog
-> LlmPort
-> AgentLoop
-> React UI必须掌握的概念
1. LlmPort
模型能力应该抽象成接口,不要直接绑定某个供应商。
2. ToolRegistry
工具注册、Schema 和 execute 要集中管理。
3. SessionLog
用只追加日志保存消息和工具结果。
4. AgentLoop
AgentLoop 负责把输入、模型、工具和日志串起来。
5. UI
React UI 负责输入、流式展示和工具状态。
DeepSeek Harness 对应实现
DeepSeek Harness 的最小组成可以简化为:
ctx.sessionsctx.systemPromptctx.toolsctx.agentsctx.agentLoopctx.llm
你不必复刻 Cordis,但可以复刻这些抽象。
进一步阅读:
packages/core/README.mddocs/architecture.md
代码示例
最小 TypeScript Harness
ts
type Role = 'user' | 'assistant' | 'tool'
interface Message {
role: Role
content: string
}
interface Tool {
name: string
description: string
run(args: string): Promise<string>
}
interface LlmPort {
complete(messages: Message[], tools: Tool[]): Promise<{
text: string
toolCall?: { name: string; args: string }
}>
}
class ToolRegistry {
private tools = new Map<string, Tool>()
register(tool: Tool): void {
this.tools.set(tool.name, tool)
}
list(): Tool[] {
return [...this.tools.values()]
}
async run(name: string, args: string): Promise<string> {
const tool = this.tools.get(name)
if (!tool) throw new Error(`unknown tool "${name}"`)
return tool.run(args)
}
}
class SessionLog {
private messages: Message[] = []
append(message: Message): void {
this.messages.push(message)
}
all(): Message[] {
return [...this.messages]
}
}
class AgentLoop {
constructor(
private llm: LlmPort,
private tools: ToolRegistry,
private log: SessionLog,
) {}
async run(task: string): Promise<string> {
this.log.append({ role: 'user', content: task })
for (let step = 0; step < 8; step += 1) {
const reply = await this.llm.complete(this.log.all(), this.tools.list())
this.log.append({ role: 'assistant', content: reply.text })
if (!reply.toolCall) return reply.text
const result = await this.tools.run(reply.toolCall.name, reply.toolCall.args)
this.log.append({ role: 'tool', content: result })
}
return 'max steps reached'
}
}最小 React 页面
tsx
import { useState } from 'react'
function AgentPage({ agent }: { agent: { run(task: string): Promise<string> } }) {
const [task, setTask] = useState('')
const [answer, setAnswer] = useState('')
async function submit() {
setAnswer('')
const result = await agent.run(task)
setAnswer(result)
}
return (
<div>
<textarea value={task} onChange={event => setTask(event.target.value)} />
<button onClick={submit}>运行</button>
<pre>{answer}</pre>
</div>
)
}面试怎么问
问:如果让你设计一个前端 AI Agent 平台,你会怎么开始?
回答要点:
- 先拆出模型层、工具层、会话层和 UI 层。
- 模型层用接口隔离供应商。
- 工具层用注册表和策略管线。
- 会话层用事件日志。
- UI 层处理流式、工具状态和权限。
问:你如何证明自己理解 Agent,而不只是用过 API?
回答要点:
- 展示自己实现的 Agent Loop。
- 说明如何回传工具结果。
- 说明如何处理取消、流式和错误。
- 说明事件日志如何支持恢复和审计。
问:3 到 5 个月怎么规划学习?
回答要点:
- 第 1 个月:LLM、流式、ReAct、工具调用。
- 第 2 个月:DeepSeek Harness 架构和 Cordis。
- 第 3 个月:React-free 对象层、快照、前端流式 UI。
- 第 4 个月:RAG、MCP、安全、持久化。
- 第 5 个月:最小项目、面试题和项目叙事。
自测题
- 最小 Agent Harness 需要哪五类抽象?
LlmPort的作用是什么?- 工具结果如何回传给模型?
- 你的项目叙事如何体现前端价值?
本章小结
完成一个最小 Agent 后,你已经具备回答面试问题的基础:不是“我会调 API”,而是“我理解 Agent 的循环、工具、记忆、权限和前端状态设计”。
进一步阅读:
docs/cordis-tutorial/07-into-the-harness.mddocs/cookbook/adding-a-tool.md