Claudeに新しいツール「テキストエディター」ツールが追加されました。
ユースケースとして以下のものが挙げられています。
- コードのデバッグ
- コードのリファクタリング
- ドキュメント生成
- テスト生成
ん?これは、今流行りのClineですね!
ということで、この新しいツールを使ってみて、Clineっぽいものができるのか、検証しました。
環境
ドキュメント記載内容に沿って進めることにしましたので、Anthropic SDKを使いつつ、呼び出すのはAmazon BedrockのClaude 3.7 Sonnetとします。
実装手順
まず、ライブラリーをインストールします
pip install anthropic boto3
まず、ドキュメントに従い、APIを呼び出す部分を作成します。
clientを生成する部分のみ、Bedrockを使用する形式に変更しました。
私のAWSアカウントはClaude 3.7 Sonnetのクオーターが低く設定されてしまっているため、リトライ回数を多めにしています
import anthropic
client = anthropic.AnthropicBedrock(aws_region="us-east-1", max_retries=10) # ※1
response = client.messages.create(
model="claude-3-7-sonnet-20250219",
max_tokens=1024,
tools=[
{
"type": "text_editor_20250124",
"name": "str_replace_editor"
}
],
messages=[
{
"role": "user",
"content": "There's a syntax error in my primes.py file. Can you help me fix it?"
}
]
)
toolsの指定が見慣れない形をしていますね。
通常、ツールを指定する場合は以下のように定義情報を指定しますが、テキストエディターツールの場合はツール定義の代わりにtype
とname
を指定します。
tools=[
{
"name": "get_weather",
"description": "Get the current weather in a given location",
"input_schema": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. San Francisco, CA",
}
},
"required": ["location"],
},
}
],
テキストエディターツールは、以下の5つの機能(コマンド)で構成されています。
-
viewコマンド
ファイルの内容を要求するツール。オプションで取得する行数範囲が指定される場合がある -
str_replaceコマンド
ファイルの文字列置換を要求する機能 -
createコマンド
ファイルの新規作成を要求する機能 -
insertコマンド
ファイルの特定の位置への文字列追加を要求する機能 -
undo_editコマンド
最後の修正をもとに戻すことを要求する機能
テキストエディターツールが正確に動作するように、Claudeのモデルのファインチューニングをしているのではないかな?と、推測しています。
Claudeがテキストエディターツールを使用すると判断した際には、以下のようなtool_use
ブロックがレスポンスに含まれます。
{
"type": "tool_use",
"id": "toolu_01AbCdEfGhIjKlMnOpQrStU",
"name": "str_replace_editor",
"input": {
"command": "view",
"path": "primes.py"
}
}
ツール呼び出しを要求された後のツール処理は自前で実装する必要があります。
ドキュメントでは以下のようなhandle_editor_tool
関数の記述があります。
が、
def handle_editor_tool(tool_call):
input_params = tool_call.input
command = input_params.get('command', '')
file_path = input_params.get('path', '')
if command == 'view':
# Read and return file contents
pass
elif command == 'str_replace':
# Replace text in file
pass
elif command == 'create':
# Create new file
pass
elif command == 'insert':
# Insert text at location
pass
elif command == 'undo_edit':
# Restore from backup
pass
。。。
なんと、肝心な部分の実装がありません😅
実装できなくはなさそうですが、なんやかんやめんどくさそう(?)なので、参考になるものを探しました。
で、見つけました!
AnthropicがGitHubに公開しているQuickstartsに、Computer useのデモが含まれています。
実はComputer useの機能の一部としてテキストエディターツールが含まれているため、その実装を拝借することにしました。
computer_use_demo/tools以下を丸ごとコピーしてきます。
.
├── app.py
└── tools
├── base.py
├── bash.py
├── collection.py
├── computer.py
├── edit.py
├── groups.py
├── __init__.py
└── run.py
handle_editor_tool
関数を作成します。
from tools.edit import CLIResult, EditTool20250124, ToolResult
async def handle_editor_tool(tool_call) -> CLIResult | ToolResult:
"""
テキストエディタツールを実行し、その結果を返します。
"""
input_params = tool_call.input
command = input_params.get("command", "")
file_path = input_params.get("path", "")
file_text = input_params.get("file_text")
old_str = input_params.get("old_str")
new_str = input_params.get("new_str")
insert_line = input_params.get("insert_line")
view_range = input_params.get("view_range")
editor = EditTool20250124()
try:
result = await editor(
command=command,
path=file_path,
file_text=file_text,
old_str=old_str,
new_str=new_str,
insert_line=insert_line,
view_range=view_range,
)
return result
except Exception as e:
return ToolResult(output=str(e))
あとは、ツール呼び出しが繰り返し要求されることを想定し、ループ処理を記述します。
最終、完成したのがこのようになります。
import asyncio
import anthropic
from tools.edit import CLIResult, EditTool20250124, ToolResult
client = anthropic.AnthropicBedrock(aws_region="us-east-1", max_retries=10)
async def handle_editor_tool(tool_call) -> CLIResult | ToolResult:
"""
テキストエディタツールを実行し、その結果を返します。
"""
input_params = tool_call.input
command = input_params.get("command", "")
file_path = input_params.get("path", "")
file_text = input_params.get("file_text")
old_str = input_params.get("old_str")
new_str = input_params.get("new_str")
insert_line = input_params.get("insert_line")
view_range = input_params.get("view_range")
editor = EditTool20250124()
try:
result = await editor(
command=command,
path=file_path,
file_text=file_text,
old_str=old_str,
new_str=new_str,
insert_line=insert_line,
view_range=view_range,
)
return result
except Exception as e:
return ToolResult(output=str(e))
async def main(content):
messages = []
messages.append(
{
"role": "user"
"content": content,
}
)
while True:
# Claude 3.7 Sonnetに呼び出し
response = client.messages.create(
model="us.anthropic.claude-3-7-sonnet-20250219-v1:0",
max_tokens=10240,
tools=[
{"type": "text_editor_20250124", "name": "str_replace_editor"},
],
messages=messages,
)
# 応答がtool_useでない場合、出力を表示してループを終了します。
if not response.stop_reason == "tool_use":
# 出力してBreak
for content in response.content:
if content.type == "text":
print(content.text)
if content.type == "thinking":
print(content.thinking)
if content.type == "redacted_thinking":
print(content.data)
break
# tool_useの場合、ツールの結果を処理します。
tool_result_message = {"role": "user", "content": []}
for content in response.content:
if content.type == "text":
print(content.text)
if content.type == "thinking":
print(content.thinking)
if content.type == "redacted_thinking":
print(content.data)
if content.type == "tool_use":
# Execute the tool based on command
result = await handle_editor_tool(content)
print(result.output)
# Return result to Claude
tool_result = {
"type": "tool_result",
"tool_use_id": content.id,
"content": result.output,
}}
tool_result_message["content"].append(tool_result)
messages.append({"role": "assistant", "content": response.content})
messages.append(tool_result_message)
if __name__ == "__main__":
prompt = "There's a syntax error in my primes.py file. Can you help me fix it?"
print(f"入力: {prompt}")
asyncio.run(main(prompt))
Clineっぽいことができるかな?
早速やってみましょう。
Promptを以下のように指定しました。
primes.pyにシンタックスエラーがあります。修正してください。現在のディレクトリは`{os.getcwd()}`です。
何度か試したところ、パスの情報を渡さないと期待動作にならないことがわかったので、カレントディレクトリを渡すようにしました
ちなみに仕込んだシンタックスエラーは以下の3つです。
IDEのエラー表示がなかったら、目Grep
でも結構しんどやつです。
実行してみましょう。
😃ユーザー入力:
primes.pyにシンタックスエラーがあります。修正してください。現在のディレクトリは/workspaces/2503_cline/claude-new-tool
です。
🤖Claude:
シンタックスエラーがあるというprimes.py
ファイルを確認し、修正するために、まずファイルの内容を確認しましょう。
Tool実行結果
Here's the result of running `cat -n` on /workspaces/2503_cline/claude-new-tool/primes.py:
1 import asyncio
2
3 import anthropic
4
5 from tools.edit import CLIResult, EditTool20250124, ToolResult
6
7 client = anthropic.AnthropicBedrock(aws_region="us-east-1", max_retries=10)
8
9
10 async def handle_editor_tool(tool_call) -> CLIResult | ToolResult:
11 """
12 テキストエディタツールを実行し、その結果を返します。
13 """
14 input_params = tool_call.input
15 command = input_params.get("command", "")
16 file_path = input_params.get("path", "")
17 file_text = input_params.get("file_text")
18 old_str = input_params.get("old_str")
19 new_str = input_params.get("new_str")
20 insert_line = input_params.get("insert_line")
21 view_range = input_params.get("view_range")
22
23 editor = EditTool20250124()
24
25 try:
26 result = await editor(
27 command=command,
28 path=file_path,
29 file_text=file_text,
30 old_str=old_str,
31 new_str=new_str,
32 insert_line=insert_line,
33 view_range=view_range,
34 )
35 return result
36 except Exception as e:
37 return ToolResult(output=str(e))
38
39
40 async def main(content):
41 messages = []
42 messages.append(
43 {
44 "role": "user"
45 "content": content,
46 }
47 )
48
49 while True:
50 # Claude 3.7 Sonnetに呼び出し
51 response = client.messages.create(
52 model="us.anthropic.claude-3-7-sonnet-20250219-v1:0",
53 max_tokens=10240,
54 tools=[
55 {"type": "text_editor_20250124", "name": "str_replace_editor"},
56 ],
57 messages=messages,
58 )
59
60 # 応答がtool_useでない場合、出力を表示してループを終了します。
61 if not response.stop_reason == "tool_use":
62
63 # 出力してBreak
64 for content in response.content:
65 if content.type == "text":
66 print(content.text)
67 if content.type == "thinking":
68 print(content.thinking)
69 if content.type == "redacted_thinking":
70 print(content.data)
71
72 break
73
74 # tool_useの場合、ツールの結果を処理します。
75 tool_result_message = {"role": "user", "content": []}
76
77 for content in response.content:
78 if content.type == "text":
79 print(content.text)
80 if content.type == "thinking":
81 print(content.thinking)
82 if content.type == "redacted_thinking":
83 print(content.data)
84 if content.type == "tool_use":
85 # Execute the tool based on command
86 result = asyncio.run(handle_editor_tool(content))
87
88 print(result.output)
89
90 # Return result to Claude
91 tool_result = {
92 "type": "tool_result",
93 "tool_use_id": content.id,
94 "content": result.output,
95 }}
96 tool_result_message["content"].append(tool_result)
97
98 messages.append({"role": "assistant", "content": response.content})
99 messages.append(tool_result_message)
100
101
102 if __name__ == "__main__":
103
104 prompt = "There's a syntax error in my primes.py file. Can you help me fix it?"
105
106 print(f"入力: {prompt}")
107
108 asyncio.run(main(prompt))
109
🤖Claude:
コードを確認したところ、いくつかのシンタックスエラーがあります。修正が必要な箇所は以下の通りです:
- 35行目と36行目の間でインデントエラー -
try
ブロックの後にreturn
がきてからexcept
が来ています。- 44行目の
"role": "user"
の後にカンマが抜けています。- 95行目の閉じ括弧が余分になっています。
これらのエラーを修正します:
Tool実行結果
The file /workspaces/2503_cline/claude-new-tool/primes.py has been edited. Here's the result of running `cat -n` on a snippet of /workspaces/2503_cline/claude-new-tool/primes.py:
21 view_range = input_params.get("view_range")
22
23 editor = EditTool20250124()
24
25 try:
26 result = await editor(
27 command=command,
28 path=file_path,
29 file_text=file_text,
30 old_str=old_str,
31 new_str=new_str,
32 insert_line=insert_line,
33 view_range=view_range,
34 )
35 return result
36 except Exception as e:
37 return ToolResult(output=str(e))
38
39
40 async def main(content):
41 messages = []
Review the changes and make sure they are as expected. Edit the file again if necessary.
Tool実行結果
The file /workspaces/2503_cline/claude-new-tool/primes.py has been edited. Here's the result of running `cat -n` on a snippet of /workspaces/2503_cline/claude-new-tool/primes.py:
38
39
40 async def main(content):
41 messages = []
42 messages.append(
43 {
44 "role": "user",
45 "content": content,
46 }
47 )
48
49 while True:
50 # Claude 3.7 Sonnetに呼び出し
51 response = client.messages.create(
Review the changes and make sure they are as expected. Edit the file again if necessary.
Tool実行結果
The file /workspaces/2503_cline/claude-new-tool/primes.py has been edited. Here's the result of running `cat -n` on a snippet of /workspaces/2503_cline/claude-new-tool/primes.py:
86 result = asyncio.run(handle_editor_tool(content))
87
88 print(result.output)
89
90 # Return result to Claude
91 tool_result = {
92 "type": "tool_result",
93 "tool_use_id": content.id,
94 "content": result.output,
95 }
96 tool_result_message["content"].append(tool_result)
97
98 messages.append({"role": "assistant", "content": response.content})
99 messages.append(tool_result_message)
100
Review the changes and make sure they are as expected. Edit the file again if necessary.
🤖Claude:
primes.py
のシンタックスエラーを修正しました。以下の3つの問題を解決しました:
インデントエラー(35-36行目):
try
ブロック内でreturn result
が正しくインデントされていなかったため、try
とexcept
の間に来ていましたreturn result
をtry
ブロック内に適切に配置しましたカンマの欠落(44行目):
- 辞書内の
"role": "user"
の後にカンマが欠けていました- カンマを追加して
"role": "user",
に修正しました余分な閉じ括弧(95行目):
tool_result
辞書定義の後に余分な閉じ括弧}
がありました- 余分な閉じ括弧を削除しました
これらの修正により、コードのシンタックスエラーは解決されました。プログラムが正常に実行できるようになりました。
すごい!!!
ですが、Claude 3.7 Sonnetにとってはお茶の子さいさいなのかもしれませんね。
念の為お伝えしておくと、変なところを見つけただけじゃなくて、修正まで完璧です
ちなみに
Anthropic SDKを使わずConverse APIで利用する場合は以下のようになります。
toolConfig
にtools
を指定するとバリデーションエラーになるので、additionalModelRequestFields
で指定する必要があります。
import boto3
client = boto3.client("bedrock-runtime")
response = client.converse(
modelId="us.anthropic.claude-3-7-sonnet-20250219-v1:0",
inferenceConfig={
"maxTokens": 10240,
},
messages=[
{
"role": "user",
"content": [
{
"text": "There's a syntax error in my primes.py file. Can you help me fix it?",
}
],
}
],
additionalModelRequestFields={
"tools": [
{"type": "text_editor_20250124", "name": "str_replace_editor"},
],
},
)