Cấu hình, tùy chỉnh, thiết lập nền tảng và tham chiếu API cho Pi.

RPC Chế độ

Chế độ RPC cho phép tác nhân mã hóa hoạt động không đầu thông qua giao thức JSON trên stdin/stdout. Điều này hữu ích cho việc nhúng tác nhân vào các ứng dụng, IDE hoặc giao diện người dùng tùy chỉnh khác.

Lưu ý dành cho người dùng Node.js/TypeScript: Nếu bạn đang xây dựng ứng dụng Node.js, hãy cân nhắc sử dụng AgentSession trực tiếp từ @earendil-works/pi-coding-agent thay vì tạo ra một quy trình con. Xem src/core/agent-session.ts để biết API. Đối với ứng dụng khách TypeScript dựa trên quy trình con, hãy xem src/modes/rpc/rpc-client.ts.

Đang bắt đầu chế độ RPC

pi --mode rpc [options]

Các tùy chọn phổ biến:

  • --provider <name>: Đặt nhà cung cấp LLM (anthropic, openai, google, v.v.)
  • --model <pattern>: Mẫu hoặc ID mẫu (hỗ trợ provider/id và tùy chọn :<thinking>)
  • --name <name> / -n <name>: Đặt tên hiển thị phiên khi khởi động
  • --no-session: Tắt tính năng duy trì phiên
  • --session-dir <path>: Thư mục lưu trữ phiên tùy chỉnh

Tổng quan về giao thức

  • Lệnh: JSON đối tượng được gửi đến stdin, mỗi lệnh một dòng
  • Phản hồi: JSON đối tượng có type: "response" biểu thị lệnh thành công/thất bại
  • Sự kiện: Sự kiện của đại lý được truyền trực tiếp tới stdout dưới dạng dòng JSON

Tất cả các lệnh đều hỗ trợ trường id tùy chọn để tương quan yêu cầu/phản hồi. Nếu được cung cấp, phản hồi tương ứng sẽ bao gồm id tương tự. Các sự kiện bash_execution_update cũng bao gồm id của lệnh bash ban đầu của chúng.

đóng khung

Chế độ RPC sử dụng ngữ nghĩa JSONL nghiêm ngặt với LF (\n) là dấu phân cách bản ghi duy nhất.

Điều này quan trọng đối với khách hàng:

  • Chỉ chia bản ghi trên \n
  • Chấp nhận đầu vào \r\n tùy chọn bằng cách loại bỏ dấu \r
  • Không sử dụng các trình đọc dòng chung coi dấu phân cách Unicode là dòng mới

Đặc biệt, Nút readline không tuân thủ giao thức cho chế độ RPC vì nó cũng phân tách trên U+2028U+2029, hợp lệ bên trong các chuỗi JSON.

Lệnh

Nhắc nhở

nhắc nhở

Gửi lời nhắc của người dùng đến đại lý. Phản hồi lệnh được phát ra sau khi lời nhắc được chấp nhận, xếp hàng hoặc xử lý. Các sự kiện tiếp tục phát trực tuyến không đồng bộ sau khi được chấp nhận.

{"id": "req-1", "type": "prompt", "message": "Hello, world!"}

Với hình ảnh:

{"type": "prompt", "message": "What's in this image?", "images": [{"type": "image", "data": "base64-encoded-data", "mimeType": "image/png"}]}

Trong khi phát trực tuyến: Nếu tác nhân đã phát trực tuyến, bạn phải chỉ định streamingBehavior để xếp hàng tin nhắn:

{"type": "prompt", "message": "New instruction", "streamingBehavior": "steer"}
  • "steer": Xếp hàng tin nhắn trong khi tác nhân đang chạy. Nó được phân phối sau khi lượt trợ lý hiện tại hoàn tất việc thực hiện các lệnh gọi công cụ của nó, trước lệnh gọi LLM tiếp theo.
  • "followUp": Đợi cho đến khi nhân viên kết thúc. Tin nhắn chỉ được gửi khi tác nhân dừng lại.

Nếu tác nhân đang phát trực tuyến và không chỉ định streamingBehavior thì lệnh sẽ trả về lỗi.

Lệnh mở rộng: Nếu thông báo là lệnh mở rộng (ví dụ: /mycommand), thì nó sẽ thực thi ngay lập tức ngay cả khi đang phát trực tuyến. Các lệnh mở rộng quản lý tương tác LLM của riêng chúng thông qua pi.sendMessage().

Mở rộng đầu vào: Lệnh kỹ năng (/skill:name) và prompt templates (/template) được mở rộng trước khi gửi/xếp hàng.

Phản ứng:

{"id": "req-1", "type": "response", "command": "prompt", "success": true}

success: true có nghĩa là lời nhắc đã được chấp nhận, xếp hàng hoặc xử lý ngay lập tức. success: false có nghĩa là lời nhắc đã bị từ chối trước khi được chấp nhận. Lỗi sau khi chấp nhận được báo cáo thông qua luồng tin nhắn và sự kiện thông thường, không phải dưới dạng response thứ hai cho cùng một id yêu cầu.

Trường images là tùy chọn. Mỗi hình ảnh sử dụng định dạng ImageContent: {"type": "image", "data": "base64-encoded-data", "mimeType": "image/png"}.

chỉ đạo

Xếp hàng tin nhắn chỉ đạo trong khi tác nhân đang chạy. Nó được phân phối sau khi lượt trợ lý hiện tại hoàn tất việc thực hiện các lệnh gọi công cụ của nó, trước lệnh gọi LLM tiếp theo. Các lệnh kỹ năng và prompt templates được mở rộng. Các lệnh mở rộng không được phép (thay vào đó hãy sử dụng prompt).

{"type": "steer", "message": "Stop and do this instead"}

Với hình ảnh:

{"type": "steer", "message": "Look at this instead", "images": [{"type": "image", "data": "base64-encoded-data", "mimeType": "image/png"}]}

Trường images là tùy chọn. Mỗi hình ảnh sử dụng định dạng ImageContent (giống như prompt).

Phản ứng:

{"type": "response", "command": "steer", "success": true}

Xem set_steering_mode để biết cách xử lý thông báo điều khiển.

theo dõi

Xếp hàng tin nhắn tiếp theo để xử lý sau khi tác nhân kết thúc. Chỉ được gửi khi đại lý không còn cuộc gọi công cụ hoặc tin nhắn chỉ đạo nào nữa. Các lệnh kỹ năng và prompt templates được mở rộng. Các lệnh mở rộng không được phép (thay vào đó hãy sử dụng prompt).

{"type": "follow_up", "message": "After you're done, also do this"}

Với hình ảnh:

{"type": "follow_up", "message": "Also check this image", "images": [{"type": "image", "data": "base64-encoded-data", "mimeType": "image/png"}]}

Trường images là tùy chọn. Mỗi hình ảnh sử dụng định dạng ImageContent (giống như prompt).

Phản ứng:

{"type": "response", "command": "follow_up", "success": true}

Xem set_follow_up_mode để kiểm soát cách xử lý tin nhắn tiếp theo.

hủy bỏ

Hủy bỏ hoạt động đại lý hiện tại.

{"type": "abort"}

Phản ứng:

{"type": "response", "command": "abort", "success": true}

phiên_mới

Bắt đầu một phiên mới. Có thể bị hủy bởi trình xử lý sự kiện mở rộng session_before_switch.

{"type": "new_session"}

Với tính năng theo dõi phiên gốc tùy chọn:

{"type": "new_session", "parentSession": "/path/to/parent-session.jsonl"}

Phản ứng:

{"type": "response", "command": "new_session", "success": true, "data": {"cancelled": false}}

Nếu tiện ích mở rộng bị hủy:

{"type": "response", "command": "new_session", "success": true, "data": {"cancelled": true}}

Tình trạng

get_state

Nhận trạng thái phiên hiện tại.

{"type": "get_state"}

Phản ứng:

{
  "type": "response",
  "command": "get_state",
  "success": true,
  "data": {
    "model": {...},
    "thinkingLevel": "medium",
    "isStreaming": false,
    "isCompacting": false,
    "steeringMode": "all",
    "followUpMode": "one-at-a-time",
    "sessionFile": "/path/to/session.jsonl",
    "sessionId": "abc123",
    "sessionName": "my-feature-work",
    "autoCompactionEnabled": true,
    "messageCount": 5,
    "pendingMessageCount": 0
  }
}

Trường model là đối tượng Model đầy đủ hoặc null. Trường sessionName là tên hiển thị được đặt qua set_session_name hoặc bị bỏ qua nếu không được đặt.

get_messages

Nhận tất cả tin nhắn trong cuộc trò chuyện.

{"type": "get_messages"}

Phản ứng:

{
  "type": "response",
  "command": "get_messages",
  "success": true,
  "data": {"messages": [...]}
}

Tin nhắn là đối tượng AgentMessage (xem Message Types).

Người mẫu

set_model

Chuyển sang một mô hình cụ thể.

{"type": "set_model", "provider": "anthropic", "modelId": "claude-sonnet-4-20250514"}

Phản hồi chứa đối tượng Model đầy đủ:

{
  "type": "response",
  "command": "set_model",
  "success": true,
  "data": {...}
}

mô hình chu kỳ

Chuyển sang mô hình có sẵn tiếp theo. Trả về dữ liệu null nếu chỉ có một mô hình.

{"type": "cycle_model"}

Phản ứng:

{
  "type": "response",
  "command": "cycle_model",
  "success": true,
  "data": {
    "model": {...},
    "thinkingLevel": "medium",
    "isScoped": false
  }
}

Trường model là một đối tượng Model đầy đủ.

get_available_models

Liệt kê tất cả các mô hình được cấu hình.

{"type": "get_available_models"}

Phản hồi chứa một mảng các đối tượng Model đầy đủ:

{
  "type": "response",
  "command": "get_available_models",
  "success": true,
  "data": {
    "models": [...]
  }
}

suy nghĩ

set_thinking_level

Đặt mức độ lý luận/tư duy cho các mô hình hỗ trợ nó.

{"type": "set_thinking_level", "level": "high"}

Cấp độ: "off", "minimal", "low", "medium", "high", "xhigh", "max"

"xhigh""max" chỉ hiển thị khi được mô hình đã chọn hỗ trợ. Một số kiểu máy, bao gồm GPT-5.6, có cả hai.

Phản ứng:

{"type": "response", "command": "set_thinking_level", "success": true}

chu kỳ_thinking_level

Xoay vòng qua các cấp độ tư duy sẵn có. Trả về dữ liệu null nếu mô hình không hỗ trợ suy nghĩ.

{"type": "cycle_thinking_level"}

Phản ứng:

{
  "type": "response",
  "command": "cycle_thinking_level",
  "success": true,
  "data": {"level": "high"}
}

get_available_thinking_levels

Liệt kê các cấp độ tư duy được hỗ trợ bởi mô hình hiện tại. Trả về ["off"] cho một mô hình không có hỗ trợ lý luận.

{"type": "get_available_thinking_levels"}

Phản ứng:

{
  "type": "response",
  "command": "get_available_thinking_levels",
  "success": true,
  "data": {
    "levels": ["off", "minimal", "low", "medium", "high"]
  }
}

Chế độ xếp hàng

set_steering_mode

Kiểm soát cách gửi thông báo chỉ đạo (từ steer).

{"type": "set_steering_mode", "mode": "one-at-a-time"}

Chế độ:

  • "all": Gửi tất cả thông báo chỉ đạo sau khi lượt trợ lý hiện tại thực hiện xong lệnh gọi công cụ của mình
  • "one-at-a-time": Gửi một thông báo chỉ đạo cho mỗi lượt trợ lý đã hoàn thành (mặc định)

Phản ứng:

{"type": "response", "command": "set_steering_mode", "success": true}

set_follow_up_mode

Kiểm soát cách gửi tin nhắn tiếp theo (từ follow_up).

{"type": "set_follow_up_mode", "mode": "one-at-a-time"}

Chế độ:

  • "all": Gửi tất cả các tin nhắn tiếp theo khi nhân viên hỗ trợ kết thúc
  • "one-at-a-time": Gửi một tin nhắn tiếp theo cho mỗi lần hoàn thành của tổng đài viên (mặc định)

Phản ứng:

{"type": "response", "command": "set_follow_up_mode", "success": true}

nén chặt

nhỏ gọn

Ngữ cảnh hội thoại được thu gọn theo cách thủ công để giảm mức sử dụng mã thông báo.

{"type": "compact"}

Với hướng dẫn tùy chỉnh:

{"type": "compact", "customInstructions": "Focus on code changes"}

Phản ứng:

{
  "type": "response",
  "command": "compact",
  "success": true,
  "data": {
    "summary": "Summary of conversation...",
    "firstKeptEntryId": "abc123",
    "tokensBefore": 150000,
    "estimatedTokensAfter": 32000,
    "usage": {
      "input": 32000,
      "output": 1200,
      "cacheRead": 0,
      "cacheWrite": 0,
      "totalTokens": 33200,
      "cost": {"input": 0.01, "output": 0.02, "cacheRead": 0, "cacheWrite": 0, "total": 0.03}
    },
    "details": {}
  }
}

estimatedTokensAfter là ước tính theo kinh nghiệm đối với bối cảnh thông báo được xây dựng lại ngay sau khi nén, không phải là số lượng mã thông báo chính xác của nhà cung cấp. usage báo cáo cuộc gọi LLM hoặc các cuộc gọi đã tạo ra bản tóm tắt và có thể bị trình xử lý nén tùy chỉnh bỏ qua.

set_auto_compaction

Bật hoặc tắt tính năng nén tự động khi ngữ cảnh gần đầy.

{"type": "set_auto_compaction", "enabled": true}

Phản ứng:

{"type": "response", "command": "set_auto_compaction", "success": true}

Thử lại

set_auto_retry

Bật hoặc tắt tính năng tự động thử lại đối với các lỗi nhất thời (quá tải, giới hạn tốc độ, 5xx).

{"type": "set_auto_retry", "enabled": true}

Phản ứng:

{"type": "response", "command": "set_auto_retry", "success": true}

hủy bỏ_retry

Hủy bỏ quá trình thử lại đang diễn ra (hủy bỏ thời gian trì hoãn và dừng thử lại).

{"type": "abort_retry"}

Phản ứng:

{"type": "response", "command": "abort_retry", "success": true}

đánh

bash

Thực thi lệnh shell và thêm đầu ra vào ngữ cảnh hội thoại. Luồng đầu ra dưới dạng sự kiện bash_execution_update trong khi lệnh chạy; phản hồi chứa kết quả cuối cùng.

{"id": "req-1", "type": "bash", "command": "ls -la"}

Bao gồm id để liên kết các sự kiện bash_execution_update được truyền trực tuyến với lệnh này.

Phản ứng:

{
  "id": "req-1",
  "type": "response",
  "command": "bash",
  "success": true,
  "data": {
    "output": "total 48\ndrwxr-xr-x ...",
    "exitCode": 0,
    "cancelled": false,
    "truncated": false
  }
}

Nếu đầu ra bị cắt bớt, bao gồm fullOutputPath:

{
  "type": "response",
  "command": "bash",
  "success": true,
  "data": {
    "output": "truncated output...",
    "exitCode": 0,
    "cancelled": false,
    "truncated": true,
    "fullOutputPath": "/tmp/pi-bash-abc123.log"
  }
}

** Làm thế nào bash kết quả đạt được LLM:**

Lệnh bash thực thi ngay lập tức và trả về BashResult. Trong nội bộ, BashExecutionMessage được tạo và lưu trữ ở trạng thái tin nhắn của tác nhân.

Khi lệnh prompt tiếp theo được gửi, tất cả tin nhắn (bao gồm BashExecutionMessage) sẽ được chuyển đổi trước khi gửi đến LLM. BashExecutionMessage được chuyển đổi thành UserMessage với định dạng sau:

Ran `ls -la`
```
tổng cộng 48
drwxr-xr-x...
```

Điều này có nghĩa là:

  1. Đầu ra Bash được đưa vào ngữ cảnh LLM trên dấu nhắc tiếp theo, không phải ngay lập tức
  2. Nhiều lệnh bash có thể được thực thi trước lời nhắc; tất cả đầu ra sẽ được bao gồm

hủy bỏ_bash

Hủy bỏ lệnh bash đang chạy.

{"type": "abort_bash"}

Phản ứng:

{"type": "response", "command": "abort_bash", "success": true}

Phiên họp

get_session_stats

Nhận mức sử dụng mã thông báo, thống kê chi phí và mức sử dụng cửa sổ ngữ cảnh hiện tại.

{"type": "get_session_stats"}

Phản ứng:

{
  "type": "response",
  "command": "get_session_stats",
  "success": true,
  "data": {
    "sessionFile": "/path/to/session.jsonl",
    "sessionId": "abc123",
    "userMessages": 5,
    "assistantMessages": 5,
    "toolCalls": 12,
    "toolResults": 12,
    "totalMessages": 22,
    "tokens": {
      "input": 50000,
      "output": 10000,
      "cacheRead": 40000,
      "cacheWrite": 5000,
      "total": 105000
    },
    "cost": 0.45,
    "contextUsage": {
      "tokens": 60000,
      "contextWindow": 200000,
      "percent": 30
    }
  }
}

tokenscost bao gồm các thông báo hỗ trợ, mức sử dụng được các công cụ báo cáo và tạo bản tóm tắt/tóm tắt nhánh trong toàn bộ phiên. contextUsage chứa ước tính cửa sổ ngữ cảnh hiện tại thực tế được sử dụng để nén và hiển thị chân trang.

contextUsage bị bỏ qua khi không có sẵn mô hình hoặc cửa sổ ngữ cảnh. contextUsage.tokenscontextUsage.percentnull ngay sau khi nén cho đến khi có phản hồi trợ lý mới sau khi nén cung cấp dữ liệu sử dụng hợp lệ.

xuất_html

Xuất phiên sang tệp HTML.

{"type": "export_html"}

Với đường dẫn tùy chỉnh:

{"type": "export_html", "outputPath": "/tmp/session.html"}

Phản ứng:

{
  "type": "response",
  "command": "export_html",
  "success": true,
  "data": {"path": "/tmp/session.html"}
}

switch_session

Tải một tập tin phiên khác. Có thể bị hủy bởi trình xử lý sự kiện mở rộng session_before_switch.

{"type": "switch_session", "sessionPath": "/path/to/session.jsonl"}

Phản ứng:

{"type": "response", "command": "switch_session", "success": true, "data": {"cancelled": false}}

Nếu tiện ích mở rộng đã hủy quá trình chuyển đổi:

{"type": "response", "command": "switch_session", "success": true, "data": {"cancelled": true}}

cái nĩa

Tạo một nhánh mới từ tin nhắn của người dùng trước đó trên nhánh đang hoạt động. Có thể bị hủy bởi trình xử lý sự kiện mở rộng session_before_fork. Trả về nội dung của tin nhắn được phân nhánh từ đó.

{"type": "fork", "entryId": "abc123"}

Phản ứng:

{
  "type": "response",
  "command": "fork",
  "success": true,
  "data": {"text": "The original prompt text...", "cancelled": false}
}

Nếu tiện ích mở rộng đã hủy phân nhánh:

{
  "type": "response",
  "command": "fork",
  "success": true,
  "data": {"text": "The original prompt text...", "cancelled": true}
}

dòng vô tính

Sao chép nhánh hoạt động hiện tại vào một phiên mới ở vị trí hiện tại. Có thể bị hủy bởi trình xử lý sự kiện mở rộng session_before_fork.

{"type": "clone"}

Phản ứng:

{
  "type": "response",
  "command": "clone",
  "success": true,
  "data": {"cancelled": false}
}

Nếu tiện ích mở rộng đã hủy bản sao:

{
  "type": "response",
  "command": "clone",
  "success": true,
  "data": {"cancelled": true}
}

get_fork_messages

Nhận tin nhắn của người dùng có sẵn để phân nhánh.

{"type": "get_fork_messages"}

Phản ứng:

{
  "type": "response",
  "command": "get_fork_messages",
  "success": true,
  "data": {
    "messages": [
      {"entryId": "abc123", "text": "First prompt..."},
      {"entryId": "def456", "text": "Second prompt..."}
    ]
  }
}

get_entries

Nhận tất cả các mục phiên theo thứ tự nối thêm (không bao gồm tiêu đề phiên). Phiên là một cây mục chỉ bổ sung có id ổn định, do đó, id mục nhập hoạt động như một con trỏ bền: chuyển id mục nhập cuối cùng mà bạn đã thấy là since để chỉ nhận các mục nhập nghiêm ngặt sau nó, ngay cả khi máy khách khởi động lại. Không giống như get_messages, phần này bao gồm lịch sử nén trước và các nhánh bị bỏ rơi.

{"type": "get_entries"}

Với một con trỏ:

{"type": "get_entries", "since": "abc123"}

Phản ứng:

{
  "type": "response",
  "command": "get_entries",
  "success": true,
  "data": {
    "entries": [
      {"type": "message", "id": "def456", "parentId": "abc123", "timestamp": "...", "message": {"role": "user", "...": "..."}}
    ],
    "leafId": "def456"
  }
}

leafId là id của mục nhập lá hiện tại (null cho phiên trống), do đó, khách hàng có thể biết trong một lần xem liệu nhánh đang hoạt động có di chuyển hay không. Nếu since không khớp với bất kỳ id mục nhập nào thì phản hồi là success: false.

lấy_cây

Nhận phiên làm việc dưới dạng cây mục. Mỗi nút là {entry, children, label?, labelTimestamp?}. Một phiên được định dạng đúng có một gốc duy nhất; các mục mồ côi (chuỗi gốc bị hỏng) cũng xuất hiện dưới dạng gốc.

{"type": "get_tree"}

Phản ứng:

{
  "type": "response",
  "command": "get_tree",
  "success": true,
  "data": {
    "tree": [
      {
        "entry": {"type": "message", "id": "abc123", "parentId": null, "...": "..."},
        "children": [
          {"entry": {"type": "message", "id": "def456", "parentId": "abc123", "...": "..."}, "children": []}
        ]
      }
    ],
    "leafId": "def456"
  }
}

get_last_assistant_text

Lấy nội dung văn bản của tin nhắn trợ lý cuối cùng.

{"type": "get_last_assistant_text"}

Phản ứng:

{
  "type": "response",
  "command": "get_last_assistant_text",
  "success": true,
  "data": {"text": "The assistant's response..."}
}

Trả về {"text": null} nếu không có tin nhắn trợ lý nào tồn tại.

set_session_name

Đặt tên hiển thị cho phiên hiện tại. Tên xuất hiện trong danh sách phiên và giúp xác định phiên.

{"type": "set_session_name", "name": "my-feature-work"}

Phản ứng:

{
  "type": "response",
  "command": "set_session_name",
  "success": true
}

Tên phiên hiện tại có sẵn thông qua get_state trong trường sessionName. Để đặt tên ban đầu khi bắt đầu chế độ RPC, hãy chuyển --name <name> hoặc -n <name> cho quy trình pi --mode rpc.

Lệnh

get_commands

Nhận các lệnh có sẵn (lệnh mở rộng, prompt templates và kỹ năng). Chúng có thể được gọi thông qua lệnh prompt bằng cách thêm tiền tố /.

{"type": "get_commands"}

Phản ứng:

{
  "type": "response",
  "command": "get_commands",
  "success": true,
  "data": {
    "commands": [
      {"name": "session-name", "description": "Set or clear session name", "source": "extension", "path": "/home/user/.pi/agent/extensions/session.ts"},
      {"name": "fix-tests", "description": "Fix failing tests", "source": "prompt", "location": "project", "path": "/home/user/myproject/.pi/agent/prompts/fix-tests.md"},
      {"name": "skill:brave-search", "description": "Web search via Brave API", "source": "skill", "location": "user", "path": "/home/user/.pi/agent/skills/brave-search/SKILL.md"}
    ]
  }
}

Mỗi lệnh có:

  • name: Tên lệnh (gọi bằng /name)
  • description: Mô tả mà con người có thể đọc được (tùy chọn cho các lệnh mở rộng)
  • source: Loại lệnh gì:
    • "extension": Đã đăng ký qua pi.registerCommand() trong tiện ích mở rộng
    • "prompt": Được tải từ tệp mẫu lời nhắc .md
    • "skill": Được tải từ thư mục kỹ năng (tên có tiền tố là skill:)
  • location: Nó được tải từ đâu (tùy chọn, không có cho tiện ích mở rộng):
    • "user": Cấp độ người dùng (~/.pi/agent/)
    • "project": Cấp dự án (./.pi/agent/)
    • "path": Đường dẫn rõ ràng qua CLI hoặc cài đặt
  • path: Đường dẫn tệp tuyệt đối đến nguồn lệnh (tùy chọn)

Lưu ý: Không bao gồm các lệnh TUI tích hợp (/settings, /hotkeys, v.v.). Chúng chỉ được xử lý ở chế độ tương tác và sẽ không thực thi nếu được gửi qua prompt.

Sự kiện

Các sự kiện được truyền trực tuyến đến các dòng stdout dưới dạng JSON trong quá trình vận hành tổng đài viên. Các sự kiện thường không bao gồm trường id; bash_execution_update bao gồm id của lệnh bash ban đầu khi lệnh này được cung cấp.

Các loại sự kiện

Sự kiện Sự miêu tả
agent_start Đại lý bắt đầu xử lý
agent_end Một lần chạy tác nhân cấp thấp hoàn tất (vẫn có thể được theo sau bởi việc thử lại, nén hoặc tiếp tục xếp hàng đợi)
agent_settled Hoạt động đại lý đã được giải quyết đầy đủ; không còn tự động thử lại, thử lại nén hoặc tiếp tục xếp hàng
turn_start Lượt mới bắt đầu
turn_end Lần lượt hoàn tất (bao gồm thông báo trợ lý và kết quả công cụ)
message_start Tin nhắn bắt đầu
message_update Cập nhật trực tuyến (văn bản/suy nghĩ/đồng bằng cuộc gọi công cụ)
message_end Tin nhắn hoàn tất
bash_execution_update Đoạn đầu ra lệnh trực tiếp RPC bash
tool_execution_start Công cụ bắt đầu thực hiện
tool_execution_update Tiến trình thực hiện công cụ (đầu ra phát trực tuyến)
tool_execution_end Công cụ hoàn thành
queue_update Đã thay đổi hàng đợi chỉ đạo/theo dõi đang chờ xử lý
compaction_start Quá trình nén bắt đầu
compaction_end Quá trình nén hoàn tất
auto_retry_start Tự động thử lại bắt đầu (sau lỗi thoáng qua)
auto_retry_end Tự động thử lại hoàn tất (thành công hoặc thất bại cuối cùng)
summarization_retry_scheduled Đã lên lịch thử lại cho lỗi nén tạm thời hoặc lỗi tóm tắt tóm tắt nhánh
summarization_retry_attempt_start Đã bắt đầu yêu cầu tóm tắt lại
summarization_retry_finished Vòng lặp thử lại tóm tắt hoàn tất
extension_error Tiện ích mở rộng đã gây ra lỗi

đại lý_bắt đầu

Được phát ra khi tác nhân bắt đầu xử lý lời nhắc.

{"type": "agent_start"}

đại lý_end

Được phát ra khi một lần chạy tác nhân cấp thấp hoàn tất. Chứa tất cả các tin nhắn được tạo trong quá trình chạy này. Nếu willRetry đúng, quá trình thử lại tự động sẽ diễn ra.

{
  "type": "agent_end",
  "messages": [...],
  "willRetry": false
}

đại lý_đã giải quyết

Được phát ra sau khi quá trình chạy ở cấp phiên hoàn tất được giải quyết. Tại thời điểm này, Pi sẽ không tự động tiếp tục thông qua thử lại, thử lại nén hoặc các tin nhắn theo dõi được xếp hàng đợi.

{"type": "agent_settled"}

lượt_bắt đầu / lượt_kết thúc

Một lượt bao gồm một phản hồi trợ lý cộng với bất kỳ cuộc gọi và kết quả công cụ nào được tạo ra.

{"type": "turn_start"}
{
  "type": "turn_end",
  "message": {...},
  "toolResults": [...]
}

tin nhắn_bắt đầu / tin nhắn_end

Được phát ra khi một tin nhắn bắt đầu và kết thúc. Trường message chứa AgentMessage.

{"type": "message_start", "message": {...}}
{"type": "message_end", "message": {...}}

message_update (Truyền phát)

Được phát ra trong quá trình truyền phát tin nhắn trợ lý. Chứa sự kiện delta không có ảnh chụp nhanh thông báo tích lũy.

{
  "type": "message_update",
  "assistantMessageEvent": {
    "type": "text_delta",
    "contentIndex": 0,
    "delta": "Hello "
  }
}

Trường assistantMessageEvent chứa một trong các loại delta sau:

Kiểu Sự miêu tả
text_start Khối nội dung văn bản đã bắt đầu
text_delta Đoạn nội dung văn bản
text_end Khối nội dung văn bản đã kết thúc
thinking_start Khối suy nghĩ bắt đầu
thinking_delta Đoạn nội dung suy nghĩ
thinking_end Khối suy nghĩ đã kết thúc
toolcall_start Cuộc gọi công cụ đã bắt đầu
toolcall_delta Đoạn đối số cuộc gọi công cụ
toolcall_end Cuộc gọi công cụ đã kết thúc (bao gồm đối tượng toolCall đầy đủ)

Ví dụ truyền phát phản hồi văn bản:

{"type":"message_update","assistantMessageEvent":{"type":"text_start","contentIndex":0}}
{"type":"message_update","assistantMessageEvent":{"type":"text_delta","contentIndex":0,"delta":"Hello"}}
{"type":"message_update","assistantMessageEvent":{"type":"text_delta","contentIndex":0,"delta":" world"}}
{"type":"message_update","assistantMessageEvent":{"type":"text_end","contentIndex":0,"content":"Hello world"}}

message_update cố tình bỏ qua trường message tích lũy trước đây và assistantMessageEvent.partial. Khách hàng cần tin nhắn trực tiếp một phần phải tập hợp nó từ message_start và các sự kiện tiếp theo sử dụng contentIndex. Điều trị message_end.message như có thẩm quyền. Đối với các lệnh gọi công cụ, bộ đệm toolcall_delta.delta; toolcall_end.toolCall chứa cuộc gọi đã hoàn thành.

bash_thực thi_cập nhật

Được phát ra một lần cho mỗi đoạn đầu ra từ lệnh bash trực tiếp. id khớp với id của lệnh, cho phép khách hàng liên kết đầu ra với lệnh chính xác.

Các sự kiện truyền phát tất cả đầu ra trong khi lệnh chạy, ngay cả khi phản hồi bash cuối cùng của output bị cắt bớt.

{
  "type": "bash_execution_update",
  "id": "req-1",
  "delta": "total 48\n"
}

tool_execution_start / tool_execution_update / tool_execution_end

Được phát ra khi một công cụ bắt đầu, truyền tiến trình và hoàn tất quá trình thực thi.

{
  "type": "tool_execution_start",
  "toolCallId": "call_abc123",
  "toolName": "bash",
  "args": {"command": "ls -la"}
}

Trong quá trình thực thi, các sự kiện tool_execution_update truyền một phần kết quả (ví dụ: đầu ra bash khi nó đến):

{
  "type": "tool_execution_update",
  "toolCallId": "call_abc123",
  "toolName": "bash",
  "args": {"command": "ls -la"},
  "partialResult": {
    "content": [{"type": "text", "text": "partial output so far..."}],
    "details": {"truncation": null, "fullOutputPath": null}
  }
}

Khi hoàn thành:

{
  "type": "tool_execution_end",
  "toolCallId": "call_abc123",
  "toolName": "bash",
  "result": {
    "content": [{"type": "text", "text": "total 48\n..."}],
    "details": {...}
  },
  "isError": false
}

Sử dụng toolCallId để liên hệ các sự kiện. partialResult trong tool_execution_update chứa đầu ra tích lũy cho đến nay (không chỉ delta), cho phép khách hàng chỉ cần thay thế màn hình của họ trên mỗi bản cập nhật.

hàng_cập nhật

Được phát ra bất cứ khi nào chỉ đạo đang chờ xử lý hoặc hàng đợi theo dõi thay đổi.

{
  "type": "queue_update",
  "steering": ["Focus on error handling"],
  "followUp": ["After that, summarize the result"]
}

nén_start / nén_end

Phát ra khi quá trình nén diễn ra, dù là thủ công hay tự động.

{"type": "compaction_start", "reason": "threshold"}

Trường reason"manual", "threshold" hoặc "overflow".

{
  "type": "compaction_end",
  "reason": "threshold",
  "result": {
    "summary": "Summary of conversation...",
    "firstKeptEntryId": "abc123",
    "tokensBefore": 150000,
    "estimatedTokensAfter": 32000,
    "usage": {
      "input": 32000,
      "output": 1200,
      "cacheRead": 0,
      "cacheWrite": 0,
      "totalTokens": 33200,
      "cost": {"input": 0.01, "output": 0.02, "cacheRead": 0, "cacheWrite": 0, "total": 0.03}
    },
    "details": {}
  },
  "aborted": false,
  "willRetry": false
}

Nếu reason"overflow" và quá trình nén thành công, willRetrytrue và tác nhân sẽ tự động thử lại lời nhắc.

Nếu quá trình nén bị hủy bỏ, resultnullabortedtrue.

Nếu quá trình nén không thành công (ví dụ: vượt quá hạn ngạch API), resultnull, abortedfalseerrorMessage chứa mô tả lỗi.

auto_retry_start / auto_retry_end

Được phát ra khi kích hoạt tự động thử lại sau một lỗi nhất thời (quá tải, giới hạn tốc độ, 5xx).

{
  "type": "auto_retry_start",
  "attempt": 1,
  "maxAttempts": 3,
  "delayMs": 2000,
  "errorMessage": "529 {\"type\":\"error\",\"error\":{\"type\":\"overloaded_error\",\"message\":\"Overloaded\"}}"
}
{
  "type": "auto_retry_end",
  "success": true,
  "attempt": 2
}

Trong lần thất bại cuối cùng (vượt quá số lần thử lại tối đa):

{
  "type": "auto_retry_end",
  "success": false,
  "attempt": 3,
  "finalError": "529 overloaded_error: Overloaded"
}

tóm tắt_retry_scheduled / tóm tắt_retry_attempt_start / tóm tắt_retry_kết thúc

Được phát ra khi thử lại quá trình nén hoặc tóm tắt tóm tắt nhánh sau lỗi của nhà cung cấp tạm thời. Những sự kiện này sử dụng cài đặt thử lại giống như các lần thử lại theo lượt trợ lý tự động.

{
  "type": "summarization_retry_scheduled",
  "attempt": 1,
  "maxAttempts": 3,
  "delayMs": 2000,
  "errorMessage": "terminated"
}
{
  "type": "summarization_retry_attempt_start",
  "source": "compaction",
  "reason": "threshold"
}

Đối với tóm tắt nhánh, source"branchSummary" và không có reason.

{
  "type": "summarization_retry_finished"
}

phần mở rộng_error

Được phát ra khi tiện ích mở rộng gặp lỗi.

{
  "type": "extension_error",
  "extensionPath": "/path/to/extension.ts",
  "event": "tool_call",
  "error": "Error message..."
}

Giao thức UI mở rộng

Extensions có ​​thể yêu cầu tương tác của người dùng thông qua ctx.ui.select(), ctx.ui.confirm(), v.v. Ở chế độ RPC, những điều này được dịch thành giao thức phụ yêu cầu/phản hồi ở đầu luồng lệnh/sự kiện cơ sở.

Có hai loại phương thức giao diện người dùng mở rộng:

  • Phương thức hộp thoại (select, confirm, input, editor): phát ra extension_ui_request trên stdout và chặn cho đến khi khách hàng gửi lại extension_ui_response trên stdin với id phù hợp.
  • Phương pháp bắn và quên (notify, setStatus, setWidget, setTitle, set_editor_text): phát ra extension_ui_request trên stdout nhưng không mong đợi phản hồi. Khách hàng có thể hiển thị thông tin hoặc bỏ qua nó.

Nếu phương thức hộp thoại bao gồm trường timeout thì phía tác nhân sẽ tự động giải quyết bằng giá trị mặc định khi hết thời gian chờ. Khách hàng không cần phải theo dõi thời gian chờ.

Một số phương thức ExtensionUIContext không được hỗ trợ hoặc xuống cấp ở chế độ RPC vì chúng yêu cầu quyền truy cập trực tiếp TUI:

  • custom() trả về undefined
  • setWorkingMessage(), setWorkingIndicator(), setFooter(), setHeader(), setEditorComponent(), setToolsExpanded() là không hoạt động
  • getEditorText() trả về ""
  • getToolsExpanded() trả về false
  • pasteToEditor() ủy quyền cho setEditorText() (không xử lý dán/thu gọn)
  • getAllThemes() trả về []
  • getTheme() trả về undefined
  • setTheme() trả về { success: false, error: "..." }

Lưu ý: ctx.mode"rpc"ctx.hasUItrue ở chế độ RPC vì hộp thoại và các phương thức bắn và quên hoạt động thông qua giao thức phụ UI mở rộng. Sử dụng ctx.mode === "tui" để bảo vệ các tính năng dành riêng cho TUI như custom() yêu cầu thiết bị đầu cuối thực sự.

Yêu cầu giao diện người dùng mở rộng (stdout)

Tất cả các yêu cầu đều có type: "extension_ui_request", một trường id duy nhất và một trường method.

lựa chọn

Nhắc người dùng chọn từ danh sách. Các phương thức hộp thoại có trường timeout bao gồm thời gian chờ tính bằng mili giây; đại lý tự động giải quyết bằng undefined nếu khách hàng không phản hồi kịp thời.

{
  "type": "extension_ui_request",
  "id": "uuid-1",
  "method": "select",
  "title": "Allow dangerous command?",
  "options": ["Allow", "Block"],
  "timeout": 10000
}

Phản hồi dự kiến: extension_ui_response với value (chuỗi tùy chọn đã chọn) hoặc cancelled: true.

xác nhận

Nhắc người dùng xác nhận có/không.

{
  "type": "extension_ui_request",
  "id": "uuid-2",
  "method": "confirm",
  "title": "Clear session?",
  "message": "All messages will be lost.",
  "timeout": 5000
}

Phản hồi dự kiến: extension_ui_response với confirmed: true/false hoặc cancelled: true.

đầu vào

Nhắc người dùng về văn bản dạng tự do.

{
  "type": "extension_ui_request",
  "id": "uuid-3",
  "method": "input",
  "title": "Enter a value",
  "placeholder": "type something..."
}

Phản hồi dự kiến: extension_ui_response với value (văn bản đã nhập) hoặc cancelled: true.

biên tập viên

Mở trình soạn thảo văn bản nhiều dòng với nội dung được điền sẵn tùy chọn.

{
  "type": "extension_ui_request",
  "id": "uuid-4",
  "method": "editor",
  "title": "Edit some text",
  "prefill": "Line 1\nLine 2\nLine 3"
}

Phản hồi dự kiến: extension_ui_response với value (văn bản đã chỉnh sửa) hoặc cancelled: true.

thông báo

Hiển thị một thông báo. Bắn và quên, không có phản hồi mong đợi.

{
  "type": "extension_ui_request",
  "id": "uuid-5",
  "method": "notify",
  "message": "Command blocked by user",
  "notifyType": "warning"
}

Trường notifyType"info", "warning" hoặc "error". Mặc định là "info" nếu bị bỏ qua.

setStatus

Đặt hoặc xóa mục nhập trạng thái trong thanh chân trang/trạng thái. Bắn và quên đi.

{
  "type": "extension_ui_request",
  "id": "uuid-6",
  "method": "setStatus",
  "statusKey": "my-ext",
  "statusText": "Turn 3 running..."
}

Gửi statusText: undefined (hoặc bỏ qua) để xóa mục nhập trạng thái cho khóa đó.

setWidget

Đặt hoặc xóa một tiện ích (khối dòng văn bản) hiển thị phía trên hoặc bên dưới trình chỉnh sửa. Bắn và quên đi.

{
  "type": "extension_ui_request",
  "id": "uuid-7",
  "method": "setWidget",
  "widgetKey": "my-ext",
  "widgetLines": ["--- My Widget ---", "Line 1", "Line 2"],
  "widgetPlacement": "aboveEditor"
}

Gửi widgetLines: undefined (hoặc bỏ qua) để xóa tiện ích. Trường widgetPlacement"aboveEditor" (mặc định) hoặc "belowEditor". Chỉ mảng chuỗi được hỗ trợ ở chế độ RPC; các nhà máy sản xuất linh kiện được bỏ qua.

setTitle

Đặt tiêu đề cửa sổ/tab đầu cuối. Bắn và quên đi.

{
  "type": "extension_ui_request",
  "id": "uuid-8",
  "method": "setTitle",
  "title": "pi - my project"
}

set_editor_text

Đặt văn bản trong trình soạn thảo đầu vào. Bắn và quên đi.

{
  "type": "extension_ui_request",
  "id": "uuid-9",
  "method": "set_editor_text",
  "text": "prefilled text for the user"
}

Phản hồi giao diện người dùng tiện ích mở rộng (stdin)

Phản hồi chỉ được gửi cho các phương thức hộp thoại (select, confirm, input, editor). id phải phù hợp với yêu cầu.

Phản hồi giá trị (chọn, nhập, chỉnh sửa)

{"type": "extension_ui_response", "id": "uuid-1", "value": "Allow"}

Phản hồi xác nhận (xác nhận)

{"type": "extension_ui_response", "id": "uuid-2", "confirmed": true}

Phản hồi hủy (bất kỳ hộp thoại nào)

Loại bỏ bất kỳ phương pháp hộp thoại nào. Tiện ích mở rộng nhận được undefined (để chọn/nhập/chỉnh sửa) hoặc false (để xác nhận).

{"type": "extension_ui_response", "id": "uuid-3", "cancelled": true}

Xử lý lỗi

Các lệnh không thành công sẽ trả về phản hồi với success: false:

{
  "type": "response",
  "command": "set_model",
  "success": false,
  "error": "Model not found: invalid/model"
}

Lỗi phân tích cú pháp:

{
  "type": "response",
  "command": "parse",
  "success": false,
  "error": "Failed to parse command: Unexpected token..."
}

Các loại

Các tập tin nguồn:

Người mẫu

{
  "id": "claude-sonnet-4-20250514",
  "name": "Claude Sonnet 4",
  "api": "anthropic-messages",
  "provider": "anthropic",
  "baseUrl": "https://api.anthropic.com",
  "reasoning": true,
  "input": ["text", "image"],
  "contextWindow": 200000,
  "maxTokens": 16384,
  "cost": {
    "input": 3.0,
    "output": 15.0,
    "cacheRead": 0.3,
    "cacheWrite": 3.75
  }
}

Tin nhắn người dùng

{
  "role": "user",
  "content": "Hello!",
  "timestamp": 1733234567890,
  "attachments": []
}

Trường content có thể là một chuỗi hoặc một mảng gồm các khối TextContent/ImageContent.

Trợ lýTin nhắn

{
  "role": "assistant",
  "content": [
    {"type": "text", "text": "Hello! How can I help?"},
    {"type": "thinking", "thinking": "User is greeting me..."},
    {"type": "toolCall", "id": "call_123", "name": "bash", "arguments": {"command": "ls"}}
  ],
  "api": "anthropic-messages",
  "provider": "anthropic",
  "model": "claude-sonnet-4-20250514",
  "usage": {
    "input": 100,
    "output": 50,
    "cacheRead": 0,
    "cacheWrite": 0,
    "cost": {"input": 0.0003, "output": 0.00075, "cacheRead": 0, "cacheWrite": 0, "total": 0.00105}
  },
  "stopReason": "stop",
  "timestamp": 1733234567890
}

Lý do dừng: "stop", "length", "toolUse", "error", "aborted"

Công cụKết quảThông báo

{
  "role": "toolResult",
  "toolCallId": "call_123",
  "toolName": "bash",
  "content": [{"type": "text", "text": "total 48\ndrwxr-xr-x ..."}],
  "usage": {
    "input": 100,
    "output": 50,
    "cacheRead": 0,
    "cacheWrite": 0,
    "totalTokens": 150,
    "cost": {"input": 0.0003, "output": 0.00075, "cacheRead": 0, "cacheWrite": 0, "total": 0.00105}
  },
  "isError": false,
  "timestamp": 1733234567890
}

usage là tùy chọn và báo cáo công việc LLM lồng nhau được thực hiện bởi công cụ. Khi xuất hiện, nó đóng góp vào mã thông báo phiên và tổng chi phí.

BashThực thiTin nhắn

Được tạo bởi lệnh bash RPC (không phải bằng lệnh gọi công cụ LLM):

{
  "role": "bashExecution",
  "command": "ls -la",
  "output": "total 48\ndrwxr-xr-x ...",
  "exitCode": 0,
  "cancelled": false,
  "truncated": false,
  "fullOutputPath": null,
  "timestamp": 1733234567890
}

Tệp đính kèm

{
  "id": "img1",
  "type": "image",
  "fileName": "photo.jpg",
  "mimeType": "image/jpeg",
  "size": 102400,
  "content": "base64-encoded-data...",
  "extractedText": null,
  "preview": null
}

Ví dụ: Máy khách cơ bản (Python)

import subprocess
import json

proc = subprocess.Popen(
    ["pi", "--mode", "rpc", "--no-session"],
    stdin=subprocess.PIPE,
    stdout=subprocess.PIPE,
    text=True
)

def send(cmd):
    proc.stdin.write(json.dumps(cmd) + "\n")
    proc.stdin.flush()

def read_events():
    for line in proc.stdout:
        yield json.loads(line)

# Send prompt
send({"type": "prompt", "message": "Hello!"})

# Process events
for event in read_events():
    if event.get("type") == "message_update":
        delta = event.get("assistantMessageEvent", {})
        if delta.get("type") == "text_delta":
            print(delta["delta"], end="", flush=True)
    
    if event.get("type") == "agent_end":
        print()
        break

Ví dụ: Ứng dụng khách tương tác (Node.js)

Xem test/rpc-example.ts để biết ví dụ tương tác hoàn chỉnh hoặc src/modes/rpc/rpc-client.ts để biết cách triển khai ứng dụng khách đã nhập.

Để biết ví dụ đầy đủ về cách xử lý giao thức UI tiện ích mở rộng, hãy xem examples/rpc-extension-ui.ts kết hợp với tiện ích mở rộng examples/extensions/rpc-demo.ts.

const { spawn } = require("child_process");
const { StringDecoder } = require("string_decoder");

const agent = spawn("pi", ["--mode", "rpc", "--no-session"]);

function attachJsonlReader(stream, onLine) {
    const decoder = new StringDecoder("utf8");
    let buffer = "";

    stream.on("data", (chunk) => {
        buffer += typeof chunk === "string" ? chunk : decoder.write(chunk);

        while (true) {
            const newlineIndex = buffer.indexOf("\n");
            if (newlineIndex === -1) break;

            let line = buffer.slice(0, newlineIndex);
            buffer = buffer.slice(newlineIndex + 1);
            if (line.endsWith("\r")) line = line.slice(0, -1);
            onLine(line);
        }
    });

    stream.on("end", () => {
        buffer += decoder.end();
        if (buffer.length > 0) {
            onLine(buffer.endsWith("\r") ? buffer.slice(0, -1) : buffer);
        }
    });
}

attachJsonlReader(agent.stdout, (line) => {
    const event = JSON.parse(line);

    if (event.type === "message_update") {
        const { assistantMessageEvent } = event;
        if (assistantMessageEvent.type === "text_delta") {
            process.stdout.write(assistantMessageEvent.delta);
        }
    }
});

// Send prompt
agent.stdin.write(JSON.stringify({ type: "prompt", message: "Hello" }) + "\n");

// Abort on Ctrl+C
process.on("SIGINT", () => {
    agent.stdin.write(JSON.stringify({ type: "abort" }) + "\n");
});