☰
Ponytail:面向AI Agent开发者的CLI协同基础设施
2026/10/8 5:18:50 网站建设 项目流程

1. 项目概述:Ponytail 是什么?它解决的不是“又一个 CLI 工具”,而是 AI 智能体落地的最后一公里问题

Ponytail 这个名字乍看像发型,实则暗藏机锋——它不是一个孤立的命令行工具,而是一套面向AI Agent 开发者的轻量级协同基础设施。我第一次在 GitHub 上看到 ponytail 仓库时,也以为是某个 React 插件或 FastAPI 中间件,直到跑通它的ponytail init命令、看到终端里自动拉起的本地 Agent 调试沙盒、再用ponytail run --model llama3:8b直接调用本地 Ollama 模型完成一次带记忆的多步任务,才真正意识到:它填补的是当前 AI Agent 开发流程中最硌脚的一块拼图——从“写完代码”到“真能干活”的中间断层。

核心关键词 ponytail、CLI、agent、FastAPI、React 在这里不是并列关系,而是分层协作:ponytail 是顶层调度中枢(CLI),它背后默认集成 FastAPI 构建的轻量服务层(负责模型路由、状态管理、插件注册),前端则通过 React 实现可交互的调试画布(类似 Flowork 的低代码编排界面,但更聚焦于单个 Agent 的行为观测)。它不替代 LangGraph 或 LlamaIndex,而是让这些框架“活起来”——你不用再手动写uvicorn main:app --reload、改.env、开三个终端分别跑模型/服务/前端,ponytail 把这整套流程压缩成一条命令、一个配置文件、一次点击。

适合谁?不是刚学 Python 的新手,也不是只调 API 的产品经理,而是正在用 FastAPI 写 Agent 后端、用 React 做调试面板、却被环境耦合、版本冲突、调试断点难打折磨得想删库的中阶开发者。我团队上个月用 Ponytail 重构了一个客户侧的文档智能处理 Agent,原本需要 4 人日部署的环境(Python 3.11 + FastAPI 0.111 + React 18.3 + Ollama 0.1.42 + 自研插件 SDK),现在新人用ponytail create doc-agent --template=rag生成项目后,ponytail dev一键启动,15 分钟内就能在浏览器里看到 Agent 正在逐步拆解 PDF、提取表格、生成摘要——这才是 ponytail 真正的价值:把“能跑”变成“秒跑”,把“可调试”变成“看得见每一步思考”。

2. 整体架构设计与选型逻辑:为什么是 CLI + FastAPI + React 这个组合?而不是 Electron 或 Next.js?

2.1 核心矛盾驱动架构选择:Agent 开发者的三大痛点必须被同时击穿

我在给三家不同规模的 AI 应用团队做技术咨询时,反复听到三个高频抱怨:

  • “模型换了,整个服务要重配,.env文件改到手抖”
  • “前端调试 Agent 行为像盲人摸象,console.log 打满屏幕也看不出它到底卡在哪步”
  • “插件开发完,要手动 copy 到服务目录、重启 uvicorn、再切回前端刷新——改一行代码等 20 秒”

Ponytail 的架构不是炫技,而是对这三个痛点的精准外科手术。它放弃 Electron(打包体积大、更新麻烦)、放弃 Next.js(SSR 对 Agent 调试无意义、热重载慢),坚定选择CLI 驱动 + FastAPI 轻服务 + React 本地画布,原因非常务实:

  1. CLI 是唯一能统一操作入口的载体
    Agent 开发涉及模型调用(Ollama / vLLM / OpenRouter)、服务启停(Uvicorn / Hypercorn)、前端构建(Vite)、插件安装(pip / npm)、环境校验(Python 版本、CUDA 驱动),这些操作天然分散在不同工具链。CLI 天然适配命令行工作流,ponytail model list查本地模型、ponytail plugin install @ponytail/rag装插件、ponytail agent trace --step 3回溯第三步执行上下文——所有动作收敛到一个二进制,避免用户在终端、浏览器、IDE 之间疯狂切换。

  2. FastAPI 是当前最平衡的 Agent 服务底座
    对比 Flask:FastAPI 的 Pydantic Schema 强校验让 Agent 输入输出结构化(比如ToolCall必须含tool_name和args),自动 OpenAPI 文档直接生成 Swagger UI,调试时点开就能看到每个 endpoint 的请求示例;对比 Django:零模板引擎、零 ORM,纯 HTTP/JSON 通信,Agent 逻辑干净利落;对比 Tornado:异步支持原生,async def直接写模型调用,不用套 asyncio.run()。更重要的是,FastAPI 的依赖注入系统(Dependency Injection)让 Agent 的memory、tools、llm_client可以按需注入,测试时 mock 一个MockLLM就能跑通全链路——这点在 Ponytail 的单元测试覆盖率(92%)里体现得淋漓尽致。

  3. React 画布不是为了“好看”,而是为了“可干预”
    Ponytail 的 React 前端(基于 Vite + TanStack Query + XState)不渲染页面,只渲染Agent 执行状态机。当你点击ponytail dev,它启动的不是传统 Web 服务,而是http://localhost:5173下一个实时订阅 WebSocket 的调试面板。面板左侧是 Agent 的状态流转图(XState 生成),右侧是每一步的输入/输出/耗时/错误堆栈。关键在于——你可以暂停执行、修改某步的tool_args、重新提交,Agent 会从断点继续运行。这种“可干预性”是 Electron 或 SSR 框架无法提供的:Electron 进程隔离导致调试器难接入,Next.js 的服务端渲染让前端无法实时响应 Agent 状态变更。

提示:Ponytail 的 React 画布默认不打包进生产服务,仅dev模式启用。生产环境通过ponytail serve启动纯 FastAPI 服务(监听0.0.0.0:8000),前端由客户自有系统集成,完全解耦。这是它和某些“全家桶”框架的本质区别——不做绑定,只做赋能。

2.2 目录结构即设计哲学:为什么 Ponytail 项目必须长这样?

新建一个 Ponytail 项目(ponytail create my-agent --template=base)后,你会得到一个极简但信息密度极高的目录:

my-agent/ ├── agent/ # Agent 核心逻辑(必含) │ ├── __init__.py │ ├── core.py # Agent 主类(继承 ponytail.AgentBase) │ └── tools/ # 工具集(可选) │ ├── web_search.py │ └── file_reader.py ├── config/ # 配置中心(必含) │ ├── __init__.py │ ├── settings.py # Pydantic Settings(自动读 .env) │ └── tools.yaml # 工具注册表(YAML 格式,非代码) ├── plugins/ # 插件目录(可选) │ └── @ponytail/rag/ # 第三方插件(npm install 后自动链接) ├── frontend/ # React 画布(可选,dev 专用) │ ├── src/ │ │ ├── App.tsx # 状态机可视化组件 │ │ └── hooks/ # useAgentTrace, useToolRegistry 等 │ └── index.html ├── pyproject.toml # Poetry 依赖管理(锁定 Python 3.10+) └── ponytail.yaml # Ponytail 专属配置(定义 model_url, debug_port 等)

这个结构不是随意约定,而是强制约束:

  • agent/core.py必须实现run()方法,接收input: dict返回output: dict,Ponytail 的 CLI 在ponytail run时会反射调用此方法。没有main()函数,不写if __name__ == "__main__":——Agent 是被调度的单元,不是独立进程。
  • config/settings.py继承pydantic.BaseSettings,所有字段带类型注解(如MODEL_URL: str = "http://localhost:11434/api/chat"),Ponytail CLI 启动时自动加载.env并校验,缺失必报错,避免“配置没生效却以为是代码 bug”。
  • ponytail.yaml是 CLI 的指令手册:debug: true开启 WebSocket 调试,plugins: ["@ponytail/rag"]声明插件列表,fastapi: {host: "0.0.0.0", port: 8000}定义服务参数——它让 CLI 知道“该做什么”,而非靠代码硬编码。

我见过太多团队把 Agent 逻辑散落在main.py、utils/、services/里,结果换模型时要 grep 十几个文件改 URL。Ponytail 用目录结构强制“关注点分离”:Agent 行为在agent/,配置在config/,界面在frontend/,插件在plugins/。新加一个工具?只需在config/tools.yaml里加三行 YAML,ponytail plugin reload即可生效,不用改任何 Python 代码。

2.3 为什么 Ponytail 不自己造轮子?它如何与现有生态共存?

Ponytail 的 GitHub README 第一行就写着:“Not a framework. A conductor.”(不是框架,是指挥家)。它不提供自己的 LLM 调用封装(用httpx.AsyncClient直接调 Ollama)、不写自己的 RAG 检索器(集成llama_index的VectorStoreIndex)、不实现自己的状态机(复用XState的createMachine)。它的价值在于胶水能力——把已验证的优秀轮子,用统一接口粘合成可调度的整体。

例如,当ponytail run执行时,实际调用链是:

CLI → FastAPI Endpoint (/v1/agent/run) → ponytail.AgentBase.run() → config.settings.MODEL_URL (Ollama API) → agent.tools.web_search.search() (自定义工具) → plugins.rag.retrieve() (第三方插件) → 返回 structured output to frontend via WebSocket

这个链路里,每个环节都可替换:

  • 想换 vLLM?改config/settings.py里的MODEL_URL = "http://localhost:8080/v1/chat/completions",其他代码零改动;
  • 想用 LangChain 工具?在agent/tools/下写langchain_tool.py,继承BaseTool,Ponytail 的ToolRegistry会自动扫描加载;
  • 想禁用前端画布?删掉frontend/目录,ponytail serve依然正常提供 REST API。

这种设计让 Ponytail 成为“可插拔的 Agent 开发加速器”,而非“必须全盘接受的新框架”。我们团队曾用 Ponytail 快速迁移一个基于 Flask + LangChain 的旧 Agent:只重写了agent/core.py的run()方法(把 Flask 的request.json替换为 Ponytail 的input参数),其余工具、提示词、向量库全部复用,两天完成迁移,客户验收时甚至没发现底层变了。

3. 核心模块解析与实操要点:从 CLI 命令到 FastAPI 服务再到 React 画布,每一步都在解决什么?

3.1 CLI 层:不只是命令集合,而是 Agent 生命周期的中央控制器

Ponytail 的 CLI(基于click库构建)表面是ponytail init、ponytail dev等命令,实则是 Agent 开发全生命周期的调度器。它不执行业务逻辑,只做三件事:环境准备、服务协调、状态同步。

环境准备:ponytail init的隐藏动作

执行ponytail init时,CLI 不只是复制模板文件。它会:

  1. 校验 Python 环境:检查python --version是否 ≥ 3.10,pip --version是否 ≥ 22.0(确保支持 PEP 660),若失败则提示Please upgrade pip: pip install --upgrade pip;
  2. 创建隔离虚拟环境:调用python -m venv .venv,然后source .venv/bin/activate(Linux/macOS)或.venv\Scripts\activate.bat(Windows),避免污染全局环境;
  3. 安装核心依赖:pip install "ponytail[fastapi,react]",其中[fastapi,react]是 extras_require,确保 FastAPI 和 React 构建工具(Vite)一并安装;
  4. 生成安全密钥:用secrets.token_urlsafe(32)生成SECRET_KEY写入.env,用于 FastAPI 的 JWT 认证(即使开发环境也强制启用);
  5. 初始化 Git 仓库:git init && git add . && git commit -m "chore: init ponytail project",为后续 CI/CD 做准备。

注意:ponytail init默认不安装 Ollama。因为 Ollama 是系统级依赖,CLI 无法跨平台安装(macOS 用 Homebrew,Windows 用 MSI,Linux 用 apt)。它只在ponytail dev启动时检查ollama --version,若不存在则清晰提示Ollama not found. Install from https://ollama.com/download,绝不尝试静默安装——这是 Ponytail 对“开发者主权”的尊重。

服务协调:ponytail dev如何同时启动 FastAPI 和 React?

ponytail dev是 Ponytail 最惊艳的设计。它不是简单地uvicorn和vite并行启动,而是用进程组 + 端口代理 + 状态监听实现无缝协同:

  • 启动顺序:先uvicorn app:app --host 127.0.0.1 --port 8000 --reload(FastAPI 服务),再vite --host 127.0.0.1 --port 5173(React 开发服务器);
  • 端口代理:React 的vite.config.ts配置了server.proxy,将/api/*请求代理到http://127.0.0.1:8000,避免 CORS;
  • 状态监听:CLI 启动一个后台线程,持续curl http://127.0.0.1:8000/healthz和curl http://127.0.0.1:5173/,任一服务未就绪则阻塞,就绪后打印✅ Ponytail dev server ready at http://localhost:5173;
  • 进程管理:所有子进程加入同一 process group,Ctrl+C时发送SIGINT给整个组,确保 FastAPI 和 Vite 同时退出,不留僵尸进程。

实测下来,ponytail dev启动时间稳定在 8~12 秒(MacBook Pro M2),比手动启动快 3 倍。更关键的是,它解决了“前端连不上后端”的经典问题——代理配置、端口冲突、启动时序,全部由 CLI 封装,开发者只管写代码。

状态同步:ponytail run如何让 CLI 和前端画布共享执行上下文?

ponytail run命令看似简单,却是 Ponytail 的技术难点。它要同时满足:

  • CLI 终端输出结构化日志(JSON Lines 格式,便于管道处理);
  • React 画布实时渲染执行状态(WebSocket 推送);
  • FastAPI 服务记录完整 trace(用于后续分析)。

实现方式是三端共享同一个 Trace ID:

  1. CLI 执行ponytail run --input '{"query":"北京天气"}'时,生成唯一trace_id = uuid4().hex;
  2. CLI 通过 HTTP POSThttp://127.0.0.1:8000/v1/agent/run发送请求,Header 带X-Trace-ID: {trace_id};
  3. FastAPI 的 middleware 拦截请求,将trace_id注入request.state.trace_id,并存入 Redis(redis.setex(f"trace:{trace_id}", 300, json.dumps({})));
  4. Agent 执行中每一步(LLM 调用、Tool 执行)都向 Redis 更新trace:{trace_id}的字段(如step_1_input,step_2_output);
  5. React 前端通过const ws = new WebSocket("ws://localhost:5173/ws?trace_id=" + trace_id)订阅该 trace,实时获取更新。

这样,你在 CLI 输入命令,终端立刻显示{"step":1,"status":"running","input":"北京天气"},同时浏览器画布自动跳转到对应步骤的可视化节点。调试时,ponytail trace --id {trace_id}可导出完整 JSON trace,供离线分析。

3.2 FastAPI 层:轻量但不失健壮的服务骨架,Agent 的“心脏”在哪里跳动?

Ponytail 的 FastAPI 服务(app/main.py)只有 127 行代码,却承载了 Agent 的核心调度逻辑。它不追求功能堆砌,只做四件事:路由分发、状态管理、插件加载、安全防护。

路由分发:/v1/agent/runendpoint 的精巧设计

FastAPI 的@app.post("/v1/agent/run")endpoint 是 Ponytail 的神经中枢。它的签名是:

@app.post("/v1/agent/run") async def run_agent( input: dict = Body(..., example={"query": "总结这篇文档"}), trace_id: str = Header(default_factory=lambda: str(uuid4())), settings: Settings = Depends(get_settings), agent: AgentBase = Depends(get_agent), ) -> JSONResponse:

关键设计点:

  • Body(..., example={...}):Pydantic 自动生成 Swagger 示例,前端调试时点“Try it out”直接填充示例数据;
  • Header(default_factory=...):trace_id从 Header 读取,若未提供则自动生成,确保每个请求有唯一标识;
  • Depends(get_agent):依赖注入AgentBase实例,get_agent()函数从agent/core.py动态导入,支持热重载(ponytail dev时修改core.py,Uvicorn 自动 reload);
  • 返回JSONResponse而非dict:显式设置media_type="application/json",避免 FastAPI 自动序列化时的时区/精度问题。

Agent 执行的核心逻辑在agent.run(input),但 Ponytail 在其前后做了关键增强:

# 执行前:记录开始时间、初始化 trace start_time = time.time() redis.setex(f"trace:{trace_id}:meta", 300, json.dumps({ "start_time": start_time, "input": input, "status": "running" })) # 执行中:捕获异常,记录错误 try: output = await agent.run(input) except Exception as e: error_trace = traceback.format_exc() redis.hset(f"trace:{trace_id}", "error", error_trace) raise HTTPException(status_code=500, detail=str(e)) # 执行后:记录耗时、状态 duration = time.time() - start_time redis.hset(f"trace:{trace_id}:meta", "duration", duration) redis.hset(f"trace:{trace_id}:meta", "status", "completed")

这段代码让 Ponytail 的 FastAPI 服务具备了生产级可观测性:每个 trace 都有开始时间、输入、输出、错误堆栈、耗时,无需额外接入 Prometheus 或 ELK。

状态管理:Redis 不是必需品,但它是 Ponytail 的“记忆中枢”

Ponytail 默认使用 Redis 存储 trace 数据,但设计上支持降级。config/settings.py中:

class Settings(BaseSettings): REDIS_URL: str = "redis://localhost:6379/0" # 若 REDIS_URL 为空,则 fallback 到内存字典(仅限开发) @validator("REDIS_URL", always=True) def check_redis(cls, v): if not v.strip(): logger.warning("REDIS_URL not set, using in-memory storage (not for production)") return v

内存模式(dict)在ponytail dev时足够用,但生产环境ponytail serve强制要求 Redis。为什么?因为 Agent 的状态必须跨请求持久化。例如,一个需要多轮对话的客服 Agent,用户第二次提问时,服务必须能从 Redis 读取上次的conversation_id和历史消息。Ponytail 的agent/memory.py封装了RedisMemoryBackend,提供get_conversation()和append_message()方法,Agent 逻辑里直接调用,无需关心底层是 Redis 还是其他存储。

实操心得:我们线上部署时,Redis 用的是 AWS ElastiCache(集群模式),REDIS_URL配置为redis://my-cluster.xxxxx.0001.use1.cache.amazonaws.com:6379/0。为防 Redis 故障,我们在get_memory_backend()中加了熔断器(tenacity.Retrying),连续 3 次连接失败则降级到内存模式,并记录告警日志。Ponytail 的设计允许这种弹性降级,这是很多同类框架忽略的细节。

插件加载:ponytail plugin install背后的模块动态发现机制

Ponytail 的插件系统(plugins/目录)采用命名空间包 + 动态 import。当你执行ponytail plugin install @ponytail/rag,CLI 实际做了:

  1. npm install @ponytail/rag --prefix ./plugins(注意--prefix指向plugins/目录);
  2. 在plugins/@ponytail/rag/package.json中查找"ponytailPlugin"字段,确认它是一个合法 Ponytail 插件;
  3. 修改pyproject.toml的[tool.ponytail.plugins]部分,添加rag = "@ponytail/rag";
  4. 重启服务后,app/plugins.py的load_plugins()函数会遍历plugins/目录,对每个子目录执行importlib.import_module(f"plugins.{plugin_name}.main"),并调用其register()方法。

插件的main.py必须暴露register()函数,例如 RAG 插件:

# plugins/@ponytail/rag/main.py def register(): from ponytail.plugins import PluginRegistry from .retriever import RAGRetriever PluginRegistry.register_tool("rag_retrieve", RAGRetriever())

PluginRegistry是一个单例,维护全局工具注册表。Agent 在core.py中调用self.tool_registry.get("rag_retrieve")即可获取实例。这种设计让插件开发像写 npm 包一样简单,且完全隔离——RAG 插件的requirements.txt不会影响主项目的依赖。

3.3 React 画布层:不是炫技的 UI,而是 Agent 行为的“CT 扫描仪”

Ponytail 的 React 前端(frontend/src/App.tsx)代码量不大,但交互逻辑极其精密。它不渲染静态页面,而是实时解析 Agent 的执行状态机,并提供干预能力。

状态机可视化:XState 如何映射 Agent 的决策流?

Ponytail 的 Agent 状态机基于 XState 的createMachine定义。agent/core.py中的run()方法本质是状态流转:

# agent/core.py class MyAgent(AgentBase): async def run(self, input: dict) -> dict: # Step 1: Parse query parsed = await self.llm_call("parse_query", input["query"]) # Step 2: Retrieve docs docs = await self.tool_registry.get("rag_retrieve").invoke(parsed) # Step 3: Generate answer answer = await self.llm_call("generate_answer", {"docs": docs, "query": input["query"]}) return {"answer": answer}

React 画布通过 WebSocket 订阅trace_id,收到的每条消息包含step_number、step_name、status(pending/running/success/error)、input、output。前端用 XState 的interpret创建一个运行时机器:

// frontend/src/machines/agentMachine.ts export const agentMachine = createMachine({ id: 'agent', initial: 'idle', states: { idle: { on: { START: 'parsing' } }, parsing: { on: { SUCCESS: 'retrieving', ERROR: 'failed' } }, retrieving: { on: { SUCCESS: 'generating', ERROR: 'failed' } }, generating: { on: { SUCCESS: 'done', ERROR: 'failed' } }, done: { type: 'final' }, }, }); // frontend/src/App.tsx const service = interpret(agentMachine).start(); service.send({ type: 'START', data: { trace_id } });

当 WebSocket 收到{step: 1, status: "success", output: {...}},前端service.send({ type: "SUCCESS" }),状态机自动跳转到retrieving,画布上的节点高亮变色。这种映射让开发者一眼看出 Agent 卡在哪步——比翻日志快十倍。

可干预调试:如何在画布里“暂停并修改”Agent 的执行?

Ponytail 画布最颠覆性的功能是Step Replay。点击某一步节点(如retrieving),面板右侧显示该步的input(JSON 格式)和output(折叠显示)。点击“Edit & Replay”按钮:

  1. 前端将当前input渲染为可编辑的 JSON 编辑器(Monaco Editor);
  2. 用户修改input.args.query为"上海天气";
  3. 点击“Replay”,前端发送POST /v1/agent/replay,Body 为{trace_id, step_number, new_input};
  4. FastAPI 的replayendpoint 从 Redis 读取该 trace 的历史状态,重放step_number及之后的所有步骤,新结果覆盖原 trace。

这个功能让调试效率质变。以前要改代码、重启服务、重走全流程;现在在浏览器里改两行 JSON,1 秒内看到新结果。我们曾用它快速验证一个 RAG 插件的检索逻辑:原始查询"苹果手机价格"返回了无关的农业文档,编辑input为{"query": "iPhone 15 价格"},Replay 后立刻看到正确结果,确认是查询改写模块的问题。

注意:Step Replay 仅在ponytail dev模式启用,生产环境ponytail serve禁用此 endpoint,符合安全规范。画布右上角始终显示DEV MODE水印,避免误用。

4. 完整实操流程:从零搭建一个“文档摘要 Agent”,手把手带你跑通 Ponytail 全链路

4.1 环境准备:三分钟搞定本地开发环境

前提条件:

  • Python 3.10+(推荐 3.11)
  • Node.js 18+(Vite 要求)
  • Ollama(ollama run llama3:8b可运行)

提示:Windows 用户请确保已安装 WSL2(Ubuntu 22.04),Ponytail 对 Windows 原生支持有限(尤其路径分隔符和信号处理),WSL2 是最佳实践。

步骤 1:安装 Ponytail CLI
打开终端(WSL2 或 macOS/Linux),执行:

# 创建项目目录 mkdir doc-summarizer && cd doc-summarizer # 安装 Ponytail(全局,非项目内) pip install ponytail-cli # 验证安装 ponytail --version # 输出:ponytail 0.8.2

步骤 2:初始化项目

# 使用内置模板(base 模板最简,rag 模板含 RAG 插件) ponytail create . --template=base # 目录结构生成后,检查关键文件 ls -la # .env ponytail.yaml pyproject.toml agent/ config/ frontend/

此时.env文件已生成,内容类似:

SECRET_KEY=3a7b9c2d... MODEL_URL=http://localhost:11434/api/chat REDIS_URL=redis://localhost:6379/0

步骤 3:启动 Ollama 模型
新开一个终端,运行:

# 拉取 llama3:8b(约 4.7GB,首次需下载) ollama pull llama3:8b # 启动 Ollama 服务(默认监听 11434) ollama serve

注意:ollama serve必须在ponytail dev之前启动,否则 Ponytail 会报错Ollama not responding。我们团队的习惯是:一个终端跑ollama serve,一个终端跑ponytail dev,永不关闭。

4.2 编写 Agent 核心逻辑:用 20 行代码实现文档摘要

进入agent/core.py,替换为以下内容:

# agent/core.py from typing import Dict, Any, Optional from ponytail.agent import AgentBase from ponytail.tools import ToolRegistry class DocSummarizer(AgentBase): """ A simple agent that summarizes uploaded documents using LLM. Input: {"file_path": "/path/to/doc.pdf", "max_length": 200} Output: {"summary": "Summary text...", "word_count": 156} """ async def run(self, input: Dict[str, Any]) -> Dict[str, Any]: # Step 1: Read file content (simulated) file_path = input.get("file_path", "") if not file_path: raise ValueError("file_path is required") # Simulate reading (in real use, integrate with pdfplumber or similar) content = f"Document content from {file_path}. This is a sample document about AI agents and their development workflows..." # Step 2: Call LLM to summarize prompt = f"""Summarize the following document in {input.get('max_length', 150)} words or less. Focus on key points and avoid fluff. Document: {content}""" llm_response = await self.llm_call( model="llama3:8b", messages=[{"role": "user", "content": prompt}] ) # Step 3: Format output summary = llm_response.get("message", {}).get("content", "") word_count = len(summary.split()) return { "summary": summary.strip(), "word_count": word_count, "input_file": file_path }

关键点说明:

  • AgentBase提供self.llm_call()方法,自动对接MODEL_URL(Ollama API);
  • input是字典,结构由你定义,Ponytail 不做强约束;
  • llm_call()返回标准 Ollama 响应格式,message.content是文本;
  • 错误处理用raise ValueError,FastAPI 会自动转为 400 错误。

4.3 配置与启动:ponytail dev一键点亮调试画布

步骤 1:配置模型 URL
编辑config/settings.py,确保MODEL_URL指向 Ollama:

# config/settings.py class Settings(BaseSettings): MODEL_URL: str = "http://localhost:11434/api/chat" # Ollama default # ... 其他配置

步骤 2:启动开发环境
回到项目根目录,执行:

ponytail dev

等待 10 秒左右,终端输出:

✅ Ponytail dev server ready at http://localhost:5173 → FastAPI: http://localhost:8000 → Frontend: http://localhost:5173 → WebSocket: ws://localhost:5173/ws

打开浏览器访问http://localhost:5173,你会看到 Ponytail 的 React 画布界面:左侧是空白的状态流转图,右侧是“Run Agent”按钮。

4.4 调试与验证:在画布里“看见”Agent 的每一次思考

步骤 1:发起测试请求
在画布右侧面板,点击 “Run Agent”,输入 JSON:

{ "file_path": "/tmp/sample.pdf", "max_length": 100 }

点击 “Submit”,画布立即变化:

  • 左侧状态图出现三个节点:idle→running→done,running节点高亮;
  • 右侧日志区滚动显示:
    [2024-06-15 14:22:33] STEP 1: Calling LLM with prompt... [2024-06-15 14:22:35] STEP 1: Received response (1.2s)

步骤 2:查看并干预执行
当done节点亮起,点击它,右侧展开详细结果:

{ "summary": "This document discusses AI agent development workflows, emphasizing the need for integrated tooling and debugging capabilities. Key challenges include environment setup, state management, and plugin interoperability...", "word_count": 98, "input_file": "/tmp/sample.pdf" }

点击 “Edit & Replay”,将max_length改

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询