Skip to content

Latest commit

ย 

History

History
827 lines (686 loc) ยท 34.1 KB

File metadata and controls

827 lines (686 loc) ยท 34.1 KB

Claude Code Python ๅฎž็Žฐๅˆ†ๆžๆŠฅๅ‘Š

้กน็›ฎ่ทฏๅพ„: claude-code-python/ ็Šถๆ€: ๐Ÿšง ่ฟ›่กŒไธญ๏ผˆ็ผบๅฐ‘ memory/hook ๆจกๅ—๏ผ‰ ๅฏนๅบ”ๆบ็ : instructkr/claude-code


ไธ€ใ€ๆ•ดไฝ“ๆžถๆž„ๆ€ป่งˆ

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                        main.py                              โ”‚
โ”‚                   (CLI ๅ…ฅๅฃ ยท typer)                       โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                         โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                     QueryEngine                             โ”‚
โ”‚                   (ๆ ธๅฟƒ Agent Loop)                         โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚
โ”‚  โ”‚  while not done:                                     โ”‚  โ”‚
โ”‚  โ”‚    response = call_llm(context)                     โ”‚  โ”‚
โ”‚  โ”‚    parse tool_calls from response                    โ”‚  โ”‚
โ”‚  โ”‚    execute tools via registry                        โ”‚  โ”‚
โ”‚  โ”‚    add results to context                            โ”‚  โ”‚
โ”‚  โ”‚    repeat                                           โ”‚  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                         โ”‚
         โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
         โ–ผ               โ–ผ               โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ AgentContext โ”‚  โ”‚ ToolRegistry โ”‚  โ”‚  LLM Client  โ”‚
โ”‚  (ไธŠไธ‹ๆ–‡็ฎก็†) โ”‚  โ”‚  (ๅทฅๅ…ทๆณจๅ†Œ)  โ”‚  โ”‚  (API ่ฐƒ็”จ)  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                         โ”‚
                         โ–ผ
              โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
              โ”‚  Tool (ๆŠฝ่ฑกๅŸบ็ฑป)    โ”‚
              โ”‚  โ”œโ”€ FileReadTool   โ”‚
              โ”‚  โ”œโ”€ FileEditTool   โ”‚
              โ”‚  โ”œโ”€ BashTool       โ”‚
              โ”‚  โ””โ”€ GlobTool       โ”‚
              โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

ๆ ธๅฟƒๆจกๅ—่Œ่ดฃ

ๆจกๅ— ๆ–‡ไปถ ่Œ่ดฃ
QueryEngine agent/query_engine.py Agent ไธปๅพช็Žฏ๏ผšๅ‘่ฏทๆฑ‚โ†’่งฃๆžโ†’ๆ‰ง่กŒโ†’ๅพช็Žฏ
AgentContext agent/context.py ๆถˆๆฏๅކๅฒใ€token ไผฐ็ฎ—ใ€ไธŠไธ‹ๆ–‡ๆˆชๆ–ญ
ToolRegistry tool/registry.py ๅ…จๅฑ€ๅทฅๅ…ทๆณจๅ†Œ่กจ๏ผŒLLM ๆŸฅ่ฏขๅทฅๅ…ท
Tool tool/base.py ๅทฅๅ…ทๆŠฝ่ฑกๅŸบ็ฑป + ๆƒ้™ๆจกๅž‹
ๅ†…็ฝฎๅทฅๅ…ท tool/builtin/*.py ๅ…ทไฝ“ๅทฅๅ…ทๅฎž็Žฐ

ไบŒใ€QueryEngine ่ฏฆ่งฃ๏ผˆๆ ธๅฟƒๅผ•ๆ“Ž๏ผ‰

2.1 ไป€ไนˆๆ˜ฏ QueryEngine๏ผŸ

QueryEngine ๆ˜ฏ Agent ็š„ไธปๅพช็Žฏๅผ•ๆ“Ž๏ผŒๅฏนๅบ” Claude Code ๆบ็ ไธญ็š„ src/query.tsใ€‚ๅฎƒ้ฉฑๅŠจๆ•ดไธช Agent ็š„่ฟ่ฝฌ๏ผš

็”จๆˆท่พ“ๅ…ฅ โ†’ QueryEngine.run() โ†’ LLM API โ†’ ่งฃๆžๅ“ๅบ” โ†’ ๆ‰ง่กŒๅทฅๅ…ท โ†’ ่ฟ”ๅ›ž็ป“ๆžœ

2.2 ๆ ธๅฟƒๆต็จ‹ๅ›พ

                    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                    โ”‚  ็”จๆˆท่พ“ๅ…ฅๆถˆๆฏ   โ”‚
                    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                             โ–ผ
              โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
              โ”‚  self.context.add_user_   โ”‚
              โ”‚    message(msg)           โ”‚
              โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                       โ–ผ
         โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
         โ”‚     while ๅพช็Žฏๅผ€ๅง‹         โ”‚
         โ”‚  iteration++              โ”‚
         โ”‚  state = THINKING         โ”‚
         โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                  โ–ผ
    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
    โ”‚   self._call_llm()         โ”‚ โ—„โ”€โ”€โ”€ ๆ ธๅฟƒ่ฐƒ็”จ
    โ”‚   ๆž„้€ ่ฏทๆฑ‚:                 โ”‚
    โ”‚   - model                   โ”‚
    โ”‚   - max_tokens              โ”‚
    โ”‚   - messages (ไธŠไธ‹ๆ–‡)       โ”‚
    โ”‚   - tools (ๅทฅๅ…ทๅฎšไน‰)         โ”‚
    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
             โ–ผ
    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
    โ”‚  ่งฃๆž LLM ๅ“ๅบ”:            โ”‚
    โ”‚  - content (ๆ–‡ๆœฌๅ›žๅค)       โ”‚
    โ”‚  - tool_calls (ๅทฅๅ…ท่ฐƒ็”จ)    โ”‚
    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
             โ–ผ
    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
    โ”‚  self.context.add_assistant_message โ”‚
    โ”‚  (ๅฐ†ๅ›žๅคๅŠ ๅ…ฅไธŠไธ‹ๆ–‡)                 โ”‚
    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
             โ–ผ
    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
    โ”‚  ๆœ‰ tool_calls ?                    โ”‚
    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
             โ”‚
     โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
     โ”‚ Yes           โ”‚ No
     โ–ผ               โ–ผ
    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
    โ”‚ๆ‰ง่กŒๅทฅๅ…ท โ”‚    โ”‚  ็ป“ๆŸ (break)  โ”‚
    โ”‚ๅพช็Žฏ     โ”‚    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
    โ””โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”˜
         โ–ผ
    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
    โ”‚  _execute_tool_calls()     โ”‚
    โ”‚  ้ๅކๆฏไธช tool_call:        โ”‚
    โ”‚  1. lookup tool in registryโ”‚
    โ”‚  2. check_permission()     โ”‚
    โ”‚  3. prompt if ASK mode      โ”‚
    โ”‚  4. tool.execute()         โ”‚
    โ”‚  5. context.add_tool_resultโ”‚
    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
             โ–ผ
    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
    โ”‚  Check:                         โ”‚
    โ”‚  - iteration >= max_iterations  โ”‚
    โ”‚  - _stop_event is set           โ”‚
    โ”‚  - no more tool_calls           โ”‚
    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
             โ”‚
      โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”
      โ”‚ Continue    โ”‚โ”€โ”€โ–บ ๅ›žๅˆฐ while ๅพช็Žฏ
      โ”‚ or Break    โ”‚
      โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

2.3 ๅ…ณ้”ฎ็ฑป่ฎพ่ฎก

AgentConfig๏ผˆ้…็ฝฎ็ฑป๏ผ‰

@dataclass
class AgentConfig:
    model: str = "claude-sonnet-4-20250514"      # LLM ๆจกๅž‹
    max_iterations: int = 100                     # ๆœ€ๅคงๅพช็Žฏๆฌกๆ•ฐ
    max_tool_calls_per_iteration: int = 128       # ๅ•ๆฌกๆœ€ๅคงๅทฅๅ…ท่ฐƒ็”จๆ•ฐ
    temperature: float = 0                        # ๆธฉๅบฆๅ‚ๆ•ฐ
    system_prompt: Optional[str] = None            # ็ณป็ปŸๆ็คบ่ฏ
    stream: bool = True                            # ๆ˜ฏๅฆๆตๅผ่พ“ๅ‡บ
    verbose: bool = False                          # ่ฏฆ็ป†ๆจกๅผ

AgentState๏ผˆ็Šถๆ€ๆžšไธพ๏ผ‰

class AgentState(Enum):
    IDLE = "idle"                    # ็ฉบ้—ฒ
    THINKING = "thinking"              # ๆ€่€ƒไธญ๏ผˆ่ฐƒ็”จ LLM๏ผ‰
    TOOL_CALLING = "tool_calling"     # ๆ‰ง่กŒๅทฅๅ…ทไธญ
    AWAITING_PERMISSION = "awaiting_permission"  # ็ญ‰ๅพ…็”จๆˆทๆŽˆๆƒ
    DONE = "done"                     # ๅฎŒๆˆ
    ERROR = "error"                   # ้”™่ฏฏ

2.4 LLM ่ฐƒ็”จ่ฏฆ่งฃ๏ผˆ_call_llm๏ผ‰

async def _call_llm(self) -> Any:
    # 1. ่Žทๅ–ๆˆ–ๅˆ›ๅปบ LLM ๅฎขๆˆท็ซฏ
    if self.llm_client is None:
        api_key = os.environ.get("ANTHROPIC_API_KEY")
        self.llm_client = AsyncAnthropic(api_key=api_key)
    
    # 2. ๆž„้€ ่ฏทๆฑ‚ๆถˆๆฏ
    messages = self.context.get_messages()    # ไปŽไธŠไธ‹ๆ–‡่Žทๅ–ๅކๅฒ
    tools = self.tool_registry.get_llm_tools() # ่Žทๅ–ๅทฅๅ…ทๅฎšไน‰
    
    request_options = {
        "model": self.config.model,
        "max_tokens": 8192,
        "messages": messages,
        "tools": tools,
    }
    
    # 3. ๅ‘้€่ฏทๆฑ‚๏ผˆๆ”ฏๆŒๆตๅผ๏ผ‰
    if self.config.stream:
        async with self.llm_client.messages.stream(**request_options) as stream:
            response = await stream.get_final_message()
    else:
        response = await self.llm_client.messages.create(**request_options)
    
    return response

2.5 ๅทฅๅ…ทๆ‰ง่กŒ่ฏฆ่งฃ๏ผˆ_execute_tool_calls๏ผ‰

async def _execute_tool_calls(self, tool_calls: List[Dict[str, Any]]) -> None:
    for tc in tool_calls:
        tool_name = tc["name"]
        tool_input = tc["input"]
        tool_id = tc["id"]
        
        # Step 1: ไปŽๆณจๅ†Œ่กจๆŸฅๆ‰พๅทฅๅ…ท
        tool = self.tool_registry.get(tool_name)
        if tool is None:
            self.context.add_tool_result(f"Unknown tool: {tool_name}", tool_id)
            continue
        
        # Step 2: ๆƒ้™ๆฃ€ๆŸฅ
        allowed, reason = tool.check_permission()
        if not allowed:
            self.context.add_tool_result(f"Permission denied: {reason}", tool_id)
            continue
        
        # Step 3: ASK ๆจกๅผ้œ€็”จๆˆท็กฎ่ฎค
        if tool.permission.mode == PermissionMode.ASK:
            if not self._prompt_permission(tool):
                self.context.add_tool_result("User denied permission", tool_id)
                continue
        
        # Step 4: ๆ‰ง่กŒๅทฅๅ…ท
        try:
            result = await tool.execute(tool_input)
        except Exception as e:
            result = ToolResult.err(f"Tool execution error: {e}")
        
        # Step 5: ๅฐ†็ป“ๆžœๅŠ ๅ…ฅไธŠไธ‹ๆ–‡
        self.context.add_tool_result(result.content, tool_id)

ไธ‰ใ€Context ็ณป็ปŸ่ฏฆ่งฃ๏ผˆไธŠไธ‹ๆ–‡็ฎก็†ๆ ธๅฟƒ๏ผ‰

Context ็ณป็ปŸๆ˜ฏ Agent ็š„่ฎฐๅฟ†ไธญๆžข๏ผŒ่ดŸ่ดฃ็ฎก็†ๆ•ดไธชๅฏน่ฏ็š„็”Ÿๅ‘ฝๅ‘จๆœŸใ€‚Agent ๅš็š„ไธ€ๅˆ‡ๅ†ณ็ญ–้ƒฝๅŸบไบŽไธŠไธ‹ๆ–‡๏ผŒๆฒกๆœ‰ไธŠไธ‹ๆ–‡ๅฐฑๆฒกๆœ‰ๆ™บ่ƒฝใ€‚

3.1 ไธบไป€ไนˆ้œ€่ฆ Context ็ณป็ปŸ๏ผŸ

LLM ๆœ‰ไธŠไธ‹ๆ–‡็ช—ๅฃ้™ๅˆถ๏ผˆClaude ็บฆ 200K tokens๏ผ‰ใ€‚ๅฝ“ๅฏน่ฏ่ถŠๆฅ่ถŠ้•ฟๆ—ถ๏ผš

้—ฎ้ข˜๏ผš
1. ๅฏน่ฏๅކๅฒ่ถŠๆฅ่ถŠ้•ฟ โ†’ ่ถ…่ฟ‡ไธŠไธ‹ๆ–‡็ช—ๅฃ
2. Token ๆˆๆœฌ่ถŠๆฅ่ถŠ้ซ˜ โ†’ ๆตช่ดน้’ฑ
3. ๆจกๅž‹ๅฎนๆ˜“"้—ๅฟ˜"ๆ—ฉๆœŸไฟกๆฏ โ†’ ๅ†ณ็ญ–่ดจ้‡ไธ‹้™

Context ็ณป็ปŸ่งฃๅ†ณๆ–นๆกˆ๏ผš
1. ่ทŸ่ธชๆถˆๆฏๅކๅฒ๏ผŒ่‡ชๅŠจ็ฎก็†็”Ÿๅ‘ฝๅ‘จๆœŸ
2. ็ฒพ็กฎไผฐ็ฎ— token๏ผŒๆŽฅ่ฟ‘ไธŠ้™ๆ—ถๆˆชๆ–ญ
3. ไผ˜ๅ…ˆไฟ็•™ๆœ€้‡่ฆ็š„ไฟกๆฏ๏ผˆ็ณป็ปŸๆ็คบ + ๆœ€่ฟ‘ๅฏน่ฏ๏ผ‰

3.2 ๆ ธๅฟƒๆ•ฐๆฎ็ป“ๆž„

MessageRole๏ผˆๆถˆๆฏ่ง’่‰ฒๆžšไธพ๏ผ‰

class MessageRole(Enum):
    """
    ๆถˆๆฏ็š„ๅ‘้€่€…่ง’่‰ฒ๏ผŒๅ†ณๅฎšไบ† LLM ๅฆ‚ไฝ•็†่งฃ่ฟ™ๆกๆถˆๆฏใ€‚
    ๅฏนๅบ” Claude Code ๆบ็ : src/context.ts ~Role~
    
    ๅ››็ง่ง’่‰ฒ๏ผš
    - system: ็ณป็ปŸๆŒ‡ไปค๏ผˆๅช่ฏป๏ผŒไธ่ฎกๅ…ฅๅทฅๅ…ท่ฐƒ็”จ๏ผ‰
    - user: ็”จๆˆท่พ“ๅ…ฅ
    - assistant: LLM ่‡ชๅทฑ็š„ๅ›žๅค
    - tool: ๅทฅๅ…ทๆ‰ง่กŒ็ป“ๆžœ
    """
    SYSTEM = "system"      # ็ณป็ปŸๆ็คบ๏ผŒๆœ€้‡่ฆ๏ผŒๆฐธไธๅˆ ้™ค
    USER = "user"          # ็”จๆˆทๆถˆๆฏ
    ASSISTANT = "assistant"# LLM ๅ›žๅค๏ผˆๅซ tool_calls๏ผ‰
    TOOL = "tool"          # ๅทฅๅ…ท็ป“ๆžœ

Message๏ผˆๅ•ๆกๆถˆๆฏ๏ผ‰

@dataclass
class Message:
    """
    ๅฏนๅบ” Claude Code ๆบ็ : src/context.ts ~Message~ ๆŽฅๅฃ
    
    ไธ€ๆกๆถˆๆฏ็š„ๆ ธๅฟƒ็ป“ๆž„๏ผš
    - role: ่ฐ่ฏด็š„
    - content: ่ฏดไบ†ไป€ไนˆ
    - tool_calls: ๅทฅๅ…ท่ฐƒ็”จๅˆ—่กจ๏ผˆassistant ๆถˆๆฏไธ“็”จ๏ผ‰
    - tool_call_id: ๆœฌๆถˆๆฏๅฏนๅบ”็š„ๅทฅๅ…ท่ฐƒ็”จ ID๏ผˆtool ๆถˆๆฏไธ“็”จ๏ผ‰
    """
    role: MessageRole
    content: str = ""
    tool_calls: Optional[List[Dict[str, Any]]] = None  # assistant ไธ“็”จ
    tool_call_id: Optional[str] = None                  # tool ไธ“็”จ
    name: Optional[str] = None                          # ๅทฅๅ…ทๅ๏ผˆtool ไธ“็”จ๏ผ‰
    timestamp: float = field(default_factory=time.time)  # ๆ—ถ้—ดๆˆณ

ๆถˆๆฏ็”Ÿๅ‘ฝๅ‘จๆœŸ็คบไพ‹๏ผš

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    ไธ€ไธชๅฎŒๆ•ด็š„ Tool Call ๅฏน่ฏ                      โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                                  โ”‚
โ”‚  [User]                                                           โ”‚
โ”‚  { role: "user", content: "Read ./README.md" }                   โ”‚
โ”‚                                                                  โ”‚
โ”‚  [Assistant + ToolCall]                                           โ”‚
โ”‚  {                                                             โ”‚
โ”‚    role: "assistant",                                           โ”‚
โ”‚    content: "I'll read the file...",                            โ”‚
โ”‚    tool_calls: [                                                โ”‚
โ”‚      { id: "tool_1", name: "read", input: { path: "./README" } }โ”‚
โ”‚    ]                                                             โ”‚
โ”‚  }                                                               โ”‚
โ”‚                                                                  โ”‚
โ”‚  [Tool Result]                                                    โ”‚
โ”‚  {                                                             โ”‚
โ”‚    role: "tool",                                                โ”‚
โ”‚    content: "# Project\n\nThis is...",                          โ”‚
โ”‚    tool_call_id: "tool_1",                                      โ”‚
โ”‚    name: "read"                                                 โ”‚
โ”‚  }                                                               โ”‚
โ”‚                                                                  โ”‚
โ”‚  [Assistant Final]                                                โ”‚
โ”‚  {                                                             โ”‚
โ”‚    role: "assistant",                                           โ”‚
โ”‚    content: "The README contains..."                             โ”‚
โ”‚  }                                                               โ”‚
โ”‚                                                                  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

ToolCall๏ผˆๅทฅๅ…ท่ฐƒ็”จ๏ผ‰

@dataclass
class ToolCall:
    """
    ไปฃ่กจไธ€ๆฌกๅทฅๅ…ท่ฐƒ็”จ่ฏทๆฑ‚ใ€‚
    ๅฏนๅบ” Claude Code ๆบ็ : src/context.ts ~ToolCall~ ๆŽฅๅฃ
    
    LLM ็”Ÿๆˆ tool_calls๏ผŒๆ ผๅผไธบ๏ผš
    {
      "id": "tool_1",           # ๅ”ฏไธ€ๆ ‡่ฏ†
      "name": "read",           # ๅทฅๅ…ทๅ
      "input": { "path": "..." } # ๅทฅๅ…ทๅ‚ๆ•ฐ
    }
    """
    id: str                      # ๅ”ฏไธ€ ID
    name: str                    # ๅทฅๅ…ทๅ
    input_data: Dict[str, Any]  # ๅทฅๅ…ทๅ‚ๆ•ฐ

ToolResultBlock๏ผˆๅทฅๅ…ท็ป“ๆžœๅ—๏ผ‰

@dataclass
class ToolResultBlock:
    """
    ๅทฅๅ…ท็ป“ๆžœ็š„ๅ†…ๅฎนๅ—ใ€‚
    ๅฏนๅบ” Claude Code ๆบ็ : src/context.ts ~ToolResultBlock~
    
    ็”จไบŽๅฐ†ๅทฅๅ…ทๆ‰ง่กŒ็ป“ๆžœๆ ผๅผๅŒ–ๅŽ่ฟ”ๅ›ž็ป™ LLMใ€‚
    """
    type: str = "tool_result"
    tool_use_id: str = ""       # ๅฏนๅบ”็š„ tool_call id
    content: str = ""           # ๆ‰ง่กŒ็ป“ๆžœๅ†…ๅฎน

3.3 AgentContext ๆ ธๅฟƒๆ“ไฝœ

class AgentContext:
    """
    ๅฏนๅบ” Claude Code ๆบ็ : src/context.ts ~Context~ ็ฑป
    
    ่Œ่ดฃ๏ผš
    1. ๅญ˜ๅ‚จๆถˆๆฏๅކๅฒ๏ผˆ_messages ๅˆ—่กจ๏ผ‰
    2. ๅทฅๅ…ท็ป“ๆžœ็ผ“ๅญ˜๏ผˆ_tool_results ๅญ—ๅ…ธ๏ผ‰
    3. Token ่ฎกๆ•ฐไธŽไผฐ็ฎ—
    4. ไธŠไธ‹ๆ–‡ๆˆชๆ–ญ๏ผˆๅฝ“่ถ…่ฟ‡้™ๅˆถๆ—ถ๏ผ‰
    """
    
    DEFAULT_MAX_TOKENS = 200_000  # Claude ไธŠไธ‹ๆ–‡็ช—ๅฃ็บฆ 200K
    TOKEN_ESTIMATE_CHARS = 4     # ๅค‡็”จไผฐ็ฎ—๏ผš1 token โ‰ˆ 4 ๅญ—็ฌฆ
    
    def __init__(self, system_prompt: str = None):
        self._messages: List[Message] = []
        self._tool_results: Dict[str, str] = {}  # tool_id โ†’ result
        self.max_tokens = self.DEFAULT_MAX_TOKENS
        self._encoder = None
        
        # ๅˆๅง‹ๅŒ– tiktoken๏ผˆ็ฒพ็กฎ token ่ฎกๆ•ฐ๏ผ‰
        try:
            self._encoder = tiktoken.get_encoding("cl100k_base")
        except:
            pass  # ๅ›ž้€€ๅˆฐๅญ—็ฌฆไผฐ็ฎ—

ๆทปๅŠ ๆถˆๆฏ

def add_user_message(self, content: str) -> None:
    """ๆทปๅŠ ็”จๆˆทๆถˆๆฏ"""
    self._messages.append(Message(role=MessageRole.USER, content=content))

def add_assistant_message(self, content: str, tool_calls: List[ToolCall] = None):
    """ๆทปๅŠ ๅŠฉๆ‰‹ๆถˆๆฏ๏ผˆๅฏๅธฆๅทฅๅ…ท่ฐƒ็”จ๏ผ‰"""
    tc_dicts = [tc.to_dict() for tc in tool_calls] if tool_calls else None
    self._messages.append(Message(
        role=MessageRole.ASSISTANT,
        content=content,
        tool_calls=tc_dicts
    ))

def add_tool_result(self, content: str, tool_call_id: str) -> None:
    """ๆทปๅŠ ๅทฅๅ…ท็ป“ๆžœ"""
    self._tool_results[tool_call_id] = content
    self._messages.append(Message(
        role=MessageRole.TOOL,
        content=content,
        tool_call_id=tool_call_id
    ))

3.4 Token ไผฐ็ฎ—็ณป็ปŸ

def estimate_tokens(self, text: str) -> int:
    """
    ็ฒพ็กฎไผฐ็ฎ— token ๆ•ฐ้‡ใ€‚
    
    ไฝฟ็”จ tiktoken๏ผˆcl100k_base็ผ–็ ๅ™จ๏ผ‰๏ผŒ่ฟ™ๆ˜ฏ Claude ๅ…ผๅฎน็š„็ผ–็ ใ€‚
    1 token โ‰ˆ 4 ๅญ—็ฌฆ๏ผˆ่‹ฑๆ–‡๏ผ‰๏ผŒไธญๆ–‡็บฆ 1-2 ๅญ—็ฌฆ/token
    """
    if self._encoder:
        return len(self._encoder.encode(text))
    return len(text) // self.TOKEN_ESTIMATE_CHARS  # ๅค‡็”จไผฐ็ฎ—

def estimate_total_tokens(self) -> int:
    """ไผฐ็ฎ—ๆ•ดไธชไธŠไธ‹ๆ–‡็š„ๆ€ป token ๆ•ฐ"""
    total = 0
    for msg in self._messages:
        total += self.estimate_tokens(msg.content)
    return total

Token ไผฐ็ฎ—็คบไพ‹๏ผš

่พ“ๅ…ฅ: "Hello, world!"
็ผ–็ : [15339, 11, 1917, 0]
Token ๆ•ฐ: 4

่พ“ๅ…ฅ: "# Title\n่ฟ™ๆ˜ฏไธญๆ–‡ๅ†…ๅฎน"
็ผ–็ : [35, 5767, 4, 24826, 21487, ...]
Token ๆ•ฐ: ~15

3.5 ไธŠไธ‹ๆ–‡ๆˆชๆ–ญ๏ผˆๆ™บ่ƒฝ็ฎก็†๏ผ‰

def truncate_if_needed(self, keep_recent: int = 20) -> List[Message]:
    """
    ๅฝ“ไธŠไธ‹ๆ–‡่ถ…้™ๆ—ถ๏ผŒๆ™บ่ƒฝๆˆชๆ–ญใ€‚
    
    ็ญ–็•ฅ๏ผš
    1. ไฟ็•™็ณป็ปŸๆถˆๆฏ๏ผˆๆฐธไธๅŠจ๏ผ‰
    2. ไฟ็•™ๆœ€่ฟ‘ N ๆกๆถˆๆฏ
    3. ไปŽไธญ้—ดๅผ€ๅง‹ๅˆ ้™คๆœ€ๆ—ง็š„ๆถˆๆฏ
    
    ๅฏนๅบ” Claude Code ๆบ็ : src/context.ts ~truncateContext~
    
    ไธบไป€ไนˆ่ฟ™ๆ ท่ฎพ่ฎก๏ผŸ
    - ็ณป็ปŸๆ็คบๆ˜ฏ Agent ็š„"ไธชๆ€ง"๏ผŒๅฟ…้กปไฟ็•™
    - ๆœ€่ฟ‘็š„ๆถˆๆฏๅฏนๅฝ“ๅ‰ไปปๅŠกๆœ€็›ธๅ…ณ
    - ๆ—ฉๆœŸๆถˆๆฏๅฏ่ƒฝ่ขซ"้—ๅฟ˜"ๅฝฑๅ“ๆœ€ๅฐ
    """
    removed = []
    
    while self.estimate_total_tokens() > self.max_tokens and len(self._messages) > 2:
        # ่ทณ่ฟ‡็ณป็ปŸๆถˆๆฏ๏ผˆindex 0๏ผ‰๏ผŒไปŽๆœ€ๆ—ง็š„็”จๆˆท/ๅŠฉๆ‰‹ๆถˆๆฏๅผ€ๅง‹ๅˆ 
        for i, msg in enumerate(self._messages):
            if msg.role != MessageRole.SYSTEM:
                removed.append(self._messages.pop(i))
                break
    
    return removed  # ่ฟ”ๅ›ž่ขซๅˆ ้™ค็š„ๆถˆๆฏ๏ผˆๅฏ่ฎฐๅฝ•ๆ—ฅๅฟ—๏ผ‰

ๆˆชๆ–ญ็คบๆ„ๅ›พ๏ผš

ๆˆชๆ–ญๅ‰๏ผˆๅ‡่ฎพ่ถ…้™๏ผ‰:
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Sys  โ”‚ [System] You are Claude Code...               โ”‚ โ—„โ”€โ”€ ไฟ็•™
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ Msg  โ”‚ [User] Read ./README.md                       โ”‚
โ”‚ Msg  โ”‚ [Assistant] I'll read it...                   โ”‚
โ”‚ Msg  โ”‚ [Tool] file content...                        โ”‚โ”€โ”€โ–บ ่ขซๅˆ ้™ค
โ”‚ Msg  โ”‚ [Assistant] The file shows...                 โ”‚โ”€โ”€โ–บ ่ขซๅˆ ้™ค
โ”‚ Msg  โ”‚ [User] Now edit main.py                       โ”‚โ”€โ”€โ–บ ่ขซๅˆ ้™ค
โ”‚ Msg  โ”‚ [Assistant] I'll edit...                      โ”‚ โ—„โ”€โ”€ ไฟ็•™
โ”‚ Msg  โ”‚ [Tool] edit complete                          โ”‚ โ—„โ”€โ”€ ไฟ็•™
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

ๆˆชๆ–ญๅŽ:
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Sys  โ”‚ [System] You are Claude Code...               โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ Msg  โ”‚ [Assistant] I'll edit...                      โ”‚
โ”‚ Msg  โ”‚ [Tool] edit complete                          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

3.6 ๆถˆๆฏๆ ผๅผๅŒ–๏ผˆAPI ๅฏนๆŽฅ๏ผ‰

def get_messages(self) -> List[Dict[str, Any]]:
    """
    ๅฐ†ๆถˆๆฏๆ ผๅผๅŒ–ไธบ LLM API ๆ ผๅผใ€‚
    
    ๅฏนๅบ” Claude Code ๆบ็ : src/context.ts ~formatMessages~
    
    ่ฟ”ๅ›žๆ ผๅผ็ฌฆๅˆ Anthropic API ่ฆๆฑ‚๏ผš
    [
        { "role": "system", "content": "..." },
        { "role": "user", "content": "..." },
        { "role": "assistant", "content": "...", "tool_calls": [...] },
        { "role": "tool", "content": "...", "tool_call_id": "..." }
    ]
    """
    return [msg.to_dict() for msg in self._messages]

3.7 ไธŠไธ‹ๆ–‡็ณป็ปŸไธŽ QueryEngine ็š„ๅไฝœ

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                        QueryEngine.run()                        โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                โ”‚
                                โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  1. ็”จๆˆท่พ“ๅ…ฅ                                                     โ”‚
โ”‚     engine.context.add_user_message("Read ./README.md")         โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                โ”‚
                                โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  2. ้ฆ–ๆฌก LLM ่ฐƒ็”จ                                                โ”‚
โ”‚     messages = context.get_messages()  # ่Žทๅ–ๆ ผๅผๅŒ–ๆถˆๆฏ           โ”‚
โ”‚     response = await call_llm(messages, tools)                   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                โ”‚
                                โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  3. LLM ่ฟ”ๅ›ž tool_call                                           โ”‚
โ”‚     context.add_assistant_message(response.content, tool_calls)   โ”‚
โ”‚                                                                  โ”‚
โ”‚     # ๆญคๆ—ถไธŠไธ‹ๆ–‡ๅทฒๅŒ…ๅซ:                                           โ”‚
โ”‚     # - System                                                    โ”‚
โ”‚     # - User: Read ./README.md                                   โ”‚
โ”‚     # - Assistant: I'll read it... + tool_calls                 โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                โ”‚
                                โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  4. ๆ‰ง่กŒๅทฅๅ…ท                                                      โ”‚
โ”‚     result = await tool.execute(input)                          โ”‚
โ”‚     context.add_tool_result(result, tool_call_id)               โ”‚
โ”‚                                                                  โ”‚
โ”‚     # ไธŠไธ‹ๆ–‡ๆ–ฐๅขž:                                                 โ”‚
โ”‚     # - Tool: file content (tool_call_id: tool_1)               โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                โ”‚
                                โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  5. ๅ†ๆฌก LLM ่ฐƒ็”จ๏ผˆๅธฆไธŠๅทฅๅ…ท็ป“ๆžœ๏ผ‰                                  โ”‚
โ”‚     # LLM ็œ‹ๅˆฐๅทฅๅ…ท็ป“ๆžœๅŽ๏ผŒ็ป™ๅ‡บๆœ€็ปˆๅ›žๅค                            โ”‚
โ”‚     context.add_assistant_message("The file contains...")       โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                โ”‚
                                โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  6. Token ๆฃ€ๆŸฅ                                                    โ”‚
โ”‚     if context.estimate_total_tokens() > context.max_tokens:     โ”‚
โ”‚         context.truncate_if_needed()  # ๆ™บ่ƒฝๆˆชๆ–ญ                 โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

3.8 ไธŽ Claude Code ๆบ็ ็š„็ฒพ็กฎๅฏนๅบ”

ๆœฌ้กน็›ฎ Claude Code ๆบ็  ่ฏดๆ˜Ž
MessageRole src/context.ts ~Role~ ๆถˆๆฏ่ง’่‰ฒๆžšไธพ
Message src/context.ts ~Message~ ๅ•ๆกๆถˆๆฏ
AgentContext src/context.ts ~Context~ ไธŠไธ‹ๆ–‡็ฎก็†ๅ™จ
ToolCall src/context.ts ~ToolCall~ ๅทฅๅ…ท่ฐƒ็”จๅฏน่ฑก
ToolResultBlock src/context.ts ~ToolResultBlock~ ๅทฅๅ…ท็ป“ๆžœๅ—
get_messages() src/context.ts ~formatMessages~ ๆ ผๅผๅŒ– API ๆถˆๆฏ
truncate_if_needed() src/context.ts ~truncateContext~ ไธŠไธ‹ๆ–‡ๆˆชๆ–ญ

ๅ››ใ€Tool ็ณป็ปŸ่ฏฆ่งฃ

4.1 Tool ๆŠฝ่ฑกๅŸบ็ฑป

class Tool(ABC):
    """ๆ‰€ๆœ‰ๅทฅๅ…ท็š„ๅŸบ็ฑป๏ผŒๅฎšไน‰็ปŸไธ€ๆŽฅๅฃ"""
    
    name: str = ""              # ๅทฅๅ…ทๅ”ฏไธ€ๆ ‡่ฏ†
    description: str = ""       # ไพ› LLM ็†่งฃ็š„ๆ่ฟฐ
    permission: Permission = Permission()  # ๆƒ้™้…็ฝฎ
    
    @abstractmethod
    def get_input_schema(self) -> Dict[str, Any]:
        """่ฟ”ๅ›ž JSON Schema๏ผŒLLM ๆฎๆญคๆž„้€ ่ฐƒ็”จๅ‚ๆ•ฐ"""
        raise NotImplementedError
    
    @abstractmethod
    async def execute(self, input_data: Dict[str, Any]) -> ToolResult:
        """ๆ ธๅฟƒๆ‰ง่กŒ้€ป่พ‘"""
        raise NotImplementedError

4.2 ๆƒ้™ๆจกๅž‹๏ผˆไธ‰็บงๆƒ้™๏ผ‰

class PermissionMode(Enum):
    ASK = "ask"           # ๆฏๆฌกๆ‰ง่กŒๅ‰่ฏข้—ฎ็”จๆˆท
    AUTOMATIC = "automatic" # ่‡ชๅŠจๆ‰ง่กŒ๏ผˆๅฏไฟกๆ“ไฝœ๏ผ‰
    NEVER = "never"       # ็ฆ็”จ

class PermissionScope(Enum):
    READ = "read"         # ๅช่ฏปๆ“ไฝœ
    WRITE = "write"       # ๅ†™ๆ“ไฝœ
    NETWORK = "network"   # ็ฝ‘็ปœ่ฎฟ้—ฎ
    ENVIRONMENT = "environment"  # ็Žฏๅขƒๅ˜้‡
    ALL = "all"           # ๅ…จ้ƒจๆƒ้™

4.3 ๅ†…็ฝฎๅทฅๅ…ทไธ€่งˆ

ๅทฅๅ…ท ๆ–‡ไปถ ๆ ธๅฟƒๅŠŸ่ƒฝ
BashTool tool/builtin/bash.py ๆ‰ง่กŒ shell ๅ‘ฝไปค
FileReadTool tool/builtin/file_read.py ่ฏปๅ–ๆ–‡ไปถๅ†…ๅฎน
FileEditTool tool/builtin/file_edit.py Search-replace ็ผ–่พ‘
GlobTool tool/builtin/glob.py ๆจกๅผๅŒน้…ๆ–‡ไปถ

4.4 FileEditTool ่ฏฆ่งฃ๏ผˆSearch-Replace ๆจกๅผ๏ผ‰

class FileEditTool(Tool):
    """
    ้€š่ฟ‡ 'old_text' ็ฒพ็กฎๅŒน้…ๅฎž็Žฐๆ–‡ไปถ็ผ–่พ‘
    LLM ๅฟ…้กปๆไพ› exact old_text ๆฅๅฎšไฝ็ผ–่พ‘ไฝ็ฝฎ
    """
    
    async def execute(self, input_data: Dict[str, Any]) -> ToolResult:
        file_path = input_data["file_path"]
        old_text = input_data["old_text"]
        new_text = input_data.get("new_text", "")
        
        # 1. ่ฏปๅ–ๅŽŸๆ–‡ไปถ
        with open(file_path, "r", encoding="utf-8") as f:
            content = f.read()
        
        # 2. ้ชŒ่ฏ old_text ็ฒพ็กฎๅŒน้…
        if old_text not in content:
            return ToolResult.err(f"old_text not found in file")
        
        # 3. ๆ‰ง่กŒๆ›ฟๆข
        new_content = content.replace(old_text, new_text, 1)
        
        # 4. ๅ†™ๅ›žๆ–‡ไปถ
        with open(file_path, "w", encoding="utf-8") as f:
            f.write(new_content)
        
        return ToolResult.ok(f"Edited {file_path}")

ไบ”ใ€ToolRegistry ๆณจๅ†Œ่กจๆจกๅผ

5.1 ๆ ธๅฟƒ่ฎพ่ฎก

class ToolRegistry:
    """ๅ…จๅฑ€ๅทฅๅ…ทๆณจๅ†Œ่กจ๏ผˆๅ•ไพ‹ๆจกๅผ๏ผ‰"""
    
    def __init__(self):
        self._tools: Dict[str, Tool] = {}
    
    def register(self, tool: Tool) -> None:
        """ๆณจๅ†Œๅทฅๅ…ท"""
        self._tools[tool.name] = tool
    
    def get(self, name: str) -> Optional[Tool]:
        """ๆ นๆฎๅ็งฐ่Žทๅ–ๅทฅๅ…ท"""
        return self._tools.get(name)
    
    def get_llm_tools(self) -> List[Dict[str, Any]]:
        """่Žทๅ–ๆ‰€ๆœ‰ๅทฅๅ…ท็š„ LLM ๅฎšไน‰"""
        return [tool.get_metadata() for tool in self._tools.values()]

5.2 ๆณจๅ†Œๆต็จ‹

# main.py ๅฏๅŠจๆ—ถ
registry = ToolRegistry()

# ๆณจๅ†Œๅ†…็ฝฎๅทฅๅ…ท
registry.register(FileReadTool())
registry.register(FileEditTool())
registry.register(BashTool())
registry.register(GlobTool())

# ๅฐ†ๆณจๅ†Œ่กจๆณจๅ…ฅ QueryEngine
engine = QueryEngine(tool_registry=registry)

ๅ…ญใ€ไธŽ Claude Code ๆบ็ ็š„ๅฏนๅบ”ๅ…ณ็ณป

ๆœฌ้กน็›ฎ Claude Code ๆบ็  ่ฏดๆ˜Ž
agent/query_engine.py src/query.ts ๆ ธๅฟƒ QueryEngine ็ฑป
agent/context.py src/context.ts Context ็ฑป + Message
tool/base.py src/Tool.ts Tool ๆŠฝ่ฑก็ฑป + PermissionModel
tool/registry.py src/tools.ts ๅทฅๅ…ทๆณจๅ†Œ่กจ
tool/builtin/*.py src/tools/*.ts ๅ†…็ฝฎๅทฅๅ…ทๅฎž็Žฐ

Claude Code ๅฎŒๆ•ดๆจกๅ—ๆ ‘๏ผˆ~1900 ๆ–‡ไปถ๏ผ‰

src/
โ”œโ”€โ”€ main.tsx              # CLI ๅ…ฅๅฃ
โ”œโ”€โ”€ query.ts              # QueryEngine ๆ ธๅฟƒ
โ”œโ”€โ”€ context.ts            # ไธŠไธ‹ๆ–‡็ฎก็†
โ”œโ”€โ”€ Tool.ts               # Tool ๅŸบ็ฑป
โ”œโ”€โ”€ tools.ts              # ๅทฅๅ…ทๆณจๅ†Œ่กจ
โ”œโ”€โ”€ tools/                # ๅ†…็ฝฎๅทฅๅ…ท (~40 ไธช)
โ”‚   โ”œโ”€โ”€ BashTool.ts
โ”‚   โ”œโ”€โ”€ FileReadTool.ts
โ”‚   โ”œโ”€โ”€ FileEditTool.ts
โ”‚   โ”œโ”€โ”€ GlobTool.ts
โ”‚   โ”œโ”€โ”€ GrepTool.ts
โ”‚   โ”œโ”€โ”€ WebSearchTool.ts
โ”‚   โ”œโ”€โ”€ AgentTool.ts      # ๅญ Agent
โ”‚   โ”œโ”€โ”€ TaskTool.ts       # ไปปๅŠก็ฎก็†
โ”‚   โ””โ”€โ”€ ...
โ”œโ”€โ”€ state/                # Zustand ็Šถๆ€็ฎก็†
โ”œโ”€โ”€ coordinator/          # ๅคš Agent ๅไฝœ
โ”œโ”€โ”€ skill/                # Skill ็ณป็ปŸ
โ”œโ”€โ”€ mcp/                  # MCP ๅ่ฎฎ
โ””โ”€โ”€ hooks/                # ้’ฉๅญ็ณป็ปŸ

ไธƒใ€่ฎพ่ฎกๆจกๅผๆ€ป็ป“

ๆจกๅผ ๅบ”็”จๅœบๆ™ฏ
Template Method Tool ๅŸบ็ฑปๅฎšไน‰้ชจๆžถ๏ผŒๅญ็ฑปๅฎž็Žฐ execute()
Strategy ไธๅŒๅทฅๅ…ทๅฐ่ฃ…ไธๅŒ็ญ–็•ฅ
Registry/Singleton ๅ…จๅฑ€ ToolRegistry
Command ๆฏไธช Tool ๆ˜ฏไธ€ไธชๅฏๆ‰ง่กŒ็š„ๅ‘ฝไปคๅฏน่ฑก
Observer ่ฟ›ๅบฆๆก็›‘ๅฌๅทฅๅ…ทๆ‰ง่กŒ็Šถๆ€
Factory Tool ๅญ็ฑปๅฎžไพ‹ๅŒ–

ๅ…ซใ€ๅพ…ๅฎŒๆˆๆจกๅ—

8.1 Memory ๆจกๅ—๏ผˆ่ฎฐๅฟ†็ณป็ปŸ๏ผ‰

ๆ–‡ไปถ ๅŠŸ่ƒฝ
memory/base.py ่ฎฐๅฟ†ๆŠฝ่ฑกๅŸบ็ฑป
memory/session.py ไผš่ฏ่ฎฐๅฟ†็ฎก็†
memory/compact.py ไธŠไธ‹ๆ–‡ๅŽ‹็ผฉ

8.2 Hook ๆจกๅ—๏ผˆ้’ฉๅญ็ณป็ปŸ๏ผ‰

ๆ–‡ไปถ ๅŠŸ่ƒฝ
hook/base.py ้’ฉๅญๆŠฝ่ฑกๅŸบ็ฑป
hook/registry.py ้’ฉๅญๆณจๅ†Œ่กจ
hook/events.py ไบ‹ไปถ็ฑปๅž‹ๅฎšไน‰
hook/builtin/session_memory.py ไผš่ฏ่ฎฐๅฟ†้’ฉๅญ
hook/builtin/command_logger.py ๅ‘ฝไปคๆ—ฅๅฟ—้’ฉๅญ
hook/builtin/boot_md.py ๅฏๅŠจ้’ฉๅญ

ไนใ€่ฟ่กŒๆ–นๅผ

cd claaude-code-python

pip install -r requirements.txt
set ANTHROPIC_API_KEY=sk-ant-...   # Windows
# export ANTHROPIC_API_KEY=sk-ant-...  # Linux/Mac

# ่ฟ่กŒ
python main.py "Read ./README.md and explain"
python main.py list-tools

ๆŠฅๅ‘Š็”Ÿๆˆๆ—ถ้—ด: 2026-04-01 ๆœ€ๅŽๆ›ดๆ–ฐ: 2026-04-01 05:22