Skip to content
签到签到

进阶第 12 章:结项 Agent 项目

业务问题

前面章节是零散知识。最后一章要完成一个完整项目,并把知识组织成面试可讲的故事。

交互图解:Agent、Harness 与插件服务全景 · 查看图解导读

核心原理

项目不是堆功能,而是证明你能处理:

  • 架构分层。
  • 流式状态。
  • 工具调用。
  • 权限和取消。
  • 会话恢复。
  • 错误和观测。

真实项目中的映射

项目:一个 AI 编码助手 / 客服 Agent。

核心页面:

  • 会话列表。
  • 聊天流。
  • 工具调用卡片。
  • 权限批准。
  • 执行轨迹。
  • 模型选择。

核心后端抽象:

  • SessionLog。
  • ToolRegistry。
  • LlmPort。
  • AgentLoop。
  • PermissionPolicy。

DeepSeek Harness 对应实现

真实项目可以借鉴 DeepSeek Harness:

  • 会话日志作为真相。
  • 工具通过 registry 注册。
  • 模型通过 adapter seam 接入。
  • 前端使用 React-free 对象层和快照。
  • 权限通过 pre-execute 决策。

进一步阅读:

  • docs/architecture.md
  • docs/cookbook/extension-cookbook.md

代码示例

最小 Agent 服务

ts
interface AgentServices {
  log: {
    append(message: { role: string; content: string }): void
    all(): Array<{ role: string; content: string }>
  }
  tools: {
    list(): Array<{ name: string; description: string }>
    run(name: string, args: string): Promise<string>
  }
  llm: {
    complete(messages: Array<{ role: string; content: string }>): Promise<{
      text: string
      toolCall?: { name: string; args: string }
    }>
  }
}

async function runAgent(services: AgentServices, task: string): Promise<string> {
  services.log.append({ role: 'user', content: task })

  for (let step = 0; step < 8; step += 1) {
    const reply = await services.llm.complete(services.log.all())
    services.log.append({ role: 'assistant', content: reply.text })

    if (!reply.toolCall) return reply.text

    const result = await services.tools.run(reply.toolCall.name, reply.toolCall.args)
    services.log.append({ role: 'tool', content: result })
  }

  return 'max steps reached'
}

React 页面

tsx
import { useState } from 'react'

function AgentApp({ agent }: { agent: { run(task: string): Promise<string> } }) {
  const [task, setTask] = useState('')
  const [answer, setAnswer] = useState('')
  const [status, setStatus] = useState<'idle' | 'running' | 'done'>('idle')

  async function submit() {
    setStatus('running')
    setAnswer('')
    const result = await agent.run(task)
    setAnswer(result)
    setStatus('done')
  }

  return (
    <main>
      <textarea value={task} onChange={event => setTask(event.target.value)} />
      <button onClick={submit} disabled={status === 'running'}>运行</button>
      <pre>{answer}</pre>
    </main>
  )
}

这个最小项目可以用 mock LLM 和本地工具运行,不需要真实 API Key。

面试追问

问:这个项目的难点是什么?

回答要点:

  • 流式 token 高频更新。
  • 工具调用中间状态。
  • 权限和取消。
  • 会话恢复和回放。
  • 模型输出不可控。

问:你如何证明项目不只是 demo?

回答要点:

  • 有状态机。
  • 有错误处理。
  • 有事件日志。
  • 有测试。
  • 有权限和审计。

问:如果重新设计,你会改进什么?

回答要点:

  • 增强重连和去重。
  • 增加压缩策略。
  • 增加评估集。
  • 增加 trace 和监控。
  • 使用更成熟协议。

实践任务

完成一个最小 AI 编码助手 / 客服 Agent:

  • 实现 SessionLog、ToolRegistry、LlmPort、AgentLoop。
  • 用 mock LLM 完成一个工具调用闭环。
  • 用 React 展示流式结果和工具状态。
  • 写出 README、测试和面试叙事。

验收标准

  • 项目能运行,不依赖真实 API Key。
  • 能展示至少一次工具调用。
  • 能处理取消和错误。
  • 能解释架构、难点和监控方案。