Chuyển tới nội dung chính

Nội bộ vòng lặp đại lý

Công cụ điều phối cốt lõi là lớp AIAgent của run_agent.py — khoảng 9.200 dòng xử lý mọi thứ từ lắp ráp nhanh chóng đến gửi công cụ đến chuyển đổi dự phòng của nhà cung cấp.

Trách nhiệm cốt lõi

AIAgent chịu trách nhiệm:

  • Tập hợp các lược đồ công cụ và nhắc nhở hệ thống hiệu quả thông qua prompt_builder.py
  • Chọn đúng chế độ nhà cung cấp/API (chat_completions, codex_responses, anthropic_messages)
  • Thực hiện cuộc gọi mô hình bị gián đoạn với hỗ trợ hủy
  • Thực hiện các lệnh gọi công cụ (tuần tự hoặc đồng thời thông qua nhóm luồng)
  • Duy trì lịch sử hội thoại ở định dạng tin nhắn OpenAI
  • Xử lý việc nén, thử lại và chuyển đổi mô hình dự phòng
  • Theo dõi ngân sách lặp lại giữa các đại lý cha và con
  • Xóa bộ nhớ liên tục trước khi bối cảnh bị mất

Hai điểm vào

# Simple interface — returns final response string
response = agent.chat("Fix the bug in main.py")

# Full interface — returns dict with messages, metadata, usage stats
result = agent.run_conversation(
user_message="Fix the bug in main.py",
system_message=None, # auto-built if omitted
conversation_history=None, # auto-loaded from session if omitted
task_id="task_abc123"
)

chat() là một trình bao bọc mỏng xung quanh run_conversation() trích xuất trường final_response từ lệnh kết quả.

Chế độ API

Hermes hỗ trợ ba chế độ thực thi API, được giải quyết từ việc lựa chọn nhà cung cấp, đối số rõ ràng và chẩn đoán URL cơ sở:

Chế độ APIĐược sử dụng choLoại khách hàng
chat_hoàn thànhĐiểm cuối tương thích với OpenAI (OpenRouter, tùy chỉnh, hầu hết các nhà cung cấp)openai.OpenAI
codex_responsesOpenAI Codex / API phản hồiopenai.OpenAI với định dạng Phản hồi
thông điệp nhân loạiAPI thông điệp nhân loại bản địaanthropic.Anthropic thông qua bộ chuyển đổi

Chế độ này xác định cách định dạng thông báo, cách cấu trúc lệnh gọi công cụ, cách phân tích cú pháp phản hồi và cách hoạt động của bộ nhớ đệm/truyền phát. Cả ba đều hội tụ về cùng một định dạng thông báo nội bộ (các lệnh role/content/tool_calls kiểu OpenAI) trước và sau lệnh gọi API.

Thứ tự phân giải chế độ:

  1. Hàm tạo api_mode rõ ràng (mức độ ưu tiên cao nhất)
  2. Phát hiện dành riêng cho nhà cung cấp (ví dụ: nhà cung cấp anthropicanthropic_messages)
  3. Chẩn đoán URL cơ sở (ví dụ: api.anthropic.comanthropic_messages)
  4. Mặc định: chat_completions

Xoay vòng đời

Mỗi lần lặp của vòng lặp tác nhân tuân theo trình tự sau:

run_conversation()
1. Generate task_id if not provided
2. Append user message to conversation history
3. Build or reuse cached system prompt (prompt_builder.py)
4. Check if preflight compression is needed (>50% context)
5. Build API messages from conversation history
- chat_completions: OpenAI format as-is
- codex_responses: convert to Responses API input items
- anthropic_messages: convert via anthropic_adapter.py
6. Inject ephemeral prompt layers (budget warnings, context pressure)
7. Apply prompt caching markers if on Anthropic
8. Make interruptible API call (_api_call_with_interrupt)
9. Parse response:
- If tool_calls: execute them, append results, loop back to step 5
- If text response: persist session, flush memory if needed, return

Định dạng tin nhắn

Tất cả các tin nhắn đều sử dụng định dạng tương thích với OpenAI trong nội bộ:

{"role": "system", "content": "..."}
{"role": "user", "content": "..."}
{"role": "assistant", "content": "...", "tool_calls": [...]}
{"role": "tool", "tool_call_id": "...", "content": "..."}

Nội dung lý luận (từ các mô hình hỗ trợ tư duy mở rộng) được lưu trữ trong assistant_msg["reasoning"] và được hiển thị tùy ý thông qua reasoning_callback.

Quy tắc luân phiên tin nhắn

Vòng lặp tác nhân thực thi việc luân phiên vai trò thông báo nghiêm ngặt:

  • Sau thông báo hệ thống: Người dùng → Trợ lý → Người dùng → Trợ lý → ...
  • Trong khi gọi công cụ: Trợ lý (với tool_calls) → Công cụ → Công cụ → ... → Trợ lý
  • Không bao giờ hai tin nhắn trợ lý liên tiếp
  • Không bao giờ hai tin nhắn của người dùng liên tiếp
  • Chỉ vai trò tool có thể có các mục liên tiếp (kết quả của công cụ song song)

Nhà cung cấp xác thực các trình tự này và sẽ từ chối các lịch sử không đúng định dạng.

Lệnh gọi API bị gián đoạn

Các yêu cầu API được gói trong _api_call_with_interrupt() chạy lệnh gọi HTTP thực tế trong một luồng nền trong khi theo dõi một sự kiện gián đoạn:

┌──────────────────────┐     ┌──────────────┐
│ Main thread │ │ API thread │
│ wait on: │────▶│ HTTP POST │
│ - response ready │ │ to provider │
│ - interrupt event │ └──────────────┘
│ - timeout │
└──────────────────────┘

Khi bị gián đoạn (người dùng gửi tin nhắn mới, lệnh /stop hoặc tín hiệu):

  • Chuỗi API bị hủy (phản hồi bị loại bỏ)
  • Tác nhân có thể xử lý đầu vào mới hoặc tắt sạch
  • Không có phản hồi một phần nào được đưa vào lịch sử hội thoại

Thực thi công cụ

Tuần tự và Đồng thời

Khi mô hình trả về các lệnh gọi công cụ:

  • Cuộc gọi công cụ đơn → được thực thi trực tiếp trong luồng chính
  • Nhiều lệnh gọi công cụ → được thực thi đồng thời thông qua ThreadPoolExecutor
  • Ngoại lệ: các công cụ được đánh dấu là tương tác (ví dụ: clarify) buộc thực hiện tuần tự
  • Kết quả được chèn lại theo thứ tự gọi công cụ ban đầu bất kể thứ tự hoàn thành

Luồng thực thi

for each tool_call in response.tool_calls:
1. Resolve handler from tools/registry.py
2. Fire pre_tool_call plugin hook
3. Check if dangerous command (tools/approval.py)
- If dangerous: invoke approval_callback, wait for user
4. Execute handler with args + task_id
5. Fire post_tool_call plugin hook
6. Append {"role": "tool", "content": result} to history

Công cụ cấp đại lý

Một số công cụ bị chặn bởi run_agent.py trước khi đạt đến handle_function_call():

Công cụTại sao bị chặn
việc cần làmĐọc/ghi trạng thái tác vụ cục bộ của tác nhân
ký ứcGhi vào các tập tin bộ nhớ liên tục có giới hạn ký tự
phiên_tìm kiếmTruy vấn lịch sử phiên thông qua phiên DB của đại lý
đại biểu_taskSinh ra (các) tác nhân phụ với bối cảnh biệt lập

Những công cụ này trực tiếp sửa đổi trạng thái tác nhân và trả về kết quả của công cụ tổng hợp mà không cần thông qua sổ đăng ký.

Bề mặt gọi lại

AIAgent hỗ trợ các lệnh gọi lại dành riêng cho nền tảng cho phép tiến trình theo thời gian thực trong tích hợp CLI, cổng và ACP:

Gọi lạiKhi bị sa thảiĐược sử dụng bởi
tool_progress_callbackTrước/sau mỗi lần thực hiện công cụCLI spinner, thông báo tiến trình cổng
suy nghĩ_gọi lạiKhi người mẫu bắt đầu/ngưng suy nghĩChỉ báo CLI "suy nghĩ..."
lý luận_gọi lạiKhi mô hình trả về nội dung suy luậnHiển thị lý luận CLI, khối lý luận cổng
làm rõ_gọi lạiKhi công cụ clarify được gọiLời nhắc đầu vào CLI, thông báo tương tác cổng
bước_gọi lạiSau mỗi lượt đại lý hoàn thànhTheo dõi bước cổng, tiến trình ACP
stream_delta_callbackMỗi mã thông báo phát trực tuyến (khi được bật)Hiển thị phát trực tuyến CLI
tool_gen_callbackKhi lệnh gọi công cụ được phân tích cú pháp từ luồngXem trước công cụ CLI trong spinner
trạng thái_gọi lạiThay đổi trạng thái (suy nghĩ, thực thi, v.v.)Cập nhật trạng thái ACP

Hành vi ngân sách và dự phòng

Ngân sách lặp lại

Tác nhân theo dõi các lần lặp lại thông qua IterationBudget:

  • Mặc định: 90 lần lặp (có thể định cấu hình qua agent.max_turns)
  • Được chia sẻ giữa các đại lý gốc và đại lý con — một đại lý phụ tiêu thụ từ ngân sách của đại lý gốc
  • Áp lực ngân sách hai tầng thông qua _get_budget_warning():
  • Ở mức sử dụng trên 70% (cấp thận trọng): thêm [NGÂN SÁCH: Lặp lại X/Y. N lần lặp còn lại. Bắt đầu hợp nhất công việc của bạn.] đến kết quả công cụ cuối cùng
  • Ở mức sử dụng trên 90% (cấp cảnh báo): thêm [CẢNH BÁO NGÂN SÁCH: Lặp lại X/Y. Chỉ còn lại N lần lặp. Cung cấp phản hồi cuối cùng của bạn NGAY BÂY GIỜ.]
  • Khi đạt 100%, đại lý dừng và trả về bản tóm tắt công việc đã thực hiện

Mô hình dự phòng

Khi mô hình chính bị lỗi (giới hạn tốc độ 429, lỗi máy chủ 5xx, lỗi xác thực 401/403):

  1. Kiểm tra danh sách fallback_providers trong config
  2. Thử từng phương án dự phòng theo thứ tự
  3. Khi thành công, hãy tiếp tục cuộc trò chuyện với nhà cung cấp mới
  4. Vào ngày 401/403, hãy thử làm mới thông tin xác thực trước khi vượt qua

Hệ thống dự phòng cũng bao gồm các tác vụ phụ trợ một cách độc lập — mỗi tác vụ trực quan, nén, trích xuất web và tìm kiếm theo phiên đều có chuỗi dự phòng riêng có thể định cấu hình thông qua phần cấu hình phụ trợ.*.

Nén và kiên trì

Khi quá trình nén được kích hoạt

  • Preflight (trước lệnh gọi API): Nếu cuộc hội thoại vượt quá 50% cửa sổ ngữ cảnh của mô hình
  • Tự động nén cổng: Nếu cuộc trò chuyện vượt quá 85% (tích cực hơn, chạy giữa các lượt)

Điều gì xảy ra trong quá trình nén

  1. Bộ nhớ được xóa vào đĩa trước (ngăn ngừa mất dữ liệu)
  2. Các lượt hội thoại giữa được tóm tắt thành một bản tóm tắt ngắn gọn
  3. N tin nhắn cuối cùng được giữ nguyên (compression.protect_last_n, mặc định: 20)
  4. Các cặp thông báo cuộc gọi/kết quả công cụ được giữ cùng nhau (không bao giờ tách rời)
  5. ID dòng phiên mới được tạo (nén tạo ra phiên "con")

Sự kiên trì của phiên

Sau mỗi lượt:

  • Tin nhắn được lưu vào kho phiên (SQLite via hermes_state.py)
  • Các thay đổi về bộ nhớ được xóa thành MEMORY.md / USER.md
  • Phiên này có thể được tiếp tục lại sau thông qua /resume hoặc hermes chat --resume

Tệp nguồn chính

Tập tinMục đích
run_agent.pyLớp AIAgent - vòng lặp tác nhân hoàn chỉnh (~ 9.200 dòng)
agent/Prompt_builder.pyHệ thống nhắc nhở từ trí nhớ, kỹ năng, ngữ cảnh, tính cách
tác nhân/bối cảnh_compressor.pyThuật toán nén hội thoại
agent/Prompt_caching.pyDấu hiệu bộ nhớ đệm nhanh chóng của con người và số liệu bộ nhớ đệm
tác nhân/phụ_client.pyỨng dụng khách LLM phụ trợ cho các nhiệm vụ phụ (tầm nhìn, tóm tắt)
model_tools.pyBộ sưu tập lược đồ công cụ, công văn handle_function_call()

Tài liệu liên quan